mirror of
https://github.com/lubosz/overte.git
synced 2025-04-23 09:33:45 +02:00
Merge branch 'master' into feature/dual-quat
This commit is contained in:
commit
7753b8a0b5
118 changed files with 4765 additions and 1229 deletions
|
@ -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(
|
||||
|
|
642
interface/resources/qml/CurrentAPI.qml
Normal file
642
interface/resources/qml/CurrentAPI.qml
Normal 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);
|
||||
}
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 |
|
@ -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;
|
||||
|
|
|
@ -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...");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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];
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -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 };
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,6 +28,8 @@ private:
|
|||
|
||||
AnimPose _prevHips; // sensor frame
|
||||
bool _prevHipsValid { false };
|
||||
|
||||
std::map<int, int> _jointRotationFrameOffsetMap;
|
||||
};
|
||||
|
||||
#endif // hifi_MySkeletonModel_h
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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);
|
||||
};
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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>,
|
||||
|
|
|
@ -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>,
|
||||
|
|
|
@ -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>,
|
||||
|
|
|
@ -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>,
|
||||
|
|
|
@ -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>,
|
||||
|
|
|
@ -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>,
|
||||
|
|
|
@ -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>,
|
||||
|
|
|
@ -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.
|
||||
*/
|
||||
|
|
|
@ -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>,
|
||||
|
|
|
@ -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>,
|
||||
|
|
|
@ -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>,
|
||||
|
|
|
@ -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>,
|
||||
|
|
|
@ -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>,
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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) :
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -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;
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -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 };
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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));
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
155
libraries/entities/src/AmbientLightPropertyGroup.cpp
Normal file
155
libraries/entities/src/AmbientLightPropertyGroup.cpp
Normal 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;
|
||||
}
|
|
@ -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
|
|
@ -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 {
|
||||
|
|
|
@ -469,6 +469,9 @@ public:
|
|||
static QString _marketplacePublicKey;
|
||||
static void retrieveMarketplacePublicKey();
|
||||
|
||||
signals:
|
||||
void requestRenderUpdate();
|
||||
|
||||
protected:
|
||||
QHash<ChangeHandlerId, ChangeHandlerCallback> _changeHandlers;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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, "");
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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 };
|
||||
|
|
|
@ -60,6 +60,7 @@ public:
|
|||
QVector<int> indices;
|
||||
QVector<glm::vec3> vertices;
|
||||
QVector<glm::vec3> normals;
|
||||
QVector<glm::vec3> tangents;
|
||||
};
|
||||
|
||||
struct FBXJointShapeInfo {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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 };
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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; }
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -202,6 +202,8 @@ enum class EntityVersion : PacketVersion {
|
|||
HazeEffect,
|
||||
StaticCertJsonVersionOne,
|
||||
OwnershipChallengeFix,
|
||||
ZoneLightInheritModes,
|
||||
ZoneStageRemoved
|
||||
};
|
||||
|
||||
enum class EntityScriptCallMethodVersion : PacketVersion {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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) :
|
||||
|
|
|
@ -67,7 +67,6 @@ public:
|
|||
NetworkTexturePointer _ambientTexture;
|
||||
QString _ambientTextureURL;
|
||||
bool _pendingAmbientTexture { false };
|
||||
bool _validAmbientTextureURL { false };
|
||||
|
||||
protected:
|
||||
model::LightPointer _light;
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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>;
|
||||
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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--;
|
||||
{
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
@ -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;
|
||||
|
|
|
@ -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"
|
||||
|
|
325
scripts/developer/sunModel.js
Normal file
325
scripts/developer/sunModel.js
Normal 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
|
||||
);
|
||||
};
|
||||
});
|
|
@ -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);
|
||||
|
|
BIN
scripts/system/assets/animations/Cheering.fbx
Normal file
BIN
scripts/system/assets/animations/Cheering.fbx
Normal file
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue