mirror of
https://github.com/overte-org/overte.git
synced 2025-08-08 20:36:49 +02:00
Merge remote-tracking branch 'upstream/master' into fix_radar_zoom_in
This commit is contained in:
commit
c26ae3592c
85 changed files with 1924 additions and 576 deletions
|
@ -470,7 +470,6 @@ void EntityServer::startDynamicDomainVerification() {
|
||||||
// Delete the entity if it doesn't pass static certificate verification
|
// Delete the entity if it doesn't pass static certificate verification
|
||||||
tree->deleteEntity(i.value(), true);
|
tree->deleteEntity(i.value(), true);
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance();
|
QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance();
|
||||||
QNetworkRequest networkRequest;
|
QNetworkRequest networkRequest;
|
||||||
networkRequest.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true);
|
networkRequest.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true);
|
||||||
|
@ -490,9 +489,13 @@ void EntityServer::startDynamicDomainVerification() {
|
||||||
|
|
||||||
if (networkReply->error() == QNetworkReply::NoError) {
|
if (networkReply->error() == QNetworkReply::NoError) {
|
||||||
if (jsonObject["domain_id"].toString() != thisDomainID) {
|
if (jsonObject["domain_id"].toString() != thisDomainID) {
|
||||||
qCDebug(entities) << "Entity's cert's domain ID" << jsonObject["domain_id"].toString()
|
if (entity->getAge() > (_MAXIMUM_DYNAMIC_DOMAIN_VERIFICATION_TIMER_MS/MSECS_PER_SECOND)) {
|
||||||
<< "doesn't match the current Domain ID" << thisDomainID << "; deleting entity" << i.value();
|
qCDebug(entities) << "Entity's cert's domain ID" << jsonObject["domain_id"].toString()
|
||||||
tree->deleteEntity(i.value(), true);
|
<< "doesn't match the current Domain ID" << thisDomainID << "; deleting entity" << i.value();
|
||||||
|
tree->deleteEntity(i.value(), true);
|
||||||
|
} else {
|
||||||
|
qCDebug(entities) << "Entity failed dynamic domain verification, but was created too recently to necessitate deletion:" << i.value();
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
qCDebug(entities) << "Entity passed dynamic domain verification:" << i.value();
|
qCDebug(entities) << "Entity passed dynamic domain verification:" << i.value();
|
||||||
}
|
}
|
||||||
|
|
|
@ -105,8 +105,6 @@ EntityScriptServer::~EntityScriptServer() {
|
||||||
static const QString ENTITY_SCRIPT_SERVER_LOGGING_NAME = "entity-script-server";
|
static const QString ENTITY_SCRIPT_SERVER_LOGGING_NAME = "entity-script-server";
|
||||||
|
|
||||||
void EntityScriptServer::handleReloadEntityServerScriptPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode) {
|
void EntityScriptServer::handleReloadEntityServerScriptPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode) {
|
||||||
// These are temporary checks until we can ensure that nodes eventually disconnect if the Domain Server stops telling them
|
|
||||||
// about each other.
|
|
||||||
if (senderNode->getCanRez() || senderNode->getCanRezTmp() || senderNode->getCanRezCertified() || senderNode->getCanRezTmpCertified()) {
|
if (senderNode->getCanRez() || senderNode->getCanRezTmp() || senderNode->getCanRezCertified() || senderNode->getCanRezTmpCertified()) {
|
||||||
auto entityID = QUuid::fromRfc4122(message->read(NUM_BYTES_RFC4122_UUID));
|
auto entityID = QUuid::fromRfc4122(message->read(NUM_BYTES_RFC4122_UUID));
|
||||||
|
|
||||||
|
@ -119,8 +117,6 @@ void EntityScriptServer::handleReloadEntityServerScriptPacket(QSharedPointer<Rec
|
||||||
}
|
}
|
||||||
|
|
||||||
void EntityScriptServer::handleEntityScriptGetStatusPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode) {
|
void EntityScriptServer::handleEntityScriptGetStatusPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode) {
|
||||||
// These are temporary checks until we can ensure that nodes eventually disconnect if the Domain Server stops telling them
|
|
||||||
// about each other.
|
|
||||||
if (senderNode->getCanRez() || senderNode->getCanRezTmp() || senderNode->getCanRezCertified() || senderNode->getCanRezTmpCertified()) {
|
if (senderNode->getCanRez() || senderNode->getCanRezTmp() || senderNode->getCanRezCertified() || senderNode->getCanRezTmpCertified()) {
|
||||||
MessageID messageID;
|
MessageID messageID;
|
||||||
message->readPrimitive(&messageID);
|
message->readPrimitive(&messageID);
|
||||||
|
@ -190,15 +186,14 @@ void EntityScriptServer::updateEntityPPS() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void EntityScriptServer::handleEntityServerScriptLogPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode) {
|
void EntityScriptServer::handleEntityServerScriptLogPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode) {
|
||||||
// These are temporary checks until we can ensure that nodes eventually disconnect if the Domain Server stops telling them
|
bool canRezAny = senderNode->getCanRez() || senderNode->getCanRezTmp() || senderNode->getCanRezCertified() || senderNode->getCanRezTmpCertified();
|
||||||
// about each other.
|
|
||||||
bool enable = false;
|
bool enable = false;
|
||||||
message->readPrimitive(&enable);
|
message->readPrimitive(&enable);
|
||||||
|
|
||||||
auto senderUUID = senderNode->getUUID();
|
auto senderUUID = senderNode->getUUID();
|
||||||
auto it = _logListeners.find(senderUUID);
|
auto it = _logListeners.find(senderUUID);
|
||||||
|
|
||||||
if (enable && senderNode->getCanRez()) {
|
if (enable && canRezAny) {
|
||||||
if (it == std::end(_logListeners)) {
|
if (it == std::end(_logListeners)) {
|
||||||
_logListeners.insert(senderUUID);
|
_logListeners.insert(senderUUID);
|
||||||
qCInfo(entity_script_server) << "Node" << senderUUID << "subscribed to log stream";
|
qCInfo(entity_script_server) << "Node" << senderUUID << "subscribed to log stream";
|
||||||
|
|
|
@ -1042,41 +1042,7 @@ void DomainServer::processListRequestPacket(QSharedPointer<ReceivedMessage> mess
|
||||||
|
|
||||||
bool DomainServer::isInInterestSet(const SharedNodePointer& nodeA, const SharedNodePointer& nodeB) {
|
bool DomainServer::isInInterestSet(const SharedNodePointer& nodeA, const SharedNodePointer& nodeB) {
|
||||||
auto nodeAData = static_cast<DomainServerNodeData*>(nodeA->getLinkedData());
|
auto nodeAData = static_cast<DomainServerNodeData*>(nodeA->getLinkedData());
|
||||||
auto nodeBData = static_cast<DomainServerNodeData*>(nodeB->getLinkedData());
|
return nodeAData && nodeAData->getNodeInterestSet().contains(nodeB->getType());
|
||||||
|
|
||||||
// if we have no linked data for node A then B can't possibly be in the interest set
|
|
||||||
if (!nodeAData) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// first check if the general interest set A contains the type for B
|
|
||||||
if (nodeAData->getNodeInterestSet().contains(nodeB->getType())) {
|
|
||||||
// given that there is a match in the general interest set, do any special checks
|
|
||||||
|
|
||||||
// (1/19/17) Agents only need to connect to Entity Script Servers to perform administrative tasks
|
|
||||||
// related to entity server scripts. Only agents with rez permissions should be doing that, so
|
|
||||||
// if the agent does not have those permissions, we do not want them and the server to incur the
|
|
||||||
// overhead of connecting to one another. Additionally we exclude agents that do not care about the
|
|
||||||
// Entity Script Server and won't attempt to connect to it.
|
|
||||||
|
|
||||||
bool isAgentWithoutRights = nodeA->getType() == NodeType::Agent
|
|
||||||
&& nodeB->getType() == NodeType::EntityScriptServer
|
|
||||||
&& !nodeA->getCanRez() && !nodeA->getCanRezTmp()
|
|
||||||
&& !nodeA->getCanRezCertified() && !nodeA->getCanRezTmpCertified();
|
|
||||||
|
|
||||||
if (isAgentWithoutRights) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool isScriptServerForIneffectiveAgent =
|
|
||||||
(nodeA->getType() == NodeType::EntityScriptServer && nodeB->getType() == NodeType::Agent)
|
|
||||||
&& ((nodeBData && !nodeBData->getNodeInterestSet().contains(NodeType::EntityScriptServer))
|
|
||||||
|| (!nodeB->getCanRez() && !nodeB->getCanRezTmp() && !nodeB->getCanRezCertified() && !nodeB->getCanRezTmpCertified()));
|
|
||||||
|
|
||||||
return !isScriptServerForIneffectiveAgent;
|
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned int DomainServer::countConnectedUsers() {
|
unsigned int DomainServer::countConnectedUsers() {
|
||||||
|
@ -3476,4 +3442,4 @@ void DomainServer::handleOctreeFileReplacementRequest(QSharedPointer<ReceivedMes
|
||||||
if (node->getCanReplaceContent()) {
|
if (node->getCanReplaceContent()) {
|
||||||
handleOctreeFileReplacement(message->readAll());
|
handleOctreeFileReplacement(message->readAll());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -114,7 +114,7 @@
|
||||||
{ "from": "Keyboard.W", "when": "!Keyboard.Control", "to": "Actions.LONGITUDINAL_FORWARD" },
|
{ "from": "Keyboard.W", "when": "!Keyboard.Control", "to": "Actions.LONGITUDINAL_FORWARD" },
|
||||||
{ "from": "Keyboard.S", "when": "!Keyboard.Control", "to": "Actions.LONGITUDINAL_BACKWARD" },
|
{ "from": "Keyboard.S", "when": "!Keyboard.Control", "to": "Actions.LONGITUDINAL_BACKWARD" },
|
||||||
{ "from": "Keyboard.Shift", "when": ["!Keyboard.Left", "!Keyboard.Right"], "to": "Actions.SPRINT" },
|
{ "from": "Keyboard.Shift", "when": ["!Keyboard.Left", "!Keyboard.Right"], "to": "Actions.SPRINT" },
|
||||||
{ "from": "Keyboard.Control", "to": "Actions.VERTICAL_DOWN" },
|
{ "from": "Keyboard.C", "to": "Actions.VERTICAL_DOWN" },
|
||||||
{ "from": "Keyboard.Left", "when": "Keyboard.Shift", "to": "Actions.LATERAL_LEFT" },
|
{ "from": "Keyboard.Left", "when": "Keyboard.Shift", "to": "Actions.LATERAL_LEFT" },
|
||||||
{ "from": "Keyboard.Right", "when": "Keyboard.Shift", "to": "Actions.LATERAL_RIGHT" },
|
{ "from": "Keyboard.Right", "when": "Keyboard.Shift", "to": "Actions.LATERAL_RIGHT" },
|
||||||
{ "from": "Keyboard.Up", "when": "Application.CameraFirstPerson", "to": "Actions.LONGITUDINAL_FORWARD" },
|
{ "from": "Keyboard.Up", "when": "Application.CameraFirstPerson", "to": "Actions.LONGITUDINAL_FORWARD" },
|
||||||
|
|
|
@ -163,10 +163,18 @@ TextField {
|
||||||
text: textField.label
|
text: textField.label
|
||||||
colorScheme: textField.colorScheme
|
colorScheme: textField.colorScheme
|
||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
anchors.right: parent.right
|
|
||||||
|
Binding on anchors.right {
|
||||||
|
when: parent.right
|
||||||
|
value: parent.right
|
||||||
|
}
|
||||||
|
Binding on wrapMode {
|
||||||
|
when: parent.right
|
||||||
|
value: Text.WordWrap
|
||||||
|
}
|
||||||
|
|
||||||
anchors.bottom: parent.top
|
anchors.bottom: parent.top
|
||||||
anchors.bottomMargin: 3
|
anchors.bottomMargin: 3
|
||||||
wrapMode: Text.WordWrap
|
|
||||||
visible: label != ""
|
visible: label != ""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,6 +39,7 @@ Windows.ScrollingWindow {
|
||||||
property var assetMappingsModel: Assets.mappingModel;
|
property var assetMappingsModel: Assets.mappingModel;
|
||||||
property var currentDirectory;
|
property var currentDirectory;
|
||||||
property var selectedItemCount: treeView.selection.selectedIndexes.length;
|
property var selectedItemCount: treeView.selection.selectedIndexes.length;
|
||||||
|
property int updatesCount: 0; // this is used for notifying model-dependent bindings about model updates
|
||||||
|
|
||||||
Settings {
|
Settings {
|
||||||
category: "Overlay.AssetServer"
|
category: "Overlay.AssetServer"
|
||||||
|
@ -51,6 +52,9 @@ Windows.ScrollingWindow {
|
||||||
ApplicationInterface.uploadRequest.connect(uploadClicked);
|
ApplicationInterface.uploadRequest.connect(uploadClicked);
|
||||||
assetMappingsModel.errorGettingMappings.connect(handleGetMappingsError);
|
assetMappingsModel.errorGettingMappings.connect(handleGetMappingsError);
|
||||||
assetMappingsModel.autoRefreshEnabled = true;
|
assetMappingsModel.autoRefreshEnabled = true;
|
||||||
|
assetMappingsModel.updated.connect(function() {
|
||||||
|
++updatesCount;
|
||||||
|
});
|
||||||
|
|
||||||
reload();
|
reload();
|
||||||
}
|
}
|
||||||
|
@ -852,12 +856,17 @@ Windows.ScrollingWindow {
|
||||||
checked = Qt.binding(isChecked);
|
checked = Qt.binding(isChecked);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getStatus() {
|
||||||
|
// kind of hack for ensuring getStatus() will be re-evaluated on updatesCount changes
|
||||||
|
return updatesCount, assetProxyModel.data(treeView.selection.currentIndex, 0x105);
|
||||||
|
}
|
||||||
|
|
||||||
function isEnabled() {
|
function isEnabled() {
|
||||||
if (!treeView.selection.hasSelection) {
|
if (!treeView.selection.hasSelection) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
var status = assetProxyModel.data(treeView.selection.currentIndex, 0x105);
|
var status = getStatus();
|
||||||
if (status === "--") {
|
if (status === "--") {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -882,9 +891,9 @@ Windows.ScrollingWindow {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
var status = assetProxyModel.data(treeView.selection.currentIndex, 0x105);
|
var status = getStatus();
|
||||||
return isEnabled() && status !== "Not Baked";
|
return isEnabled() && status !== "Not Baked";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
|
|
|
@ -96,6 +96,7 @@ Rectangle {
|
||||||
root.activeView = "checkoutFailure";
|
root.activeView = "checkoutFailure";
|
||||||
UserActivityLogger.commercePurchaseFailure(root.itemId, root.itemAuthor, root.itemPrice, !root.alreadyOwned, result.message);
|
UserActivityLogger.commercePurchaseFailure(root.itemId, root.itemAuthor, root.itemPrice, !root.alreadyOwned, result.message);
|
||||||
} else {
|
} else {
|
||||||
|
root.certificateId = result.data.certificate_id;
|
||||||
root.itemHref = result.data.download_url;
|
root.itemHref = result.data.download_url;
|
||||||
if (result.data.categories.indexOf("Wearables") > -1) {
|
if (result.data.categories.indexOf("Wearables") > -1) {
|
||||||
root.itemType = "wearable";
|
root.itemType = "wearable";
|
||||||
|
@ -188,7 +189,7 @@ Rectangle {
|
||||||
onItemHrefChanged: {
|
onItemHrefChanged: {
|
||||||
if (root.itemHref.indexOf(".fst") > -1) {
|
if (root.itemHref.indexOf(".fst") > -1) {
|
||||||
root.itemType = "avatar";
|
root.itemType = "avatar";
|
||||||
} else if (root.itemHref.indexOf('.json.gz') > -1) {
|
} else if (root.itemHref.indexOf('.json.gz') > -1 || root.itemHref.indexOf('.content.zip') > -1) {
|
||||||
root.itemType = "contentSet";
|
root.itemType = "contentSet";
|
||||||
} else if (root.itemHref.indexOf('.app.json') > -1) {
|
} else if (root.itemHref.indexOf('.app.json') > -1) {
|
||||||
root.itemType = "app";
|
root.itemType = "app";
|
||||||
|
@ -772,7 +773,7 @@ Rectangle {
|
||||||
lightboxPopup.button1text = "CANCEL";
|
lightboxPopup.button1text = "CANCEL";
|
||||||
lightboxPopup.button1method = "root.visible = false;"
|
lightboxPopup.button1method = "root.visible = false;"
|
||||||
lightboxPopup.button2text = "CONFIRM";
|
lightboxPopup.button2text = "CONFIRM";
|
||||||
lightboxPopup.button2method = "Commerce.replaceContentSet('" + root.itemHref + "');" +
|
lightboxPopup.button2method = "Commerce.replaceContentSet('" + root.itemHref + "', '" + root.certificateId + "');" +
|
||||||
"root.visible = false;rezzedNotifContainer.visible = true; rezzedNotifContainerTimer.start();" +
|
"root.visible = false;rezzedNotifContainer.visible = true; rezzedNotifContainerTimer.start();" +
|
||||||
"UserActivityLogger.commerceEntityRezzed('" + root.itemId + "', 'checkout', '" + root.itemType + "');";
|
"UserActivityLogger.commerceEntityRezzed('" + root.itemId + "', 'checkout', '" + root.itemType + "');";
|
||||||
lightboxPopup.visible = true;
|
lightboxPopup.visible = true;
|
||||||
|
|
|
@ -564,7 +564,7 @@ Item {
|
||||||
onClicked: {
|
onClicked: {
|
||||||
Tablet.playSound(TabletEnums.ButtonClick);
|
Tablet.playSound(TabletEnums.ButtonClick);
|
||||||
if (root.itemType === "contentSet") {
|
if (root.itemType === "contentSet") {
|
||||||
sendToPurchases({method: 'showReplaceContentLightbox', itemHref: root.itemHref});
|
sendToPurchases({method: 'showReplaceContentLightbox', itemHref: root.itemHref, certID: root.certificateId});
|
||||||
} else if (root.itemType === "avatar") {
|
} else if (root.itemType === "avatar") {
|
||||||
sendToPurchases({method: 'showChangeAvatarLightbox', itemName: root.itemName, itemHref: root.itemHref});
|
sendToPurchases({method: 'showChangeAvatarLightbox', itemName: root.itemName, itemHref: root.itemHref});
|
||||||
} else if (root.itemType === "app") {
|
} else if (root.itemType === "app") {
|
||||||
|
|
|
@ -486,7 +486,7 @@ Rectangle {
|
||||||
lightboxPopup.button1text = "CANCEL";
|
lightboxPopup.button1text = "CANCEL";
|
||||||
lightboxPopup.button1method = "root.visible = false;"
|
lightboxPopup.button1method = "root.visible = false;"
|
||||||
lightboxPopup.button2text = "CONFIRM";
|
lightboxPopup.button2text = "CONFIRM";
|
||||||
lightboxPopup.button2method = "Commerce.replaceContentSet('" + msg.itemHref + "'); root.visible = false;";
|
lightboxPopup.button2method = "Commerce.replaceContentSet('" + msg.itemHref + "', '" + msg.certID + "'); root.visible = false;";
|
||||||
lightboxPopup.visible = true;
|
lightboxPopup.visible = true;
|
||||||
} else if (msg.method === "showChangeAvatarLightbox") {
|
} else if (msg.method === "showChangeAvatarLightbox") {
|
||||||
lightboxPopup.titleText = "Change Avatar";
|
lightboxPopup.titleText = "Change Avatar";
|
||||||
|
@ -792,7 +792,7 @@ Rectangle {
|
||||||
currentItemType = "avatar";
|
currentItemType = "avatar";
|
||||||
} else if (currentCategories.indexOf("Wearables") > -1) {
|
} else if (currentCategories.indexOf("Wearables") > -1) {
|
||||||
currentItemType = "wearable";
|
currentItemType = "wearable";
|
||||||
} else if (currentRootFileUrl.endsWith('.json.gz')) {
|
} else if (currentRootFileUrl.endsWith('.json.gz') || currentRootFileUrl.endsWith('.content.zip')) {
|
||||||
currentItemType = "contentSet";
|
currentItemType = "contentSet";
|
||||||
} else if (currentRootFileUrl.endsWith('.app.json')) {
|
} else if (currentRootFileUrl.endsWith('.app.json')) {
|
||||||
currentItemType = "app";
|
currentItemType = "app";
|
||||||
|
|
|
@ -24,6 +24,18 @@ Item {
|
||||||
HifiConstants { id: hifi; }
|
HifiConstants { id: hifi; }
|
||||||
|
|
||||||
id: root;
|
id: root;
|
||||||
|
|
||||||
|
// This will cause a bug -- if you bring up passphrase selection in HUD mode while
|
||||||
|
// in HMD while having HMD preview enabled, then move, then finish passphrase selection,
|
||||||
|
// HMD preview will stay off.
|
||||||
|
// TODO: Fix this unlikely bug
|
||||||
|
onVisibleChanged: {
|
||||||
|
if (visible) {
|
||||||
|
sendSignalToWallet({method: 'disableHmdPreview'});
|
||||||
|
} else {
|
||||||
|
sendSignalToWallet({method: 'maybeEnableHmdPreview'});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Username Text
|
// Username Text
|
||||||
RalewayRegular {
|
RalewayRegular {
|
||||||
|
|
|
@ -68,10 +68,6 @@ Item {
|
||||||
propagateComposedEvents: false;
|
propagateComposedEvents: false;
|
||||||
hoverEnabled: true;
|
hoverEnabled: true;
|
||||||
}
|
}
|
||||||
|
|
||||||
Component.onDestruction: {
|
|
||||||
sendSignalToParent({method: 'maybeEnableHmdPreview'});
|
|
||||||
}
|
|
||||||
|
|
||||||
// This will cause a bug -- if you bring up passphrase selection in HUD mode while
|
// This will cause a bug -- if you bring up passphrase selection in HUD mode while
|
||||||
// in HMD while having HMD preview enabled, then move, then finish passphrase selection,
|
// in HMD while having HMD preview enabled, then move, then finish passphrase selection,
|
||||||
|
|
|
@ -61,9 +61,6 @@ Item {
|
||||||
if (root.shouldImmediatelyFocus) {
|
if (root.shouldImmediatelyFocus) {
|
||||||
focusFirstTextField();
|
focusFirstTextField();
|
||||||
}
|
}
|
||||||
sendMessageToLightbox({method: 'disableHmdPreview'});
|
|
||||||
} else {
|
|
||||||
sendMessageToLightbox({method: 'maybeEnableHmdPreview'});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -44,6 +44,17 @@ Item {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This will cause a bug -- if you bring up security image selection in HUD mode while
|
||||||
|
// in HMD while having HMD preview enabled, then move, then finish passphrase selection,
|
||||||
|
// HMD preview will stay off.
|
||||||
|
// TODO: Fix this unlikely bug
|
||||||
|
onVisibleChanged: {
|
||||||
|
if (visible) {
|
||||||
|
sendSignalToWallet({method: 'disableHmdPreview'});
|
||||||
|
} else {
|
||||||
|
sendSignalToWallet({method: 'maybeEnableHmdPreview'});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Security Image
|
// Security Image
|
||||||
Item {
|
Item {
|
||||||
|
|
|
@ -25,18 +25,6 @@ Item {
|
||||||
|
|
||||||
id: root;
|
id: root;
|
||||||
property alias currentIndex: securityImageGrid.currentIndex;
|
property alias currentIndex: securityImageGrid.currentIndex;
|
||||||
|
|
||||||
// This will cause a bug -- if you bring up security image selection in HUD mode while
|
|
||||||
// in HMD while having HMD preview enabled, then move, then finish passphrase selection,
|
|
||||||
// HMD preview will stay off.
|
|
||||||
// TODO: Fix this unlikely bug
|
|
||||||
onVisibleChanged: {
|
|
||||||
if (visible) {
|
|
||||||
sendSignalToWallet({method: 'disableHmdPreview'});
|
|
||||||
} else {
|
|
||||||
sendSignalToWallet({method: 'maybeEnableHmdPreview'});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
SecurityImageModel {
|
SecurityImageModel {
|
||||||
id: gridModel;
|
id: gridModel;
|
||||||
|
|
|
@ -237,7 +237,7 @@ Rectangle {
|
||||||
} else {
|
} else {
|
||||||
sendToScript(msg);
|
sendToScript(msg);
|
||||||
}
|
}
|
||||||
} else if (msg.method === 'maybeEnableHmdPreview') {
|
} else {
|
||||||
sendToScript(msg);
|
sendToScript(msg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -76,6 +76,12 @@ Item {
|
||||||
var currentStepNumber = root.activeView.substring(5);
|
var currentStepNumber = root.activeView.substring(5);
|
||||||
UserActivityLogger.commerceWalletSetupProgress(timestamp, root.setupAttemptID,
|
UserActivityLogger.commerceWalletSetupProgress(timestamp, root.setupAttemptID,
|
||||||
Math.round((timestamp - root.startingTimestamp)/1000), currentStepNumber, root.setupStepNames[currentStepNumber - 1]);
|
Math.round((timestamp - root.startingTimestamp)/1000), currentStepNumber, root.setupStepNames[currentStepNumber - 1]);
|
||||||
|
|
||||||
|
if (root.activeView === "step_2" || root.activeView === "step_3") {
|
||||||
|
sendSignalToWallet({method: 'disableHmdPreview'});
|
||||||
|
} else {
|
||||||
|
sendSignalToWallet({method: 'maybeEnableHmdPreview'});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
|
@ -441,7 +447,7 @@ Item {
|
||||||
}
|
}
|
||||||
Item {
|
Item {
|
||||||
id: choosePassphraseContainer;
|
id: choosePassphraseContainer;
|
||||||
visible: root.hasShownSecurityImageTip && root.activeView === "step_3";
|
visible: root.activeView === "step_3";
|
||||||
// Anchors
|
// Anchors
|
||||||
anchors.top: titleBarContainer.bottom;
|
anchors.top: titleBarContainer.bottom;
|
||||||
anchors.topMargin: 30;
|
anchors.topMargin: 30;
|
||||||
|
@ -451,10 +457,7 @@ Item {
|
||||||
|
|
||||||
onVisibleChanged: {
|
onVisibleChanged: {
|
||||||
if (visible) {
|
if (visible) {
|
||||||
sendSignalToWallet({method: 'disableHmdPreview'});
|
|
||||||
Commerce.getWalletAuthenticatedStatus();
|
Commerce.getWalletAuthenticatedStatus();
|
||||||
} else {
|
|
||||||
sendSignalToWallet({method: 'maybeEnableHmdPreview'});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -40,6 +40,7 @@ Rectangle {
|
||||||
property var assetMappingsModel: Assets.mappingModel;
|
property var assetMappingsModel: Assets.mappingModel;
|
||||||
property var currentDirectory;
|
property var currentDirectory;
|
||||||
property var selectedItemCount: treeView.selection.selectedIndexes.length;
|
property var selectedItemCount: treeView.selection.selectedIndexes.length;
|
||||||
|
property int updatesCount: 0; // this is used for notifying model-dependent bindings about model updates
|
||||||
|
|
||||||
Settings {
|
Settings {
|
||||||
category: "Overlay.AssetServer"
|
category: "Overlay.AssetServer"
|
||||||
|
@ -51,6 +52,9 @@ Rectangle {
|
||||||
ApplicationInterface.uploadRequest.connect(uploadClicked);
|
ApplicationInterface.uploadRequest.connect(uploadClicked);
|
||||||
assetMappingsModel.errorGettingMappings.connect(handleGetMappingsError);
|
assetMappingsModel.errorGettingMappings.connect(handleGetMappingsError);
|
||||||
assetMappingsModel.autoRefreshEnabled = true;
|
assetMappingsModel.autoRefreshEnabled = true;
|
||||||
|
assetMappingsModel.updated.connect(function() {
|
||||||
|
++updatesCount;
|
||||||
|
});
|
||||||
|
|
||||||
reload();
|
reload();
|
||||||
}
|
}
|
||||||
|
@ -850,12 +854,17 @@ Rectangle {
|
||||||
checked = Qt.binding(isChecked);
|
checked = Qt.binding(isChecked);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getStatus() {
|
||||||
|
// kind of hack for ensuring getStatus() will be re-evaluated on updatesCount changes
|
||||||
|
return updatesCount, assetProxyModel.data(treeView.selection.currentIndex, 0x105);
|
||||||
|
}
|
||||||
|
|
||||||
function isEnabled() {
|
function isEnabled() {
|
||||||
if (!treeView.selection.hasSelection) {
|
if (!treeView.selection.hasSelection) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
var status = assetProxyModel.data(treeView.selection.currentIndex, 0x105);
|
var status = getStatus();
|
||||||
if (status === "--") {
|
if (status === "--") {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -880,7 +889,7 @@ Rectangle {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
var status = assetProxyModel.data(treeView.selection.currentIndex, 0x105);
|
var status = getStatus();
|
||||||
return isEnabled() && status !== "Not Baked";
|
return isEnabled() && status !== "Not Baked";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6244,8 +6244,9 @@ bool Application::canAcceptURL(const QString& urlString) const {
|
||||||
|
|
||||||
bool Application::acceptURL(const QString& urlString, bool defaultUpload) {
|
bool Application::acceptURL(const QString& urlString, bool defaultUpload) {
|
||||||
QUrl url(urlString);
|
QUrl url(urlString);
|
||||||
if (isDomainURL(url)) {
|
|
||||||
// this is a URL for a domain, either hifi:// or serverless - have the AddressManager handle it
|
if (url.scheme() == URL_SCHEME_HIFI) {
|
||||||
|
// this is a hifi URL - have the AddressManager handle it
|
||||||
QMetaObject::invokeMethod(DependencyManager::get<AddressManager>().data(), "handleLookupString",
|
QMetaObject::invokeMethod(DependencyManager::get<AddressManager>().data(), "handleLookupString",
|
||||||
Qt::AutoConnection, Q_ARG(const QString&, urlString));
|
Qt::AutoConnection, Q_ARG(const QString&, urlString));
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -86,10 +86,6 @@ void LODManager::autoAdjustLOD(float realTimeDelta) {
|
||||||
if (_octreeSizeScale < ADJUST_LOD_MIN_SIZE_SCALE) {
|
if (_octreeSizeScale < ADJUST_LOD_MIN_SIZE_SCALE) {
|
||||||
_octreeSizeScale = ADJUST_LOD_MIN_SIZE_SCALE;
|
_octreeSizeScale = ADJUST_LOD_MIN_SIZE_SCALE;
|
||||||
}
|
}
|
||||||
qCDebug(interfaceapp) << "adjusting LOD DOWN"
|
|
||||||
<< "fps =" << currentFPS
|
|
||||||
<< "targetFPS =" << getLODDecreaseFPS()
|
|
||||||
<< "octreeSizeScale =" << _octreeSizeScale;
|
|
||||||
emit LODDecreased();
|
emit LODDecreased();
|
||||||
// Assuming the LOD adjustment will work: we optimistically reset _avgRenderTime
|
// Assuming the LOD adjustment will work: we optimistically reset _avgRenderTime
|
||||||
// to provide an FPS just above the decrease threshold. It will drift close to its
|
// to provide an FPS just above the decrease threshold. It will drift close to its
|
||||||
|
@ -111,10 +107,6 @@ void LODManager::autoAdjustLOD(float realTimeDelta) {
|
||||||
if (_octreeSizeScale > ADJUST_LOD_MAX_SIZE_SCALE) {
|
if (_octreeSizeScale > ADJUST_LOD_MAX_SIZE_SCALE) {
|
||||||
_octreeSizeScale = ADJUST_LOD_MAX_SIZE_SCALE;
|
_octreeSizeScale = ADJUST_LOD_MAX_SIZE_SCALE;
|
||||||
}
|
}
|
||||||
qCDebug(interfaceapp) << "adjusting LOD UP"
|
|
||||||
<< "fps =" << currentFPS
|
|
||||||
<< "targetFPS =" << getLODDecreaseFPS()
|
|
||||||
<< "octreeSizeScale =" << _octreeSizeScale;
|
|
||||||
emit LODIncreased();
|
emit LODIncreased();
|
||||||
// Assuming the LOD adjustment will work: we optimistically reset _avgRenderTime
|
// Assuming the LOD adjustment will work: we optimistically reset _avgRenderTime
|
||||||
// to provide an FPS just below the increase threshold. It will drift close to its
|
// to provide an FPS just below the increase threshold. It will drift close to its
|
||||||
|
|
|
@ -67,8 +67,8 @@ using namespace std;
|
||||||
|
|
||||||
const float DEFAULT_REAL_WORLD_FIELD_OF_VIEW_DEGREES = 30.0f;
|
const float DEFAULT_REAL_WORLD_FIELD_OF_VIEW_DEGREES = 30.0f;
|
||||||
|
|
||||||
const float YAW_SPEED_DEFAULT = 100.0f; // degrees/sec
|
const float YAW_SPEED_DEFAULT = 75.0f; // degrees/sec
|
||||||
const float PITCH_SPEED_DEFAULT = 75.0f; // degrees/sec
|
const float PITCH_SPEED_DEFAULT = 50.0f; // degrees/sec
|
||||||
|
|
||||||
const float MAX_BOOST_SPEED = 0.5f * DEFAULT_AVATAR_MAX_WALKING_SPEED; // action motor gets additive boost below this speed
|
const float MAX_BOOST_SPEED = 0.5f * DEFAULT_AVATAR_MAX_WALKING_SPEED; // action motor gets additive boost below this speed
|
||||||
const float MIN_AVATAR_SPEED = 0.05f;
|
const float MIN_AVATAR_SPEED = 0.05f;
|
||||||
|
@ -2227,7 +2227,7 @@ void MyAvatar::updateActionMotor(float deltaTime) {
|
||||||
}
|
}
|
||||||
|
|
||||||
float boomChange = getDriveKey(ZOOM);
|
float boomChange = getDriveKey(ZOOM);
|
||||||
_boomLength += 2.0f * _boomLength * boomChange + boomChange * boomChange;
|
_boomLength += 4.0f * _boomLength * boomChange + boomChange * boomChange;
|
||||||
_boomLength = glm::clamp<float>(_boomLength, ZOOM_MIN, ZOOM_MAX);
|
_boomLength = glm::clamp<float>(_boomLength, ZOOM_MIN, ZOOM_MAX);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2760,6 +2760,18 @@ bool MyAvatar::isDriveKeyDisabled(DriveKeys key) const {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MyAvatar::triggerVerticalRecenter() {
|
||||||
|
_follow.setForceActivateVertical(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MyAvatar::triggerHorizontalRecenter() {
|
||||||
|
_follow.setForceActivateHorizontal(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MyAvatar::triggerRotationRecenter() {
|
||||||
|
_follow.setForceActivateRotation(true);
|
||||||
|
}
|
||||||
|
|
||||||
// old school meat hook style
|
// old school meat hook style
|
||||||
glm::mat4 MyAvatar::deriveBodyFromHMDSensor() const {
|
glm::mat4 MyAvatar::deriveBodyFromHMDSensor() const {
|
||||||
glm::vec3 headPosition;
|
glm::vec3 headPosition;
|
||||||
|
@ -2957,7 +2969,9 @@ void MyAvatar::FollowHelper::decrementTimeRemaining(float dt) {
|
||||||
bool MyAvatar::FollowHelper::shouldActivateRotation(const MyAvatar& myAvatar, const glm::mat4& desiredBodyMatrix, const glm::mat4& currentBodyMatrix) const {
|
bool MyAvatar::FollowHelper::shouldActivateRotation(const MyAvatar& myAvatar, const glm::mat4& desiredBodyMatrix, const glm::mat4& currentBodyMatrix) const {
|
||||||
const float FOLLOW_ROTATION_THRESHOLD = cosf(PI / 6.0f); // 30 degrees
|
const float FOLLOW_ROTATION_THRESHOLD = cosf(PI / 6.0f); // 30 degrees
|
||||||
glm::vec2 bodyFacing = getFacingDir2D(currentBodyMatrix);
|
glm::vec2 bodyFacing = getFacingDir2D(currentBodyMatrix);
|
||||||
|
|
||||||
return glm::dot(-myAvatar.getHeadControllerFacingMovingAverage(), bodyFacing) < FOLLOW_ROTATION_THRESHOLD;
|
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 {
|
bool MyAvatar::FollowHelper::shouldActivateHorizontal(const MyAvatar& myAvatar, const glm::mat4& desiredBodyMatrix, const glm::mat4& currentBodyMatrix) const {
|
||||||
|
@ -2974,6 +2988,7 @@ bool MyAvatar::FollowHelper::shouldActivateHorizontal(const MyAvatar& myAvatar,
|
||||||
const float MAX_FORWARD_LEAN = 0.15f;
|
const float MAX_FORWARD_LEAN = 0.15f;
|
||||||
const float MAX_BACKWARD_LEAN = 0.1f;
|
const float MAX_BACKWARD_LEAN = 0.1f;
|
||||||
|
|
||||||
|
|
||||||
if (forwardLeanAmount > 0 && forwardLeanAmount > MAX_FORWARD_LEAN) {
|
if (forwardLeanAmount > 0 && forwardLeanAmount > MAX_FORWARD_LEAN) {
|
||||||
return true;
|
return true;
|
||||||
} else if (forwardLeanAmount < 0 && forwardLeanAmount < -MAX_BACKWARD_LEAN) {
|
} else if (forwardLeanAmount < 0 && forwardLeanAmount < -MAX_BACKWARD_LEAN) {
|
||||||
|
@ -2981,6 +2996,7 @@ bool MyAvatar::FollowHelper::shouldActivateHorizontal(const MyAvatar& myAvatar,
|
||||||
}
|
}
|
||||||
|
|
||||||
return fabs(lateralLeanAmount) > MAX_LATERAL_LEAN;
|
return fabs(lateralLeanAmount) > MAX_LATERAL_LEAN;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MyAvatar::FollowHelper::shouldActivateVertical(const MyAvatar& myAvatar, const glm::mat4& desiredBodyMatrix, const glm::mat4& currentBodyMatrix) const {
|
bool MyAvatar::FollowHelper::shouldActivateVertical(const MyAvatar& myAvatar, const glm::mat4& desiredBodyMatrix, const glm::mat4& currentBodyMatrix) const {
|
||||||
|
@ -2988,6 +3004,7 @@ bool MyAvatar::FollowHelper::shouldActivateVertical(const MyAvatar& myAvatar, co
|
||||||
const float CYLINDER_BOTTOM = -1.5f;
|
const float CYLINDER_BOTTOM = -1.5f;
|
||||||
|
|
||||||
glm::vec3 offset = extractTranslation(desiredBodyMatrix) - extractTranslation(currentBodyMatrix);
|
glm::vec3 offset = extractTranslation(desiredBodyMatrix) - extractTranslation(currentBodyMatrix);
|
||||||
|
|
||||||
return (offset.y > CYLINDER_TOP) || (offset.y < CYLINDER_BOTTOM);
|
return (offset.y > CYLINDER_TOP) || (offset.y < CYLINDER_BOTTOM);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3005,6 +3022,19 @@ void MyAvatar::FollowHelper::prePhysicsUpdate(MyAvatar& myAvatar, const glm::mat
|
||||||
if (!isActive(Vertical) && (shouldActivateVertical(myAvatar, desiredBodyMatrix, currentBodyMatrix) || hasDriveInput)) {
|
if (!isActive(Vertical) && (shouldActivateVertical(myAvatar, desiredBodyMatrix, currentBodyMatrix) || hasDriveInput)) {
|
||||||
activate(Vertical);
|
activate(Vertical);
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
if (!isActive(Rotation) && getForceActivateRotation()) {
|
||||||
|
activate(Rotation);
|
||||||
|
setForceActivateRotation(false);
|
||||||
|
}
|
||||||
|
if (!isActive(Horizontal) && getForceActivateHorizontal()) {
|
||||||
|
activate(Horizontal);
|
||||||
|
setForceActivateHorizontal(false);
|
||||||
|
}
|
||||||
|
if (!isActive(Vertical) && getForceActivateVertical()) {
|
||||||
|
activate(Vertical);
|
||||||
|
setForceActivateVertical(false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
glm::mat4 desiredWorldMatrix = myAvatar.getSensorToWorldMatrix() * desiredBodyMatrix;
|
glm::mat4 desiredWorldMatrix = myAvatar.getSensorToWorldMatrix() * desiredBodyMatrix;
|
||||||
|
@ -3054,6 +3084,30 @@ glm::mat4 MyAvatar::FollowHelper::postPhysicsUpdate(const MyAvatar& myAvatar, co
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool MyAvatar::FollowHelper::getForceActivateRotation() const {
|
||||||
|
return _forceActivateRotation;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MyAvatar::FollowHelper::setForceActivateRotation(bool val) {
|
||||||
|
_forceActivateRotation = val;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MyAvatar::FollowHelper::getForceActivateVertical() const {
|
||||||
|
return _forceActivateVertical;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MyAvatar::FollowHelper::setForceActivateVertical(bool val) {
|
||||||
|
_forceActivateVertical = val;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MyAvatar::FollowHelper::getForceActivateHorizontal() const {
|
||||||
|
return _forceActivateHorizontal;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MyAvatar::FollowHelper::setForceActivateHorizontal(bool val) {
|
||||||
|
_forceActivateHorizontal = val;
|
||||||
|
}
|
||||||
|
|
||||||
float MyAvatar::getAccelerationEnergy() {
|
float MyAvatar::getAccelerationEnergy() {
|
||||||
glm::vec3 velocity = getWorldVelocity();
|
glm::vec3 velocity = getWorldVelocity();
|
||||||
int changeInVelocity = abs(velocity.length() - priorVelocity.length());
|
int changeInVelocity = abs(velocity.length() - priorVelocity.length());
|
||||||
|
|
|
@ -404,6 +404,32 @@ public:
|
||||||
Q_INVOKABLE void enableDriveKey(DriveKeys key);
|
Q_INVOKABLE void enableDriveKey(DriveKeys key);
|
||||||
Q_INVOKABLE bool isDriveKeyDisabled(DriveKeys key) const;
|
Q_INVOKABLE bool isDriveKeyDisabled(DriveKeys key) const;
|
||||||
|
|
||||||
|
/**jsdoc
|
||||||
|
*The triggerVerticalRecenter function activates one time the recentering
|
||||||
|
*behaviour in the vertical direction. This call is only takes effect when the property
|
||||||
|
*MyAvatar.hmdLeanRecenterEnabled is set to false.
|
||||||
|
*@function MyAvatar.triggerVerticalRecenter
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**jsdoc
|
||||||
|
*The triggerHorizontalRecenter function activates one time the recentering behaviour
|
||||||
|
*in the horizontal direction. This call is only takes effect when the property
|
||||||
|
*MyAvatar.hmdLeanRecenterEnabled is set to false.
|
||||||
|
*@function MyAvatar.triggerHorizontalRecenter
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**jsdoc
|
||||||
|
*The triggerRotationRecenter function activates one time the recentering behaviour
|
||||||
|
*in the rotation of the root of the avatar. This call is only takes effect when the property
|
||||||
|
*MyAvatar.hmdLeanRecenterEnabled is set to false.
|
||||||
|
*@function MyAvatar.triggerRotationRecenter
|
||||||
|
*/
|
||||||
|
|
||||||
|
Q_INVOKABLE void triggerVerticalRecenter();
|
||||||
|
Q_INVOKABLE void triggerHorizontalRecenter();
|
||||||
|
Q_INVOKABLE void triggerRotationRecenter();
|
||||||
|
|
||||||
eyeContactTarget getEyeContactTarget();
|
eyeContactTarget getEyeContactTarget();
|
||||||
|
|
||||||
const MyHead* getMyHead() const;
|
const MyHead* getMyHead() const;
|
||||||
|
@ -803,6 +829,15 @@ private:
|
||||||
bool shouldActivateHorizontal(const MyAvatar& myAvatar, const glm::mat4& desiredBodyMatrix, const glm::mat4& currentBodyMatrix) const;
|
bool shouldActivateHorizontal(const MyAvatar& myAvatar, const glm::mat4& desiredBodyMatrix, const glm::mat4& currentBodyMatrix) const;
|
||||||
void prePhysicsUpdate(MyAvatar& myAvatar, const glm::mat4& bodySensorMatrix, const glm::mat4& currentBodyMatrix, bool hasDriveInput);
|
void prePhysicsUpdate(MyAvatar& myAvatar, const glm::mat4& bodySensorMatrix, const glm::mat4& currentBodyMatrix, bool hasDriveInput);
|
||||||
glm::mat4 postPhysicsUpdate(const MyAvatar& myAvatar, const glm::mat4& currentBodyMatrix);
|
glm::mat4 postPhysicsUpdate(const MyAvatar& myAvatar, const glm::mat4& currentBodyMatrix);
|
||||||
|
bool getForceActivateRotation() const;
|
||||||
|
void setForceActivateRotation(bool val);
|
||||||
|
bool getForceActivateVertical() const;
|
||||||
|
void setForceActivateVertical(bool val);
|
||||||
|
bool getForceActivateHorizontal() const;
|
||||||
|
void setForceActivateHorizontal(bool val);
|
||||||
|
std::atomic<bool> _forceActivateRotation{ false };
|
||||||
|
std::atomic<bool> _forceActivateVertical{ false };
|
||||||
|
std::atomic<bool> _forceActivateHorizontal{ false };
|
||||||
};
|
};
|
||||||
FollowHelper _follow;
|
FollowHelper _follow;
|
||||||
|
|
||||||
|
@ -839,6 +874,7 @@ private:
|
||||||
|
|
||||||
bool _hmdLeanRecenterEnabled { true };
|
bool _hmdLeanRecenterEnabled { true };
|
||||||
bool _sprint { false };
|
bool _sprint { false };
|
||||||
|
|
||||||
AnimPose _prePhysicsRoomPose;
|
AnimPose _prePhysicsRoomPose;
|
||||||
std::mutex _holdActionsMutex;
|
std::mutex _holdActionsMutex;
|
||||||
std::vector<AvatarActionHold*> _holdActions;
|
std::vector<AvatarActionHold*> _holdActions;
|
||||||
|
|
|
@ -293,7 +293,7 @@ void Ledger::account() {
|
||||||
// The api/failResponse is called just for the side effect of logging.
|
// The api/failResponse is called just for the side effect of logging.
|
||||||
void Ledger::updateLocationSuccess(QNetworkReply& reply) { apiResponse("updateLocation", reply); }
|
void Ledger::updateLocationSuccess(QNetworkReply& reply) { apiResponse("updateLocation", reply); }
|
||||||
void Ledger::updateLocationFailure(QNetworkReply& reply) { failResponse("updateLocation", reply); }
|
void Ledger::updateLocationFailure(QNetworkReply& reply) { failResponse("updateLocation", reply); }
|
||||||
void Ledger::updateLocation(const QString& asset_id, const QString location, const bool controlledFailure) {
|
void Ledger::updateLocation(const QString& asset_id, const QString& location, const bool& alsoUpdateSiblings, const bool controlledFailure) {
|
||||||
auto wallet = DependencyManager::get<Wallet>();
|
auto wallet = DependencyManager::get<Wallet>();
|
||||||
auto walletScriptingInterface = DependencyManager::get<WalletScriptingInterface>();
|
auto walletScriptingInterface = DependencyManager::get<WalletScriptingInterface>();
|
||||||
uint walletStatus = walletScriptingInterface->getWalletStatus();
|
uint walletStatus = walletScriptingInterface->getWalletStatus();
|
||||||
|
@ -308,6 +308,9 @@ void Ledger::updateLocation(const QString& asset_id, const QString location, con
|
||||||
QJsonObject transaction;
|
QJsonObject transaction;
|
||||||
transaction["certificate_id"] = asset_id;
|
transaction["certificate_id"] = asset_id;
|
||||||
transaction["place_name"] = location;
|
transaction["place_name"] = location;
|
||||||
|
if (alsoUpdateSiblings) {
|
||||||
|
transaction["also_update_siblings"] = true;
|
||||||
|
}
|
||||||
QJsonDocument transactionDoc{ transaction };
|
QJsonDocument transactionDoc{ transaction };
|
||||||
auto transactionString = transactionDoc.toJson(QJsonDocument::Compact);
|
auto transactionString = transactionDoc.toJson(QJsonDocument::Compact);
|
||||||
signedSend("transaction", transactionString, key, "location", "updateLocationSuccess", "updateLocationFailure", controlledFailure);
|
signedSend("transaction", transactionString, key, "location", "updateLocationSuccess", "updateLocationFailure", controlledFailure);
|
||||||
|
|
|
@ -31,7 +31,7 @@ public:
|
||||||
void inventory(const QStringList& keys);
|
void inventory(const QStringList& keys);
|
||||||
void history(const QStringList& keys, const int& pageNumber);
|
void history(const QStringList& keys, const int& pageNumber);
|
||||||
void account();
|
void account();
|
||||||
void updateLocation(const QString& asset_id, const QString location, const bool controlledFailure = false);
|
void updateLocation(const QString& asset_id, const QString& location, const bool& alsoUpdateSiblings = false, const bool controlledFailure = false);
|
||||||
void certificateInfo(const QString& certificateId);
|
void certificateInfo(const QString& certificateId);
|
||||||
void transferHfcToNode(const QString& hfc_key, const QString& nodeID, const int& amount, const QString& optionalMessage);
|
void transferHfcToNode(const QString& hfc_key, const QString& nodeID, const int& amount, const QString& optionalMessage);
|
||||||
void transferHfcToUsername(const QString& hfc_key, const QString& username, const int& amount, const QString& optionalMessage);
|
void transferHfcToUsername(const QString& hfc_key, const QString& username, const int& amount, const QString& optionalMessage);
|
||||||
|
|
|
@ -190,7 +190,9 @@ void QmlCommerce::transferHfcToUsername(const QString& username, const int& amou
|
||||||
ledger->transferHfcToUsername(key, username, amount, optionalMessage);
|
ledger->transferHfcToUsername(key, username, amount, optionalMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
void QmlCommerce::replaceContentSet(const QString& itemHref) {
|
void QmlCommerce::replaceContentSet(const QString& itemHref, const QString& certificateID) {
|
||||||
|
auto ledger = DependencyManager::get<Ledger>();
|
||||||
|
ledger->updateLocation(certificateID, DependencyManager::get<AddressManager>()->getPlaceName(), true);
|
||||||
qApp->replaceDomainContent(itemHref);
|
qApp->replaceDomainContent(itemHref);
|
||||||
QJsonObject messageProperties = {
|
QJsonObject messageProperties = {
|
||||||
{ "status", "SuccessfulRequestToReplaceContent" },
|
{ "status", "SuccessfulRequestToReplaceContent" },
|
||||||
|
|
|
@ -84,7 +84,7 @@ protected:
|
||||||
Q_INVOKABLE void transferHfcToNode(const QString& nodeID, const int& amount, const QString& optionalMessage);
|
Q_INVOKABLE void transferHfcToNode(const QString& nodeID, const int& amount, const QString& optionalMessage);
|
||||||
Q_INVOKABLE void transferHfcToUsername(const QString& username, const int& amount, const QString& optionalMessage);
|
Q_INVOKABLE void transferHfcToUsername(const QString& username, const int& amount, const QString& optionalMessage);
|
||||||
|
|
||||||
Q_INVOKABLE void replaceContentSet(const QString& itemHref);
|
Q_INVOKABLE void replaceContentSet(const QString& itemHref, const QString& certificateID);
|
||||||
|
|
||||||
Q_INVOKABLE QString getInstalledApps();
|
Q_INVOKABLE QString getInstalledApps();
|
||||||
Q_INVOKABLE bool installApp(const QString& appHref);
|
Q_INVOKABLE bool installApp(const QString& appHref);
|
||||||
|
|
|
@ -81,26 +81,14 @@ void LaserPointer::editRenderState(const std::string& state, const QVariant& sta
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void LaserPointer::updateRenderStateOverlay(const OverlayID& id, const QVariant& props) {
|
PickResultPointer LaserPointer::getVisualPickResult(const PickResultPointer& pickResult) {
|
||||||
if (!id.isNull() && props.isValid()) {
|
PickResultPointer visualPickResult = pickResult;
|
||||||
QVariantMap propMap = props.toMap();
|
auto rayPickResult = std::static_pointer_cast<RayPickResult>(visualPickResult);
|
||||||
propMap.remove("visible");
|
IntersectionType type = rayPickResult ? rayPickResult->type : IntersectionType::NONE;
|
||||||
qApp->getOverlays().editOverlay(id, propMap);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void LaserPointer::updateRenderState(const RenderState& renderState, const IntersectionType type, float distance, const QUuid& objectID, const PickRay& pickRay, bool defaultState) {
|
if (type != IntersectionType::HUD) {
|
||||||
if (!renderState.getStartID().isNull()) {
|
glm::vec3 endVec;
|
||||||
QVariantMap startProps;
|
PickRay pickRay = rayPickResult ? PickRay(rayPickResult->pickVariant) : PickRay();
|
||||||
startProps.insert("position", vec3toVariant(pickRay.origin));
|
|
||||||
startProps.insert("visible", true);
|
|
||||||
startProps.insert("ignoreRayIntersection", renderState.doesStartIgnoreRays());
|
|
||||||
qApp->getOverlays().editOverlay(renderState.getStartID(), startProps);
|
|
||||||
}
|
|
||||||
glm::vec3 endVec;
|
|
||||||
if (((defaultState || !_lockEnd) && _lockEndObject.id.isNull()) || type == IntersectionType::HUD) {
|
|
||||||
endVec = pickRay.origin + pickRay.direction * distance;
|
|
||||||
} else {
|
|
||||||
if (!_lockEndObject.id.isNull()) {
|
if (!_lockEndObject.id.isNull()) {
|
||||||
glm::vec3 pos;
|
glm::vec3 pos;
|
||||||
glm::quat rot;
|
glm::quat rot;
|
||||||
|
@ -122,17 +110,54 @@ void LaserPointer::updateRenderState(const RenderState& renderState, const Inter
|
||||||
}
|
}
|
||||||
const glm::vec3 DEFAULT_REGISTRATION_POINT = glm::vec3(0.5f);
|
const glm::vec3 DEFAULT_REGISTRATION_POINT = glm::vec3(0.5f);
|
||||||
endVec = pos + rot * (dim * (DEFAULT_REGISTRATION_POINT - registrationPoint));
|
endVec = pos + rot * (dim * (DEFAULT_REGISTRATION_POINT - registrationPoint));
|
||||||
} else {
|
glm::vec3 direction = endVec - pickRay.origin;
|
||||||
|
float distance = glm::distance(pickRay.origin, endVec);
|
||||||
|
glm::vec3 normalizedDirection = glm::normalize(direction);
|
||||||
|
|
||||||
|
rayPickResult->type = _lockEndObject.isOverlay ? IntersectionType::OVERLAY : IntersectionType::ENTITY;
|
||||||
|
rayPickResult->objectID = _lockEndObject.id;
|
||||||
|
rayPickResult->intersection = endVec;
|
||||||
|
rayPickResult->distance = distance;
|
||||||
|
rayPickResult->surfaceNormal = -normalizedDirection;
|
||||||
|
rayPickResult->pickVariant["direction"] = vec3toVariant(normalizedDirection);
|
||||||
|
} else if (type != IntersectionType::NONE && _lockEnd) {
|
||||||
if (type == IntersectionType::ENTITY) {
|
if (type == IntersectionType::ENTITY) {
|
||||||
endVec = DependencyManager::get<EntityScriptingInterface>()->getEntityTransform(objectID)[3];
|
endVec = DependencyManager::get<EntityScriptingInterface>()->getEntityTransform(rayPickResult->objectID)[3];
|
||||||
} else if (type == IntersectionType::OVERLAY) {
|
} else if (type == IntersectionType::OVERLAY) {
|
||||||
endVec = vec3FromVariant(qApp->getOverlays().getProperty(objectID, "position").value);
|
endVec = vec3FromVariant(qApp->getOverlays().getProperty(rayPickResult->objectID, "position").value);
|
||||||
} else if (type == IntersectionType::AVATAR) {
|
} else if (type == IntersectionType::AVATAR) {
|
||||||
endVec = DependencyManager::get<AvatarHashMap>()->getAvatar(objectID)->getPosition();
|
endVec = DependencyManager::get<AvatarHashMap>()->getAvatar(rayPickResult->objectID)->getPosition();
|
||||||
}
|
}
|
||||||
|
glm::vec3 direction = endVec - pickRay.origin;
|
||||||
|
float distance = glm::distance(pickRay.origin, endVec);
|
||||||
|
glm::vec3 normalizedDirection = glm::normalize(direction);
|
||||||
|
rayPickResult->intersection = endVec;
|
||||||
|
rayPickResult->distance = distance;
|
||||||
|
rayPickResult->surfaceNormal = -normalizedDirection;
|
||||||
|
rayPickResult->pickVariant["direction"] = vec3toVariant(normalizedDirection);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return visualPickResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
void LaserPointer::updateRenderStateOverlay(const OverlayID& id, const QVariant& props) {
|
||||||
|
if (!id.isNull() && props.isValid()) {
|
||||||
|
QVariantMap propMap = props.toMap();
|
||||||
|
propMap.remove("visible");
|
||||||
|
qApp->getOverlays().editOverlay(id, propMap);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void LaserPointer::updateRenderState(const RenderState& renderState, const IntersectionType type, float distance, const QUuid& objectID, const PickRay& pickRay) {
|
||||||
|
if (!renderState.getStartID().isNull()) {
|
||||||
|
QVariantMap startProps;
|
||||||
|
startProps.insert("position", vec3toVariant(pickRay.origin));
|
||||||
|
startProps.insert("visible", true);
|
||||||
|
startProps.insert("ignoreRayIntersection", renderState.doesStartIgnoreRays());
|
||||||
|
qApp->getOverlays().editOverlay(renderState.getStartID(), startProps);
|
||||||
|
}
|
||||||
|
glm::vec3 endVec = pickRay.origin + pickRay.direction * distance;
|
||||||
|
|
||||||
QVariant end = vec3toVariant(endVec);
|
QVariant end = vec3toVariant(endVec);
|
||||||
if (!renderState.getPathID().isNull()) {
|
if (!renderState.getPathID().isNull()) {
|
||||||
QVariantMap pathProps;
|
QVariantMap pathProps;
|
||||||
|
@ -195,15 +220,15 @@ void LaserPointer::updateVisuals(const PickResultPointer& pickResult) {
|
||||||
IntersectionType type = rayPickResult ? rayPickResult->type : IntersectionType::NONE;
|
IntersectionType type = rayPickResult ? rayPickResult->type : IntersectionType::NONE;
|
||||||
if (_enabled && !_currentRenderState.empty() && _renderStates.find(_currentRenderState) != _renderStates.end() &&
|
if (_enabled && !_currentRenderState.empty() && _renderStates.find(_currentRenderState) != _renderStates.end() &&
|
||||||
(type != IntersectionType::NONE || _laserLength > 0.0f || !_lockEndObject.id.isNull())) {
|
(type != IntersectionType::NONE || _laserLength > 0.0f || !_lockEndObject.id.isNull())) {
|
||||||
PickRay pickRay(rayPickResult->pickVariant);
|
PickRay pickRay = rayPickResult ? PickRay(rayPickResult->pickVariant): PickRay();
|
||||||
QUuid uid = rayPickResult->objectID;
|
QUuid uid = rayPickResult->objectID;
|
||||||
float distance = _laserLength > 0.0f ? _laserLength : rayPickResult->distance;
|
float distance = _laserLength > 0.0f ? _laserLength : rayPickResult->distance;
|
||||||
updateRenderState(_renderStates[_currentRenderState], type, distance, uid, pickRay, false);
|
updateRenderState(_renderStates[_currentRenderState], type, distance, uid, pickRay);
|
||||||
disableRenderState(_defaultRenderStates[_currentRenderState].second);
|
disableRenderState(_defaultRenderStates[_currentRenderState].second);
|
||||||
} else if (_enabled && !_currentRenderState.empty() && _defaultRenderStates.find(_currentRenderState) != _defaultRenderStates.end()) {
|
} else if (_enabled && !_currentRenderState.empty() && _defaultRenderStates.find(_currentRenderState) != _defaultRenderStates.end()) {
|
||||||
disableRenderState(_renderStates[_currentRenderState]);
|
disableRenderState(_renderStates[_currentRenderState]);
|
||||||
PickRay pickRay = rayPickResult ? PickRay(rayPickResult->pickVariant) : PickRay();
|
PickRay pickRay = rayPickResult ? PickRay(rayPickResult->pickVariant) : PickRay();
|
||||||
updateRenderState(_defaultRenderStates[_currentRenderState].second, IntersectionType::NONE, _defaultRenderStates[_currentRenderState].first, QUuid(), pickRay, true);
|
updateRenderState(_defaultRenderStates[_currentRenderState].second, IntersectionType::NONE, _defaultRenderStates[_currentRenderState].first, QUuid(), pickRay);
|
||||||
} else if (!_currentRenderState.empty()) {
|
} else if (!_currentRenderState.empty()) {
|
||||||
disableRenderState(_renderStates[_currentRenderState]);
|
disableRenderState(_renderStates[_currentRenderState]);
|
||||||
disableRenderState(_defaultRenderStates[_currentRenderState].second);
|
disableRenderState(_defaultRenderStates[_currentRenderState].second);
|
||||||
|
@ -386,4 +411,4 @@ glm::vec2 LaserPointer::findPos2D(const PickedObject& pickedObject, const glm::v
|
||||||
default:
|
default:
|
||||||
return glm::vec2(NAN);
|
return glm::vec2(NAN);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -82,6 +82,7 @@ public:
|
||||||
protected:
|
protected:
|
||||||
PointerEvent buildPointerEvent(const PickedObject& target, const PickResultPointer& pickResult, const std::string& button = "", bool hover = true) override;
|
PointerEvent buildPointerEvent(const PickedObject& target, const PickResultPointer& pickResult, const std::string& button = "", bool hover = true) override;
|
||||||
|
|
||||||
|
PickResultPointer getVisualPickResult(const PickResultPointer& pickResult) override;
|
||||||
PickedObject getHoveredObject(const PickResultPointer& pickResult) override;
|
PickedObject getHoveredObject(const PickResultPointer& pickResult) override;
|
||||||
Pointer::Buttons getPressedButtons(const PickResultPointer& pickResult) override;
|
Pointer::Buttons getPressedButtons(const PickResultPointer& pickResult) override;
|
||||||
|
|
||||||
|
@ -102,7 +103,7 @@ private:
|
||||||
LockEndObject _lockEndObject;
|
LockEndObject _lockEndObject;
|
||||||
|
|
||||||
void updateRenderStateOverlay(const OverlayID& id, const QVariant& props);
|
void updateRenderStateOverlay(const OverlayID& id, const QVariant& props);
|
||||||
void updateRenderState(const RenderState& renderState, const IntersectionType type, float distance, const QUuid& objectID, const PickRay& pickRay, bool defaultState);
|
void updateRenderState(const RenderState& renderState, const IntersectionType type, float distance, const QUuid& objectID, const PickRay& pickRay);
|
||||||
void disableRenderState(const RenderState& renderState);
|
void disableRenderState(const RenderState& renderState);
|
||||||
|
|
||||||
struct TriggerState {
|
struct TriggerState {
|
||||||
|
|
|
@ -80,7 +80,7 @@ void AssetMappingsScriptingInterface::uploadFile(QString path, QString mapping,
|
||||||
auto result = offscreenUi->inputDialog(OffscreenUi::ICON_INFORMATION, "Specify Asset Path",
|
auto result = offscreenUi->inputDialog(OffscreenUi::ICON_INFORMATION, "Specify Asset Path",
|
||||||
dropEvent ? dropHelpText : helpText, mapping);
|
dropEvent ? dropHelpText : helpText, mapping);
|
||||||
|
|
||||||
if (!result.isValid()) {
|
if (!result.isValid() || result.toString() == "") {
|
||||||
completedCallback.call({ -1 });
|
completedCallback.call({ -1 });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -110,7 +110,6 @@ bool SelectionScriptingInterface::enableListHighlight(const QString& listName, c
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!(*highlightStyle).isBoundToList()) {
|
if (!(*highlightStyle).isBoundToList()) {
|
||||||
setupHandler(listName);
|
|
||||||
(*highlightStyle).setBoundToList(true);
|
(*highlightStyle).setBoundToList(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -172,6 +171,18 @@ render::HighlightStyle SelectionScriptingInterface::getHighlightStyle(const QStr
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool SelectionScriptingInterface::enableListToScene(const QString& listName) {
|
||||||
|
setupHandler(listName);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SelectionScriptingInterface::disableListToScene(const QString& listName) {
|
||||||
|
removeHandler(listName);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
template <class T> bool SelectionScriptingInterface::addToGameplayObjects(const QString& listName, T idToAdd) {
|
template <class T> bool SelectionScriptingInterface::addToGameplayObjects(const QString& listName, T idToAdd) {
|
||||||
{
|
{
|
||||||
QWriteLocker lock(&_selectionListsLock);
|
QWriteLocker lock(&_selectionListsLock);
|
||||||
|
@ -303,6 +314,15 @@ void SelectionScriptingInterface::setupHandler(const QString& selectionName) {
|
||||||
(*handler)->initialize(selectionName);
|
(*handler)->initialize(selectionName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SelectionScriptingInterface::removeHandler(const QString& selectionName) {
|
||||||
|
QWriteLocker lock(&_selectionHandlersLock);
|
||||||
|
auto handler = _handlerMap.find(selectionName);
|
||||||
|
if (handler != _handlerMap.end()) {
|
||||||
|
delete handler.value();
|
||||||
|
_handlerMap.erase(handler);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void SelectionScriptingInterface::onSelectedItemsListChanged(const QString& listName) {
|
void SelectionScriptingInterface::onSelectedItemsListChanged(const QString& listName) {
|
||||||
{
|
{
|
||||||
QWriteLocker lock(&_selectionHandlersLock);
|
QWriteLocker lock(&_selectionHandlersLock);
|
||||||
|
|
|
@ -160,13 +160,14 @@ public:
|
||||||
* If the Selection doesn't exist, it will be created.
|
* If the Selection doesn't exist, it will be created.
|
||||||
* All objects in the list will be displayed with the highlight effect as specified from the highlightStyle.
|
* All objects in the list will be displayed with the highlight effect as specified from the highlightStyle.
|
||||||
* The function can be called several times with different values in the style to modify it.
|
* The function can be called several times with different values in the style to modify it.
|
||||||
*
|
*
|
||||||
* @function Selection.enableListHighlight
|
* @function Selection.enableListHighlight
|
||||||
* @param listName {string} name of the selection
|
* @param listName {string} name of the selection
|
||||||
* @param highlightStyle {jsObject} highlight style fields (see Selection.getListHighlightStyle for a detailed description of the highlightStyle).
|
* @param highlightStyle {jsObject} highlight style fields (see Selection.getListHighlightStyle for a detailed description of the highlightStyle).
|
||||||
* @returns {bool} true if the selection was successfully enabled for highlight.
|
* @returns {bool} true if the selection was successfully enabled for highlight.
|
||||||
*/
|
*/
|
||||||
Q_INVOKABLE bool enableListHighlight(const QString& listName, const QVariantMap& highlightStyle);
|
Q_INVOKABLE bool enableListHighlight(const QString& listName, const QVariantMap& highlightStyle);
|
||||||
|
|
||||||
/**jsdoc
|
/**jsdoc
|
||||||
* Disable highlighting for the named selection.
|
* Disable highlighting for the named selection.
|
||||||
* If the Selection doesn't exist or wasn't enabled for highliting then nothing happens simply returning false.
|
* If the Selection doesn't exist or wasn't enabled for highliting then nothing happens simply returning false.
|
||||||
|
@ -175,7 +176,27 @@ public:
|
||||||
* @param listName {string} name of the selection
|
* @param listName {string} name of the selection
|
||||||
* @returns {bool} true if the selection was successfully disabled for highlight, false otherwise.
|
* @returns {bool} true if the selection was successfully disabled for highlight, false otherwise.
|
||||||
*/
|
*/
|
||||||
Q_INVOKABLE bool disableListHighlight(const QString& listName);
|
Q_INVOKABLE bool disableListHighlight(const QString& listName);
|
||||||
|
/**jsdoc
|
||||||
|
* Enable scene selection for the named selection.
|
||||||
|
* If the Selection doesn't exist, it will be created.
|
||||||
|
* All objects in the list will be sent to a scene selection.
|
||||||
|
*
|
||||||
|
* @function Selection.enableListToScene
|
||||||
|
* @param listName {string} name of the selection
|
||||||
|
* @returns {bool} true if the selection was successfully enabled on the scene.
|
||||||
|
*/
|
||||||
|
Q_INVOKABLE bool enableListToScene(const QString& listName);
|
||||||
|
/**jsdoc
|
||||||
|
* Disable scene selection for the named selection.
|
||||||
|
* If the Selection doesn't exist or wasn't enabled on the scene then nothing happens simply returning false.
|
||||||
|
*
|
||||||
|
* @function Selection.disableListToScene
|
||||||
|
* @param listName {string} name of the selection
|
||||||
|
* @returns {bool} true if the selection was successfully disabled on the scene, false otherwise.
|
||||||
|
*/
|
||||||
|
Q_INVOKABLE bool disableListToScene(const QString& listName);
|
||||||
|
|
||||||
/**jsdoc
|
/**jsdoc
|
||||||
* Query the highlight style values for the named selection.
|
* Query the highlight style values for the named selection.
|
||||||
* If the Selection doesn't exist or hasn't been highlight enabled yet, it will return an empty object.
|
* If the Selection doesn't exist or hasn't been highlight enabled yet, it will return an empty object.
|
||||||
|
@ -223,7 +244,7 @@ private:
|
||||||
template <class T> bool removeFromGameplayObjects(const QString& listName, T idToRemove);
|
template <class T> bool removeFromGameplayObjects(const QString& listName, T idToRemove);
|
||||||
|
|
||||||
void setupHandler(const QString& selectionName);
|
void setupHandler(const QString& selectionName);
|
||||||
|
void removeHandler(const QString& selectionName);
|
||||||
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -50,7 +50,9 @@ ContextOverlayInterface::ContextOverlayInterface() {
|
||||||
_entityPropertyFlags += PROP_OWNING_AVATAR_ID;
|
_entityPropertyFlags += PROP_OWNING_AVATAR_ID;
|
||||||
|
|
||||||
auto entityScriptingInterface = DependencyManager::get<EntityScriptingInterface>().data();
|
auto entityScriptingInterface = DependencyManager::get<EntityScriptingInterface>().data();
|
||||||
connect(entityScriptingInterface, &EntityScriptingInterface::mousePressOnEntity, this, &ContextOverlayInterface::createOrDestroyContextOverlay);
|
connect(entityScriptingInterface, &EntityScriptingInterface::clickDownOnEntity, this, &ContextOverlayInterface::clickDownOnEntity);
|
||||||
|
connect(entityScriptingInterface, &EntityScriptingInterface::holdingClickOnEntity, this, &ContextOverlayInterface::holdingClickOnEntity);
|
||||||
|
connect(entityScriptingInterface, &EntityScriptingInterface::mouseReleaseOnEntity, this, &ContextOverlayInterface::mouseReleaseOnEntity);
|
||||||
connect(entityScriptingInterface, &EntityScriptingInterface::hoverEnterEntity, this, &ContextOverlayInterface::contextOverlays_hoverEnterEntity);
|
connect(entityScriptingInterface, &EntityScriptingInterface::hoverEnterEntity, this, &ContextOverlayInterface::contextOverlays_hoverEnterEntity);
|
||||||
connect(entityScriptingInterface, &EntityScriptingInterface::hoverLeaveEntity, this, &ContextOverlayInterface::contextOverlays_hoverLeaveEntity);
|
connect(entityScriptingInterface, &EntityScriptingInterface::hoverLeaveEntity, this, &ContextOverlayInterface::contextOverlays_hoverLeaveEntity);
|
||||||
connect(_tabletScriptingInterface->getTablet("com.highfidelity.interface.tablet.system"), &TabletProxy::tabletShownChanged, this, [&]() {
|
connect(_tabletScriptingInterface->getTablet("com.highfidelity.interface.tablet.system"), &TabletProxy::tabletShownChanged, this, [&]() {
|
||||||
|
@ -97,6 +99,31 @@ void ContextOverlayInterface::setEnabled(bool enabled) {
|
||||||
_enabled = enabled;
|
_enabled = enabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ContextOverlayInterface::clickDownOnEntity(const EntityItemID& entityItemID, const PointerEvent& event) {
|
||||||
|
if (_enabled && event.getButton() == PointerEvent::SecondaryButton && contextOverlayFilterPassed(entityItemID)) {
|
||||||
|
_mouseDownEntity = entityItemID;
|
||||||
|
_mouseDownEntityTimestamp = usecTimestampNow();
|
||||||
|
} else {
|
||||||
|
if (!_currentEntityWithContextOverlay.isNull()) {
|
||||||
|
disableEntityHighlight(_currentEntityWithContextOverlay);
|
||||||
|
destroyContextOverlay(_currentEntityWithContextOverlay, event);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static const float CONTEXT_OVERLAY_CLICK_HOLD_TIME_MSEC = 400.0f;
|
||||||
|
void ContextOverlayInterface::holdingClickOnEntity(const EntityItemID& entityItemID, const PointerEvent& event) {
|
||||||
|
if (!_mouseDownEntity.isNull() && ((usecTimestampNow() - _mouseDownEntityTimestamp) > (CONTEXT_OVERLAY_CLICK_HOLD_TIME_MSEC * USECS_PER_MSEC))) {
|
||||||
|
_mouseDownEntity = EntityItemID();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ContextOverlayInterface::mouseReleaseOnEntity(const EntityItemID& entityItemID, const PointerEvent& event) {
|
||||||
|
if (_enabled && event.getButton() == PointerEvent::SecondaryButton && contextOverlayFilterPassed(entityItemID) && _mouseDownEntity == entityItemID) {
|
||||||
|
createOrDestroyContextOverlay(entityItemID, event);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool ContextOverlayInterface::createOrDestroyContextOverlay(const EntityItemID& entityItemID, const PointerEvent& event) {
|
bool ContextOverlayInterface::createOrDestroyContextOverlay(const EntityItemID& entityItemID, const PointerEvent& event) {
|
||||||
if (_enabled && event.getButton() == PointerEvent::SecondaryButton) {
|
if (_enabled && event.getButton() == PointerEvent::SecondaryButton) {
|
||||||
if (contextOverlayFilterPassed(entityItemID)) {
|
if (contextOverlayFilterPassed(entityItemID)) {
|
||||||
|
|
|
@ -64,6 +64,10 @@ signals:
|
||||||
void contextOverlayClicked(const QUuid& currentEntityWithContextOverlay);
|
void contextOverlayClicked(const QUuid& currentEntityWithContextOverlay);
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
|
void clickDownOnEntity(const EntityItemID& entityItemID, const PointerEvent& event);
|
||||||
|
void holdingClickOnEntity(const EntityItemID& entityItemID, const PointerEvent& event);
|
||||||
|
void mouseReleaseOnEntity(const EntityItemID& entityItemID, const PointerEvent& event);
|
||||||
|
|
||||||
bool createOrDestroyContextOverlay(const EntityItemID& entityItemID, const PointerEvent& event);
|
bool createOrDestroyContextOverlay(const EntityItemID& entityItemID, const PointerEvent& event);
|
||||||
bool destroyContextOverlay(const EntityItemID& entityItemID, const PointerEvent& event);
|
bool destroyContextOverlay(const EntityItemID& entityItemID, const PointerEvent& event);
|
||||||
bool destroyContextOverlay(const EntityItemID& entityItemID);
|
bool destroyContextOverlay(const EntityItemID& entityItemID);
|
||||||
|
@ -84,6 +88,8 @@ private:
|
||||||
};
|
};
|
||||||
bool _verboseLogging{ true };
|
bool _verboseLogging{ true };
|
||||||
bool _enabled { true };
|
bool _enabled { true };
|
||||||
|
EntityItemID _mouseDownEntity{};
|
||||||
|
quint64 _mouseDownEntityTimestamp;
|
||||||
EntityItemID _currentEntityWithContextOverlay{};
|
EntityItemID _currentEntityWithContextOverlay{};
|
||||||
EntityItemID _lastInspectedEntity{};
|
EntityItemID _lastInspectedEntity{};
|
||||||
QString _entityMarketplaceID;
|
QString _entityMarketplaceID;
|
||||||
|
|
|
@ -1246,6 +1246,7 @@ void Rig::updateHands(bool leftHandEnabled, bool rightHandEnabled, bool hipsEnab
|
||||||
const FBXJointShapeInfo& hipsShapeInfo, const FBXJointShapeInfo& spineShapeInfo,
|
const FBXJointShapeInfo& hipsShapeInfo, const FBXJointShapeInfo& spineShapeInfo,
|
||||||
const FBXJointShapeInfo& spine1ShapeInfo, const FBXJointShapeInfo& spine2ShapeInfo) {
|
const FBXJointShapeInfo& spine1ShapeInfo, const FBXJointShapeInfo& spine2ShapeInfo) {
|
||||||
|
|
||||||
|
const bool ENABLE_POLE_VECTORS = false;
|
||||||
const float ELBOW_POLE_VECTOR_BLEND_FACTOR = 0.95f;
|
const float ELBOW_POLE_VECTOR_BLEND_FACTOR = 0.95f;
|
||||||
|
|
||||||
int hipsIndex = indexOfJoint("Hips");
|
int hipsIndex = indexOfJoint("Hips");
|
||||||
|
@ -1268,7 +1269,7 @@ void Rig::updateHands(bool leftHandEnabled, bool rightHandEnabled, bool hipsEnab
|
||||||
int handJointIndex = _animSkeleton->nameToJointIndex("LeftHand");
|
int handJointIndex = _animSkeleton->nameToJointIndex("LeftHand");
|
||||||
int armJointIndex = _animSkeleton->nameToJointIndex("LeftArm");
|
int armJointIndex = _animSkeleton->nameToJointIndex("LeftArm");
|
||||||
int elbowJointIndex = _animSkeleton->nameToJointIndex("LeftForeArm");
|
int elbowJointIndex = _animSkeleton->nameToJointIndex("LeftForeArm");
|
||||||
if (!leftArmEnabled && handJointIndex >= 0 && armJointIndex >= 0 && elbowJointIndex >= 0) {
|
if (ENABLE_POLE_VECTORS && !leftArmEnabled && handJointIndex >= 0 && armJointIndex >= 0 && elbowJointIndex >= 0) {
|
||||||
glm::vec3 poleVector = calculateElbowPoleVector(handJointIndex, elbowJointIndex, armJointIndex, hipsIndex, true);
|
glm::vec3 poleVector = calculateElbowPoleVector(handJointIndex, elbowJointIndex, armJointIndex, hipsIndex, true);
|
||||||
|
|
||||||
// smooth toward desired pole vector from previous pole vector... to reduce jitter
|
// smooth toward desired pole vector from previous pole vector... to reduce jitter
|
||||||
|
@ -1315,7 +1316,7 @@ void Rig::updateHands(bool leftHandEnabled, bool rightHandEnabled, bool hipsEnab
|
||||||
int handJointIndex = _animSkeleton->nameToJointIndex("RightHand");
|
int handJointIndex = _animSkeleton->nameToJointIndex("RightHand");
|
||||||
int armJointIndex = _animSkeleton->nameToJointIndex("RightArm");
|
int armJointIndex = _animSkeleton->nameToJointIndex("RightArm");
|
||||||
int elbowJointIndex = _animSkeleton->nameToJointIndex("RightForeArm");
|
int elbowJointIndex = _animSkeleton->nameToJointIndex("RightForeArm");
|
||||||
if (!rightArmEnabled && handJointIndex >= 0 && armJointIndex >= 0 && elbowJointIndex >= 0) {
|
if (ENABLE_POLE_VECTORS && !rightArmEnabled && handJointIndex >= 0 && armJointIndex >= 0 && elbowJointIndex >= 0) {
|
||||||
glm::vec3 poleVector = calculateElbowPoleVector(handJointIndex, elbowJointIndex, armJointIndex, hipsIndex, false);
|
glm::vec3 poleVector = calculateElbowPoleVector(handJointIndex, elbowJointIndex, armJointIndex, hipsIndex, false);
|
||||||
|
|
||||||
// smooth toward desired pole vector from previous pole vector... to reduce jitter
|
// smooth toward desired pole vector from previous pole vector... to reduce jitter
|
||||||
|
@ -1555,18 +1556,21 @@ void Rig::updateFromControllerParameters(const ControllerParameters& params, flo
|
||||||
updateFeet(leftFootEnabled, rightFootEnabled,
|
updateFeet(leftFootEnabled, rightFootEnabled,
|
||||||
params.primaryControllerPoses[PrimaryControllerType_LeftFoot], params.primaryControllerPoses[PrimaryControllerType_RightFoot]);
|
params.primaryControllerPoses[PrimaryControllerType_LeftFoot], params.primaryControllerPoses[PrimaryControllerType_RightFoot]);
|
||||||
|
|
||||||
|
|
||||||
|
if (headEnabled) {
|
||||||
|
// Blend IK chains toward the joint limit centers, this should stablize head and hand ik.
|
||||||
|
_animVars.set("solutionSource", (int)AnimInverseKinematics::SolutionSource::RelaxToLimitCenterPoses);
|
||||||
|
} else {
|
||||||
|
// Blend IK chains toward the UnderPoses, so some of the animaton motion is present in the IK solution.
|
||||||
|
_animVars.set("solutionSource", (int)AnimInverseKinematics::SolutionSource::RelaxToUnderPoses);
|
||||||
|
}
|
||||||
|
|
||||||
// if the hips or the feet are being controlled.
|
// if the hips or the feet are being controlled.
|
||||||
if (hipsEnabled || rightFootEnabled || leftFootEnabled) {
|
if (hipsEnabled || rightFootEnabled || leftFootEnabled) {
|
||||||
// for more predictable IK solve from the center of the joint limits, not from the underpose
|
|
||||||
_animVars.set("solutionSource", (int)AnimInverseKinematics::SolutionSource::RelaxToLimitCenterPoses);
|
|
||||||
|
|
||||||
// replace the feet animation with the default pose, this is to prevent unexpected toe wiggling.
|
// replace the feet animation with the default pose, this is to prevent unexpected toe wiggling.
|
||||||
_animVars.set("defaultPoseOverlayAlpha", 1.0f);
|
_animVars.set("defaultPoseOverlayAlpha", 1.0f);
|
||||||
_animVars.set("defaultPoseOverlayBoneSet", (int)AnimOverlay::BothFeetBoneSet);
|
_animVars.set("defaultPoseOverlayBoneSet", (int)AnimOverlay::BothFeetBoneSet);
|
||||||
} else {
|
} else {
|
||||||
// augment the IK with the underPose.
|
|
||||||
_animVars.set("solutionSource", (int)AnimInverseKinematics::SolutionSource::RelaxToUnderPoses);
|
|
||||||
|
|
||||||
// feet should follow source animation
|
// feet should follow source animation
|
||||||
_animVars.unset("defaultPoseOverlayAlpha");
|
_animVars.unset("defaultPoseOverlayAlpha");
|
||||||
_animVars.unset("defaultPoseOverlayBoneSet");
|
_animVars.unset("defaultPoseOverlayBoneSet");
|
||||||
|
|
|
@ -6,10 +6,43 @@
|
||||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
//
|
//
|
||||||
|
|
||||||
// NOTE: we don't need to include this header unless/until we add additional symbols.
|
#include "JSEndpoint.h"
|
||||||
// By removing this header we prevent these warnings on windows:
|
#include "../../Logging.h"
|
||||||
//
|
|
||||||
// warning LNK4221: This object file does not define any previously undefined public symbols,
|
using namespace controller;
|
||||||
// so it will not be used by any link operation that consumes this library
|
|
||||||
//
|
QString formatException(const QJSValue& exception) {
|
||||||
//#include "JSEndpoint.h"
|
QString note { "UncaughtException" };
|
||||||
|
QString result;
|
||||||
|
|
||||||
|
const auto message = exception.toString();
|
||||||
|
const auto fileName = exception.property("fileName").toString();
|
||||||
|
const auto lineNumber = exception.property("lineNumber").toString();
|
||||||
|
const auto stacktrace = exception.property("stack").toString();
|
||||||
|
|
||||||
|
const QString SCRIPT_EXCEPTION_FORMAT = "[%0] %1 in %2:%3";
|
||||||
|
const QString SCRIPT_BACKTRACE_SEP = "\n ";
|
||||||
|
|
||||||
|
result = QString(SCRIPT_EXCEPTION_FORMAT).arg(note, message, fileName, lineNumber);
|
||||||
|
if (!stacktrace.isEmpty()) {
|
||||||
|
result += QString("\n[Backtrace]%1%2").arg(SCRIPT_BACKTRACE_SEP).arg(stacktrace);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
float JSEndpoint::peek() const {
|
||||||
|
QJSValue result = _callable.call();
|
||||||
|
if (result.isError()) {
|
||||||
|
qCDebug(controllers).noquote() << formatException(result);
|
||||||
|
return 0.0f;
|
||||||
|
} else {
|
||||||
|
return (float)result.toNumber();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void JSEndpoint::apply(float newValue, const Pointer& source) {
|
||||||
|
QJSValue result = _callable.call(QJSValueList({ QJSValue(newValue) }));
|
||||||
|
if (result.isError()) {
|
||||||
|
qCDebug(controllers).noquote() << formatException(result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -24,16 +24,11 @@ public:
|
||||||
: Endpoint(Input::INVALID_INPUT), _callable(callable) {
|
: Endpoint(Input::INVALID_INPUT), _callable(callable) {
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual float peek() const override {
|
virtual float peek() const override;
|
||||||
return (float)const_cast<JSEndpoint*>(this)->_callable.call().toNumber();
|
virtual void apply(float newValue, const Pointer& source) override;
|
||||||
}
|
|
||||||
|
|
||||||
virtual void apply(float newValue, const Pointer& source) override {
|
|
||||||
_callable.call(QJSValueList({ QJSValue(newValue) }));
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QJSValue _callable;
|
mutable QJSValue _callable;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
//
|
//
|
||||||
|
|
||||||
#include "ScriptEndpoint.h"
|
#include "ScriptEndpoint.h"
|
||||||
|
#include "../../Logging.h"
|
||||||
|
|
||||||
#include <QtCore/QThread>
|
#include <QtCore/QThread>
|
||||||
|
|
||||||
|
@ -14,6 +15,25 @@
|
||||||
|
|
||||||
using namespace controller;
|
using namespace controller;
|
||||||
|
|
||||||
|
QString formatException(const QScriptValue& exception) {
|
||||||
|
QString note { "UncaughtException" };
|
||||||
|
QString result;
|
||||||
|
|
||||||
|
const auto message = exception.toString();
|
||||||
|
const auto fileName = exception.property("fileName").toString();
|
||||||
|
const auto lineNumber = exception.property("lineNumber").toString();
|
||||||
|
const auto stacktrace = exception.property("stack").toString();
|
||||||
|
|
||||||
|
const QString SCRIPT_EXCEPTION_FORMAT = "[%0] %1 in %2:%3";
|
||||||
|
const QString SCRIPT_BACKTRACE_SEP = "\n ";
|
||||||
|
|
||||||
|
result = QString(SCRIPT_EXCEPTION_FORMAT).arg(note, message, fileName, lineNumber);
|
||||||
|
if (!stacktrace.isEmpty()) {
|
||||||
|
result += QString("\n[Backtrace]%1%2").arg(SCRIPT_BACKTRACE_SEP).arg(stacktrace);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
float ScriptEndpoint::peek() const {
|
float ScriptEndpoint::peek() const {
|
||||||
const_cast<ScriptEndpoint*>(this)->updateValue();
|
const_cast<ScriptEndpoint*>(this)->updateValue();
|
||||||
return _lastValueRead;
|
return _lastValueRead;
|
||||||
|
@ -26,10 +46,11 @@ void ScriptEndpoint::updateValue() {
|
||||||
}
|
}
|
||||||
|
|
||||||
QScriptValue result = _callable.call();
|
QScriptValue result = _callable.call();
|
||||||
|
if (result.isError()) {
|
||||||
// If the callable ever returns a non-number, we assume it's a pose
|
// print JavaScript exception
|
||||||
// and start reporting ourselves as a pose.
|
qCDebug(controllers).noquote() << formatException(result);
|
||||||
if (result.isNumber()) {
|
_lastValueRead = 0.0f;
|
||||||
|
} else if (result.isNumber()) {
|
||||||
_lastValueRead = (float)_callable.call().toNumber();
|
_lastValueRead = (float)_callable.call().toNumber();
|
||||||
} else {
|
} else {
|
||||||
Pose::fromScriptValue(result, _lastPoseRead);
|
Pose::fromScriptValue(result, _lastPoseRead);
|
||||||
|
@ -52,8 +73,12 @@ void ScriptEndpoint::internalApply(float value, int sourceID) {
|
||||||
Q_ARG(int, sourceID));
|
Q_ARG(int, sourceID));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
_callable.call(QScriptValue(),
|
QScriptValue result = _callable.call(QScriptValue(),
|
||||||
QScriptValueList({ QScriptValue(value), QScriptValue(sourceID) }));
|
QScriptValueList({ QScriptValue(value), QScriptValue(sourceID) }));
|
||||||
|
if (result.isError()) {
|
||||||
|
// print JavaScript exception
|
||||||
|
qCDebug(controllers).noquote() << formatException(result);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Pose ScriptEndpoint::peekPose() const {
|
Pose ScriptEndpoint::peekPose() const {
|
||||||
|
@ -67,6 +92,10 @@ void ScriptEndpoint::updatePose() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
QScriptValue result = _callable.call();
|
QScriptValue result = _callable.call();
|
||||||
|
if (result.isError()) {
|
||||||
|
// print JavaScript exception
|
||||||
|
qCDebug(controllers).noquote() << formatException(result);
|
||||||
|
}
|
||||||
Pose::fromScriptValue(result, _lastPoseRead);
|
Pose::fromScriptValue(result, _lastPoseRead);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -85,6 +114,10 @@ void ScriptEndpoint::internalApply(const Pose& newPose, int sourceID) {
|
||||||
Q_ARG(int, sourceID));
|
Q_ARG(int, sourceID));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
_callable.call(QScriptValue(),
|
QScriptValue result = _callable.call(QScriptValue(),
|
||||||
QScriptValueList({ Pose::toScriptValue(_callable.engine(), newPose), QScriptValue(sourceID) }));
|
QScriptValueList({ Pose::toScriptValue(_callable.engine(), newPose), QScriptValue(sourceID) }));
|
||||||
|
if (result.isError()) {
|
||||||
|
// print JavaScript exception
|
||||||
|
qCDebug(controllers).noquote() << formatException(result);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -131,6 +131,8 @@ ItemKey ShapeEntityRenderer::getKey() {
|
||||||
withReadLock([&] {
|
withReadLock([&] {
|
||||||
if (isTransparent()) {
|
if (isTransparent()) {
|
||||||
builder.withTransparent();
|
builder.withTransparent();
|
||||||
|
} else if (_canCastShadow) {
|
||||||
|
builder.withShadowCaster();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -254,17 +254,6 @@ QUuid EntityScriptingInterface::addEntity(const EntityItemProperties& properties
|
||||||
propertiesWithSimID = convertPropertiesFromScriptSemantics(propertiesWithSimID, scalesWithParent);
|
propertiesWithSimID = convertPropertiesFromScriptSemantics(propertiesWithSimID, scalesWithParent);
|
||||||
propertiesWithSimID.setDimensionsInitialized(properties.dimensionsChanged());
|
propertiesWithSimID.setDimensionsInitialized(properties.dimensionsChanged());
|
||||||
|
|
||||||
auto dimensions = propertiesWithSimID.getDimensions();
|
|
||||||
float volume = dimensions.x * dimensions.y * dimensions.z;
|
|
||||||
auto density = propertiesWithSimID.getDensity();
|
|
||||||
auto newVelocity = propertiesWithSimID.getVelocity().length();
|
|
||||||
float cost = calculateCost(density * volume, 0, newVelocity);
|
|
||||||
cost *= costMultiplier;
|
|
||||||
|
|
||||||
if (cost > _currentAvatarEnergy) {
|
|
||||||
return QUuid();
|
|
||||||
}
|
|
||||||
|
|
||||||
EntityItemID id = EntityItemID(QUuid::createUuid());
|
EntityItemID id = EntityItemID(QUuid::createUuid());
|
||||||
|
|
||||||
// If we have a local entity tree set, then also update it.
|
// If we have a local entity tree set, then also update it.
|
||||||
|
@ -295,9 +284,7 @@ QUuid EntityScriptingInterface::addEntity(const EntityItemProperties& properties
|
||||||
|
|
||||||
// queue the packet
|
// queue the packet
|
||||||
if (success) {
|
if (success) {
|
||||||
emit debitEnergySource(cost);
|
|
||||||
queueEntityMessage(PacketType::EntityAdd, id, propertiesWithSimID);
|
queueEntityMessage(PacketType::EntityAdd, id, propertiesWithSimID);
|
||||||
|
|
||||||
return id;
|
return id;
|
||||||
} else {
|
} else {
|
||||||
return QUuid();
|
return QUuid();
|
||||||
|
@ -378,27 +365,9 @@ QUuid EntityScriptingInterface::editEntity(QUuid id, const EntityItemProperties&
|
||||||
|
|
||||||
EntityItemProperties properties = scriptSideProperties;
|
EntityItemProperties properties = scriptSideProperties;
|
||||||
|
|
||||||
auto dimensions = properties.getDimensions();
|
|
||||||
float volume = dimensions.x * dimensions.y * dimensions.z;
|
|
||||||
auto density = properties.getDensity();
|
|
||||||
auto newVelocity = properties.getVelocity().length();
|
|
||||||
float oldVelocity = { 0.0f };
|
|
||||||
|
|
||||||
EntityItemID entityID(id);
|
EntityItemID entityID(id);
|
||||||
if (!_entityTree) {
|
if (!_entityTree) {
|
||||||
queueEntityMessage(PacketType::EntityEdit, entityID, properties);
|
queueEntityMessage(PacketType::EntityEdit, entityID, properties);
|
||||||
|
|
||||||
//if there is no local entity entity tree, no existing velocity, use 0.
|
|
||||||
float cost = calculateCost(density * volume, oldVelocity, newVelocity);
|
|
||||||
cost *= costMultiplier;
|
|
||||||
|
|
||||||
if (cost > _currentAvatarEnergy) {
|
|
||||||
return QUuid();
|
|
||||||
} else {
|
|
||||||
//debit the avatar energy and continue
|
|
||||||
emit debitEnergySource(cost);
|
|
||||||
}
|
|
||||||
|
|
||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
// If we have a local entity tree set, then also update it.
|
// If we have a local entity tree set, then also update it.
|
||||||
|
@ -420,9 +389,6 @@ QUuid EntityScriptingInterface::editEntity(QUuid id, const EntityItemProperties&
|
||||||
// All of parentID, parentJointIndex, position, rotation are needed to make sense of any of them.
|
// All of parentID, parentJointIndex, position, rotation are needed to make sense of any of them.
|
||||||
// If any of these changed, pull any missing properties from the entity.
|
// If any of these changed, pull any missing properties from the entity.
|
||||||
|
|
||||||
//existing entity, retrieve old velocity for check down below
|
|
||||||
oldVelocity = entity->getWorldVelocity().length();
|
|
||||||
|
|
||||||
if (!scriptSideProperties.parentIDChanged()) {
|
if (!scriptSideProperties.parentIDChanged()) {
|
||||||
properties.setParentID(entity->getParentID());
|
properties.setParentID(entity->getParentID());
|
||||||
}
|
}
|
||||||
|
@ -442,23 +408,11 @@ QUuid EntityScriptingInterface::editEntity(QUuid id, const EntityItemProperties&
|
||||||
properties.setClientOnly(entity->getClientOnly());
|
properties.setClientOnly(entity->getClientOnly());
|
||||||
properties.setOwningAvatarID(entity->getOwningAvatarID());
|
properties.setOwningAvatarID(entity->getOwningAvatarID());
|
||||||
properties = convertPropertiesFromScriptSemantics(properties, properties.getScalesWithParent());
|
properties = convertPropertiesFromScriptSemantics(properties, properties.getScalesWithParent());
|
||||||
|
updatedEntity = _entityTree->updateEntity(entityID, properties);
|
||||||
float cost = calculateCost(density * volume, oldVelocity, newVelocity);
|
|
||||||
cost *= costMultiplier;
|
|
||||||
|
|
||||||
if (cost > _currentAvatarEnergy) {
|
|
||||||
updatedEntity = false;
|
|
||||||
} else {
|
|
||||||
//debit the avatar energy and continue
|
|
||||||
updatedEntity = _entityTree->updateEntity(entityID, properties);
|
|
||||||
if (updatedEntity) {
|
|
||||||
emit debitEnergySource(cost);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// FIXME: We need to figure out a better way to handle this. Allowing these edits to go through potentially
|
// FIXME: We need to figure out a better way to handle this. Allowing these edits to go through potentially
|
||||||
// breaks avatar energy and entities that are parented.
|
// breaks entities that are parented.
|
||||||
//
|
//
|
||||||
// To handle cases where a script needs to edit an entity with a _known_ entity id but doesn't exist
|
// To handle cases where a script needs to edit an entity with a _known_ entity id but doesn't exist
|
||||||
// in the local entity tree, we need to allow those edits to go through to the server.
|
// in the local entity tree, we need to allow those edits to go through to the server.
|
||||||
|
@ -577,21 +531,6 @@ void EntityScriptingInterface::deleteEntity(QUuid id) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto dimensions = entity->getScaledDimensions();
|
|
||||||
float volume = dimensions.x * dimensions.y * dimensions.z;
|
|
||||||
auto density = entity->getDensity();
|
|
||||||
auto velocity = entity->getWorldVelocity().length();
|
|
||||||
float cost = calculateCost(density * volume, velocity, 0);
|
|
||||||
cost *= costMultiplier;
|
|
||||||
|
|
||||||
if (cost > _currentAvatarEnergy) {
|
|
||||||
shouldDelete = false;
|
|
||||||
return;
|
|
||||||
} else {
|
|
||||||
//debit the avatar energy and continue
|
|
||||||
emit debitEnergySource(cost);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (entity->getLocked()) {
|
if (entity->getLocked()) {
|
||||||
shouldDelete = false;
|
shouldDelete = false;
|
||||||
} else {
|
} else {
|
||||||
|
@ -1812,23 +1751,6 @@ void EntityScriptingInterface::emitScriptEvent(const EntityItemID& entityID, con
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
float EntityScriptingInterface::calculateCost(float mass, float oldVelocity, float newVelocity) {
|
|
||||||
return std::abs(mass * (newVelocity - oldVelocity));
|
|
||||||
}
|
|
||||||
|
|
||||||
void EntityScriptingInterface::setCurrentAvatarEnergy(float energy) {
|
|
||||||
// qCDebug(entities) << "NEW AVATAR ENERGY IN ENTITY SCRIPTING INTERFACE: " << energy;
|
|
||||||
_currentAvatarEnergy = energy;
|
|
||||||
}
|
|
||||||
|
|
||||||
float EntityScriptingInterface::getCostMultiplier() {
|
|
||||||
return costMultiplier;
|
|
||||||
}
|
|
||||||
|
|
||||||
void EntityScriptingInterface::setCostMultiplier(float value) {
|
|
||||||
costMultiplier = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO move this someplace that makes more sense...
|
// TODO move this someplace that makes more sense...
|
||||||
bool EntityScriptingInterface::AABoxIntersectsCapsule(const glm::vec3& low, const glm::vec3& dimensions,
|
bool EntityScriptingInterface::AABoxIntersectsCapsule(const glm::vec3& low, const glm::vec3& dimensions,
|
||||||
const glm::vec3& start, const glm::vec3& end, float radius) {
|
const glm::vec3& start, const glm::vec3& end, float radius) {
|
||||||
|
|
|
@ -94,8 +94,6 @@ void RayToEntityIntersectionResultFromScriptValue(const QScriptValue& object, Ra
|
||||||
* Interface has displayed and so knows about.
|
* Interface has displayed and so knows about.
|
||||||
*
|
*
|
||||||
* @namespace Entities
|
* @namespace Entities
|
||||||
* @property {number} currentAvatarEnergy - <strong>Deprecated</strong>
|
|
||||||
* @property {number} costMultiplier - <strong>Deprecated</strong>
|
|
||||||
* @property {Uuid} keyboardFocusEntity - Get or set the {@link Entities.EntityType|Web} entity that has keyboard focus.
|
* @property {Uuid} keyboardFocusEntity - Get or set the {@link Entities.EntityType|Web} entity that has keyboard focus.
|
||||||
* If no entity has keyboard focus, get returns <code>null</code>; set to <code>null</code> or {@link Uuid|Uuid.NULL} to
|
* If no entity has keyboard focus, get returns <code>null</code>; set to <code>null</code> or {@link Uuid|Uuid.NULL} to
|
||||||
* clear keyboard focus.
|
* clear keyboard focus.
|
||||||
|
@ -104,8 +102,6 @@ void RayToEntityIntersectionResultFromScriptValue(const QScriptValue& object, Ra
|
||||||
class EntityScriptingInterface : public OctreeScriptingInterface, public Dependency {
|
class EntityScriptingInterface : public OctreeScriptingInterface, public Dependency {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
Q_PROPERTY(float currentAvatarEnergy READ getCurrentAvatarEnergy WRITE setCurrentAvatarEnergy)
|
|
||||||
Q_PROPERTY(float costMultiplier READ getCostMultiplier WRITE setCostMultiplier)
|
|
||||||
Q_PROPERTY(QUuid keyboardFocusEntity READ getKeyboardFocusEntity WRITE setKeyboardFocusEntity)
|
Q_PROPERTY(QUuid keyboardFocusEntity READ getKeyboardFocusEntity WRITE setKeyboardFocusEntity)
|
||||||
|
|
||||||
friend EntityPropertyMetadataRequest;
|
friend EntityPropertyMetadataRequest;
|
||||||
|
@ -126,7 +122,6 @@ public:
|
||||||
void setEntityTree(EntityTreePointer modelTree);
|
void setEntityTree(EntityTreePointer modelTree);
|
||||||
EntityTreePointer getEntityTree() { return _entityTree; }
|
EntityTreePointer getEntityTree() { return _entityTree; }
|
||||||
void setEntitiesScriptEngine(QSharedPointer<EntitiesScriptEngineProvider> engine);
|
void setEntitiesScriptEngine(QSharedPointer<EntitiesScriptEngineProvider> engine);
|
||||||
float calculateCost(float mass, float oldVelocity, float newVelocity);
|
|
||||||
|
|
||||||
void resetActivityTracking();
|
void resetActivityTracking();
|
||||||
ActivityTracking getActivityTracking() const { return _activityTracking; }
|
ActivityTracking getActivityTracking() const { return _activityTracking; }
|
||||||
|
@ -1834,14 +1829,6 @@ signals:
|
||||||
*/
|
*/
|
||||||
void clearingEntities();
|
void clearingEntities();
|
||||||
|
|
||||||
/**jsdoc
|
|
||||||
* @function Entities.debitEnergySource
|
|
||||||
* @param {number} value - The amount to debit.
|
|
||||||
* @returns {Signal}
|
|
||||||
* @deprecated This function is deprecated and will soon be removed.
|
|
||||||
*/
|
|
||||||
void debitEnergySource(float value);
|
|
||||||
|
|
||||||
/**jsdoc
|
/**jsdoc
|
||||||
* Triggered in when a script in a {@link Entities.EntityType|Web} entity's Web page script sends an event over the
|
* Triggered in when a script in a {@link Entities.EntityType|Web} entity's Web page script sends an event over the
|
||||||
* script's <code>EventBridge</code>.
|
* script's <code>EventBridge</code>.
|
||||||
|
@ -1882,14 +1869,8 @@ private:
|
||||||
QSharedPointer<EntitiesScriptEngineProvider> _entitiesScriptEngine;
|
QSharedPointer<EntitiesScriptEngineProvider> _entitiesScriptEngine;
|
||||||
|
|
||||||
bool _bidOnSimulationOwnership { false };
|
bool _bidOnSimulationOwnership { false };
|
||||||
float _currentAvatarEnergy = { FLT_MAX };
|
|
||||||
float getCurrentAvatarEnergy() { return _currentAvatarEnergy; }
|
|
||||||
void setCurrentAvatarEnergy(float energy);
|
|
||||||
|
|
||||||
ActivityTracking _activityTracking;
|
ActivityTracking _activityTracking;
|
||||||
float costMultiplier = { 0.01f };
|
|
||||||
float getCostMultiplier();
|
|
||||||
void setCostMultiplier(float value);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // hifi_EntityScriptingInterface_h
|
#endif // hifi_EntityScriptingInterface_h
|
||||||
|
|
|
@ -425,8 +425,8 @@ bool EntityTree::updateEntity(EntityItemPointer entity, const EntityItemProperti
|
||||||
if (!childEntity) {
|
if (!childEntity) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
EntityTreeElementPointer containingElement = childEntity->getElement();
|
EntityTreeElementPointer childContainingElement = childEntity->getElement();
|
||||||
if (!containingElement) {
|
if (!childContainingElement) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -440,7 +440,7 @@ bool EntityTree::updateEntity(EntityItemPointer entity, const EntityItemProperti
|
||||||
addToNeedsParentFixupList(childEntity);
|
addToNeedsParentFixupList(childEntity);
|
||||||
}
|
}
|
||||||
|
|
||||||
UpdateEntityOperator theChildOperator(getThisPointer(), containingElement, childEntity, queryCube);
|
UpdateEntityOperator theChildOperator(getThisPointer(), childContainingElement, childEntity, queryCube);
|
||||||
recurseTreeWithOperator(&theChildOperator);
|
recurseTreeWithOperator(&theChildOperator);
|
||||||
foreach (SpatiallyNestablePointer childChild, childEntity->getChildren()) {
|
foreach (SpatiallyNestablePointer childChild, childEntity->getChildren()) {
|
||||||
if (childChild && childChild->getNestableType() == NestableType::Entity) {
|
if (childChild && childChild->getNestableType() == NestableType::Entity) {
|
||||||
|
|
|
@ -288,7 +288,7 @@ OctreeElementPointer UpdateEntityOperator::possiblyCreateChildAt(const OctreeEle
|
||||||
int indexOfChildContainingNewEntity = element->getMyChildContaining(_newEntityBox);
|
int indexOfChildContainingNewEntity = element->getMyChildContaining(_newEntityBox);
|
||||||
|
|
||||||
if (childIndex == indexOfChildContainingNewEntity) {
|
if (childIndex == indexOfChildContainingNewEntity) {
|
||||||
return element->addChildAtIndex(childIndex);;
|
return element->addChildAtIndex(childIndex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -55,6 +55,7 @@ GLBackend::CommandCall GLBackend::_commandCalls[Batch::NUM_COMMANDS] =
|
||||||
(&::gpu::gl::GLBackend::do_setUniformBuffer),
|
(&::gpu::gl::GLBackend::do_setUniformBuffer),
|
||||||
(&::gpu::gl::GLBackend::do_setResourceBuffer),
|
(&::gpu::gl::GLBackend::do_setResourceBuffer),
|
||||||
(&::gpu::gl::GLBackend::do_setResourceTexture),
|
(&::gpu::gl::GLBackend::do_setResourceTexture),
|
||||||
|
(&::gpu::gl::GLBackend::do_setResourceTextureTable),
|
||||||
(&::gpu::gl::GLBackend::do_setResourceFramebufferSwapChainTexture),
|
(&::gpu::gl::GLBackend::do_setResourceFramebufferSwapChainTexture),
|
||||||
|
|
||||||
(&::gpu::gl::GLBackend::do_setFramebuffer),
|
(&::gpu::gl::GLBackend::do_setFramebuffer),
|
||||||
|
|
|
@ -40,7 +40,6 @@
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Let these be configured by the one define picked above
|
// Let these be configured by the one define picked above
|
||||||
#ifdef GPU_STEREO_TECHNIQUE_DOUBLED_SIMPLE
|
#ifdef GPU_STEREO_TECHNIQUE_DOUBLED_SIMPLE
|
||||||
#define GPU_STEREO_DRAWCALL_DOUBLED
|
#define GPU_STEREO_DRAWCALL_DOUBLED
|
||||||
|
@ -102,6 +101,12 @@ public:
|
||||||
static const int MAX_NUM_RESOURCE_TEXTURES = 16;
|
static const int MAX_NUM_RESOURCE_TEXTURES = 16;
|
||||||
size_t getMaxNumResourceTextures() const { return MAX_NUM_RESOURCE_TEXTURES; }
|
size_t getMaxNumResourceTextures() const { return MAX_NUM_RESOURCE_TEXTURES; }
|
||||||
|
|
||||||
|
// Texture Tables offers 2 dedicated slot (taken from the ubo slots)
|
||||||
|
static const int MAX_NUM_RESOURCE_TABLE_TEXTURES = 2;
|
||||||
|
static const int RESOURCE_TABLE_TEXTURE_SLOT_OFFSET = TRANSFORM_CAMERA_SLOT + 1;
|
||||||
|
size_t getMaxNumResourceTextureTables() const { return MAX_NUM_RESOURCE_TABLE_TEXTURES; }
|
||||||
|
|
||||||
|
|
||||||
// Draw Stage
|
// Draw Stage
|
||||||
virtual void do_draw(const Batch& batch, size_t paramOffset) = 0;
|
virtual void do_draw(const Batch& batch, size_t paramOffset) = 0;
|
||||||
virtual void do_drawIndexed(const Batch& batch, size_t paramOffset) = 0;
|
virtual void do_drawIndexed(const Batch& batch, size_t paramOffset) = 0;
|
||||||
|
@ -130,6 +135,7 @@ public:
|
||||||
// Resource Stage
|
// Resource Stage
|
||||||
virtual void do_setResourceBuffer(const Batch& batch, size_t paramOffset) final;
|
virtual void do_setResourceBuffer(const Batch& batch, size_t paramOffset) final;
|
||||||
virtual void do_setResourceTexture(const Batch& batch, size_t paramOffset) final;
|
virtual void do_setResourceTexture(const Batch& batch, size_t paramOffset) final;
|
||||||
|
virtual void do_setResourceTextureTable(const Batch& batch, size_t paramOffset);
|
||||||
virtual void do_setResourceFramebufferSwapChainTexture(const Batch& batch, size_t paramOffset) final;
|
virtual void do_setResourceFramebufferSwapChainTexture(const Batch& batch, size_t paramOffset) final;
|
||||||
|
|
||||||
// Pipeline Stage
|
// Pipeline Stage
|
||||||
|
@ -230,6 +236,10 @@ protected:
|
||||||
|
|
||||||
void recycle() const override;
|
void recycle() const override;
|
||||||
|
|
||||||
|
// FIXME instead of a single flag, create a features struct similar to
|
||||||
|
// https://www.khronos.org/registry/vulkan/specs/1.0/man/html/VkPhysicalDeviceFeatures.html
|
||||||
|
virtual bool supportsBindless() const { return false; }
|
||||||
|
|
||||||
static const size_t INVALID_OFFSET = (size_t)-1;
|
static const size_t INVALID_OFFSET = (size_t)-1;
|
||||||
bool _inRenderTransferPass { false };
|
bool _inRenderTransferPass { false };
|
||||||
int32_t _uboAlignment { 0 };
|
int32_t _uboAlignment { 0 };
|
||||||
|
@ -389,6 +399,10 @@ protected:
|
||||||
virtual bool bindResourceBuffer(uint32_t slot, BufferPointer& buffer) = 0;
|
virtual bool bindResourceBuffer(uint32_t slot, BufferPointer& buffer) = 0;
|
||||||
virtual void releaseResourceBuffer(uint32_t slot) = 0;
|
virtual void releaseResourceBuffer(uint32_t slot) = 0;
|
||||||
|
|
||||||
|
// Helper function that provides common code used by do_setResourceTexture and
|
||||||
|
// do_setResourceTextureTable (in non-bindless mode)
|
||||||
|
void bindResourceTexture(uint32_t slot, const TexturePointer& texture);
|
||||||
|
|
||||||
// update resource cache and do the gl unbind call with the current gpu::Texture cached at slot s
|
// update resource cache and do the gl unbind call with the current gpu::Texture cached at slot s
|
||||||
void releaseResourceTexture(uint32_t slot);
|
void releaseResourceTexture(uint32_t slot);
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
//
|
//
|
||||||
// GLBackendPipeline.cpp
|
// GLBackendPipeline.cpp
|
||||||
// libraries/gpu/src/gpu
|
// libraries/gpu/src/gpu
|
||||||
//
|
//
|
||||||
|
@ -9,6 +9,8 @@
|
||||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
//
|
//
|
||||||
#include "GLBackend.h"
|
#include "GLBackend.h"
|
||||||
|
#include <gpu/TextureTable.h>
|
||||||
|
|
||||||
#include "GLShared.h"
|
#include "GLShared.h"
|
||||||
#include "GLPipeline.h"
|
#include "GLPipeline.h"
|
||||||
#include "GLShader.h"
|
#include "GLShader.h"
|
||||||
|
@ -247,8 +249,11 @@ void GLBackend::do_setResourceTexture(const Batch& batch, size_t paramOffset) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
TexturePointer resourceTexture = batch._textures.get(batch._params[paramOffset + 0]._uint);
|
const auto& resourceTexture = batch._textures.get(batch._params[paramOffset + 0]._uint);
|
||||||
|
bindResourceTexture(slot, resourceTexture);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GLBackend::bindResourceTexture(uint32_t slot, const TexturePointer& resourceTexture) {
|
||||||
if (!resourceTexture) {
|
if (!resourceTexture) {
|
||||||
releaseResourceTexture(slot);
|
releaseResourceTexture(slot);
|
||||||
return;
|
return;
|
||||||
|
@ -306,6 +311,19 @@ void GLBackend::setResourceTexture(unsigned int slot, const TexturePointer& reso
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GLBackend::do_setResourceTextureTable(const Batch& batch, size_t paramOffset) {
|
||||||
|
const auto& textureTablePointer = batch._textureTables.get(batch._params[paramOffset]._uint);
|
||||||
|
if (!textureTablePointer) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto& textureTable = *textureTablePointer;
|
||||||
|
const auto& textures = textureTable.getTextures();
|
||||||
|
for (GLuint slot = 0; slot < textures.size(); ++slot) {
|
||||||
|
bindResourceTexture(slot, textures[slot]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
int GLBackend::ResourceStageState::findEmptyTextureSlot() const {
|
int GLBackend::ResourceStageState::findEmptyTextureSlot() const {
|
||||||
// start from the end of the slots, try to find an empty one that can be used
|
// start from the end of the slots, try to find an empty one that can be used
|
||||||
for (auto i = MAX_NUM_RESOURCE_TEXTURES - 1; i > 0; i--) {
|
for (auto i = MAX_NUM_RESOURCE_TEXTURES - 1; i > 0; i--) {
|
||||||
|
@ -315,4 +333,3 @@ int GLBackend::ResourceStageState::findEmptyTextureSlot() const {
|
||||||
}
|
}
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -78,6 +78,11 @@ R"SHADER(
|
||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// TextureTable specific defines
|
||||||
|
static const std::string textureTableVersion {
|
||||||
|
"#extension GL_ARB_bindless_texture : require\n#define GPU_TEXTURE_TABLE_BINDLESS\n"
|
||||||
|
};
|
||||||
|
|
||||||
// Versions specific of the shader
|
// Versions specific of the shader
|
||||||
static const std::array<std::string, GLShader::NumVersions> VERSION_DEFINES { {
|
static const std::array<std::string, GLShader::NumVersions> VERSION_DEFINES { {
|
||||||
"",
|
"",
|
||||||
|
@ -96,9 +101,9 @@ GLShader* GLBackend::compileBackendShader(const Shader& shader, const Shader::Co
|
||||||
auto& shaderObject = shaderObjects[version];
|
auto& shaderObject = shaderObjects[version];
|
||||||
|
|
||||||
std::string shaderDefines = getBackendShaderHeader() + "\n"
|
std::string shaderDefines = getBackendShaderHeader() + "\n"
|
||||||
|
+ (supportsBindless() ? textureTableVersion : "\n")
|
||||||
+ DOMAIN_DEFINES[shader.getType()] + "\n"
|
+ DOMAIN_DEFINES[shader.getType()] + "\n"
|
||||||
+ VERSION_DEFINES[version];
|
+ VERSION_DEFINES[version];
|
||||||
|
|
||||||
if (handler) {
|
if (handler) {
|
||||||
bool retest = true;
|
bool retest = true;
|
||||||
std::string currentSrc = shaderSource;
|
std::string currentSrc = shaderSource;
|
||||||
|
@ -120,7 +125,7 @@ GLShader* GLBackend::compileBackendShader(const Shader& shader, const Shader::Co
|
||||||
compilationLogs[version].compiled = ::gl::compileShader(shaderDomain, shaderSource, shaderDefines, shaderObject.glshader, compilationLogs[version].message);
|
compilationLogs[version].compiled = ::gl::compileShader(shaderDomain, shaderSource, shaderDefines, shaderObject.glshader, compilationLogs[version].message);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!compilationLogs[version].compiled) {
|
if (!compilationLogs[version].compiled) {
|
||||||
qCWarning(gpugllogging) << "GLBackend::compileBackendProgram - Shader didn't compile:\n" << compilationLogs[version].message.c_str();
|
qCWarning(gpugllogging) << "GLBackend::compileBackendProgram - Shader didn't compile:\n" << compilationLogs[version].message.c_str();
|
||||||
shader.setCompilationLogs(compilationLogs);
|
shader.setCompilationLogs(compilationLogs);
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
|
@ -167,7 +167,6 @@ void GL41Texture::syncSampler() const {
|
||||||
glTexParameteri(_target, GL_TEXTURE_WRAP_R, WRAP_MODES[sampler.getWrapModeW()]);
|
glTexParameteri(_target, GL_TEXTURE_WRAP_R, WRAP_MODES[sampler.getWrapModeW()]);
|
||||||
|
|
||||||
glTexParameterfv(_target, GL_TEXTURE_BORDER_COLOR, (const float*)&sampler.getBorderColor());
|
glTexParameterfv(_target, GL_TEXTURE_BORDER_COLOR, (const float*)&sampler.getBorderColor());
|
||||||
glTexParameteri(_target, GL_TEXTURE_BASE_LEVEL, (uint16)sampler.getMipOffset());
|
|
||||||
|
|
||||||
glTexParameterf(_target, GL_TEXTURE_MIN_LOD, (float)sampler.getMinMip());
|
glTexParameterf(_target, GL_TEXTURE_MIN_LOD, (float)sampler.getMinMip());
|
||||||
glTexParameterf(_target, GL_TEXTURE_MAX_LOD, (sampler.getMaxMip() == Sampler::MAX_MIP_LEVEL ? 1000.f : sampler.getMaxMip()));
|
glTexParameterf(_target, GL_TEXTURE_MAX_LOD, (sampler.getMaxMip() == Sampler::MAX_MIP_LEVEL ? 1000.f : sampler.getMaxMip()));
|
||||||
|
@ -206,9 +205,6 @@ void GL41FixedAllocationTexture::allocateStorage() const {
|
||||||
void GL41FixedAllocationTexture::syncSampler() const {
|
void GL41FixedAllocationTexture::syncSampler() const {
|
||||||
Parent::syncSampler();
|
Parent::syncSampler();
|
||||||
const Sampler& sampler = _gpuObject.getSampler();
|
const Sampler& sampler = _gpuObject.getSampler();
|
||||||
auto baseMip = std::max<uint16_t>(sampler.getMipOffset(), sampler.getMinMip());
|
|
||||||
|
|
||||||
glTexParameteri(_target, GL_TEXTURE_BASE_LEVEL, baseMip);
|
|
||||||
glTexParameterf(_target, GL_TEXTURE_MIN_LOD, (float)sampler.getMinMip());
|
glTexParameterf(_target, GL_TEXTURE_MIN_LOD, (float)sampler.getMinMip());
|
||||||
glTexParameterf(_target, GL_TEXTURE_MAX_LOD, (sampler.getMaxMip() == Sampler::MAX_MIP_LEVEL ? 1000.0f : sampler.getMaxMip()));
|
glTexParameterf(_target, GL_TEXTURE_MAX_LOD, (sampler.getMaxMip() == Sampler::MAX_MIP_LEVEL ? 1000.0f : sampler.getMaxMip()));
|
||||||
}
|
}
|
||||||
|
@ -610,4 +606,3 @@ GL41ResourceTexture::GL41ResourceTexture(const std::weak_ptr<GLBackend>& backend
|
||||||
|
|
||||||
GL41ResourceTexture::~GL41ResourceTexture() {
|
GL41ResourceTexture::~GL41ResourceTexture() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -16,9 +16,11 @@
|
||||||
#include <gpu/gl/GLTexture.h>
|
#include <gpu/gl/GLTexture.h>
|
||||||
|
|
||||||
#include <thread>
|
#include <thread>
|
||||||
|
#include <gpu/TextureTable.h>
|
||||||
|
|
||||||
#define INCREMENTAL_TRANSFER 0
|
#define INCREMENTAL_TRANSFER 0
|
||||||
#define GPU_SSBO_TRANSFORM_OBJECT 1
|
#define GPU_SSBO_TRANSFORM_OBJECT 1
|
||||||
|
#define GPU_BINDLESS_TEXTURES 0
|
||||||
|
|
||||||
namespace gpu { namespace gl45 {
|
namespace gpu { namespace gl45 {
|
||||||
|
|
||||||
|
@ -31,6 +33,9 @@ class GL45Backend : public GLBackend {
|
||||||
friend class Context;
|
friend class Context;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
#if GPU_BINDLESS_TEXTURES
|
||||||
|
virtual bool supportsBindless() const override { return true; }
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef GPU_SSBO_TRANSFORM_OBJECT
|
#ifdef GPU_SSBO_TRANSFORM_OBJECT
|
||||||
static const GLint TRANSFORM_OBJECT_SLOT { 14 }; // SSBO binding slot
|
static const GLint TRANSFORM_OBJECT_SLOT { 14 }; // SSBO binding slot
|
||||||
|
@ -58,8 +63,62 @@ public:
|
||||||
void generateMips() const override;
|
void generateMips() const override;
|
||||||
Size copyMipFaceLinesFromTexture(uint16_t mip, uint8_t face, const uvec3& size, uint32_t yOffset, GLenum internalFormat, GLenum format, GLenum type, Size sourceSize, const void* sourcePointer) const override;
|
Size copyMipFaceLinesFromTexture(uint16_t mip, uint8_t face, const uvec3& size, uint32_t yOffset, GLenum internalFormat, GLenum format, GLenum type, Size sourceSize, const void* sourcePointer) const override;
|
||||||
void syncSampler() const override;
|
void syncSampler() const override;
|
||||||
|
|
||||||
|
#if GPU_BINDLESS_TEXTURES
|
||||||
|
bool isBindless() const {
|
||||||
|
return _bindless.operator bool();
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Bindless {
|
||||||
|
uint64_t handle{ 0 };
|
||||||
|
uint32_t minMip{ 0 };
|
||||||
|
uint32_t sampler{ 0 };
|
||||||
|
|
||||||
|
bool operator==(const Bindless& other) const {
|
||||||
|
return handle == other.handle && minMip == other.minMip && sampler == other.sampler;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator!=(const Bindless& other) const {
|
||||||
|
return !(*this == other);
|
||||||
|
}
|
||||||
|
|
||||||
|
operator bool() const {
|
||||||
|
return handle != 0;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
virtual const Bindless& getBindless() const;
|
||||||
|
void releaseBindless() const;
|
||||||
|
void recreateBindless() const;
|
||||||
|
private:
|
||||||
|
mutable Bindless _bindless;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static Sampler getInvalidSampler();
|
||||||
|
|
||||||
|
// This stores the texture handle (64 bits) in xy, the min mip available in z, and the sampler ID in w
|
||||||
|
mutable Sampler _cachedSampler{ getInvalidSampler() };
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#if GPU_BINDLESS_TEXTURES
|
||||||
|
class GL45TextureTable : public GLObject<TextureTable> {
|
||||||
|
static GLuint allocate();
|
||||||
|
using Parent = GLObject<TextureTable>;
|
||||||
|
public:
|
||||||
|
using BindlessArray = std::array<GL45Texture::Bindless, TextureTable::COUNT>;
|
||||||
|
|
||||||
|
GL45TextureTable(const std::weak_ptr<GLBackend>& backend, const TextureTable& texture);
|
||||||
|
~GL45TextureTable();
|
||||||
|
|
||||||
|
void update(const BindlessArray& newHandles);
|
||||||
|
|
||||||
|
// FIXME instead of making a buffer for each table, there should be a global buffer of all materials
|
||||||
|
// and we should store an offset into that buffer
|
||||||
|
BindlessArray _handles;
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// Textures that have fixed allocation sizes and cannot be managed at runtime
|
// Textures that have fixed allocation sizes and cannot be managed at runtime
|
||||||
//
|
//
|
||||||
|
@ -74,6 +133,7 @@ public:
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
Size size() const override { return _size; }
|
Size size() const override { return _size; }
|
||||||
|
|
||||||
void allocateStorage() const;
|
void allocateStorage() const;
|
||||||
void syncSampler() const override;
|
void syncSampler() const override;
|
||||||
const Size _size { 0 };
|
const Size _size { 0 };
|
||||||
|
@ -104,7 +164,6 @@ public:
|
||||||
friend class GL45Backend;
|
friend class GL45Backend;
|
||||||
using PromoteLambda = std::function<void()>;
|
using PromoteLambda = std::function<void()>;
|
||||||
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
GL45VariableAllocationTexture(const std::weak_ptr<GLBackend>& backend, const Texture& texture);
|
GL45VariableAllocationTexture(const std::weak_ptr<GLBackend>& backend, const Texture& texture);
|
||||||
~GL45VariableAllocationTexture();
|
~GL45VariableAllocationTexture();
|
||||||
|
@ -114,6 +173,9 @@ public:
|
||||||
Size copyMipFaceLinesFromTexture(uint16_t mip, uint8_t face, const uvec3& size, uint32_t yOffset, GLenum internalFormat, GLenum format, GLenum type, Size sourceSize, const void* sourcePointer) const override;
|
Size copyMipFaceLinesFromTexture(uint16_t mip, uint8_t face, const uvec3& size, uint32_t yOffset, GLenum internalFormat, GLenum format, GLenum type, Size sourceSize, const void* sourcePointer) const override;
|
||||||
void copyTextureMipsInGPUMem(GLuint srcId, GLuint destId, uint16_t srcMipOffset, uint16_t destMipOffset, uint16_t populatedMips) override;
|
void copyTextureMipsInGPUMem(GLuint srcId, GLuint destId, uint16_t srcMipOffset, uint16_t destMipOffset, uint16_t populatedMips) override;
|
||||||
|
|
||||||
|
#if GPU_BINDLESS_TEXTURES
|
||||||
|
virtual const Bindless& getBindless() const override;
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
class GL45ResourceTexture : public GL45VariableAllocationTexture {
|
class GL45ResourceTexture : public GL45VariableAllocationTexture {
|
||||||
|
@ -182,6 +244,7 @@ protected:
|
||||||
GLuint getQueryID(const QueryPointer& query) override;
|
GLuint getQueryID(const QueryPointer& query) override;
|
||||||
GLQuery* syncGPUObject(const Query& query) override;
|
GLQuery* syncGPUObject(const Query& query) override;
|
||||||
|
|
||||||
|
|
||||||
// Draw Stage
|
// Draw Stage
|
||||||
void do_draw(const Batch& batch, size_t paramOffset) override;
|
void do_draw(const Batch& batch, size_t paramOffset) override;
|
||||||
void do_drawIndexed(const Batch& batch, size_t paramOffset) override;
|
void do_drawIndexed(const Batch& batch, size_t paramOffset) override;
|
||||||
|
@ -213,6 +276,12 @@ protected:
|
||||||
|
|
||||||
// Texture Management Stage
|
// Texture Management Stage
|
||||||
void initTextureManagementStage() override;
|
void initTextureManagementStage() override;
|
||||||
|
|
||||||
|
#if GPU_BINDLESS_TEXTURES
|
||||||
|
GL45TextureTable* syncGPUObject(const TextureTablePointer& textureTable);
|
||||||
|
// Resource stage
|
||||||
|
void do_setResourceTextureTable(const Batch& batch, size_t paramOffset) override;
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
} }
|
} }
|
||||||
|
|
|
@ -165,6 +165,11 @@ void GL45Backend::makeProgramBindings(ShaderObject& shaderObject) {
|
||||||
shaderObject.transformCameraSlot = gpu::TRANSFORM_CAMERA_SLOT;
|
shaderObject.transformCameraSlot = gpu::TRANSFORM_CAMERA_SLOT;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
loc = glGetUniformBlockIndex(glprogram, "gpu_resourceTextureTable0");
|
||||||
|
if (loc >= 0) {
|
||||||
|
glUniformBlockBinding(glprogram, loc, RESOURCE_TABLE_TEXTURE_SLOT_OFFSET);
|
||||||
|
}
|
||||||
|
|
||||||
(void)CHECK_GL_ERROR();
|
(void)CHECK_GL_ERROR();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -20,14 +20,17 @@
|
||||||
#include <QtCore/QDebug>
|
#include <QtCore/QDebug>
|
||||||
|
|
||||||
#include <NumericalConstants.h>
|
#include <NumericalConstants.h>
|
||||||
|
#include <gl/Context.h>
|
||||||
|
#include <gpu/TextureTable.h>
|
||||||
#include <gpu/gl/GLTexelFormat.h>
|
#include <gpu/gl/GLTexelFormat.h>
|
||||||
|
|
||||||
using namespace gpu;
|
using namespace gpu;
|
||||||
using namespace gpu::gl;
|
using namespace gpu::gl;
|
||||||
using namespace gpu::gl45;
|
using namespace gpu::gl45;
|
||||||
|
|
||||||
#define SPARSE_PAGE_SIZE_OVERHEAD_ESTIMATE 1.3f
|
|
||||||
#define MAX_RESOURCE_TEXTURES_PER_FRAME 2
|
#define MAX_RESOURCE_TEXTURES_PER_FRAME 2
|
||||||
|
#define FORCE_STRICT_TEXTURE 0
|
||||||
|
#define ENABLE_SPARSE_TEXTURE 0
|
||||||
|
|
||||||
GLTexture* GL45Backend::syncGPUObject(const TexturePointer& texturePointer) {
|
GLTexture* GL45Backend::syncGPUObject(const TexturePointer& texturePointer) {
|
||||||
if (!texturePointer) {
|
if (!texturePointer) {
|
||||||
|
@ -51,14 +54,18 @@ GLTexture* GL45Backend::syncGPUObject(const TexturePointer& texturePointer) {
|
||||||
object = new GL45AttachmentTexture(shared_from_this(), texture);
|
object = new GL45AttachmentTexture(shared_from_this(), texture);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
#if FORCE_STRICT_TEXTURE
|
||||||
|
case TextureUsageType::RESOURCE:
|
||||||
|
#endif
|
||||||
case TextureUsageType::STRICT_RESOURCE:
|
case TextureUsageType::STRICT_RESOURCE:
|
||||||
qCDebug(gpugllogging) << "Strict texture " << texture.source().c_str();
|
qCDebug(gpugllogging) << "Strict texture " << texture.source().c_str();
|
||||||
object = new GL45StrictResourceTexture(shared_from_this(), texture);
|
object = new GL45StrictResourceTexture(shared_from_this(), texture);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
#if !FORCE_STRICT_TEXTURE
|
||||||
case TextureUsageType::RESOURCE: {
|
case TextureUsageType::RESOURCE: {
|
||||||
if (GL45VariableAllocationTexture::_frameTexturesCreated < MAX_RESOURCE_TEXTURES_PER_FRAME) {
|
if (GL45VariableAllocationTexture::_frameTexturesCreated < MAX_RESOURCE_TEXTURES_PER_FRAME) {
|
||||||
#if 0
|
#if ENABLE_SPARSE_TEXTURE
|
||||||
if (isTextureManagementSparseEnabled() && GL45Texture::isSparseEligible(texture)) {
|
if (isTextureManagementSparseEnabled() && GL45Texture::isSparseEligible(texture)) {
|
||||||
object = new GL45SparseResourceTexture(shared_from_this(), texture);
|
object = new GL45SparseResourceTexture(shared_from_this(), texture);
|
||||||
} else {
|
} else {
|
||||||
|
@ -76,7 +83,7 @@ GLTexture* GL45Backend::syncGPUObject(const TexturePointer& texturePointer) {
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
default:
|
default:
|
||||||
Q_UNREACHABLE();
|
Q_UNREACHABLE();
|
||||||
}
|
}
|
||||||
|
@ -114,6 +121,61 @@ void GL45Backend::initTextureManagementStage() {
|
||||||
|
|
||||||
using GL45Texture = GL45Backend::GL45Texture;
|
using GL45Texture = GL45Backend::GL45Texture;
|
||||||
|
|
||||||
|
|
||||||
|
class GLSamplerCache {
|
||||||
|
public:
|
||||||
|
GLuint getGLSampler(const Sampler& sampler) {
|
||||||
|
if (0 == _samplerCache.count(sampler)) {
|
||||||
|
GLuint result = 0;
|
||||||
|
glGenSamplers(1, &result);
|
||||||
|
const auto& fm = GLTexture::FILTER_MODES[sampler.getFilter()];
|
||||||
|
glSamplerParameteri(result, GL_TEXTURE_MIN_FILTER, fm.minFilter);
|
||||||
|
glSamplerParameteri(result, GL_TEXTURE_MAG_FILTER, fm.magFilter);
|
||||||
|
if (sampler.doComparison()) {
|
||||||
|
glSamplerParameteri(result, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_R_TO_TEXTURE_ARB);
|
||||||
|
glSamplerParameteri(result, GL_TEXTURE_COMPARE_FUNC, COMPARISON_TO_GL[sampler.getComparisonFunction()]);
|
||||||
|
} else {
|
||||||
|
glSamplerParameteri(result, GL_TEXTURE_COMPARE_MODE, GL_NONE);
|
||||||
|
}
|
||||||
|
|
||||||
|
glSamplerParameteri(result, GL_TEXTURE_WRAP_S, GLTexture::WRAP_MODES[sampler.getWrapModeU()]);
|
||||||
|
glSamplerParameteri(result, GL_TEXTURE_WRAP_T, GLTexture::WRAP_MODES[sampler.getWrapModeV()]);
|
||||||
|
glSamplerParameteri(result, GL_TEXTURE_WRAP_R, GLTexture::WRAP_MODES[sampler.getWrapModeW()]);
|
||||||
|
|
||||||
|
glSamplerParameterf(result, GL_TEXTURE_MAX_ANISOTROPY_EXT, sampler.getMaxAnisotropy());
|
||||||
|
glSamplerParameterfv(result, GL_TEXTURE_BORDER_COLOR, (const float*)&sampler.getBorderColor());
|
||||||
|
|
||||||
|
glSamplerParameterf(result, GL_TEXTURE_MIN_LOD, sampler.getMinMip());
|
||||||
|
glSamplerParameterf(result, GL_TEXTURE_MAX_LOD, (sampler.getMaxMip() == Sampler::MAX_MIP_LEVEL ? 1000.f : sampler.getMaxMip()));
|
||||||
|
_samplerCache[sampler] = result;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
return _samplerCache[sampler];
|
||||||
|
}
|
||||||
|
|
||||||
|
void releaseGLSampler(GLuint sampler) {
|
||||||
|
// NO OP
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::unordered_map<Sampler, GLuint> _samplerCache;
|
||||||
|
};
|
||||||
|
|
||||||
|
static GLSamplerCache SAMPLER_CACHE;
|
||||||
|
|
||||||
|
|
||||||
|
Sampler GL45Texture::getInvalidSampler() {
|
||||||
|
static Sampler INVALID_SAMPLER;
|
||||||
|
static std::once_flag once;
|
||||||
|
std::call_once(once, [] {
|
||||||
|
Sampler::Desc invalidDesc;
|
||||||
|
invalidDesc._borderColor = vec4(-1.0f);
|
||||||
|
INVALID_SAMPLER = Sampler(invalidDesc);
|
||||||
|
});
|
||||||
|
return INVALID_SAMPLER;
|
||||||
|
}
|
||||||
|
|
||||||
GL45Texture::GL45Texture(const std::weak_ptr<GLBackend>& backend, const Texture& texture)
|
GL45Texture::GL45Texture(const std::weak_ptr<GLBackend>& backend, const Texture& texture)
|
||||||
: GLTexture(backend, texture, allocate(texture)) {
|
: GLTexture(backend, texture, allocate(texture)) {
|
||||||
}
|
}
|
||||||
|
@ -121,10 +183,10 @@ GL45Texture::GL45Texture(const std::weak_ptr<GLBackend>& backend, const Texture&
|
||||||
GLuint GL45Texture::allocate(const Texture& texture) {
|
GLuint GL45Texture::allocate(const Texture& texture) {
|
||||||
GLuint result;
|
GLuint result;
|
||||||
glCreateTextures(getGLTextureType(texture), 1, &result);
|
glCreateTextures(getGLTextureType(texture), 1, &result);
|
||||||
#ifdef DEBUG
|
if (::gl::Context::enableDebugLogger()) {
|
||||||
auto source = texture.source();
|
auto source = texture.source();
|
||||||
glObjectLabel(GL_TEXTURE, result, (GLsizei)source.length(), source.data());
|
glObjectLabel(GL_TEXTURE, result, (GLsizei)source.length(), source.data());
|
||||||
#endif
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -189,8 +251,50 @@ Size GL45Texture::copyMipFaceLinesFromTexture(uint16_t mip, uint8_t face, const
|
||||||
return amountCopied;
|
return amountCopied;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if GPU_BINDLESS_TEXTURES
|
||||||
|
void GL45Texture::releaseBindless() const {
|
||||||
|
// Release the old handler
|
||||||
|
SAMPLER_CACHE.releaseGLSampler(_bindless.sampler);
|
||||||
|
glMakeTextureHandleNonResidentARB(_bindless.handle);
|
||||||
|
_bindless = Bindless();
|
||||||
|
}
|
||||||
|
|
||||||
|
void GL45Texture::recreateBindless() const {
|
||||||
|
if (isBindless()) {
|
||||||
|
releaseBindless();
|
||||||
|
} else {
|
||||||
|
// Once a texture is about to become bindless, it's base mip level MUST be set to 0
|
||||||
|
glTextureParameteri(_id, GL_TEXTURE_BASE_LEVEL, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
_bindless.sampler = SAMPLER_CACHE.getGLSampler(_cachedSampler);
|
||||||
|
_bindless.handle = glGetTextureSamplerHandleARB(_id, _bindless.sampler);
|
||||||
|
glMakeTextureHandleResidentARB(_bindless.handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
const GL45Texture::Bindless& GL45Texture::getBindless() const {
|
||||||
|
if (!_bindless) {
|
||||||
|
recreateBindless();
|
||||||
|
}
|
||||||
|
return _bindless;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
void GL45Texture::syncSampler() const {
|
void GL45Texture::syncSampler() const {
|
||||||
const Sampler& sampler = _gpuObject.getSampler();
|
const Sampler& sampler = _gpuObject.getSampler();
|
||||||
|
if (_cachedSampler == sampler) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_cachedSampler = sampler;
|
||||||
|
|
||||||
|
#if GPU_BINDLESS_TEXTURES
|
||||||
|
if (isBindless()) {
|
||||||
|
recreateBindless();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
const auto& fm = FILTER_MODES[sampler.getFilter()];
|
const auto& fm = FILTER_MODES[sampler.getFilter()];
|
||||||
glTextureParameteri(_id, GL_TEXTURE_MIN_FILTER, fm.minFilter);
|
glTextureParameteri(_id, GL_TEXTURE_MIN_FILTER, fm.minFilter);
|
||||||
|
@ -214,6 +318,8 @@ void GL45Texture::syncSampler() const {
|
||||||
glTextureParameterf(_id, GL_TEXTURE_MAX_LOD, (sampler.getMaxMip() == Sampler::MAX_MIP_LEVEL ? 1000.f : sampler.getMaxMip()));
|
glTextureParameterf(_id, GL_TEXTURE_MAX_LOD, (sampler.getMaxMip() == Sampler::MAX_MIP_LEVEL ? 1000.f : sampler.getMaxMip()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Fixed allocation textures, used for strict resources & framebuffer attachments
|
||||||
|
|
||||||
using GL45FixedAllocationTexture = GL45Backend::GL45FixedAllocationTexture;
|
using GL45FixedAllocationTexture = GL45Backend::GL45FixedAllocationTexture;
|
||||||
|
|
||||||
GL45FixedAllocationTexture::GL45FixedAllocationTexture(const std::weak_ptr<GLBackend>& backend, const Texture& texture) : GL45Texture(backend, texture), _size(texture.evalTotalSize()) {
|
GL45FixedAllocationTexture::GL45FixedAllocationTexture(const std::weak_ptr<GLBackend>& backend, const Texture& texture) : GL45Texture(backend, texture), _size(texture.evalTotalSize()) {
|
||||||
|
@ -238,8 +344,6 @@ void GL45FixedAllocationTexture::allocateStorage() const {
|
||||||
void GL45FixedAllocationTexture::syncSampler() const {
|
void GL45FixedAllocationTexture::syncSampler() const {
|
||||||
Parent::syncSampler();
|
Parent::syncSampler();
|
||||||
const Sampler& sampler = _gpuObject.getSampler();
|
const Sampler& sampler = _gpuObject.getSampler();
|
||||||
auto baseMip = std::max<uint16_t>(sampler.getMipOffset(), sampler.getMinMip());
|
|
||||||
glTextureParameteri(_id, GL_TEXTURE_BASE_LEVEL, baseMip);
|
|
||||||
glTextureParameterf(_id, GL_TEXTURE_MIN_LOD, (float)sampler.getMinMip());
|
glTextureParameterf(_id, GL_TEXTURE_MIN_LOD, (float)sampler.getMinMip());
|
||||||
glTextureParameterf(_id, GL_TEXTURE_MAX_LOD, (sampler.getMaxMip() == Sampler::MAX_MIP_LEVEL ? 1000.f : sampler.getMaxMip()));
|
glTextureParameterf(_id, GL_TEXTURE_MAX_LOD, (sampler.getMaxMip() == Sampler::MAX_MIP_LEVEL ? 1000.f : sampler.getMaxMip()));
|
||||||
}
|
}
|
||||||
|
@ -275,6 +379,9 @@ GL45StrictResourceTexture::GL45StrictResourceTexture(const std::weak_ptr<GLBacke
|
||||||
if (texture.isAutogenerateMips()) {
|
if (texture.isAutogenerateMips()) {
|
||||||
generateMips();
|
generateMips();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Re-sync the sampler to force access to the new mip level
|
||||||
|
syncSampler();
|
||||||
}
|
}
|
||||||
|
|
||||||
GL45StrictResourceTexture::~GL45StrictResourceTexture() {
|
GL45StrictResourceTexture::~GL45StrictResourceTexture() {
|
||||||
|
@ -282,3 +389,87 @@ GL45StrictResourceTexture::~GL45StrictResourceTexture() {
|
||||||
Backend::textureResidentGPUMemSize.update(size(), 0);
|
Backend::textureResidentGPUMemSize.update(size(), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Encapsulate bindless textures
|
||||||
|
#if GPU_BINDLESS_TEXTURES
|
||||||
|
using GL45TextureTable = GL45Backend::GL45TextureTable;
|
||||||
|
|
||||||
|
GLuint GL45TextureTable::allocate() {
|
||||||
|
GLuint result;
|
||||||
|
glCreateBuffers(1, &result);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
GL45TextureTable::GL45TextureTable(const std::weak_ptr<GLBackend>& backend, const TextureTable& textureTable)
|
||||||
|
: Parent(backend, textureTable, allocate()){
|
||||||
|
Backend::setGPUObject(textureTable, this);
|
||||||
|
// FIXME include these in overall buffer storage reporting
|
||||||
|
glNamedBufferStorage(_id, sizeof(uvec4) * TextureTable::COUNT, nullptr, GL_DYNAMIC_STORAGE_BIT);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GL45TextureTable::update(const BindlessArray& handles) {
|
||||||
|
if (_handles != handles) {
|
||||||
|
_handles = handles;
|
||||||
|
// FIXME include these in overall buffer storage reporting
|
||||||
|
// FIXME use a single shared buffer for bindless data
|
||||||
|
glNamedBufferSubData(_id, 0, sizeof(GL45Texture::Bindless) * TextureTable::COUNT, &_handles[0]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
GL45TextureTable::~GL45TextureTable() {
|
||||||
|
if (_id) {
|
||||||
|
auto backend = _backend.lock();
|
||||||
|
if (backend) {
|
||||||
|
// FIXME include these in overall buffer storage reporting
|
||||||
|
backend->releaseBuffer(_id, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
GL45TextureTable* GL45Backend::syncGPUObject(const TextureTablePointer& textureTablePointer) {
|
||||||
|
const auto& textureTable = *textureTablePointer;
|
||||||
|
|
||||||
|
// Find the target handles
|
||||||
|
auto defaultTextures = gpu::TextureTable::getDefault()->getTextures();
|
||||||
|
auto textures = textureTable.getTextures();
|
||||||
|
GL45TextureTable::BindlessArray handles{};
|
||||||
|
for (size_t i = 0; i < textures.size(); ++i) {
|
||||||
|
auto texture = textures[i];
|
||||||
|
if (!texture) {
|
||||||
|
texture = defaultTextures[i];
|
||||||
|
}
|
||||||
|
if (!texture) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// FIXME what if we have a non-transferrable texture here?
|
||||||
|
auto gltexture = (GL45Texture*)syncGPUObject(texture);
|
||||||
|
if (!gltexture) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
handles[i] = gltexture->getBindless();
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the object hasn't been created, or the object definition is out of date, drop and re-create
|
||||||
|
GL45TextureTable* object = Backend::getGPUObject<GL45TextureTable>(textureTable);
|
||||||
|
|
||||||
|
if (!object) {
|
||||||
|
object = new GL45TextureTable(shared_from_this(), textureTable);
|
||||||
|
}
|
||||||
|
|
||||||
|
object->update(handles);
|
||||||
|
|
||||||
|
return object;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GL45Backend::do_setResourceTextureTable(const Batch& batch, size_t paramOffset) {
|
||||||
|
auto textureTablePointer = batch._textureTables.get(batch._params[paramOffset]._uint);
|
||||||
|
if (!textureTablePointer) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto slot = batch._params[paramOffset + 1]._uint;
|
||||||
|
GL45TextureTable* glTextureTable = syncGPUObject(textureTablePointer);
|
||||||
|
if (glTextureTable) {
|
||||||
|
glBindBufferBase(GL_UNIFORM_BUFFER, slot + GLBackend::RESOURCE_TABLE_TEXTURE_SLOT_OFFSET, glTextureTable->_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
|
@ -27,6 +27,7 @@ using namespace gpu;
|
||||||
using namespace gpu::gl;
|
using namespace gpu::gl;
|
||||||
using namespace gpu::gl45;
|
using namespace gpu::gl45;
|
||||||
|
|
||||||
|
using GL45Texture = GL45Backend::GL45Texture;
|
||||||
using GL45VariableAllocationTexture = GL45Backend::GL45VariableAllocationTexture;
|
using GL45VariableAllocationTexture = GL45Backend::GL45VariableAllocationTexture;
|
||||||
|
|
||||||
GL45VariableAllocationTexture::GL45VariableAllocationTexture(const std::weak_ptr<GLBackend>& backend, const Texture& texture) : GL45Texture(backend, texture) {
|
GL45VariableAllocationTexture::GL45VariableAllocationTexture(const std::weak_ptr<GLBackend>& backend, const Texture& texture) : GL45Texture(backend, texture) {
|
||||||
|
@ -40,6 +41,17 @@ GL45VariableAllocationTexture::~GL45VariableAllocationTexture() {
|
||||||
Backend::textureResourcePopulatedGPUMemSize.update(_populatedSize, 0);
|
Backend::textureResourcePopulatedGPUMemSize.update(_populatedSize, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if GPU_BINDLESS_TEXTURES
|
||||||
|
const GL45Texture::Bindless& GL45VariableAllocationTexture::getBindless() const {
|
||||||
|
// The parent call may re-initialize the _bindless member, so we need to call it first
|
||||||
|
const auto& result = Parent::getBindless();
|
||||||
|
// Make sure the referenced structure has the correct minimum available mip value
|
||||||
|
_bindless.minMip = _populatedMip - _allocatedMip;
|
||||||
|
// Now return the result
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
Size GL45VariableAllocationTexture::copyMipFaceLinesFromTexture(uint16_t mip, uint8_t face, const uvec3& size, uint32_t yOffset, GLenum internalFormat, GLenum format, GLenum type, Size sourceSize, const void* sourcePointer) const {
|
Size GL45VariableAllocationTexture::copyMipFaceLinesFromTexture(uint16_t mip, uint8_t face, const uvec3& size, uint32_t yOffset, GLenum internalFormat, GLenum format, GLenum type, Size sourceSize, const void* sourcePointer) const {
|
||||||
Size amountCopied = 0;
|
Size amountCopied = 0;
|
||||||
amountCopied = Parent::copyMipFaceLinesFromTexture(mip, face, size, yOffset, internalFormat, format, type, sourceSize, sourcePointer);
|
amountCopied = Parent::copyMipFaceLinesFromTexture(mip, face, size, yOffset, internalFormat, format, type, sourceSize, sourcePointer);
|
||||||
|
@ -127,7 +139,13 @@ Size GL45ResourceTexture::copyMipsFromTexture() {
|
||||||
|
|
||||||
void GL45ResourceTexture::syncSampler() const {
|
void GL45ResourceTexture::syncSampler() const {
|
||||||
Parent::syncSampler();
|
Parent::syncSampler();
|
||||||
|
#if GPU_BINDLESS_TEXTURES
|
||||||
|
if (!isBindless()) {
|
||||||
|
glTextureParameteri(_id, GL_TEXTURE_BASE_LEVEL, _populatedMip - _allocatedMip);
|
||||||
|
}
|
||||||
|
#else
|
||||||
glTextureParameteri(_id, GL_TEXTURE_BASE_LEVEL, _populatedMip - _allocatedMip);
|
glTextureParameteri(_id, GL_TEXTURE_BASE_LEVEL, _populatedMip - _allocatedMip);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void GL45ResourceTexture::promote() {
|
void GL45ResourceTexture::promote() {
|
||||||
|
@ -137,6 +155,13 @@ void GL45ResourceTexture::promote() {
|
||||||
uint16_t targetAllocatedMip = _allocatedMip - std::min<uint16_t>(_allocatedMip, 2);
|
uint16_t targetAllocatedMip = _allocatedMip - std::min<uint16_t>(_allocatedMip, 2);
|
||||||
targetAllocatedMip = std::max<uint16_t>(_minAllocatedMip, targetAllocatedMip);
|
targetAllocatedMip = std::max<uint16_t>(_minAllocatedMip, targetAllocatedMip);
|
||||||
|
|
||||||
|
#if GPU_BINDLESS_TEXTURES
|
||||||
|
bool bindless = isBindless();
|
||||||
|
if (bindless) {
|
||||||
|
releaseBindless();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
GLuint oldId = _id;
|
GLuint oldId = _id;
|
||||||
auto oldSize = _size;
|
auto oldSize = _size;
|
||||||
uint16_t oldAllocatedMip = _allocatedMip;
|
uint16_t oldAllocatedMip = _allocatedMip;
|
||||||
|
@ -150,10 +175,17 @@ void GL45ResourceTexture::promote() {
|
||||||
// copy pre-existing mips
|
// copy pre-existing mips
|
||||||
copyTextureMipsInGPUMem(oldId, _id, oldAllocatedMip, _allocatedMip, _populatedMip);
|
copyTextureMipsInGPUMem(oldId, _id, oldAllocatedMip, _allocatedMip, _populatedMip);
|
||||||
|
|
||||||
|
#if GPU_BINDLESS_TEXTURES
|
||||||
|
if (bindless) {
|
||||||
|
getBindless();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
// destroy the old texture
|
// destroy the old texture
|
||||||
glDeleteTextures(1, &oldId);
|
glDeleteTextures(1, &oldId);
|
||||||
|
|
||||||
// Update sampler
|
// Update sampler
|
||||||
|
_cachedSampler = getInvalidSampler();
|
||||||
syncSampler();
|
syncSampler();
|
||||||
|
|
||||||
// update the memory usage
|
// update the memory usage
|
||||||
|
@ -170,6 +202,13 @@ void GL45ResourceTexture::demote() {
|
||||||
auto oldSize = _size;
|
auto oldSize = _size;
|
||||||
auto oldPopulatedMip = _populatedMip;
|
auto oldPopulatedMip = _populatedMip;
|
||||||
|
|
||||||
|
#if GPU_BINDLESS_TEXTURES
|
||||||
|
bool bindless = isBindless();
|
||||||
|
if (bindless) {
|
||||||
|
releaseBindless();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
// allocate new texture
|
// allocate new texture
|
||||||
const_cast<GLuint&>(_id) = allocate(_gpuObject);
|
const_cast<GLuint&>(_id) = allocate(_gpuObject);
|
||||||
uint16_t oldAllocatedMip = _allocatedMip;
|
uint16_t oldAllocatedMip = _allocatedMip;
|
||||||
|
@ -179,10 +218,17 @@ void GL45ResourceTexture::demote() {
|
||||||
// copy pre-existing mips
|
// copy pre-existing mips
|
||||||
copyTextureMipsInGPUMem(oldId, _id, oldAllocatedMip, _allocatedMip, _populatedMip);
|
copyTextureMipsInGPUMem(oldId, _id, oldAllocatedMip, _allocatedMip, _populatedMip);
|
||||||
|
|
||||||
|
#if GPU_BINDLESS_TEXTURES
|
||||||
|
if (bindless) {
|
||||||
|
getBindless();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
// destroy the old texture
|
// destroy the old texture
|
||||||
glDeleteTextures(1, &oldId);
|
glDeleteTextures(1, &oldId);
|
||||||
|
|
||||||
// Update sampler
|
// Update sampler
|
||||||
|
_cachedSampler = getInvalidSampler();
|
||||||
syncSampler();
|
syncSampler();
|
||||||
|
|
||||||
// update the memory usage
|
// update the memory usage
|
||||||
|
|
|
@ -75,6 +75,7 @@ Batch::Batch(const Batch& batch_) {
|
||||||
|
|
||||||
_buffers._items.swap(batch._buffers._items);
|
_buffers._items.swap(batch._buffers._items);
|
||||||
_textures._items.swap(batch._textures._items);
|
_textures._items.swap(batch._textures._items);
|
||||||
|
_textureTables._items.swap(batch._textureTables._items);
|
||||||
_streamFormats._items.swap(batch._streamFormats._items);
|
_streamFormats._items.swap(batch._streamFormats._items);
|
||||||
_transforms._items.swap(batch._transforms._items);
|
_transforms._items.swap(batch._transforms._items);
|
||||||
_pipelines._items.swap(batch._pipelines._items);
|
_pipelines._items.swap(batch._pipelines._items);
|
||||||
|
@ -113,6 +114,7 @@ void Batch::clear() {
|
||||||
_data.clear();
|
_data.clear();
|
||||||
_buffers.clear();
|
_buffers.clear();
|
||||||
_textures.clear();
|
_textures.clear();
|
||||||
|
_textureTables.clear();
|
||||||
_streamFormats.clear();
|
_streamFormats.clear();
|
||||||
_transforms.clear();
|
_transforms.clear();
|
||||||
_pipelines.clear();
|
_pipelines.clear();
|
||||||
|
@ -337,6 +339,12 @@ void Batch::setResourceTexture(uint32 slot, const TextureView& view) {
|
||||||
setResourceTexture(slot, view._texture);
|
setResourceTexture(slot, view._texture);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Batch::setResourceTextureTable(const TextureTablePointer& textureTable, uint32 slot) {
|
||||||
|
ADD_COMMAND(setResourceTextureTable);
|
||||||
|
_params.emplace_back(_textureTables.cache(textureTable));
|
||||||
|
_params.emplace_back(slot);
|
||||||
|
}
|
||||||
|
|
||||||
void Batch::setResourceFramebufferSwapChainTexture(uint32 slot, const FramebufferSwapChainPointer& framebuffer, unsigned int swapChainIndex, unsigned int renderBufferSlot) {
|
void Batch::setResourceFramebufferSwapChainTexture(uint32 slot, const FramebufferSwapChainPointer& framebuffer, unsigned int swapChainIndex, unsigned int renderBufferSlot) {
|
||||||
ADD_COMMAND(setResourceFramebufferSwapChainTexture);
|
ADD_COMMAND(setResourceFramebufferSwapChainTexture);
|
||||||
|
|
||||||
|
|
|
@ -187,6 +187,7 @@ public:
|
||||||
|
|
||||||
void setResourceTexture(uint32 slot, const TexturePointer& texture);
|
void setResourceTexture(uint32 slot, const TexturePointer& texture);
|
||||||
void setResourceTexture(uint32 slot, const TextureView& view); // not a command, just a shortcut from a TextureView
|
void setResourceTexture(uint32 slot, const TextureView& view); // not a command, just a shortcut from a TextureView
|
||||||
|
void setResourceTextureTable(const TextureTablePointer& table, uint32 slot = 0);
|
||||||
void setResourceFramebufferSwapChainTexture(uint32 slot, const FramebufferSwapChainPointer& framebuffer, unsigned int swpaChainIndex, unsigned int renderBufferSlot = 0U); // not a command, just a shortcut from a TextureView
|
void setResourceFramebufferSwapChainTexture(uint32 slot, const FramebufferSwapChainPointer& framebuffer, unsigned int swpaChainIndex, unsigned int renderBufferSlot = 0U); // not a command, just a shortcut from a TextureView
|
||||||
|
|
||||||
// Ouput Stage
|
// Ouput Stage
|
||||||
|
@ -302,6 +303,7 @@ public:
|
||||||
COMMAND_setUniformBuffer,
|
COMMAND_setUniformBuffer,
|
||||||
COMMAND_setResourceBuffer,
|
COMMAND_setResourceBuffer,
|
||||||
COMMAND_setResourceTexture,
|
COMMAND_setResourceTexture,
|
||||||
|
COMMAND_setResourceTextureTable,
|
||||||
COMMAND_setResourceFramebufferSwapChainTexture,
|
COMMAND_setResourceFramebufferSwapChainTexture,
|
||||||
|
|
||||||
COMMAND_setFramebuffer,
|
COMMAND_setFramebuffer,
|
||||||
|
@ -409,9 +411,10 @@ public:
|
||||||
return offset;
|
return offset;
|
||||||
}
|
}
|
||||||
|
|
||||||
Data get(uint32 offset) const {
|
const Data& get(uint32 offset) const {
|
||||||
if (offset >= _items.size()) {
|
if (offset >= _items.size()) {
|
||||||
return Data();
|
static const Data EMPTY;
|
||||||
|
return EMPTY;
|
||||||
}
|
}
|
||||||
return (_items.data() + offset)->_data;
|
return (_items.data() + offset)->_data;
|
||||||
}
|
}
|
||||||
|
@ -424,6 +427,7 @@ public:
|
||||||
|
|
||||||
typedef Cache<BufferPointer>::Vector BufferCaches;
|
typedef Cache<BufferPointer>::Vector BufferCaches;
|
||||||
typedef Cache<TexturePointer>::Vector TextureCaches;
|
typedef Cache<TexturePointer>::Vector TextureCaches;
|
||||||
|
typedef Cache<TextureTablePointer>::Vector TextureTableCaches;
|
||||||
typedef Cache<Stream::FormatPointer>::Vector StreamFormatCaches;
|
typedef Cache<Stream::FormatPointer>::Vector StreamFormatCaches;
|
||||||
typedef Cache<Transform>::Vector TransformCaches;
|
typedef Cache<Transform>::Vector TransformCaches;
|
||||||
typedef Cache<PipelinePointer>::Vector PipelineCaches;
|
typedef Cache<PipelinePointer>::Vector PipelineCaches;
|
||||||
|
@ -479,6 +483,7 @@ public:
|
||||||
|
|
||||||
BufferCaches _buffers;
|
BufferCaches _buffers;
|
||||||
TextureCaches _textures;
|
TextureCaches _textures;
|
||||||
|
TextureTableCaches _textureTables;
|
||||||
StreamFormatCaches _streamFormats;
|
StreamFormatCaches _streamFormats;
|
||||||
TransformCaches _transforms;
|
TransformCaches _transforms;
|
||||||
PipelineCaches _pipelines;
|
PipelineCaches _pipelines;
|
||||||
|
|
|
@ -91,6 +91,8 @@ namespace gpu {
|
||||||
using Textures = std::vector<TexturePointer>;
|
using Textures = std::vector<TexturePointer>;
|
||||||
class TextureView;
|
class TextureView;
|
||||||
using TextureViews = std::vector<TextureView>;
|
using TextureViews = std::vector<TextureView>;
|
||||||
|
class TextureTable;
|
||||||
|
using TextureTablePointer = std::shared_ptr<TextureTable>;
|
||||||
|
|
||||||
struct StereoState {
|
struct StereoState {
|
||||||
bool isStereo() const {
|
bool isStereo() const {
|
||||||
|
|
|
@ -17,8 +17,10 @@
|
||||||
#include <QMetaType>
|
#include <QMetaType>
|
||||||
#include <QUrl>
|
#include <QUrl>
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
#include <shared/Storage.h>
|
#include <shared/Storage.h>
|
||||||
#include <shared/FileCache.h>
|
#include <shared/FileCache.h>
|
||||||
|
#include <RegisteredMetaTypes.h>
|
||||||
#include "Forward.h"
|
#include "Forward.h"
|
||||||
#include "Resource.h"
|
#include "Resource.h"
|
||||||
#include "Metric.h"
|
#include "Metric.h"
|
||||||
|
@ -126,12 +128,23 @@ public:
|
||||||
uint8 _wrapModeV = WRAP_REPEAT;
|
uint8 _wrapModeV = WRAP_REPEAT;
|
||||||
uint8 _wrapModeW = WRAP_REPEAT;
|
uint8 _wrapModeW = WRAP_REPEAT;
|
||||||
|
|
||||||
uint8 _mipOffset = 0;
|
|
||||||
uint8 _minMip = 0;
|
uint8 _minMip = 0;
|
||||||
uint8 _maxMip = MAX_MIP_LEVEL;
|
uint8 _maxMip = MAX_MIP_LEVEL;
|
||||||
|
|
||||||
Desc() {}
|
Desc() {}
|
||||||
Desc(const Filter filter, const WrapMode wrap = WRAP_REPEAT) : _filter(filter), _wrapModeU(wrap), _wrapModeV(wrap), _wrapModeW(wrap) {}
|
Desc(const Filter filter, const WrapMode wrap = WRAP_REPEAT) : _filter(filter), _wrapModeU(wrap), _wrapModeV(wrap), _wrapModeW(wrap) {}
|
||||||
|
|
||||||
|
bool operator==(const Desc& other) const {
|
||||||
|
return _borderColor == other._borderColor &&
|
||||||
|
_maxAnisotropy == other._maxAnisotropy &&
|
||||||
|
_filter == other._filter &&
|
||||||
|
_comparisonFunc == other._comparisonFunc &&
|
||||||
|
_wrapModeU == other._wrapModeU &&
|
||||||
|
_wrapModeV == other._wrapModeV &&
|
||||||
|
_wrapModeW == other._wrapModeW &&
|
||||||
|
_minMip == other._minMip &&
|
||||||
|
_maxMip == other._maxMip;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
Sampler() {}
|
Sampler() {}
|
||||||
|
@ -151,11 +164,17 @@ public:
|
||||||
ComparisonFunction getComparisonFunction() const { return ComparisonFunction(_desc._comparisonFunc); }
|
ComparisonFunction getComparisonFunction() const { return ComparisonFunction(_desc._comparisonFunc); }
|
||||||
bool doComparison() const { return getComparisonFunction() != ALWAYS; }
|
bool doComparison() const { return getComparisonFunction() != ALWAYS; }
|
||||||
|
|
||||||
uint8 getMipOffset() const { return _desc._mipOffset; }
|
|
||||||
uint8 getMinMip() const { return _desc._minMip; }
|
uint8 getMinMip() const { return _desc._minMip; }
|
||||||
uint8 getMaxMip() const { return _desc._maxMip; }
|
uint8 getMaxMip() const { return _desc._maxMip; }
|
||||||
|
|
||||||
const Desc& getDesc() const { return _desc; }
|
const Desc& getDesc() const { return _desc; }
|
||||||
|
|
||||||
|
bool operator==(const Sampler& other) const {
|
||||||
|
return _desc == other._desc;
|
||||||
|
}
|
||||||
|
bool operator!=(const Sampler& other) const {
|
||||||
|
return !(*this == other);
|
||||||
|
}
|
||||||
protected:
|
protected:
|
||||||
Desc _desc;
|
Desc _desc;
|
||||||
};
|
};
|
||||||
|
@ -666,6 +685,17 @@ typedef std::shared_ptr< TextureSource > TextureSourcePointer;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
namespace std {
|
||||||
|
template<> struct hash<gpu::Sampler> {
|
||||||
|
size_t operator()(const gpu::Sampler& sampler) const noexcept {
|
||||||
|
size_t result = 0;
|
||||||
|
const auto& desc = sampler.getDesc();
|
||||||
|
hash_combine(result, desc._comparisonFunc, desc._filter, desc._maxAnisotropy, desc._maxMip, desc._minMip, desc._wrapModeU, desc._wrapModeV, desc._wrapModeW);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
Q_DECLARE_METATYPE(gpu::TexturePointer)
|
Q_DECLARE_METATYPE(gpu::TexturePointer)
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
54
libraries/gpu/src/gpu/TextureTable.cpp
Normal file
54
libraries/gpu/src/gpu/TextureTable.cpp
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
//
|
||||||
|
// Created by Bradley Austin Davis on 2017/01/25
|
||||||
|
// Copyright 2013-2017 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
//
|
||||||
|
#include "TextureTable.h"
|
||||||
|
#include "Texture.h"
|
||||||
|
|
||||||
|
#include <shared/GlobalAppProperties.h>
|
||||||
|
using namespace gpu;
|
||||||
|
|
||||||
|
|
||||||
|
const size_t TextureTable::COUNT{ TEXTURE_TABLE_COUNT };
|
||||||
|
|
||||||
|
TextureTable::TextureTable() { }
|
||||||
|
|
||||||
|
TextureTable::TextureTable(const std::initializer_list<TexturePointer>& textures) {
|
||||||
|
auto max = std::min<size_t>(COUNT, textures.size());
|
||||||
|
auto itr = textures.begin();
|
||||||
|
size_t index = 0;
|
||||||
|
while (itr != textures.end() && index < max) {
|
||||||
|
setTexture(index, *itr);
|
||||||
|
++index;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TextureTable::TextureTable(const Array& textures) : _textures(textures) {
|
||||||
|
}
|
||||||
|
|
||||||
|
void TextureTable::setTexture(size_t index, const TexturePointer& texturePointer) {
|
||||||
|
if (index >= COUNT || _textures[index] == texturePointer) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
{
|
||||||
|
Lock lock(_mutex);
|
||||||
|
++_stamp;
|
||||||
|
_textures[index] = texturePointer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void TextureTable::setTexture(size_t index, const TextureView& textureView) {
|
||||||
|
setTexture(index, textureView._texture);
|
||||||
|
}
|
||||||
|
|
||||||
|
TextureTable::Array TextureTable::getTextures() const {
|
||||||
|
Array result;
|
||||||
|
{
|
||||||
|
Lock lock(_mutex);
|
||||||
|
result = _textures;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
44
libraries/gpu/src/gpu/TextureTable.h
Normal file
44
libraries/gpu/src/gpu/TextureTable.h
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
//
|
||||||
|
// Created by Bradley Austin Davis on 2017/01/25
|
||||||
|
// Copyright 2013-2017 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
//
|
||||||
|
#ifndef hifi_gpu_TextureTable_h
|
||||||
|
#define hifi_gpu_TextureTable_h
|
||||||
|
|
||||||
|
#include "Forward.h"
|
||||||
|
|
||||||
|
#include <array>
|
||||||
|
|
||||||
|
#define TEXTURE_TABLE_COUNT 8
|
||||||
|
|
||||||
|
namespace gpu {
|
||||||
|
|
||||||
|
class TextureTable {
|
||||||
|
public:
|
||||||
|
static const size_t COUNT;
|
||||||
|
using Array = std::array<TexturePointer, TEXTURE_TABLE_COUNT>;
|
||||||
|
TextureTable();
|
||||||
|
TextureTable(const std::initializer_list<TexturePointer>& textures);
|
||||||
|
TextureTable(const Array& textures);
|
||||||
|
|
||||||
|
// Only for gpu::Context
|
||||||
|
const GPUObjectPointer gpuObject{};
|
||||||
|
|
||||||
|
void setTexture(size_t index, const TexturePointer& texturePointer);
|
||||||
|
void setTexture(size_t index, const TextureView& texturePointer);
|
||||||
|
|
||||||
|
Array getTextures() const;
|
||||||
|
Stamp getStamp() const { return _stamp; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
mutable Mutex _mutex;
|
||||||
|
Array _textures;
|
||||||
|
Stamp _stamp{ 1 };
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
38
libraries/gpu/src/gpu/TextureTable.slh
Normal file
38
libraries/gpu/src/gpu/TextureTable.slh
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
<!
|
||||||
|
// gpu/TextureTable.slh
|
||||||
|
//
|
||||||
|
// Created by Sam Gateau on 1/25/17.
|
||||||
|
// 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
|
||||||
|
!>
|
||||||
|
<@if not GPU_TEXTURE_TABLE_SLH@>
|
||||||
|
<@def GPU_TEXTURE_TABLE_SLH@>
|
||||||
|
|
||||||
|
#ifdef GPU_TEXTURE_TABLE_BINDLESS
|
||||||
|
#define GPU_TEXTURE_TABLE_MAX_NUM_TEXTURES 8
|
||||||
|
|
||||||
|
struct GPUTextureTable {
|
||||||
|
uvec4 _textures[GPU_TEXTURE_TABLE_MAX_NUM_TEXTURES];
|
||||||
|
};
|
||||||
|
|
||||||
|
#define TextureTable(index, name) layout (std140) uniform gpu_resourceTextureTable##index { GPUTextureTable name; }
|
||||||
|
|
||||||
|
#define tableTex(name, slot) sampler2D(name._textures[slot].xy)
|
||||||
|
#define tableTexMinLod(name, slot) float(name._textures[slot].z)
|
||||||
|
|
||||||
|
#define tableTexValue(name, slot, uv) \
|
||||||
|
tableTexValueLod(tableTex(matTex, albedoMap), tableTexMinLod(matTex, albedoMap), uv)
|
||||||
|
|
||||||
|
vec4 tableTexValueLod(sampler2D sampler, float minLod, vec2 uv) {
|
||||||
|
float queryLod = textureQueryLod(sampler, uv).x;
|
||||||
|
queryLod = max(minLod, queryLod);
|
||||||
|
return textureLod(sampler, uv, queryLod);
|
||||||
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
<@endif@>
|
|
@ -504,6 +504,8 @@ TexturePointer Texture::build(const ktx::KTXDescriptor& descriptor) {
|
||||||
gpuktxKeyValue._usage = Texture::Usage::Builder().withColor().withAlpha().build();
|
gpuktxKeyValue._usage = Texture::Usage::Builder().withColor().withAlpha().build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto samplerDesc = gpuktxKeyValue._samplerDesc;
|
||||||
|
samplerDesc._maxMip = gpu::Sampler::MAX_MIP_LEVEL;
|
||||||
auto texture = create(gpuktxKeyValue._usageType,
|
auto texture = create(gpuktxKeyValue._usageType,
|
||||||
type,
|
type,
|
||||||
texelFormat,
|
texelFormat,
|
||||||
|
@ -513,7 +515,7 @@ TexturePointer Texture::build(const ktx::KTXDescriptor& descriptor) {
|
||||||
1, // num Samples
|
1, // num Samples
|
||||||
header.getNumberOfSlices(),
|
header.getNumberOfSlices(),
|
||||||
header.getNumberOfLevels(),
|
header.getNumberOfLevels(),
|
||||||
gpuktxKeyValue._samplerDesc);
|
samplerDesc);
|
||||||
texture->setUsage(gpuktxKeyValue._usage);
|
texture->setUsage(gpuktxKeyValue._usage);
|
||||||
|
|
||||||
// Assing the mips availables
|
// Assing the mips availables
|
||||||
|
|
|
@ -20,6 +20,7 @@
|
||||||
#include <ColorUtils.h>
|
#include <ColorUtils.h>
|
||||||
|
|
||||||
#include <gpu/Resource.h>
|
#include <gpu/Resource.h>
|
||||||
|
#include <gpu/TextureTable.h>
|
||||||
|
|
||||||
class Transform;
|
class Transform;
|
||||||
|
|
||||||
|
@ -361,6 +362,8 @@ public:
|
||||||
const std::string& getModel() const { return _model; }
|
const std::string& getModel() const { return _model; }
|
||||||
void setModel(const std::string& model) { _model = model; }
|
void setModel(const std::string& model) { _model = model; }
|
||||||
|
|
||||||
|
const gpu::TextureTablePointer& getTextureTable() const { return _textureTable; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
std::string _name { "" };
|
std::string _name { "" };
|
||||||
|
|
||||||
|
@ -368,6 +371,7 @@ private:
|
||||||
mutable MaterialKey _key;
|
mutable MaterialKey _key;
|
||||||
mutable UniformBufferView _schemaBuffer;
|
mutable UniformBufferView _schemaBuffer;
|
||||||
mutable UniformBufferView _texMapArrayBuffer;
|
mutable UniformBufferView _texMapArrayBuffer;
|
||||||
|
mutable gpu::TextureTablePointer _textureTable{ std::make_shared<gpu::TextureTable>() };
|
||||||
|
|
||||||
TextureMaps _textureMaps;
|
TextureMaps _textureMaps;
|
||||||
|
|
||||||
|
|
|
@ -68,8 +68,9 @@ void Pointer::update(unsigned int pointerID) {
|
||||||
// This only needs to be a read lock because update won't change any of the properties that can be modified from scripts
|
// This only needs to be a read lock because update won't change any of the properties that can be modified from scripts
|
||||||
withReadLock([&] {
|
withReadLock([&] {
|
||||||
auto pickResult = getPrevPickResult();
|
auto pickResult = getPrevPickResult();
|
||||||
updateVisuals(pickResult);
|
auto visualPickResult = getVisualPickResult(pickResult);
|
||||||
generatePointerEvents(pointerID, pickResult);
|
updateVisuals(visualPickResult);
|
||||||
|
generatePointerEvents(pointerID, visualPickResult);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -89,6 +89,7 @@ protected:
|
||||||
|
|
||||||
virtual bool shouldHover(const PickResultPointer& pickResult) { return true; }
|
virtual bool shouldHover(const PickResultPointer& pickResult) { return true; }
|
||||||
virtual bool shouldTrigger(const PickResultPointer& pickResult) { return true; }
|
virtual bool shouldTrigger(const PickResultPointer& pickResult) { return true; }
|
||||||
|
virtual PickResultPointer getVisualPickResult(const PickResultPointer& pickResult) { return pickResult; };
|
||||||
|
|
||||||
static const float POINTER_MOVE_DELAY;
|
static const float POINTER_MOVE_DELAY;
|
||||||
static const float TOUCH_PRESS_TO_MOVE_DEADSPOT_SQUARED;
|
static const float TOUCH_PRESS_TO_MOVE_DEADSPOT_SQUARED;
|
||||||
|
|
|
@ -380,6 +380,8 @@ void Antialiasing::run(const render::RenderContextPointer& renderContext, const
|
||||||
batch.setResourceTexture(AntialiasingPass_VelocityMapSlot, nullptr);
|
batch.setResourceTexture(AntialiasingPass_VelocityMapSlot, nullptr);
|
||||||
batch.setResourceTexture(AntialiasingPass_NextMapSlot, nullptr);
|
batch.setResourceTexture(AntialiasingPass_NextMapSlot, nullptr);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
args->popViewFrustum();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -520,7 +522,7 @@ void JitterSample::run(const render::RenderContextPointer& renderContext) {
|
||||||
|
|
||||||
viewFrustum.setProjection(projMat);
|
viewFrustum.setProjection(projMat);
|
||||||
viewFrustum.calculate();
|
viewFrustum.calculate();
|
||||||
args->setViewFrustum(viewFrustum);
|
args->pushViewFrustum(viewFrustum);
|
||||||
} else {
|
} else {
|
||||||
mat4 projMats[2];
|
mat4 projMats[2];
|
||||||
args->_context->getStereoProjections(projMats);
|
args->_context->getStereoProjections(projMats);
|
||||||
|
@ -538,4 +540,4 @@ void JitterSample::run(const render::RenderContextPointer& renderContext) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -39,43 +39,52 @@ void FadeEditJob::run(const render::RenderContextPointer& renderContext, const F
|
||||||
auto scene = renderContext->_scene;
|
auto scene = renderContext->_scene;
|
||||||
|
|
||||||
if (_isEditEnabled) {
|
if (_isEditEnabled) {
|
||||||
float minIsectDistance = std::numeric_limits<float>::max();
|
static const std::string selectionName("TransitionEdit");
|
||||||
auto& itemBounds = inputs.get0();
|
auto scene = renderContext->_scene;
|
||||||
auto editedItem = findNearestItem(renderContext, itemBounds, minIsectDistance);
|
if (!scene->isSelectionEmpty(selectionName)) {
|
||||||
render::Transaction transaction;
|
auto selection = scene->getSelection(selectionName);
|
||||||
bool hasTransaction{ false };
|
auto editedItem = selection.getItems().front();
|
||||||
|
render::Transaction transaction;
|
||||||
|
bool hasTransaction{ false };
|
||||||
|
|
||||||
if (editedItem != _editedItem && render::Item::isValidID(_editedItem)) {
|
if (editedItem != _editedItem && render::Item::isValidID(_editedItem)) {
|
||||||
// Remove transition from previously edited item as we've changed edited item
|
// Remove transition from previously edited item as we've changed edited item
|
||||||
hasTransaction = true;
|
hasTransaction = true;
|
||||||
|
transaction.removeTransitionFromItem(_editedItem);
|
||||||
|
}
|
||||||
|
_editedItem = editedItem;
|
||||||
|
|
||||||
|
if (render::Item::isValidID(_editedItem)) {
|
||||||
|
static const render::Transition::Type categoryToTransition[FADE_CATEGORY_COUNT] = {
|
||||||
|
render::Transition::ELEMENT_ENTER_DOMAIN,
|
||||||
|
render::Transition::BUBBLE_ISECT_OWNER,
|
||||||
|
render::Transition::BUBBLE_ISECT_TRESPASSER,
|
||||||
|
render::Transition::USER_ENTER_DOMAIN,
|
||||||
|
render::Transition::AVATAR_CHANGE
|
||||||
|
};
|
||||||
|
|
||||||
|
auto transitionType = categoryToTransition[inputs.get1()];
|
||||||
|
|
||||||
|
transaction.queryTransitionOnItem(_editedItem, [transitionType, scene](render::ItemID id, const render::Transition* transition) {
|
||||||
|
if (transition == nullptr || transition->isFinished || transition->eventType != transitionType) {
|
||||||
|
// Relaunch transition
|
||||||
|
render::Transaction transaction;
|
||||||
|
transaction.addTransitionToItem(id, transitionType);
|
||||||
|
scene->enqueueTransaction(transaction);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
hasTransaction = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hasTransaction) {
|
||||||
|
scene->enqueueTransaction(transaction);
|
||||||
|
}
|
||||||
|
} else if (render::Item::isValidID(_editedItem)) {
|
||||||
|
// Remove transition from previously edited item as we've disabled fade edition
|
||||||
|
render::Transaction transaction;
|
||||||
transaction.removeTransitionFromItem(_editedItem);
|
transaction.removeTransitionFromItem(_editedItem);
|
||||||
}
|
|
||||||
_editedItem = editedItem;
|
|
||||||
|
|
||||||
if (render::Item::isValidID(_editedItem)) {
|
|
||||||
static const render::Transition::Type categoryToTransition[FADE_CATEGORY_COUNT] = {
|
|
||||||
render::Transition::ELEMENT_ENTER_DOMAIN,
|
|
||||||
render::Transition::BUBBLE_ISECT_OWNER,
|
|
||||||
render::Transition::BUBBLE_ISECT_TRESPASSER,
|
|
||||||
render::Transition::USER_ENTER_DOMAIN,
|
|
||||||
render::Transition::AVATAR_CHANGE
|
|
||||||
};
|
|
||||||
|
|
||||||
auto transitionType = categoryToTransition[inputs.get1()];
|
|
||||||
|
|
||||||
transaction.queryTransitionOnItem(_editedItem, [transitionType, scene](render::ItemID id, const render::Transition* transition) {
|
|
||||||
if (transition == nullptr || transition->isFinished || transition->eventType!=transitionType) {
|
|
||||||
// Relaunch transition
|
|
||||||
render::Transaction transaction;
|
|
||||||
transaction.addTransitionToItem(id, transitionType);
|
|
||||||
scene->enqueueTransaction(transaction);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
hasTransaction = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (hasTransaction) {
|
|
||||||
scene->enqueueTransaction(transaction);
|
scene->enqueueTransaction(transaction);
|
||||||
|
_editedItem = render::Item::INVALID_ITEM_ID;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (render::Item::isValidID(_editedItem)) {
|
else if (render::Item::isValidID(_editedItem)) {
|
||||||
|
@ -87,28 +96,6 @@ void FadeEditJob::run(const render::RenderContextPointer& renderContext, const F
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
render::ItemID FadeEditJob::findNearestItem(const render::RenderContextPointer& renderContext, const render::ItemBounds& inputs, float& minIsectDistance) const {
|
|
||||||
const glm::vec3 rayOrigin = renderContext->args->getViewFrustum().getPosition();
|
|
||||||
const glm::vec3 rayDirection = renderContext->args->getViewFrustum().getDirection();
|
|
||||||
BoxFace face;
|
|
||||||
glm::vec3 normal;
|
|
||||||
float isectDistance;
|
|
||||||
render::ItemID nearestItem = render::Item::INVALID_ITEM_ID;
|
|
||||||
const float minDistance = 1.f;
|
|
||||||
const float maxDistance = 50.f;
|
|
||||||
|
|
||||||
for (const auto& itemBound : inputs) {
|
|
||||||
if (!itemBound.bound.contains(rayOrigin) && itemBound.bound.findRayIntersection(rayOrigin, rayDirection, isectDistance, face, normal)) {
|
|
||||||
auto& item = renderContext->_scene->getItem(itemBound.id);
|
|
||||||
if (item.getKey().isWorldSpace() && isectDistance>minDistance && isectDistance < minIsectDistance && isectDistance<maxDistance) {
|
|
||||||
nearestItem = itemBound.id;
|
|
||||||
minIsectDistance = isectDistance;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nearestItem;
|
|
||||||
}
|
|
||||||
|
|
||||||
FadeConfig::FadeConfig()
|
FadeConfig::FadeConfig()
|
||||||
{
|
{
|
||||||
events[FADE_ELEMENT_ENTER_LEAVE_DOMAIN].noiseSize = glm::vec3{ 0.75f, 0.75f, 0.75f };
|
events[FADE_ELEMENT_ENTER_LEAVE_DOMAIN].noiseSize = glm::vec3{ 0.75f, 0.75f, 0.75f };
|
||||||
|
@ -353,11 +340,9 @@ QString FadeConfig::eventNames[FADE_CATEGORY_COUNT] = {
|
||||||
"avatar_change",
|
"avatar_change",
|
||||||
};
|
};
|
||||||
|
|
||||||
void FadeConfig::save() const {
|
void FadeConfig::save(const QString& configFilePath) const {
|
||||||
// Save will only work if the HIFI_USE_SOURCE_TREE_RESOURCES environment variable is set
|
|
||||||
assert(editedCategory < FADE_CATEGORY_COUNT);
|
assert(editedCategory < FADE_CATEGORY_COUNT);
|
||||||
QJsonObject lProperties;
|
QJsonObject lProperties;
|
||||||
const QString configFilePath = PathUtils::resourcesPath() + "config/" + eventNames[editedCategory] + ".json";
|
|
||||||
QFile file(configFilePath);
|
QFile file(configFilePath);
|
||||||
if (!file.open(QFile::WriteOnly | QFile::Text)) {
|
if (!file.open(QFile::WriteOnly | QFile::Text)) {
|
||||||
qWarning() << "Fade event configuration file " << configFilePath << " cannot be opened";
|
qWarning() << "Fade event configuration file " << configFilePath << " cannot be opened";
|
||||||
|
@ -382,8 +367,7 @@ void FadeConfig::save() const {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void FadeConfig::load() {
|
void FadeConfig::load(const QString& configFilePath) {
|
||||||
const QString configFilePath = PathUtils::resourcesPath() + "config/" + eventNames[editedCategory] + ".json";
|
|
||||||
QFile file(configFilePath);
|
QFile file(configFilePath);
|
||||||
if (!file.exists()) {
|
if (!file.exists()) {
|
||||||
qWarning() << "Fade event configuration file " << configFilePath << " does not exist";
|
qWarning() << "Fade event configuration file " << configFilePath << " does not exist";
|
||||||
|
@ -594,7 +578,7 @@ void FadeJob::run(const render::RenderContextPointer& renderContext, FadeJob::Ou
|
||||||
if (update(*jobConfig, scene, transaction, state, deltaTime)) {
|
if (update(*jobConfig, scene, transaction, state, deltaTime)) {
|
||||||
hasTransaction = true;
|
hasTransaction = true;
|
||||||
}
|
}
|
||||||
if (isFirstItem && jobConfig->manualFade && (state.threshold != jobConfig->threshold)) {
|
if (isFirstItem && (state.threshold != jobConfig->threshold)) {
|
||||||
jobConfig->setProperty("threshold", state.threshold);
|
jobConfig->setProperty("threshold", state.threshold);
|
||||||
isFirstItem = false;
|
isFirstItem = false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -160,8 +160,8 @@ public:
|
||||||
float manualThreshold{ 0.f };
|
float manualThreshold{ 0.f };
|
||||||
bool manualFade{ false };
|
bool manualFade{ false };
|
||||||
|
|
||||||
Q_INVOKABLE void save() const;
|
Q_INVOKABLE void save(const QString& filePath) const;
|
||||||
Q_INVOKABLE void load();
|
Q_INVOKABLE void load(const QString& filePath);
|
||||||
|
|
||||||
static QString eventNames[FADE_CATEGORY_COUNT];
|
static QString eventNames[FADE_CATEGORY_COUNT];
|
||||||
|
|
||||||
|
@ -190,7 +190,6 @@ private:
|
||||||
bool _isEditEnabled{ false };
|
bool _isEditEnabled{ false };
|
||||||
render::ItemID _editedItem{ render::Item::INVALID_ITEM_ID };
|
render::ItemID _editedItem{ render::Item::INVALID_ITEM_ID };
|
||||||
|
|
||||||
render::ItemID findNearestItem(const render::RenderContextPointer& renderContext, const render::ItemBounds& inputs, float& minIsectDistance) const;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class FadeJob {
|
class FadeJob {
|
||||||
|
|
|
@ -48,6 +48,74 @@ TexMapArray getTexMapArray() {
|
||||||
|
|
||||||
<@func declareMaterialTextures(withAlbedo, withRoughness, withNormal, withMetallic, withEmissive, withOcclusion, withScattering)@>
|
<@func declareMaterialTextures(withAlbedo, withRoughness, withNormal, withMetallic, withEmissive, withOcclusion, withScattering)@>
|
||||||
|
|
||||||
|
<@include gpu/TextureTable.slh@>
|
||||||
|
|
||||||
|
#ifdef GPU_TEXTURE_TABLE_BINDLESS
|
||||||
|
|
||||||
|
TextureTable(0, matTex);
|
||||||
|
<!
|
||||||
|
ALBEDO = 0,
|
||||||
|
NORMAL, 1
|
||||||
|
METALLIC, 2
|
||||||
|
EMISSIVE_LIGHTMAP, 3
|
||||||
|
ROUGHNESS, 4
|
||||||
|
OCCLUSION, 5
|
||||||
|
SCATTERING, 6
|
||||||
|
!>
|
||||||
|
|
||||||
|
<@if withAlbedo@>
|
||||||
|
#define albedoMap 0
|
||||||
|
vec4 fetchAlbedoMap(vec2 uv) {
|
||||||
|
return tableTexValue(matTex, albedoMap, uv);
|
||||||
|
}
|
||||||
|
<@endif@>
|
||||||
|
|
||||||
|
<@if withRoughness@>
|
||||||
|
#define roughnessMap 4
|
||||||
|
float fetchRoughnessMap(vec2 uv) {
|
||||||
|
return tableTexValue(matTex, roughnessMap, uv).r;
|
||||||
|
}
|
||||||
|
<@endif@>
|
||||||
|
|
||||||
|
<@if withNormal@>
|
||||||
|
#define normalMap 1
|
||||||
|
vec3 fetchNormalMap(vec2 uv) {
|
||||||
|
return tableTexValue(matTex, normalMap, uv).xyz;
|
||||||
|
}
|
||||||
|
<@endif@>
|
||||||
|
|
||||||
|
<@if withMetallic@>
|
||||||
|
#define metallicMap 2
|
||||||
|
float fetchMetallicMap(vec2 uv) {
|
||||||
|
return tableTexValue(matTex, metallicMap, uv).r;
|
||||||
|
}
|
||||||
|
<@endif@>
|
||||||
|
|
||||||
|
<@if withEmissive@>
|
||||||
|
#define emissiveMap 3
|
||||||
|
vec3 fetchEmissiveMap(vec2 uv) {
|
||||||
|
return tableTexValue(matTex, emissiveMap, uv).rgb;
|
||||||
|
}
|
||||||
|
<@endif@>
|
||||||
|
|
||||||
|
<@if withOcclusion@>
|
||||||
|
#define occlusionMap 5
|
||||||
|
float fetchOcclusionMap(vec2 uv) {
|
||||||
|
return tableTexValue(matTex, occlusionMap, uv).r;
|
||||||
|
}
|
||||||
|
<@endif@>
|
||||||
|
|
||||||
|
<@if withScattering@>
|
||||||
|
#define scatteringMap 6
|
||||||
|
float fetchScatteringMap(vec2 uv) {
|
||||||
|
float scattering = texture(tableTex(matTex, scatteringMap), uv).r; // boolean scattering for now
|
||||||
|
return max(((scattering - 0.1) / 0.9), 0.0);
|
||||||
|
return tableTexValue(matTex, scatteringMap, uv).r; // boolean scattering for now
|
||||||
|
}
|
||||||
|
<@endif@>
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
<@if withAlbedo@>
|
<@if withAlbedo@>
|
||||||
uniform sampler2D albedoMap;
|
uniform sampler2D albedoMap;
|
||||||
vec4 fetchAlbedoMap(vec2 uv) {
|
vec4 fetchAlbedoMap(vec2 uv) {
|
||||||
|
@ -102,6 +170,8 @@ float fetchScatteringMap(vec2 uv) {
|
||||||
}
|
}
|
||||||
<@endif@>
|
<@endif@>
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
<@endfunc@>
|
<@endfunc@>
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -152,7 +152,7 @@ void MeshPartPayload::bindMesh(gpu::Batch& batch) {
|
||||||
batch.setInputStream(0, _drawMesh->getVertexStream());
|
batch.setInputStream(0, _drawMesh->getVertexStream());
|
||||||
}
|
}
|
||||||
|
|
||||||
void MeshPartPayload::bindTransform(gpu::Batch& batch, RenderArgs::RenderMode renderMode) const {
|
void MeshPartPayload::bindTransform(gpu::Batch& batch, RenderArgs::RenderMode renderMode) const {
|
||||||
batch.setModelTransform(_drawTransform);
|
batch.setModelTransform(_drawTransform);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -612,7 +612,8 @@ void initZPassPipelines(ShapePlumber& shapePlumber, gpu::StatePointer state) {
|
||||||
#include "RenderPipelines.h"
|
#include "RenderPipelines.h"
|
||||||
#include <model-networking/TextureCache.h>
|
#include <model-networking/TextureCache.h>
|
||||||
|
|
||||||
void RenderPipelines::bindMaterial(graphics::MaterialPointer material, gpu::Batch& batch, bool enableTextures) {
|
// FIXME find a better way to setup the default textures
|
||||||
|
void RenderPipelines::bindMaterial(const graphics::MaterialPointer& material, gpu::Batch& batch, bool enableTextures) {
|
||||||
if (!material) {
|
if (!material) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -630,84 +631,65 @@ void RenderPipelines::bindMaterial(graphics::MaterialPointer material, gpu::Batc
|
||||||
numUnlit++;
|
numUnlit++;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!enableTextures) {
|
const auto& drawMaterialTextures = material->getTextureTable();
|
||||||
batch.setResourceTexture(ShapePipeline::Slot::ALBEDO, textureCache->getWhiteTexture());
|
|
||||||
batch.setResourceTexture(ShapePipeline::Slot::MAP::ROUGHNESS, textureCache->getWhiteTexture());
|
|
||||||
batch.setResourceTexture(ShapePipeline::Slot::MAP::NORMAL, textureCache->getBlueTexture());
|
|
||||||
batch.setResourceTexture(ShapePipeline::Slot::MAP::METALLIC, textureCache->getBlackTexture());
|
|
||||||
batch.setResourceTexture(ShapePipeline::Slot::MAP::OCCLUSION, textureCache->getWhiteTexture());
|
|
||||||
batch.setResourceTexture(ShapePipeline::Slot::MAP::SCATTERING, textureCache->getWhiteTexture());
|
|
||||||
batch.setResourceTexture(ShapePipeline::Slot::MAP::EMISSIVE_LIGHTMAP, textureCache->getBlackTexture());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Albedo
|
// Albedo
|
||||||
if (materialKey.isAlbedoMap()) {
|
if (materialKey.isAlbedoMap()) {
|
||||||
auto itr = textureMaps.find(graphics::MaterialKey::ALBEDO_MAP);
|
auto itr = textureMaps.find(graphics::MaterialKey::ALBEDO_MAP);
|
||||||
if (itr != textureMaps.end() && itr->second->isDefined()) {
|
if (enableTextures && itr != textureMaps.end() && itr->second->isDefined()) {
|
||||||
batch.setResourceTexture(ShapePipeline::Slot::ALBEDO, itr->second->getTextureView());
|
drawMaterialTextures->setTexture(ShapePipeline::Slot::ALBEDO, itr->second->getTextureView());
|
||||||
} else {
|
} else {
|
||||||
batch.setResourceTexture(ShapePipeline::Slot::ALBEDO, textureCache->getGrayTexture());
|
drawMaterialTextures->setTexture(ShapePipeline::Slot::ALBEDO, textureCache->getWhiteTexture());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Roughness map
|
// Roughness map
|
||||||
if (materialKey.isRoughnessMap()) {
|
if (materialKey.isRoughnessMap()) {
|
||||||
auto itr = textureMaps.find(graphics::MaterialKey::ROUGHNESS_MAP);
|
auto itr = textureMaps.find(graphics::MaterialKey::ROUGHNESS_MAP);
|
||||||
if (itr != textureMaps.end() && itr->second->isDefined()) {
|
if (enableTextures && itr != textureMaps.end() && itr->second->isDefined()) {
|
||||||
batch.setResourceTexture(ShapePipeline::Slot::MAP::ROUGHNESS, itr->second->getTextureView());
|
drawMaterialTextures->setTexture(ShapePipeline::Slot::ROUGHNESS, itr->second->getTextureView());
|
||||||
|
|
||||||
// texcoord are assumed to be the same has albedo
|
|
||||||
} else {
|
} else {
|
||||||
batch.setResourceTexture(ShapePipeline::Slot::MAP::ROUGHNESS, textureCache->getWhiteTexture());
|
drawMaterialTextures->setTexture(ShapePipeline::Slot::ROUGHNESS, textureCache->getWhiteTexture());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Normal map
|
// Normal map
|
||||||
if (materialKey.isNormalMap()) {
|
if (materialKey.isNormalMap()) {
|
||||||
auto itr = textureMaps.find(graphics::MaterialKey::NORMAL_MAP);
|
auto itr = textureMaps.find(graphics::MaterialKey::NORMAL_MAP);
|
||||||
if (itr != textureMaps.end() && itr->second->isDefined()) {
|
if (enableTextures && itr != textureMaps.end() && itr->second->isDefined()) {
|
||||||
batch.setResourceTexture(ShapePipeline::Slot::MAP::NORMAL, itr->second->getTextureView());
|
drawMaterialTextures->setTexture(ShapePipeline::Slot::NORMAL, itr->second->getTextureView());
|
||||||
|
|
||||||
// texcoord are assumed to be the same has albedo
|
|
||||||
} else {
|
} else {
|
||||||
batch.setResourceTexture(ShapePipeline::Slot::MAP::NORMAL, textureCache->getBlueTexture());
|
drawMaterialTextures->setTexture(ShapePipeline::Slot::NORMAL, textureCache->getBlueTexture());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Metallic map
|
// Metallic map
|
||||||
if (materialKey.isMetallicMap()) {
|
if (materialKey.isMetallicMap()) {
|
||||||
auto itr = textureMaps.find(graphics::MaterialKey::METALLIC_MAP);
|
auto itr = textureMaps.find(graphics::MaterialKey::METALLIC_MAP);
|
||||||
if (itr != textureMaps.end() && itr->second->isDefined()) {
|
if (enableTextures && itr != textureMaps.end() && itr->second->isDefined()) {
|
||||||
batch.setResourceTexture(ShapePipeline::Slot::MAP::METALLIC, itr->second->getTextureView());
|
drawMaterialTextures->setTexture(ShapePipeline::Slot::METALLIC, itr->second->getTextureView());
|
||||||
|
|
||||||
// texcoord are assumed to be the same has albedo
|
|
||||||
} else {
|
} else {
|
||||||
batch.setResourceTexture(ShapePipeline::Slot::MAP::METALLIC, textureCache->getBlackTexture());
|
drawMaterialTextures->setTexture(ShapePipeline::Slot::METALLIC, textureCache->getBlackTexture());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Occlusion map
|
// Occlusion map
|
||||||
if (materialKey.isOcclusionMap()) {
|
if (materialKey.isOcclusionMap()) {
|
||||||
auto itr = textureMaps.find(graphics::MaterialKey::OCCLUSION_MAP);
|
auto itr = textureMaps.find(graphics::MaterialKey::OCCLUSION_MAP);
|
||||||
if (itr != textureMaps.end() && itr->second->isDefined()) {
|
if (enableTextures && itr != textureMaps.end() && itr->second->isDefined()) {
|
||||||
batch.setResourceTexture(ShapePipeline::Slot::MAP::OCCLUSION, itr->second->getTextureView());
|
drawMaterialTextures->setTexture(ShapePipeline::Slot::OCCLUSION, itr->second->getTextureView());
|
||||||
|
|
||||||
// texcoord are assumed to be the same has albedo
|
|
||||||
} else {
|
} else {
|
||||||
batch.setResourceTexture(ShapePipeline::Slot::MAP::OCCLUSION, textureCache->getWhiteTexture());
|
drawMaterialTextures->setTexture(ShapePipeline::Slot::OCCLUSION, textureCache->getWhiteTexture());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Scattering map
|
// Scattering map
|
||||||
if (materialKey.isScatteringMap()) {
|
if (materialKey.isScatteringMap()) {
|
||||||
auto itr = textureMaps.find(graphics::MaterialKey::SCATTERING_MAP);
|
auto itr = textureMaps.find(graphics::MaterialKey::SCATTERING_MAP);
|
||||||
if (itr != textureMaps.end() && itr->second->isDefined()) {
|
if (enableTextures && itr != textureMaps.end() && itr->second->isDefined()) {
|
||||||
batch.setResourceTexture(ShapePipeline::Slot::MAP::SCATTERING, itr->second->getTextureView());
|
drawMaterialTextures->setTexture(ShapePipeline::Slot::SCATTERING, itr->second->getTextureView());
|
||||||
|
|
||||||
// texcoord are assumed to be the same has albedo
|
|
||||||
} else {
|
} else {
|
||||||
batch.setResourceTexture(ShapePipeline::Slot::MAP::SCATTERING, textureCache->getWhiteTexture());
|
drawMaterialTextures->setTexture(ShapePipeline::Slot::SCATTERING, textureCache->getWhiteTexture());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -715,18 +697,19 @@ void RenderPipelines::bindMaterial(graphics::MaterialPointer material, gpu::Batc
|
||||||
if (materialKey.isLightmapMap()) {
|
if (materialKey.isLightmapMap()) {
|
||||||
auto itr = textureMaps.find(graphics::MaterialKey::LIGHTMAP_MAP);
|
auto itr = textureMaps.find(graphics::MaterialKey::LIGHTMAP_MAP);
|
||||||
|
|
||||||
if (itr != textureMaps.end() && itr->second->isDefined()) {
|
if (enableTextures && itr != textureMaps.end() && itr->second->isDefined()) {
|
||||||
batch.setResourceTexture(ShapePipeline::Slot::MAP::EMISSIVE_LIGHTMAP, itr->second->getTextureView());
|
drawMaterialTextures->setTexture(ShapePipeline::Slot::EMISSIVE_LIGHTMAP, itr->second->getTextureView());
|
||||||
} else {
|
} else {
|
||||||
batch.setResourceTexture(ShapePipeline::Slot::MAP::EMISSIVE_LIGHTMAP, textureCache->getGrayTexture());
|
drawMaterialTextures->setTexture(ShapePipeline::Slot::EMISSIVE_LIGHTMAP, textureCache->getGrayTexture());
|
||||||
}
|
}
|
||||||
} else if (materialKey.isEmissiveMap()) {
|
} else if (materialKey.isEmissiveMap()) {
|
||||||
auto itr = textureMaps.find(graphics::MaterialKey::EMISSIVE_MAP);
|
auto itr = textureMaps.find(graphics::MaterialKey::EMISSIVE_MAP);
|
||||||
|
if (enableTextures && itr != textureMaps.end() && itr->second->isDefined()) {
|
||||||
if (itr != textureMaps.end() && itr->second->isDefined()) {
|
drawMaterialTextures->setTexture(ShapePipeline::Slot::EMISSIVE_LIGHTMAP, itr->second->getTextureView());
|
||||||
batch.setResourceTexture(ShapePipeline::Slot::MAP::EMISSIVE_LIGHTMAP, itr->second->getTextureView());
|
|
||||||
} else {
|
} else {
|
||||||
batch.setResourceTexture(ShapePipeline::Slot::MAP::EMISSIVE_LIGHTMAP, textureCache->getBlackTexture());
|
drawMaterialTextures->setTexture(ShapePipeline::Slot::EMISSIVE_LIGHTMAP, textureCache->getBlackTexture());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
batch.setResourceTextureTable(material->getTextureTable());
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
|
|
||||||
class RenderPipelines {
|
class RenderPipelines {
|
||||||
public:
|
public:
|
||||||
static void bindMaterial(graphics::MaterialPointer material, gpu::Batch& batch, bool enableTextures);
|
static void bindMaterial(const graphics::MaterialPointer& material, gpu::Batch& batch, bool enableTextures);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -46,7 +46,7 @@ void printOctalCode(const unsigned char* octalCode) {
|
||||||
}
|
}
|
||||||
|
|
||||||
char sectionValue(const unsigned char* startByte, char startIndexInByte) {
|
char sectionValue(const unsigned char* startByte, char startIndexInByte) {
|
||||||
char rightShift = 8 - startIndexInByte - 3;
|
int8_t rightShift = 8 - startIndexInByte - 3;
|
||||||
|
|
||||||
if (rightShift < 0) {
|
if (rightShift < 0) {
|
||||||
return ((startByte[0] << -rightShift) & 7) + (startByte[1] >> (8 + rightShift));
|
return ((startByte[0] << -rightShift) & 7) + (startByte[1] >> (8 + rightShift));
|
||||||
|
@ -73,7 +73,7 @@ int branchIndexWithDescendant(const unsigned char* ancestorOctalCode, const unsi
|
||||||
return sectionValue(descendantOctalCode + 1 + (branchStartBit / 8), branchStartBit % 8);
|
return sectionValue(descendantOctalCode + 1 + (branchStartBit / 8), branchStartBit % 8);
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned char* childOctalCode(const unsigned char* parentOctalCode, char childNumber) {
|
unsigned char* childOctalCode(const unsigned char* parentOctalCode, int childNumber) {
|
||||||
|
|
||||||
// find the length (in number of three bit code sequences)
|
// find the length (in number of three bit code sequences)
|
||||||
// in the parent
|
// in the parent
|
||||||
|
@ -111,7 +111,7 @@ unsigned char* childOctalCode(const unsigned char* parentOctalCode, char childNu
|
||||||
|
|
||||||
// calculate the amount of left shift required
|
// calculate the amount of left shift required
|
||||||
// this will be -1 or -2 if there's wrap
|
// this will be -1 or -2 if there's wrap
|
||||||
char leftShift = 8 - (startBit % 8) - 3;
|
int8_t leftShift = 8 - (startBit % 8) - 3;
|
||||||
|
|
||||||
if (leftShift < 0) {
|
if (leftShift < 0) {
|
||||||
// we have a wrap-around to accomodate
|
// we have a wrap-around to accomodate
|
||||||
|
|
|
@ -30,7 +30,7 @@ using OctalCodePtrList = std::vector<OctalCodePtr>;
|
||||||
void printOctalCode(const unsigned char* octalCode);
|
void printOctalCode(const unsigned char* octalCode);
|
||||||
size_t bytesRequiredForCodeLength(unsigned char threeBitCodes);
|
size_t bytesRequiredForCodeLength(unsigned char threeBitCodes);
|
||||||
int branchIndexWithDescendant(const unsigned char* ancestorOctalCode, const unsigned char* descendantOctalCode);
|
int branchIndexWithDescendant(const unsigned char* ancestorOctalCode, const unsigned char* descendantOctalCode);
|
||||||
unsigned char* childOctalCode(const unsigned char* parentOctalCode, char childNumber);
|
unsigned char* childOctalCode(const unsigned char* parentOctalCode, int childNumber);
|
||||||
|
|
||||||
const int OVERFLOWED_OCTCODE_BUFFER = -1;
|
const int OVERFLOWED_OCTCODE_BUFFER = -1;
|
||||||
const int UNKNOWN_OCTCODE_LENGTH = -2;
|
const int UNKNOWN_OCTCODE_LENGTH = -2;
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
var qml = Script.resourcesPath() + '/qml/AudioScope.qml';
|
var qml = Script.resourcesPath() + '/qml/AudioScopeUI.qml';
|
||||||
var window = new OverlayWindow({
|
var window = new OverlayWindow({
|
||||||
title: 'Audio Scope',
|
title: 'Audio Scope',
|
||||||
source: qml,
|
source: qml,
|
||||||
|
@ -14,4 +14,4 @@ window.closed.connect(function () {
|
||||||
AudioScope.setServerEcho(false);
|
AudioScope.setServerEcho(false);
|
||||||
AudioScope.selectAudioScopeFiveFrames();
|
AudioScope.selectAudioScopeFiveFrames();
|
||||||
Script.stop();
|
Script.stop();
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
"use strict";
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// debugTransition.js
|
// debugTransition.js
|
||||||
// developer/utilities/render
|
// developer/utilities/render
|
||||||
|
@ -12,12 +10,17 @@
|
||||||
//
|
//
|
||||||
|
|
||||||
(function() {
|
(function() {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
var TABLET_BUTTON_NAME = "Transition";
|
var TABLET_BUTTON_NAME = "Transition";
|
||||||
var QMLAPP_URL = Script.resolvePath("./transition.qml");
|
var QMLAPP_URL = Script.resolvePath("./transition.qml");
|
||||||
var ICON_URL = Script.resolvePath("../../../system/assets/images/transition-i.svg");
|
var ICON_URL = Script.resolvePath("../../../system/assets/images/transition-i.svg");
|
||||||
var ACTIVE_ICON_URL = Script.resolvePath("../../../system/assets/images/transition-a.svg");
|
var ACTIVE_ICON_URL = Script.resolvePath("../../../system/assets/images/transition-a.svg");
|
||||||
|
|
||||||
|
Script.include([
|
||||||
|
Script.resolvePath("../../../system/libraries/stringHelpers.js"),
|
||||||
|
]);
|
||||||
|
|
||||||
var onScreen = false;
|
var onScreen = false;
|
||||||
|
|
||||||
function onClicked() {
|
function onClicked() {
|
||||||
|
@ -37,6 +40,71 @@
|
||||||
|
|
||||||
var hasEventBridge = false;
|
var hasEventBridge = false;
|
||||||
|
|
||||||
|
function enableSphereVisualization() {
|
||||||
|
if (gradientSphere==undefined) {
|
||||||
|
gradientSphere = Overlays.addOverlay("sphere", {
|
||||||
|
position: MyAvatar.position,
|
||||||
|
rotation: Quat.fromPitchYawRollDegrees(0.0, 0.0, 0.0),
|
||||||
|
dimensions: { x: 1.0, y: 1.0, z: 1.0 },
|
||||||
|
color: { red: 100, green: 150, blue: 255},
|
||||||
|
alpha: 0.2,
|
||||||
|
solid: false
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (noiseSphere==undefined) {
|
||||||
|
noiseSphere = Overlays.addOverlay("sphere", {
|
||||||
|
position: MyAvatar.position,
|
||||||
|
rotation: Quat.fromPitchYawRollDegrees(0.0, 0.0, 0.0),
|
||||||
|
dimensions: { x: 1.0, y: 1.0, z: 1.0 },
|
||||||
|
color: { red: 255, green: 150, blue: 100},
|
||||||
|
alpha: 0.2,
|
||||||
|
solid: false
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function disableSphereVisualization() {
|
||||||
|
Overlays.deleteOverlay(noiseSphere);
|
||||||
|
Overlays.deleteOverlay(gradientSphere);
|
||||||
|
noiseSphere = undefined
|
||||||
|
gradientSphere = undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a Laser pointer used to pick and add objects to selections
|
||||||
|
var END_DIMENSIONS = { x: 0.05, y: 0.05, z: 0.05 };
|
||||||
|
var COLOR1 = {red: 255, green: 0, blue: 0};
|
||||||
|
var COLOR2 = {red: 0, green: 255, blue: 0};
|
||||||
|
var end1 = {
|
||||||
|
type: "sphere",
|
||||||
|
dimensions: END_DIMENSIONS,
|
||||||
|
color: COLOR1,
|
||||||
|
ignoreRayIntersection: true
|
||||||
|
}
|
||||||
|
var end2 = {
|
||||||
|
type: "sphere",
|
||||||
|
dimensions: END_DIMENSIONS,
|
||||||
|
color: COLOR2,
|
||||||
|
ignoreRayIntersection: true
|
||||||
|
}
|
||||||
|
var laser
|
||||||
|
|
||||||
|
function enablePointer() {
|
||||||
|
laser = Pointers.createPointer(PickType.Ray, {
|
||||||
|
joint: "Mouse",
|
||||||
|
filter: Picks.PICK_ENTITIES,
|
||||||
|
renderStates: [{name: "one", end: end1}],
|
||||||
|
defaultRenderStates: [{name: "one", end: end2, distance: 2.0}],
|
||||||
|
enabled: true
|
||||||
|
});
|
||||||
|
Pointers.setRenderState(laser, "one");
|
||||||
|
Pointers.enablePointer(laser)
|
||||||
|
}
|
||||||
|
|
||||||
|
function disablePointer() {
|
||||||
|
Pointers.disablePointer(laser)
|
||||||
|
Pointers.removePointer(laser);
|
||||||
|
}
|
||||||
|
|
||||||
function wireEventBridge(on) {
|
function wireEventBridge(on) {
|
||||||
if (!tablet) {
|
if (!tablet) {
|
||||||
print("Warning in wireEventBridge(): 'tablet' undefined!");
|
print("Warning in wireEventBridge(): 'tablet' undefined!");
|
||||||
|
@ -46,11 +114,15 @@
|
||||||
if (!hasEventBridge) {
|
if (!hasEventBridge) {
|
||||||
tablet.fromQml.connect(fromQml);
|
tablet.fromQml.connect(fromQml);
|
||||||
hasEventBridge = true;
|
hasEventBridge = true;
|
||||||
|
enablePointer();
|
||||||
|
Render.getConfig("RenderMainView.FadeEdit")["editFade"] = true
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (hasEventBridge) {
|
if (hasEventBridge) {
|
||||||
tablet.fromQml.disconnect(fromQml);
|
tablet.fromQml.disconnect(fromQml);
|
||||||
hasEventBridge = false;
|
hasEventBridge = false;
|
||||||
|
disablePointer();
|
||||||
|
Render.getConfig("RenderMainView.FadeEdit")["editFade"] = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -66,12 +138,87 @@
|
||||||
wireEventBridge(onScreen);
|
wireEventBridge(onScreen);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var isEditEnabled = false
|
||||||
|
var noiseSphere
|
||||||
|
var gradientSphere
|
||||||
|
var selectedEntity
|
||||||
|
var editedCategory
|
||||||
|
|
||||||
|
var FADE_MIN_SCALE = 0.001
|
||||||
|
var FADE_MAX_SCALE = 10000.0
|
||||||
|
|
||||||
|
function parameterToValuePow(parameter, minValue, maxOverMinValue) {
|
||||||
|
return minValue * Math.pow(maxOverMinValue, parameter);
|
||||||
|
//return parameter
|
||||||
|
}
|
||||||
|
|
||||||
|
function update(dt) {
|
||||||
|
var gradientProperties = Entities.getEntityProperties(selectedEntity, ["position", "dimensions"]);
|
||||||
|
if (gradientProperties!=undefined) {
|
||||||
|
var pos = gradientProperties.position
|
||||||
|
if (pos!=undefined) {
|
||||||
|
var config = Render.getConfig("RenderMainView.Fade")
|
||||||
|
//print("Center at "+pos.x+" "+pos.y+" "+pos.z)
|
||||||
|
var dim = {x:config.baseSizeX, y:config.baseSizeY, z:config.baseSizeZ}
|
||||||
|
dim.x = parameterToValuePow(dim.x, FADE_MIN_SCALE, FADE_MAX_SCALE/ FADE_MIN_SCALE)
|
||||||
|
dim.y = parameterToValuePow(dim.y, FADE_MIN_SCALE, FADE_MAX_SCALE/ FADE_MIN_SCALE)
|
||||||
|
dim.z = parameterToValuePow(dim.z, FADE_MIN_SCALE, FADE_MAX_SCALE/ FADE_MIN_SCALE)
|
||||||
|
if (editedCategory==4 || editedCategory==5) {
|
||||||
|
dim.y = gradientProperties.dimensions.y
|
||||||
|
pos.y = pos.y - gradientProperties.dimensions.y/2
|
||||||
|
}
|
||||||
|
Overlays.editOverlay(gradientSphere, { position: pos, dimensions: dim, alpha: config.baseLevel })
|
||||||
|
dim.x = parameterToValuePow(config.noiseSizeX, FADE_MIN_SCALE, FADE_MAX_SCALE/ FADE_MIN_SCALE)
|
||||||
|
dim.y = parameterToValuePow(config.noiseSizeY, FADE_MIN_SCALE, FADE_MAX_SCALE/ FADE_MIN_SCALE)
|
||||||
|
dim.z = parameterToValuePow(config.noiseSizeZ, FADE_MIN_SCALE, FADE_MAX_SCALE/ FADE_MIN_SCALE)
|
||||||
|
Overlays.editOverlay(noiseSphere, { position: pos, dimensions: dim, alpha: config.noiseLevel })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Script.update.connect(update);
|
||||||
|
|
||||||
|
function loadConfiguration(fileUrl) {
|
||||||
|
var config = Render.getConfig("RenderMainView.Fade")
|
||||||
|
config.load(fileUrl)
|
||||||
|
}
|
||||||
|
|
||||||
|
function saveConfiguration(fileUrl) {
|
||||||
|
var config = Render.getConfig("RenderMainView.Fade")
|
||||||
|
config.save(fileUrl)
|
||||||
|
}
|
||||||
|
|
||||||
function fromQml(message) {
|
function fromQml(message) {
|
||||||
|
tokens = message.split('*')
|
||||||
|
//print("Received '"+message+"' from transition.qml")
|
||||||
|
command = tokens[0].toLowerCase()
|
||||||
|
if (command=="category") {
|
||||||
|
editedCategory = parseInt(tokens[1])
|
||||||
|
} else if (command=="save") {
|
||||||
|
var filePath = tokens[1]
|
||||||
|
print("Raw token = "+filePath)
|
||||||
|
if (filePath.startsWith("file:///")) {
|
||||||
|
filePath = filePath.substr(8)
|
||||||
|
print("Saving configuration to "+filePath)
|
||||||
|
saveConfiguration(filePath)
|
||||||
|
} else {
|
||||||
|
print("Configurations can only be saved to local files")
|
||||||
|
}
|
||||||
|
} else if (command=="load") {
|
||||||
|
var filePath = tokens[1]
|
||||||
|
if (filePath.startsWith("file:///")) {
|
||||||
|
filePath = filePath.substr(8)
|
||||||
|
print("Loading configuration from "+filePath)
|
||||||
|
loadConfiguration(filePath)
|
||||||
|
} else {
|
||||||
|
print("Configurations can only be loaded from local files")
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
button.clicked.connect(onClicked);
|
button.clicked.connect(onClicked);
|
||||||
tablet.screenChanged.connect(onScreenChanged);
|
tablet.screenChanged.connect(onScreenChanged);
|
||||||
|
|
||||||
Script.scriptEnding.connect(function () {
|
Script.scriptEnding.connect(function () {
|
||||||
if (onScreen) {
|
if (onScreen) {
|
||||||
tablet.gotoHomeScreen();
|
tablet.gotoHomeScreen();
|
||||||
|
@ -81,4 +228,28 @@
|
||||||
tablet.removeButton(button);
|
tablet.removeButton(button);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
var currentSelectionName = ""
|
||||||
|
var SelectionList = "TransitionEdit"
|
||||||
|
|
||||||
|
Selection.enableListToScene(SelectionList)
|
||||||
|
Selection.clearSelectedItemsList(SelectionList)
|
||||||
|
|
||||||
|
Entities.clickDownOnEntity.connect(function (id, event) {
|
||||||
|
if (selectedEntity) {
|
||||||
|
Selection.removeFromSelectedItemsList(SelectionList, "entity", selectedEntity)
|
||||||
|
}
|
||||||
|
selectedEntity = id
|
||||||
|
Selection.addToSelectedItemsList(SelectionList, "entity", selectedEntity)
|
||||||
|
update()
|
||||||
|
})
|
||||||
|
|
||||||
|
function cleanup() {
|
||||||
|
disablePointer();
|
||||||
|
Selection.removeListFromMap(SelectionList)
|
||||||
|
Selection.disableListToScene(SelectionList);
|
||||||
|
Overlays.deleteOverlay(noiseSphere);
|
||||||
|
Overlays.deleteOverlay(gradientSphere);
|
||||||
|
}
|
||||||
|
Script.scriptEnding.connect(cleanup);
|
||||||
}());
|
}());
|
|
@ -11,6 +11,7 @@
|
||||||
import QtQuick 2.7
|
import QtQuick 2.7
|
||||||
import QtQuick.Controls 1.4
|
import QtQuick.Controls 1.4
|
||||||
import QtQuick.Layouts 1.3
|
import QtQuick.Layouts 1.3
|
||||||
|
import QtQuick.Dialogs 1.0
|
||||||
|
|
||||||
import "qrc:///qml/styles-uit"
|
import "qrc:///qml/styles-uit"
|
||||||
import "qrc:///qml/controls-uit" as HifiControls
|
import "qrc:///qml/controls-uit" as HifiControls
|
||||||
|
@ -22,11 +23,31 @@ Rectangle {
|
||||||
id: root
|
id: root
|
||||||
anchors.margins: hifi.dimensions.contentMargin.x
|
anchors.margins: hifi.dimensions.contentMargin.x
|
||||||
|
|
||||||
|
signal sendToScript(var message);
|
||||||
|
|
||||||
color: hifi.colors.baseGray;
|
color: hifi.colors.baseGray;
|
||||||
|
|
||||||
property var config: Render.getConfig("RenderMainView.Fade");
|
property var config: Render.getConfig("RenderMainView.Fade");
|
||||||
property var configEdit: Render.getConfig("RenderMainView.FadeEdit");
|
property var configEdit: Render.getConfig("RenderMainView.FadeEdit");
|
||||||
|
|
||||||
|
FileDialog {
|
||||||
|
id: fileDialog
|
||||||
|
title: "Please choose a file"
|
||||||
|
folder: shortcuts.documents
|
||||||
|
nameFilters: [ "JSON files (*.json)", "All files (*)" ]
|
||||||
|
onAccepted: {
|
||||||
|
root.sendToScript(title.split(" ")[0]+"*"+fileUrl.toString())
|
||||||
|
// This is a hack to be sure the widgets below properly reflect the change of category: delete the Component
|
||||||
|
// by setting the loader source to Null and then recreate it 500ms later
|
||||||
|
paramWidgetLoader.sourceComponent = undefined;
|
||||||
|
postpone.interval = 500
|
||||||
|
postpone.start()
|
||||||
|
}
|
||||||
|
onRejected: {
|
||||||
|
}
|
||||||
|
Component.onCompleted: visible = false
|
||||||
|
}
|
||||||
|
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
spacing: 3
|
spacing: 3
|
||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
|
@ -37,23 +58,14 @@ Rectangle {
|
||||||
}
|
}
|
||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
spacing: 20
|
spacing: 8
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
id: root_col
|
id: root_col
|
||||||
|
|
||||||
HifiControls.CheckBox {
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
boxSize: 20
|
|
||||||
text: "Edit"
|
|
||||||
checked: root.configEdit["editFade"]
|
|
||||||
onCheckedChanged: {
|
|
||||||
root.configEdit["editFade"] = checked;
|
|
||||||
Render.getConfig("RenderMainView.DrawFadedOpaqueBounds").enabled = checked;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
HifiControls.ComboBox {
|
HifiControls.ComboBox {
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
Layout.fillWidth: true
|
anchors.left : parent.left
|
||||||
|
width: 300
|
||||||
id: categoryBox
|
id: categoryBox
|
||||||
model: ["Elements enter/leave domain", "Bubble isect. - Owner POV", "Bubble isect. - Trespasser POV", "Another user leaves/arrives", "Changing an avatar"]
|
model: ["Elements enter/leave domain", "Bubble isect. - Owner POV", "Bubble isect. - Trespasser POV", "Another user leaves/arrives", "Changing an avatar"]
|
||||||
Timer {
|
Timer {
|
||||||
|
@ -61,17 +73,48 @@ Rectangle {
|
||||||
interval: 100; running: false; repeat: false
|
interval: 100; running: false; repeat: false
|
||||||
onTriggered: {
|
onTriggered: {
|
||||||
paramWidgetLoader.sourceComponent = paramWidgets
|
paramWidgetLoader.sourceComponent = paramWidgets
|
||||||
|
var isTimeBased = categoryBox.currentIndex==0 || categoryBox.currentIndex==3
|
||||||
|
paramWidgetLoader.item.isTimeBased = isTimeBased
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
onCurrentIndexChanged: {
|
onCurrentIndexChanged: {
|
||||||
|
var descriptions = [
|
||||||
|
"Time based threshold, gradients centered on object",
|
||||||
|
"Fixed threshold, gradients centered on owner avatar",
|
||||||
|
"Position based threshold (increases when trespasser moves closer to avatar), gradients centered on trespasser avatar",
|
||||||
|
"Time based threshold, gradients centered on bottom of object",
|
||||||
|
"UNSUPPORTED"
|
||||||
|
]
|
||||||
|
|
||||||
|
description.text = descriptions[currentIndex]
|
||||||
root.config["editedCategory"] = currentIndex;
|
root.config["editedCategory"] = currentIndex;
|
||||||
// This is a hack to be sure the widgets below properly reflect the change of category: delete the Component
|
// This is a hack to be sure the widgets below properly reflect the change of category: delete the Component
|
||||||
// by setting the loader source to Null and then recreate it 100ms later
|
// by setting the loader source to Null and then recreate it 100ms later
|
||||||
paramWidgetLoader.sourceComponent = undefined;
|
paramWidgetLoader.sourceComponent = undefined;
|
||||||
postpone.interval = 100
|
postpone.interval = 100
|
||||||
postpone.start()
|
postpone.start()
|
||||||
|
root.sendToScript("category*"+currentIndex)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
HifiControls.Button {
|
||||||
|
action: saveAction
|
||||||
|
Layout.fillWidth: true
|
||||||
|
anchors.top: parent.top
|
||||||
|
anchors.bottom: parent.bottom
|
||||||
|
}
|
||||||
|
HifiControls.Button {
|
||||||
|
action: loadAction
|
||||||
|
Layout.fillWidth: true
|
||||||
|
anchors.top: parent.top
|
||||||
|
anchors.bottom: parent.bottom
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
HifiControls.Label {
|
||||||
|
id: description
|
||||||
|
text: "..."
|
||||||
|
Layout.fillWidth: true
|
||||||
|
wrapMode: Text.WordWrap
|
||||||
}
|
}
|
||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
|
@ -104,19 +147,18 @@ Rectangle {
|
||||||
id: saveAction
|
id: saveAction
|
||||||
text: "Save"
|
text: "Save"
|
||||||
onTriggered: {
|
onTriggered: {
|
||||||
root.config.save()
|
fileDialog.title = "Save configuration..."
|
||||||
|
fileDialog.selectExisting = false
|
||||||
|
fileDialog.open()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Action {
|
Action {
|
||||||
id: loadAction
|
id: loadAction
|
||||||
text: "Load"
|
text: "Load"
|
||||||
onTriggered: {
|
onTriggered: {
|
||||||
root.config.load()
|
fileDialog.title = "Load configuration..."
|
||||||
// This is a hack to be sure the widgets below properly reflect the change of category: delete the Component
|
fileDialog.selectExisting = true
|
||||||
// by setting the loader source to Null and then recreate it 500ms later
|
fileDialog.open()
|
||||||
paramWidgetLoader.sourceComponent = undefined;
|
|
||||||
postpone.interval = 500
|
|
||||||
postpone.start()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -128,13 +170,8 @@ Rectangle {
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
spacing: 3
|
spacing: 3
|
||||||
width: root_col.width
|
width: root_col.width
|
||||||
|
property bool isTimeBased
|
||||||
|
|
||||||
HifiControls.CheckBox {
|
|
||||||
text: "Invert"
|
|
||||||
boxSize: 20
|
|
||||||
checked: root.config["isInverted"]
|
|
||||||
onCheckedChanged: { root.config["isInverted"] = checked }
|
|
||||||
}
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
|
||||||
|
@ -196,16 +233,32 @@ Rectangle {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
ConfigSlider {
|
spacing: 20
|
||||||
height: 36
|
height: 36
|
||||||
label: "Edge Width"
|
|
||||||
integral: false
|
HifiControls.CheckBox {
|
||||||
config: root.config
|
text: "Invert gradient"
|
||||||
property: "edgeWidth"
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
max: 1.0
|
boxSize: 20
|
||||||
min: 0.0
|
checked: root.config["isInverted"]
|
||||||
|
onCheckedChanged: { root.config["isInverted"] = checked }
|
||||||
|
}
|
||||||
|
ConfigSlider {
|
||||||
|
anchors.left: undefined
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
height: 36
|
||||||
|
width: 300
|
||||||
|
label: "Edge Width"
|
||||||
|
integral: false
|
||||||
|
config: root.config
|
||||||
|
property: "edgeWidth"
|
||||||
|
max: 1.0
|
||||||
|
min: 0.0
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
|
||||||
|
@ -278,6 +331,8 @@ Rectangle {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
|
||||||
ConfigSlider {
|
ConfigSlider {
|
||||||
|
enabled: isTimeBased
|
||||||
|
opacity: isTimeBased ? 1.0 : 0.0
|
||||||
anchors.left: undefined
|
anchors.left: undefined
|
||||||
anchors.right: undefined
|
anchors.right: undefined
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
@ -290,6 +345,8 @@ Rectangle {
|
||||||
min: 0.1
|
min: 0.1
|
||||||
}
|
}
|
||||||
HifiControls.ComboBox {
|
HifiControls.ComboBox {
|
||||||
|
enabled: isTimeBased
|
||||||
|
opacity: isTimeBased ? 1.0 : 0.0
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
model: ["Linear", "Ease In", "Ease Out", "Ease In / Out"]
|
model: ["Linear", "Ease In", "Ease Out", "Ease In / Out"]
|
||||||
currentIndex: root.config["timing"]
|
currentIndex: root.config["timing"]
|
||||||
|
@ -364,20 +421,6 @@ Rectangle {
|
||||||
id: paramWidgetLoader
|
id: paramWidgetLoader
|
||||||
sourceComponent: paramWidgets
|
sourceComponent: paramWidgets
|
||||||
}
|
}
|
||||||
|
|
||||||
Row {
|
|
||||||
anchors.left: parent.left
|
|
||||||
anchors.right: parent.right
|
|
||||||
|
|
||||||
Button {
|
|
||||||
action: saveAction
|
|
||||||
}
|
|
||||||
Button {
|
|
||||||
action: loadAction
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -89,14 +89,8 @@
|
||||||
bubbleOverlayTimestamp = nowTimestamp;
|
bubbleOverlayTimestamp = nowTimestamp;
|
||||||
Script.update.connect(update);
|
Script.update.connect(update);
|
||||||
updateConnected = true;
|
updateConnected = true;
|
||||||
|
writeButtonProperties(bubbleButtonFlashState);
|
||||||
// Flash button
|
bubbleButtonFlashState = !bubbleButtonFlashState;
|
||||||
if (!bubbleFlashTimer) {
|
|
||||||
bubbleFlashTimer = Script.setInterval(function () {
|
|
||||||
writeButtonProperties(bubbleButtonFlashState);
|
|
||||||
bubbleButtonFlashState = !bubbleButtonFlashState;
|
|
||||||
}, 500);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Called from the C++ scripting interface to show the bubble overlay
|
// Called from the C++ scripting interface to show the bubble overlay
|
||||||
|
|
|
@ -14,7 +14,7 @@
|
||||||
PICK_MAX_DISTANCE, COLORS_GRAB_SEARCHING_HALF_SQUEEZE, COLORS_GRAB_SEARCHING_FULL_SQUEEZE, COLORS_GRAB_DISTANCE_HOLD,
|
PICK_MAX_DISTANCE, COLORS_GRAB_SEARCHING_HALF_SQUEEZE, COLORS_GRAB_SEARCHING_FULL_SQUEEZE, COLORS_GRAB_DISTANCE_HOLD,
|
||||||
DEFAULT_SEARCH_SPHERE_DISTANCE, TRIGGER_OFF_VALUE, TRIGGER_ON_VALUE, ZERO_VEC, ensureDynamic,
|
DEFAULT_SEARCH_SPHERE_DISTANCE, TRIGGER_OFF_VALUE, TRIGGER_ON_VALUE, ZERO_VEC, ensureDynamic,
|
||||||
getControllerWorldLocation, projectOntoEntityXYPlane, ContextOverlay, HMD, Reticle, Overlays, isPointingAtUI
|
getControllerWorldLocation, projectOntoEntityXYPlane, ContextOverlay, HMD, Reticle, Overlays, isPointingAtUI
|
||||||
Picks, makeLaserLockInfo Xform, makeLaserParams, AddressManager, getEntityParents
|
Picks, makeLaserLockInfo Xform, makeLaserParams, AddressManager, getEntityParents, Selection
|
||||||
*/
|
*/
|
||||||
|
|
||||||
Script.include("/~/system/libraries/controllerDispatcherUtils.js");
|
Script.include("/~/system/libraries/controllerDispatcherUtils.js");
|
||||||
|
@ -295,6 +295,7 @@ Script.include("/~/system/libraries/Xform.js");
|
||||||
this.actionID = null;
|
this.actionID = null;
|
||||||
this.grabbedThingID = null;
|
this.grabbedThingID = null;
|
||||||
this.targetObject = null;
|
this.targetObject = null;
|
||||||
|
this.potentialEntityWithContextOverlay = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
this.updateRecommendedArea = function() {
|
this.updateRecommendedArea = function() {
|
||||||
|
@ -467,15 +468,17 @@ Script.include("/~/system/libraries/Xform.js");
|
||||||
Script.clearTimeout(this.contextOverlayTimer);
|
Script.clearTimeout(this.contextOverlayTimer);
|
||||||
}
|
}
|
||||||
this.contextOverlayTimer = false;
|
this.contextOverlayTimer = false;
|
||||||
if (entityID !== this.entityWithContextOverlay) {
|
if (entityID === this.entityWithContextOverlay) {
|
||||||
this.destroyContextOverlay();
|
this.destroyContextOverlay();
|
||||||
|
} else {
|
||||||
|
Selection.removeFromSelectedItemsList("contextOverlayHighlightList", "entity", entityID);
|
||||||
}
|
}
|
||||||
|
|
||||||
var targetEntity = this.targetObject.getTargetEntity();
|
var targetEntity = this.targetObject.getTargetEntity();
|
||||||
entityID = targetEntity.id;
|
entityID = targetEntity.id;
|
||||||
targetProps = targetEntity.props;
|
targetProps = targetEntity.props;
|
||||||
|
|
||||||
if (entityIsGrabbable(targetProps)) {
|
if (entityIsGrabbable(targetProps) || entityIsGrabbable(this.targetObject.entityProps)) {
|
||||||
if (!entityIsDistanceGrabbable(targetProps)) {
|
if (!entityIsDistanceGrabbable(targetProps)) {
|
||||||
this.targetObject.makeDynamic();
|
this.targetObject.makeDynamic();
|
||||||
}
|
}
|
||||||
|
|
|
@ -1618,8 +1618,18 @@ SelectionDisplay = (function() {
|
||||||
grid.snapToGrid(Vec3.sum(cornerPosition, vector), constrainMajorOnly),
|
grid.snapToGrid(Vec3.sum(cornerPosition, vector), constrainMajorOnly),
|
||||||
cornerPosition);
|
cornerPosition);
|
||||||
|
|
||||||
for (var i = 0; i < SelectionManager.selections.length; i++) {
|
// editing a parent will cause all the children to automatically follow along, so don't
|
||||||
var properties = SelectionManager.savedProperties[SelectionManager.selections[i]];
|
// edit any entity who has an ancestor in SelectionManager.selections
|
||||||
|
var toMove = SelectionManager.selections.filter(function (selection) {
|
||||||
|
if (SelectionManager.selections.indexOf(SelectionManager.savedProperties[selection].parentID) >= 0) {
|
||||||
|
return false; // a parent is also being moved, so don't issue an edit for this entity
|
||||||
|
} else {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
for (var i = 0; i < toMove.length; i++) {
|
||||||
|
var properties = SelectionManager.savedProperties[toMove[i]];
|
||||||
if (!properties) {
|
if (!properties) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -1628,7 +1638,7 @@ SelectionDisplay = (function() {
|
||||||
y: 0,
|
y: 0,
|
||||||
z: vector.z
|
z: vector.z
|
||||||
});
|
});
|
||||||
Entities.editEntity(SelectionManager.selections[i], {
|
Entities.editEntity(toMove[i], {
|
||||||
position: newPosition
|
position: newPosition
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -1653,11 +1663,11 @@ SelectionDisplay = (function() {
|
||||||
mode: mode,
|
mode: mode,
|
||||||
onBegin: function(event, pickRay, pickResult) {
|
onBegin: function(event, pickRay, pickResult) {
|
||||||
if (direction === TRANSLATE_DIRECTION.X) {
|
if (direction === TRANSLATE_DIRECTION.X) {
|
||||||
pickNormal = { x:0, y:0, z:1 };
|
pickNormal = { x:0, y:1, z:1 };
|
||||||
} else if (direction === TRANSLATE_DIRECTION.Y) {
|
} else if (direction === TRANSLATE_DIRECTION.Y) {
|
||||||
pickNormal = { x:1, y:0, z:0 };
|
pickNormal = { x:1, y:0, z:1 };
|
||||||
} else if (direction === TRANSLATE_DIRECTION.Z) {
|
} else if (direction === TRANSLATE_DIRECTION.Z) {
|
||||||
pickNormal = { x:0, y:1, z:0 };
|
pickNormal = { x:1, y:1, z:0 };
|
||||||
}
|
}
|
||||||
|
|
||||||
var rotation = spaceMode === SPACE_LOCAL ? SelectionManager.localRotation : SelectionManager.worldRotation;
|
var rotation = spaceMode === SPACE_LOCAL ? SelectionManager.localRotation : SelectionManager.worldRotation;
|
||||||
|
@ -1701,7 +1711,6 @@ SelectionDisplay = (function() {
|
||||||
onMove: function(event) {
|
onMove: function(event) {
|
||||||
pickRay = generalComputePickRay(event.x, event.y);
|
pickRay = generalComputePickRay(event.x, event.y);
|
||||||
|
|
||||||
// translate mode left/right based on view toward entity
|
|
||||||
var newIntersection = rayPlaneIntersection(pickRay, SelectionManager.worldPosition, pickNormal);
|
var newIntersection = rayPlaneIntersection(pickRay, SelectionManager.worldPosition, pickNormal);
|
||||||
var vector = Vec3.subtract(newIntersection, lastPick);
|
var vector = Vec3.subtract(newIntersection, lastPick);
|
||||||
|
|
||||||
|
@ -1719,7 +1728,7 @@ SelectionDisplay = (function() {
|
||||||
var dotVector = Vec3.dot(vector, projectionVector);
|
var dotVector = Vec3.dot(vector, projectionVector);
|
||||||
vector = Vec3.multiply(dotVector, projectionVector);
|
vector = Vec3.multiply(dotVector, projectionVector);
|
||||||
vector = grid.snapToGrid(vector);
|
vector = grid.snapToGrid(vector);
|
||||||
|
|
||||||
var wantDebug = false;
|
var wantDebug = false;
|
||||||
if (wantDebug) {
|
if (wantDebug) {
|
||||||
print("translateUpDown... ");
|
print("translateUpDown... ");
|
||||||
|
@ -1727,9 +1736,19 @@ SelectionDisplay = (function() {
|
||||||
Vec3.print(" newIntersection:", newIntersection);
|
Vec3.print(" newIntersection:", newIntersection);
|
||||||
Vec3.print(" vector:", vector);
|
Vec3.print(" vector:", vector);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (var i = 0; i < SelectionManager.selections.length; i++) {
|
// editing a parent will cause all the children to automatically follow along, so don't
|
||||||
var id = SelectionManager.selections[i];
|
// edit any entity who has an ancestor in SelectionManager.selections
|
||||||
|
var toMove = SelectionManager.selections.filter(function (selection) {
|
||||||
|
if (SelectionManager.selections.indexOf(SelectionManager.savedProperties[selection].parentID) >= 0) {
|
||||||
|
return false; // a parent is also being moved, so don't issue an edit for this entity
|
||||||
|
} else {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
for (var i = 0; i < toMove.length; i++) {
|
||||||
|
var id = toMove[i];
|
||||||
var properties = SelectionManager.savedProperties[id];
|
var properties = SelectionManager.savedProperties[id];
|
||||||
var newPosition = Vec3.sum(properties.position, vector);
|
var newPosition = Vec3.sum(properties.position, vector);
|
||||||
Entities.editEntity(id, { position: newPosition });
|
Entities.editEntity(id, { position: newPosition });
|
||||||
|
@ -2017,10 +2036,10 @@ SelectionDisplay = (function() {
|
||||||
vector = grid.snapToSpacing(vector);
|
vector = grid.snapToSpacing(vector);
|
||||||
|
|
||||||
var changeInDimensions = Vec3.multiply(NEGATE_VECTOR, vec3Mult(localSigns, vector));
|
var changeInDimensions = Vec3.multiply(NEGATE_VECTOR, vec3Mult(localSigns, vector));
|
||||||
if (directionEnum === STRETCH_DIRECTION.ALL) {
|
if (directionEnum === STRETCH_DIRECTION.ALL) {
|
||||||
var toCameraDistance = getDistanceToCamera(position);
|
var toCameraDistance = getDistanceToCamera(position);
|
||||||
var dimensionsMultiple = toCameraDistance * STRETCH_DIRECTION_ALL_CAMERA_DISTANCE_MULTIPLE;
|
var dimensionsMultiple = toCameraDistance * STRETCH_DIRECTION_ALL_CAMERA_DISTANCE_MULTIPLE;
|
||||||
changeInDimensions = Vec3.multiply(changeInDimensions, dimensionsMultiple);
|
changeInDimensions = Vec3.multiply(changeInDimensions, dimensionsMultiple);
|
||||||
}
|
}
|
||||||
|
|
||||||
var newDimensions;
|
var newDimensions;
|
||||||
|
@ -2166,8 +2185,19 @@ SelectionDisplay = (function() {
|
||||||
// the selections center point. Otherwise, the rotation will be around the entities
|
// the selections center point. Otherwise, the rotation will be around the entities
|
||||||
// registration point which does not need repositioning.
|
// registration point which does not need repositioning.
|
||||||
var reposition = (SelectionManager.selections.length > 1);
|
var reposition = (SelectionManager.selections.length > 1);
|
||||||
for (var i = 0; i < SelectionManager.selections.length; i++) {
|
|
||||||
var entityID = SelectionManager.selections[i];
|
// editing a parent will cause all the children to automatically follow along, so don't
|
||||||
|
// edit any entity who has an ancestor in SelectionManager.selections
|
||||||
|
var toRotate = SelectionManager.selections.filter(function (selection) {
|
||||||
|
if (SelectionManager.selections.indexOf(SelectionManager.savedProperties[selection].parentID) >= 0) {
|
||||||
|
return false; // a parent is also being moved, so don't issue an edit for this entity
|
||||||
|
} else {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
for (var i = 0; i < toRotate.length; i++) {
|
||||||
|
var entityID = toRotate[i];
|
||||||
var initialProperties = SelectionManager.savedProperties[entityID];
|
var initialProperties = SelectionManager.savedProperties[entityID];
|
||||||
|
|
||||||
var newProperties = {
|
var newProperties = {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
"use strict";
|
"use strict";
|
||||||
/*jslint vars:true, plusplus:true, forin:true*/
|
/*jslint vars:true, plusplus:true, forin:true*/
|
||||||
/*global Script, Settings, Window, Controller, Overlays, SoundArray, LODManager, MyAvatar, Tablet, Camera, HMD, Menu, Quat, Vec3*/
|
/*global Script, Settings, Window, Controller, Overlays, SoundArray, MyAvatar, Tablet, Camera, HMD, Menu, Quat, Vec3*/
|
||||||
//
|
//
|
||||||
// notifications.js
|
// notifications.js
|
||||||
// Version 0.801
|
// Version 0.801
|
||||||
|
@ -84,21 +84,18 @@
|
||||||
var NOTIFICATION_MENU_ITEM_POST = " Notifications";
|
var NOTIFICATION_MENU_ITEM_POST = " Notifications";
|
||||||
var PLAY_NOTIFICATION_SOUNDS_SETTING = "play_notification_sounds";
|
var PLAY_NOTIFICATION_SOUNDS_SETTING = "play_notification_sounds";
|
||||||
var PLAY_NOTIFICATION_SOUNDS_TYPE_SETTING_PRE = "play_notification_sounds_type_";
|
var PLAY_NOTIFICATION_SOUNDS_TYPE_SETTING_PRE = "play_notification_sounds_type_";
|
||||||
var lodTextID = false;
|
var NOTIFICATIONS_MESSAGE_CHANNEL = "Hifi-Notifications";
|
||||||
var NOTIFICATIONS_MESSAGE_CHANNEL = "Hifi-Notifications"
|
|
||||||
|
|
||||||
var NotificationType = {
|
var NotificationType = {
|
||||||
UNKNOWN: 0,
|
UNKNOWN: 0,
|
||||||
SNAPSHOT: 1,
|
SNAPSHOT: 1,
|
||||||
LOD_WARNING: 2,
|
CONNECTION_REFUSED: 2,
|
||||||
CONNECTION_REFUSED: 3,
|
EDIT_ERROR: 3,
|
||||||
EDIT_ERROR: 4,
|
TABLET: 4,
|
||||||
TABLET: 5,
|
CONNECTION: 5,
|
||||||
CONNECTION: 6,
|
WALLET: 6,
|
||||||
WALLET: 7,
|
|
||||||
properties: [
|
properties: [
|
||||||
{ text: "Snapshot" },
|
{ text: "Snapshot" },
|
||||||
{ text: "Level of Detail" },
|
|
||||||
{ text: "Connection Refused" },
|
{ text: "Connection Refused" },
|
||||||
{ text: "Edit error" },
|
{ text: "Edit error" },
|
||||||
{ text: "Tablet" },
|
{ text: "Tablet" },
|
||||||
|
@ -153,10 +150,6 @@
|
||||||
|
|
||||||
// This handles the final dismissal of a notification after fading
|
// This handles the final dismissal of a notification after fading
|
||||||
function dismiss(firstNoteOut, firstButOut, firstOut) {
|
function dismiss(firstNoteOut, firstButOut, firstOut) {
|
||||||
if (firstNoteOut === lodTextID) {
|
|
||||||
lodTextID = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
Overlays.deleteOverlay(firstNoteOut);
|
Overlays.deleteOverlay(firstNoteOut);
|
||||||
Overlays.deleteOverlay(firstButOut);
|
Overlays.deleteOverlay(firstButOut);
|
||||||
notifications.splice(firstOut, 1);
|
notifications.splice(firstOut, 1);
|
||||||
|
@ -418,9 +411,6 @@
|
||||||
|
|
||||||
function deleteNotification(index) {
|
function deleteNotification(index) {
|
||||||
var notificationTextID = notifications[index];
|
var notificationTextID = notifications[index];
|
||||||
if (notificationTextID === lodTextID) {
|
|
||||||
lodTextID = false;
|
|
||||||
}
|
|
||||||
Overlays.deleteOverlay(notificationTextID);
|
Overlays.deleteOverlay(notificationTextID);
|
||||||
Overlays.deleteOverlay(buttons[index]);
|
Overlays.deleteOverlay(buttons[index]);
|
||||||
notifications.splice(index, 1);
|
notifications.splice(index, 1);
|
||||||
|
@ -674,20 +664,6 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
LODManager.LODDecreased.connect(function () {
|
|
||||||
var warningText = "\n" +
|
|
||||||
"Due to the complexity of the content, the \n" +
|
|
||||||
"level of detail has been decreased. " +
|
|
||||||
"You can now see: \n" +
|
|
||||||
LODManager.getLODFeedbackText();
|
|
||||||
|
|
||||||
if (lodTextID === false) {
|
|
||||||
lodTextID = createNotification(warningText, NotificationType.LOD_WARNING);
|
|
||||||
} else {
|
|
||||||
Overlays.editOverlay(lodTextID, { text: warningText });
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
Controller.keyPressEvent.connect(keyPressEvent);
|
Controller.keyPressEvent.connect(keyPressEvent);
|
||||||
Controller.mousePressEvent.connect(mousePressEvent);
|
Controller.mousePressEvent.connect(mousePressEvent);
|
||||||
Controller.keyReleaseEvent.connect(keyReleaseEvent);
|
Controller.keyReleaseEvent.connect(keyReleaseEvent);
|
||||||
|
|
|
@ -24,15 +24,11 @@ extern AutoTester* autoTester;
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
|
|
||||||
Test::Test() {
|
Test::Test() {
|
||||||
QString regex(EXPECTED_IMAGE_PREFIX + QString("\\\\d").repeated(NUM_DIGITS) + ".png");
|
|
||||||
|
|
||||||
expectedImageFilenameFormat = QRegularExpression(regex);
|
|
||||||
|
|
||||||
mismatchWindow.setModal(true);
|
mismatchWindow.setModal(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Test::createTestResultsFolderPath(QString directory) {
|
bool Test::createTestResultsFolderPath(QString directory) {
|
||||||
QDateTime now = QDateTime::currentDateTime();
|
QDateTime now = QDateTime::currentDateTime();
|
||||||
testResultsFolderPath = directory + "/" + TEST_RESULTS_FOLDER + "--" + now.toString(DATETIME_FORMAT);
|
testResultsFolderPath = directory + "/" + TEST_RESULTS_FOLDER + "--" + now.toString(DATETIME_FORMAT);
|
||||||
QDir testResultsFolder(testResultsFolderPath);
|
QDir testResultsFolder(testResultsFolderPath);
|
||||||
|
|
||||||
|
@ -76,7 +72,7 @@ bool Test::compareImageLists(bool isInteractiveMode, QProgressBar* progressBar)
|
||||||
QImage expectedImage(expectedImagesFullFilenames[i]);
|
QImage expectedImage(expectedImagesFullFilenames[i]);
|
||||||
|
|
||||||
if (resultImage.width() != expectedImage.width() || resultImage.height() != expectedImage.height()) {
|
if (resultImage.width() != expectedImage.width() || resultImage.height() != expectedImage.height()) {
|
||||||
messageBox.critical(0, "Internal error #1", "Images are not the same size");
|
messageBox.critical(0, "Internal error: " + QString(__FILE__) + ":" + QString::number(__LINE__), "Images are not the same size");
|
||||||
exit(-1);
|
exit(-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -84,7 +80,7 @@ bool Test::compareImageLists(bool isInteractiveMode, QProgressBar* progressBar)
|
||||||
try {
|
try {
|
||||||
similarityIndex = imageComparer.compareImages(resultImage, expectedImage);
|
similarityIndex = imageComparer.compareImages(resultImage, expectedImage);
|
||||||
} catch (...) {
|
} catch (...) {
|
||||||
messageBox.critical(0, "Internal error #2", "Image not in expected format");
|
messageBox.critical(0, "Internal error: " + QString(__FILE__) + ":" + QString::number(__LINE__), "Image not in expected format");
|
||||||
exit(-1);
|
exit(-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -131,20 +127,20 @@ bool Test::compareImageLists(bool isInteractiveMode, QProgressBar* progressBar)
|
||||||
|
|
||||||
void Test::appendTestResultsToFile(QString testResultsFolderPath, TestFailure testFailure, QPixmap comparisonImage) {
|
void Test::appendTestResultsToFile(QString testResultsFolderPath, TestFailure testFailure, QPixmap comparisonImage) {
|
||||||
if (!QDir().exists(testResultsFolderPath)) {
|
if (!QDir().exists(testResultsFolderPath)) {
|
||||||
messageBox.critical(0, "Internal error #3", "Folder " + testResultsFolderPath + " not found");
|
messageBox.critical(0, "Internal error: " + QString(__FILE__) + ":" + QString::number(__LINE__), "Folder " + testResultsFolderPath + " not found");
|
||||||
exit(-1);
|
exit(-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
QString failureFolderPath { testResultsFolderPath + "/" + "Failure_" + QString::number(index) };
|
QString failureFolderPath { testResultsFolderPath + "/" + "Failure_" + QString::number(index) };
|
||||||
if (!QDir().mkdir(failureFolderPath)) {
|
if (!QDir().mkdir(failureFolderPath)) {
|
||||||
messageBox.critical(0, "Internal error #4", "Failed to create folder " + failureFolderPath);
|
messageBox.critical(0, "Internal error: " + QString(__FILE__) + ":" + QString::number(__LINE__), "Failed to create folder " + failureFolderPath);
|
||||||
exit(-1);
|
exit(-1);
|
||||||
}
|
}
|
||||||
++index;
|
++index;
|
||||||
|
|
||||||
QFile descriptionFile(failureFolderPath + "/" + TEST_RESULTS_FILENAME);
|
QFile descriptionFile(failureFolderPath + "/" + TEST_RESULTS_FILENAME);
|
||||||
if (!descriptionFile.open(QIODevice::ReadWrite)) {
|
if (!descriptionFile.open(QIODevice::ReadWrite)) {
|
||||||
messageBox.critical(0, "Internal error #5", "Failed to create file " + TEST_RESULTS_FILENAME);
|
messageBox.critical(0, "Internal error: " + QString(__FILE__) + ":" + QString::number(__LINE__), "Failed to create file " + TEST_RESULTS_FILENAME);
|
||||||
exit(-1);
|
exit(-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -164,14 +160,14 @@ void Test::appendTestResultsToFile(QString testResultsFolderPath, TestFailure te
|
||||||
sourceFile = testFailure._pathname + testFailure._expectedImageFilename;
|
sourceFile = testFailure._pathname + testFailure._expectedImageFilename;
|
||||||
destinationFile = failureFolderPath + "/" + "Expected Image.jpg";
|
destinationFile = failureFolderPath + "/" + "Expected Image.jpg";
|
||||||
if (!QFile::copy(sourceFile, destinationFile)) {
|
if (!QFile::copy(sourceFile, destinationFile)) {
|
||||||
messageBox.critical(0, "Internal error #6", "Failed to copy " + sourceFile + " to " + destinationFile);
|
messageBox.critical(0, "Internal error: " + QString(__FILE__) + ":" + QString::number(__LINE__), "Failed to copy " + sourceFile + " to " + destinationFile);
|
||||||
exit(-1);
|
exit(-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
sourceFile = testFailure._pathname + testFailure._actualImageFilename;
|
sourceFile = testFailure._pathname + testFailure._actualImageFilename;
|
||||||
destinationFile = failureFolderPath + "/" + "Actual Image.jpg";
|
destinationFile = failureFolderPath + "/" + "Actual Image.jpg";
|
||||||
if (!QFile::copy(sourceFile, destinationFile)) {
|
if (!QFile::copy(sourceFile, destinationFile)) {
|
||||||
messageBox.critical(0, "Internal error #7", "Failed to copy " + sourceFile + " to " + destinationFile);
|
messageBox.critical(0, "Internal error: " + QString(__FILE__) + ":" + QString::number(__LINE__), "Failed to copy " + sourceFile + " to " + destinationFile);
|
||||||
exit(-1);
|
exit(-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -209,11 +205,6 @@ void Test::startTestsEvaluation() {
|
||||||
QStringList sortedTestResultsFilenames = createListOfAll_imagesInDirectory("png", pathToTestResultsDirectory);
|
QStringList sortedTestResultsFilenames = createListOfAll_imagesInDirectory("png", pathToTestResultsDirectory);
|
||||||
QStringList expectedImagesURLs;
|
QStringList expectedImagesURLs;
|
||||||
|
|
||||||
const QString URLPrefix("https://raw.githubusercontent.com");
|
|
||||||
const QString githubUser("NissimHadar");
|
|
||||||
const QString testsRepo("hifi_tests");
|
|
||||||
const QString branch("addRecursionToAutotester");
|
|
||||||
|
|
||||||
resultImagesFullFilenames.clear();
|
resultImagesFullFilenames.clear();
|
||||||
expectedImagesFilenames.clear();
|
expectedImagesFilenames.clear();
|
||||||
expectedImagesFullFilenames.clear();
|
expectedImagesFullFilenames.clear();
|
||||||
|
@ -226,16 +217,16 @@ void Test::startTestsEvaluation() {
|
||||||
QString expectedImagePartialSourceDirectory = getExpectedImagePartialSourceDirectory(currentFilename);
|
QString expectedImagePartialSourceDirectory = getExpectedImagePartialSourceDirectory(currentFilename);
|
||||||
|
|
||||||
// Images are stored on GitHub as ExpectedImage_ddddd.png
|
// Images are stored on GitHub as ExpectedImage_ddddd.png
|
||||||
// Extract the digits at the end of the filename (exluding the file extension)
|
// Extract the digits at the end of the filename (excluding the file extension)
|
||||||
QString expectedImageFilenameTail = currentFilename.left(currentFilename.length() - 4).right(NUM_DIGITS);
|
QString expectedImageFilenameTail = currentFilename.left(currentFilename.length() - 4).right(NUM_DIGITS);
|
||||||
QString expectedImageStoredFilename = EXPECTED_IMAGE_PREFIX + expectedImageFilenameTail + ".png";
|
QString expectedImageStoredFilename = EXPECTED_IMAGE_PREFIX + expectedImageFilenameTail + ".png";
|
||||||
|
|
||||||
QString imageURLString(URLPrefix + "/" + githubUser + "/" + testsRepo + "/" + branch + "/" +
|
QString imageURLString("https://github.com/" + githubUser + "/hifi_tests/blob/" + gitHubBranch + "/" +
|
||||||
expectedImagePartialSourceDirectory + "/" + expectedImageStoredFilename);
|
expectedImagePartialSourceDirectory + "/" + expectedImageStoredFilename + "?raw=true");
|
||||||
|
|
||||||
expectedImagesURLs << imageURLString;
|
expectedImagesURLs << imageURLString;
|
||||||
|
|
||||||
// The image retrieved from Github needs a unique name
|
// The image retrieved from GitHub needs a unique name
|
||||||
QString expectedImageFilename = currentFilename.replace("/", "_").replace(".", "_EI.");
|
QString expectedImageFilename = currentFilename.replace("/", "_").replace(".", "_EI.");
|
||||||
|
|
||||||
expectedImagesFilenames << expectedImageFilename;
|
expectedImagesFilenames << expectedImageFilename;
|
||||||
|
@ -273,25 +264,31 @@ bool Test::isAValidDirectory(QString pathname) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Test::importTest(QTextStream& textStream, const QString& testPathname) {
|
QString Test::extractPathFromTestsDown(QString fullPath) {
|
||||||
// `testPathname` includes the full path to the test. We need the portion below (and including) `tests`
|
// `fullPath` includes the full path to the test. We need the portion below (and including) `tests`
|
||||||
QStringList filenameParts = testPathname.split('/');
|
QStringList pathParts = fullPath.split('/');
|
||||||
int i{ 0 };
|
int i{ 0 };
|
||||||
while (i < filenameParts.length() && filenameParts[i] != "tests") {
|
while (i < pathParts.length() && pathParts[i] != "tests") {
|
||||||
++i;
|
++i;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (i == filenameParts.length()) {
|
if (i == pathParts.length()) {
|
||||||
messageBox.critical(0, "Internal error #10", "Bad testPathname");
|
messageBox.critical(0, "Internal error: " + QString(__FILE__) + ":" + QString::number(__LINE__), "Bad testPathname");
|
||||||
exit(-1);
|
exit(-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
QString filename;
|
QString partialPath;
|
||||||
for (int j = i; j < filenameParts.length(); ++j) {
|
for (int j = i; j < pathParts.length(); ++j) {
|
||||||
filename += "/" + filenameParts[j];
|
partialPath += "/" + pathParts[j];
|
||||||
}
|
}
|
||||||
|
|
||||||
textStream << "Script.include(\"" << "https://raw.githubusercontent.com/" << user << "/hifi_tests/" << branch << filename + "\");" << endl;
|
return partialPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Test::importTest(QTextStream& textStream, const QString& testPathname) {
|
||||||
|
QString partialPath = extractPathFromTestsDown(testPathname);
|
||||||
|
textStream << "Script.include(\"" << "https://github.com/" << githubUser
|
||||||
|
<< "/hifi_tests/blob/" << gitHubBranch << partialPath + "?raw=true\");" << endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Creates a single script in a user-selected folder.
|
// Creates a single script in a user-selected folder.
|
||||||
|
@ -353,7 +350,7 @@ void Test::createRecursiveScript(QString topLevelDirectory, bool interactiveMode
|
||||||
QFile allTestsFilename(topLevelDirectory + "/" + recursiveTestsFilename);
|
QFile allTestsFilename(topLevelDirectory + "/" + recursiveTestsFilename);
|
||||||
if (!allTestsFilename.open(QIODevice::WriteOnly | QIODevice::Text)) {
|
if (!allTestsFilename.open(QIODevice::WriteOnly | QIODevice::Text)) {
|
||||||
messageBox.critical(0,
|
messageBox.critical(0,
|
||||||
"Internal Error #8",
|
"Internal error: " + QString(__FILE__) + ":" + QString::number(__LINE__),
|
||||||
"Failed to create \"" + recursiveTestsFilename + "\" in directory \"" + topLevelDirectory + "\""
|
"Failed to create \"" + recursiveTestsFilename + "\" in directory \"" + topLevelDirectory + "\""
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -363,7 +360,9 @@ void Test::createRecursiveScript(QString topLevelDirectory, bool interactiveMode
|
||||||
QTextStream textStream(&allTestsFilename);
|
QTextStream textStream(&allTestsFilename);
|
||||||
textStream << "// This is an automatically generated file, created by auto-tester" << endl << endl;
|
textStream << "// This is an automatically generated file, created by auto-tester" << endl << endl;
|
||||||
|
|
||||||
textStream << "var autoTester = Script.require(\"https://raw.githubusercontent.com/" + user + "/hifi_tests/" + branch + "/tests/utils/autoTester.js\");" << endl;
|
textStream << "var autoTester = Script.require(\"https://github.com/" + githubUser + "/hifi_tests/blob/"
|
||||||
|
+ gitHubBranch + "/tests/utils/autoTester.js?raw=true\");" << endl;
|
||||||
|
|
||||||
textStream << "autoTester.enableRecursive();" << endl << endl;
|
textStream << "autoTester.enableRecursive();" << endl << endl;
|
||||||
|
|
||||||
QVector<QString> testPathnames;
|
QVector<QString> testPathnames;
|
||||||
|
@ -454,6 +453,203 @@ void Test::createTest() {
|
||||||
messageBox.information(0, "Success", "Test images have been created");
|
messageBox.information(0, "Success", "Test images have been created");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ExtractedText Test::getTestScriptLines(QString testFileName) {
|
||||||
|
ExtractedText relevantTextFromTest;
|
||||||
|
|
||||||
|
QFile inputFile(testFileName);
|
||||||
|
inputFile.open(QIODevice::ReadOnly);
|
||||||
|
if (!inputFile.isOpen()) {
|
||||||
|
messageBox.critical(0,
|
||||||
|
"Internal error: " + QString(__FILE__) + ":" + QString::number(__LINE__),
|
||||||
|
"Failed to open \"" + testFileName
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
QTextStream stream(&inputFile);
|
||||||
|
QString line = stream.readLine();
|
||||||
|
|
||||||
|
// Name of test is the string in the following line:
|
||||||
|
// autoTester.perform("Apply Material Entities to Avatars", Script.resolvePath("."), function(testType) {...
|
||||||
|
const QString ws("\\h*"); //white-space character
|
||||||
|
const QString functionPerformName(ws + "autoTester" + ws + "\\." + ws + "perform");
|
||||||
|
const QString quotedString("\\\".+\\\"");
|
||||||
|
const QString ownPath("Script" + ws + "\\." + ws + "resolvePath" + ws + "\\(" + ws + "\\\"\\.\\\"" + ws + "\\)");
|
||||||
|
const QString functionParameter("function" + ws + "\\(testType" + ws + "\\)");
|
||||||
|
QString regexTestTitle(ws + functionPerformName + "\\(" + quotedString + "\\," + ws + ownPath + "\\," + ws + functionParameter + ws + "{" + ".*");
|
||||||
|
QRegularExpression lineContainingTitle = QRegularExpression(regexTestTitle);
|
||||||
|
|
||||||
|
// Assert platform checks that test is running on the correct OS
|
||||||
|
const QString functionAssertPlatform(ws + "autoTester" + ws + "\\." + ws + "assertPlatform");
|
||||||
|
const QString regexAssertPlatform(ws + functionAssertPlatform + ws + "\\(" + ws + quotedString + ".*");
|
||||||
|
const QRegularExpression lineAssertPlatform = QRegularExpression(regexAssertPlatform);
|
||||||
|
|
||||||
|
// Assert display checks that test is running on the correct display
|
||||||
|
const QString functionAssertDisplay(ws + "autoTester" + ws + "\\." + ws + "assertDisplay");
|
||||||
|
const QString regexAssertDisplay(ws + functionAssertDisplay + ws + "\\(" + ws + quotedString + ".*");
|
||||||
|
const QRegularExpression lineAssertDisplay = QRegularExpression(regexAssertDisplay);
|
||||||
|
|
||||||
|
// Assert CPU checks that test is running on the correct type of CPU
|
||||||
|
const QString functionAssertCPU(ws + "autoTester" + ws + "\\." + ws + "assertCPU");
|
||||||
|
const QString regexAssertCPU(ws + functionAssertCPU + ws + "\\(" + ws + quotedString + ".*");
|
||||||
|
const QRegularExpression lineAssertCPU = QRegularExpression(regexAssertCPU);
|
||||||
|
|
||||||
|
// Assert GPU checks that test is running on the correct type of GPU
|
||||||
|
const QString functionAssertGPU(ws + "autoTester" + ws + "\\." + ws + "assertGPU");
|
||||||
|
const QString regexAssertGPU(ws + functionAssertGPU + ws + "\\(" + ws + quotedString + ".*");
|
||||||
|
const QRegularExpression lineAssertGPU = QRegularExpression(regexAssertGPU);
|
||||||
|
|
||||||
|
// Each step is either of the following forms:
|
||||||
|
// autoTester.addStepSnapshot("Take snapshot"...
|
||||||
|
// autoTester.addStep("Clean up after test"...
|
||||||
|
const QString functionAddStepSnapshotName(ws + "autoTester" + ws + "\\." + ws + "addStepSnapshot");
|
||||||
|
const QString regexStepSnapshot(ws + functionAddStepSnapshotName + ws + "\\(" + ws + quotedString + ".*");
|
||||||
|
const QRegularExpression lineStepSnapshot = QRegularExpression(regexStepSnapshot);
|
||||||
|
|
||||||
|
const QString functionAddStepName(ws + "autoTester" + ws + "\\." + ws + "addStep");
|
||||||
|
const QString regexStep(ws + functionAddStepName + ws + "\\(" + ws + quotedString + ws + "\\)" + ".*");
|
||||||
|
const QRegularExpression lineStep = QRegularExpression(regexStep);
|
||||||
|
|
||||||
|
while (!line.isNull()) {
|
||||||
|
line = stream.readLine();
|
||||||
|
if (lineContainingTitle.match(line).hasMatch()) {
|
||||||
|
QStringList tokens = line.split('"');
|
||||||
|
relevantTextFromTest.title = tokens[1];
|
||||||
|
} else if (lineAssertPlatform.match(line).hasMatch()) {
|
||||||
|
QStringList platforms = line.split('"');
|
||||||
|
relevantTextFromTest.platform = platforms[1];
|
||||||
|
} else if (lineAssertDisplay.match(line).hasMatch()) {
|
||||||
|
QStringList displays = line.split('"');
|
||||||
|
relevantTextFromTest.display = displays[1];
|
||||||
|
} else if (lineAssertCPU.match(line).hasMatch()) {
|
||||||
|
QStringList cpus = line.split('"');
|
||||||
|
relevantTextFromTest.cpu = cpus[1];
|
||||||
|
} else if (lineAssertGPU.match(line).hasMatch()) {
|
||||||
|
QStringList gpus = line.split('"');
|
||||||
|
relevantTextFromTest.gpu = gpus[1];
|
||||||
|
} else if (lineStepSnapshot.match(line).hasMatch()) {
|
||||||
|
QStringList tokens = line.split('"');
|
||||||
|
QString nameOfStep = tokens[1];
|
||||||
|
|
||||||
|
Step *step = new Step();
|
||||||
|
step->text = nameOfStep;
|
||||||
|
step->takeSnapshot = true;
|
||||||
|
relevantTextFromTest.stepList.emplace_back(step);
|
||||||
|
} else if (lineStep.match(line).hasMatch()) {
|
||||||
|
QStringList tokens = line.split('"');
|
||||||
|
QString nameOfStep = tokens[1];
|
||||||
|
|
||||||
|
Step *step = new Step();
|
||||||
|
step->text = nameOfStep;
|
||||||
|
step->takeSnapshot = false;
|
||||||
|
relevantTextFromTest.stepList.emplace_back(step);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inputFile.close();
|
||||||
|
|
||||||
|
return relevantTextFromTest;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create an MD file for a user-selected test.
|
||||||
|
// The folder selected must contain a script named "test.js", the file produced is named "test.md"
|
||||||
|
void Test::createMDFile() {
|
||||||
|
// Folder selection
|
||||||
|
QString testDirectory = QFileDialog::getExistingDirectory(nullptr, "Please select folder containing the test images", ".", QFileDialog::ShowDirsOnly);
|
||||||
|
if (testDirectory == "") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify folder contains test.js file
|
||||||
|
QString testFileName(testDirectory + "/" + TEST_FILENAME);
|
||||||
|
QFileInfo testFileInfo(testFileName);
|
||||||
|
if (!testFileInfo.exists()) {
|
||||||
|
messageBox.critical(0, "Error", "Could not find file: " + TEST_FILENAME);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ExtractedText testScriptLines = getTestScriptLines(testFileName);
|
||||||
|
|
||||||
|
QString mdFilename(testDirectory + "/" + "test.md");
|
||||||
|
QFile mdFile(mdFilename);
|
||||||
|
if (!mdFile.open(QIODevice::ReadWrite)) {
|
||||||
|
messageBox.critical(0, "Internal error: " + QString(__FILE__) + ":" + QString::number(__LINE__), "Failed to create file " + mdFilename);
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
QTextStream stream(&mdFile);
|
||||||
|
|
||||||
|
//Test title
|
||||||
|
QString testName = testScriptLines.title;
|
||||||
|
stream << "# " << testName << "\n";
|
||||||
|
|
||||||
|
// Find the relevant part of the path to the test (i.e. from "tests" down
|
||||||
|
QString partialPath = extractPathFromTestsDown(testDirectory);
|
||||||
|
|
||||||
|
stream << "## Run this script URL: [Manual](./test.js?raw=true) [Auto](./testAuto.js?raw=true)(from menu/Edit/Open and Run scripts from URL...)." << "\n\n";
|
||||||
|
|
||||||
|
stream << "## Preconditions" << "\n";
|
||||||
|
stream << "- In an empty region of a domain with editing rights." << "\n\n";
|
||||||
|
|
||||||
|
// Platform
|
||||||
|
QStringList platforms = testScriptLines.platform.split(" ");;
|
||||||
|
stream << "## Platforms\n";
|
||||||
|
stream << "Run the test on each of the following platforms\n";
|
||||||
|
for (int i = 0; i < platforms.size(); ++i) {
|
||||||
|
// Note that the platforms parameter may include extra spaces, these appear as empty strings in the list
|
||||||
|
if (platforms[i] != QString()) {
|
||||||
|
stream << " - " << platforms[i] << "\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Display
|
||||||
|
QStringList displays = testScriptLines.display.split(" ");
|
||||||
|
stream << "## Displays\n";
|
||||||
|
stream << "Run the test on each of the following displays\n";
|
||||||
|
for (int i = 0; i < displays.size(); ++i) {
|
||||||
|
// Note that the displays parameter may include extra spaces, these appear as empty strings in the list
|
||||||
|
if (displays[i] != QString()) {
|
||||||
|
stream << " - " << displays[i] << "\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// CPU
|
||||||
|
QStringList cpus = testScriptLines.cpu.split(" ");
|
||||||
|
stream << "## Processors\n";
|
||||||
|
stream << "Run the test on each of the following processors\n";
|
||||||
|
for (int i = 0; i < cpus.size(); ++i) {
|
||||||
|
// Note that the cpus parameter may include extra spaces, these appear as empty strings in the list
|
||||||
|
if (cpus[i] != QString()) {
|
||||||
|
stream << " - " << cpus[i] << "\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GPU
|
||||||
|
QStringList gpus = testScriptLines.gpu.split(" ");
|
||||||
|
stream << "## Graphics Cards\n";
|
||||||
|
stream << "Run the test on graphics cards from each of the following vendors\n";
|
||||||
|
for (int i = 0; i < gpus.size(); ++i) {
|
||||||
|
// Note that the gpus parameter may include extra spaces, these appear as empty strings in the list
|
||||||
|
if (gpus[i] != QString()) {
|
||||||
|
stream << " - " << gpus[i] << "\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
stream << "## Steps\n";
|
||||||
|
stream << "Press space bar to advance step by step\n\n";
|
||||||
|
|
||||||
|
int snapShotIndex { 0 };
|
||||||
|
for (size_t i = 0; i < testScriptLines.stepList.size(); ++i) {
|
||||||
|
stream << "### Step " << QString::number(i) << "\n";
|
||||||
|
stream << "- " << testScriptLines.stepList[i]->text << "\n";
|
||||||
|
if (testScriptLines.stepList[i]->takeSnapshot) {
|
||||||
|
stream << "- .rightJustified(5, '0') << ".png)\n";
|
||||||
|
++snapShotIndex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mdFile.close();
|
||||||
|
}
|
||||||
|
|
||||||
void Test::copyJPGtoPNG(QString sourceJPGFullFilename, QString destinationPNGFullFilename) {
|
void Test::copyJPGtoPNG(QString sourceJPGFullFilename, QString destinationPNGFullFilename) {
|
||||||
QFile::remove(destinationPNGFullFilename);
|
QFile::remove(destinationPNGFullFilename);
|
||||||
|
|
||||||
|
@ -526,7 +722,7 @@ QString Test::getExpectedImagePartialSourceDirectory(QString filename) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (i < 0) {
|
if (i < 0) {
|
||||||
messageBox.critical(0, "Internal error #9", "Bad filename");
|
messageBox.critical(0, "Internal error: " + QString(__FILE__) + ":" + QString::number(__LINE__), "Bad filename");
|
||||||
exit(-1);
|
exit(-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -19,6 +19,24 @@
|
||||||
#include "ImageComparer.h"
|
#include "ImageComparer.h"
|
||||||
#include "ui/MismatchWindow.h"
|
#include "ui/MismatchWindow.h"
|
||||||
|
|
||||||
|
class Step {
|
||||||
|
public:
|
||||||
|
QString text;
|
||||||
|
bool takeSnapshot;
|
||||||
|
};
|
||||||
|
|
||||||
|
using StepList = std::vector<Step*>;
|
||||||
|
|
||||||
|
class ExtractedText {
|
||||||
|
public:
|
||||||
|
QString title;
|
||||||
|
QString platform;
|
||||||
|
QString display;
|
||||||
|
QString cpu;
|
||||||
|
QString gpu;
|
||||||
|
StepList stepList;
|
||||||
|
};
|
||||||
|
|
||||||
class Test {
|
class Test {
|
||||||
public:
|
public:
|
||||||
Test();
|
Test();
|
||||||
|
@ -31,7 +49,7 @@ public:
|
||||||
void createRecursiveScript(QString topLevelDirectory, bool interactiveMode);
|
void createRecursiveScript(QString topLevelDirectory, bool interactiveMode);
|
||||||
|
|
||||||
void createTest();
|
void createTest();
|
||||||
void deleteOldSnapshots();
|
void createMDFile();
|
||||||
|
|
||||||
bool compareImageLists(bool isInteractiveMode, QProgressBar* progressBar);
|
bool compareImageLists(bool isInteractiveMode, QProgressBar* progressBar);
|
||||||
|
|
||||||
|
@ -47,7 +65,7 @@ public:
|
||||||
void zipAndDeleteTestResultsFolder();
|
void zipAndDeleteTestResultsFolder();
|
||||||
|
|
||||||
bool isAValidDirectory(QString pathname);
|
bool isAValidDirectory(QString pathname);
|
||||||
|
QString extractPathFromTestsDown(QString fullPath);
|
||||||
QString getExpectedImageDestinationDirectory(QString filename);
|
QString getExpectedImageDestinationDirectory(QString filename);
|
||||||
QString getExpectedImagePartialSourceDirectory(QString filename);
|
QString getExpectedImagePartialSourceDirectory(QString filename);
|
||||||
|
|
||||||
|
@ -62,8 +80,6 @@ private:
|
||||||
|
|
||||||
QDir imageDirectory;
|
QDir imageDirectory;
|
||||||
|
|
||||||
QRegularExpression expectedImageFilenameFormat;
|
|
||||||
|
|
||||||
MismatchWindow mismatchWindow;
|
MismatchWindow mismatchWindow;
|
||||||
|
|
||||||
ImageComparer imageComparer;
|
ImageComparer imageComparer;
|
||||||
|
@ -81,9 +97,11 @@ private:
|
||||||
QStringList resultImagesFullFilenames;
|
QStringList resultImagesFullFilenames;
|
||||||
|
|
||||||
// Used for accessing GitHub
|
// Used for accessing GitHub
|
||||||
const QString user { "NissimHadar" };
|
const QString githubUser{ "highfidelity" };
|
||||||
const QString branch { "addRecursionToAutotester" };
|
const QString gitHubBranch { "master" };
|
||||||
const QString DATETIME_FORMAT { "yyyy-MM-dd_hh-mm-ss" };
|
const QString DATETIME_FORMAT { "yyyy-MM-dd_hh-mm-ss" };
|
||||||
|
|
||||||
|
ExtractedText getTestScriptLines(QString testFileName);
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // hifi_test_h
|
#endif // hifi_test_h
|
|
@ -33,7 +33,11 @@ void AutoTester::on_createRecursiveScriptsRecursivelyButton_clicked() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void AutoTester::on_createTestButton_clicked() {
|
void AutoTester::on_createTestButton_clicked() {
|
||||||
test->createTest();
|
test->createTest();
|
||||||
|
}
|
||||||
|
|
||||||
|
void AutoTester::on_createMDFileButton_clicked() {
|
||||||
|
test->createMDFile();
|
||||||
}
|
}
|
||||||
|
|
||||||
void AutoTester::on_closeButton_clicked() {
|
void AutoTester::on_closeButton_clicked() {
|
||||||
|
|
|
@ -29,8 +29,9 @@ private slots:
|
||||||
void on_evaluateTestsButton_clicked();
|
void on_evaluateTestsButton_clicked();
|
||||||
void on_createRecursiveScriptButton_clicked();
|
void on_createRecursiveScriptButton_clicked();
|
||||||
void on_createRecursiveScriptsRecursivelyButton_clicked();
|
void on_createRecursiveScriptsRecursivelyButton_clicked();
|
||||||
void on_createTestButton_clicked();
|
void on_createTestButton_clicked();
|
||||||
void on_closeButton_clicked();
|
void on_createMDFileButton_clicked();
|
||||||
|
void on_closeButton_clicked();
|
||||||
|
|
||||||
void saveImage(int index);
|
void saveImage(int index);
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>607</width>
|
<width>607</width>
|
||||||
<height>395</height>
|
<height>514</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<property name="windowTitle">
|
<property name="windowTitle">
|
||||||
|
@ -18,7 +18,7 @@
|
||||||
<property name="geometry">
|
<property name="geometry">
|
||||||
<rect>
|
<rect>
|
||||||
<x>20</x>
|
<x>20</x>
|
||||||
<y>300</y>
|
<y>420</y>
|
||||||
<width>220</width>
|
<width>220</width>
|
||||||
<height>40</height>
|
<height>40</height>
|
||||||
</rect>
|
</rect>
|
||||||
|
@ -44,7 +44,7 @@
|
||||||
<property name="geometry">
|
<property name="geometry">
|
||||||
<rect>
|
<rect>
|
||||||
<x>20</x>
|
<x>20</x>
|
||||||
<y>135</y>
|
<y>255</y>
|
||||||
<width>220</width>
|
<width>220</width>
|
||||||
<height>40</height>
|
<height>40</height>
|
||||||
</rect>
|
</rect>
|
||||||
|
@ -70,7 +70,7 @@
|
||||||
<property name="geometry">
|
<property name="geometry">
|
||||||
<rect>
|
<rect>
|
||||||
<x>23</x>
|
<x>23</x>
|
||||||
<y>100</y>
|
<y>220</y>
|
||||||
<width>131</width>
|
<width>131</width>
|
||||||
<height>20</height>
|
<height>20</height>
|
||||||
</rect>
|
</rect>
|
||||||
|
@ -86,7 +86,7 @@
|
||||||
<property name="geometry">
|
<property name="geometry">
|
||||||
<rect>
|
<rect>
|
||||||
<x>20</x>
|
<x>20</x>
|
||||||
<y>190</y>
|
<y>310</y>
|
||||||
<width>255</width>
|
<width>255</width>
|
||||||
<height>23</height>
|
<height>23</height>
|
||||||
</rect>
|
</rect>
|
||||||
|
@ -108,6 +108,19 @@
|
||||||
<string>Create Recursive Scripts Recursively</string>
|
<string>Create Recursive Scripts Recursively</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
|
<widget class="QPushButton" name="createMDFileButton">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>20</x>
|
||||||
|
<y>90</y>
|
||||||
|
<width>220</width>
|
||||||
|
<height>40</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Create MD file</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
</widget>
|
</widget>
|
||||||
<widget class="QMenuBar" name="menuBar">
|
<widget class="QMenuBar" name="menuBar">
|
||||||
<property name="geometry">
|
<property name="geometry">
|
||||||
|
|
91
tools/bake-tools/bake.py
Normal file
91
tools/bake-tools/bake.py
Normal file
|
@ -0,0 +1,91 @@
|
||||||
|
import os, json, sys, shutil, subprocess, shlex, time
|
||||||
|
EXE = os.environ['HIFI_OVEN']
|
||||||
|
|
||||||
|
def listFiles(directory, extension):
|
||||||
|
items = os.listdir(directory)
|
||||||
|
fileList = []
|
||||||
|
for f in items:
|
||||||
|
if f.endswith('.' + extension):
|
||||||
|
fileList.append(f)
|
||||||
|
return fileList
|
||||||
|
|
||||||
|
def camelCaseString(string):
|
||||||
|
string = string.replace('-', ' ')
|
||||||
|
return ''.join(x for x in string.title() if not x.isspace())
|
||||||
|
|
||||||
|
def groupFiles(originalDirectory, newDirectory, files):
|
||||||
|
for file in files:
|
||||||
|
newPath = os.sep.join([newDirectory, file])
|
||||||
|
originalPath = os.sep.join([originalDirectory, file])
|
||||||
|
shutil.move(originalPath, newPath)
|
||||||
|
|
||||||
|
def groupKTXFiles(directory, filePath):
|
||||||
|
baseFile = os.path.basename(filePath)
|
||||||
|
filename = os.path.splitext(baseFile)[0]
|
||||||
|
camelCaseFileName = camelCaseString(filename)
|
||||||
|
path = os.sep.join([directory, camelCaseFileName])
|
||||||
|
files = listFiles(directory, 'ktx')
|
||||||
|
if len(files) > 0:
|
||||||
|
createDirectory(path)
|
||||||
|
groupFiles(directory, path, files)
|
||||||
|
|
||||||
|
newFilePath = os.sep.join([path, baseFile+'.baked.fbx'])
|
||||||
|
originalFilePath = os.sep.join([directory, baseFile+'.baked.fbx'])
|
||||||
|
originalFilePath.strip()
|
||||||
|
shutil.move(originalFilePath, newFilePath)
|
||||||
|
|
||||||
|
def bakeFile(filePath, outputDirectory):
|
||||||
|
createDirectory(outputDirectory)
|
||||||
|
cmd = EXE + ' -i ' + filePath + ' -o ' + outputDirectory + ' -t fbx'
|
||||||
|
args = shlex.split(cmd)
|
||||||
|
process = subprocess.Popen(cmd, stdout=False, stderr=False)
|
||||||
|
process.wait()
|
||||||
|
bakedFile = os.path.splitext(filePath)[0]
|
||||||
|
groupKTXFiles(outputDirectory, bakedFile)
|
||||||
|
|
||||||
|
def bakeFilesInDirectory(directory, outputDirectory):
|
||||||
|
for root, subFolders, filenames in os.walk(directory):
|
||||||
|
for filename in filenames:
|
||||||
|
if filename.endswith('.fbx'):
|
||||||
|
filePath = os.sep.join([root, filename])
|
||||||
|
absFilePath = os.path.abspath(filePath)
|
||||||
|
outputFolder = os.path.join(outputDirectory, os.path.relpath(root))
|
||||||
|
print "Baking file: " + filename
|
||||||
|
bakeFile(absFilePath, outputFolder)
|
||||||
|
else:
|
||||||
|
filePath = os.sep.join([root, filename])
|
||||||
|
absFilePath = os.path.abspath(filePath)
|
||||||
|
outputFolder = os.path.join(outputDirectory, os.path.relpath(root))
|
||||||
|
newFilePath = os.sep.join([outputFolder, filename])
|
||||||
|
createDirectory(outputFolder)
|
||||||
|
print "moving file: " + filename + " to: " + outputFolder
|
||||||
|
shutil.copy(absFilePath, newFilePath)
|
||||||
|
|
||||||
|
def createDirectory(directory):
|
||||||
|
if not os.path.exists(directory):
|
||||||
|
os.makedirs(directory)
|
||||||
|
|
||||||
|
def checkIfExeExists():
|
||||||
|
if not os.path.isfile(EXE) and os.access(EXE, os.X_OK):
|
||||||
|
print 'HIFI_OVEN evironment variable is not set'
|
||||||
|
sys.exit()
|
||||||
|
|
||||||
|
def handleOptions():
|
||||||
|
option = sys.argv[1]
|
||||||
|
if option == '--help' or option == '-h':
|
||||||
|
print 'Usage: bake.py INPUT_DIRECTORY[directory to bake] OUTPUT_DIRECTORY[directory to place backed files]'
|
||||||
|
print 'Note: Output directory will be created if directory does not exist'
|
||||||
|
sys.exit()
|
||||||
|
|
||||||
|
def main():
|
||||||
|
argsLength = len(sys.argv)
|
||||||
|
if argsLength == 3:
|
||||||
|
checkIfExeExists()
|
||||||
|
rootDirectory = sys.argv[1]
|
||||||
|
outputDirectory = os.path.abspath(sys.argv[2])
|
||||||
|
createDirectory(outputDirectory)
|
||||||
|
bakeFilesInDirectory(rootDirectory, outputDirectory)
|
||||||
|
elif argsLength == 2:
|
||||||
|
handleOptions()
|
||||||
|
|
||||||
|
main()
|
82
tools/bake-tools/convertToRelativePaths.py
Normal file
82
tools/bake-tools/convertToRelativePaths.py
Normal file
|
@ -0,0 +1,82 @@
|
||||||
|
import json, os, sys, gzip
|
||||||
|
|
||||||
|
prefix = 'file:///~/'
|
||||||
|
MAP = {}
|
||||||
|
def createAssetMapping(assetDirectory):
|
||||||
|
baseDirectory = os.path.basename(os.path.normpath(assetDirectory))
|
||||||
|
for root, subfolder, filenames in os.walk(assetDirectory):
|
||||||
|
for filename in filenames:
|
||||||
|
if not filename.endswith('.ktx'):
|
||||||
|
substring = os.path.commonprefix([assetDirectory, root])
|
||||||
|
newPath = root.replace(substring, '');
|
||||||
|
filePath = os.sep.join([newPath, filename])
|
||||||
|
if filePath[0] == '\\':
|
||||||
|
filePath = filePath[1:]
|
||||||
|
finalPath = prefix + baseDirectory + '/' + filePath
|
||||||
|
finalPath = finalPath.replace('\\', '/')
|
||||||
|
file = os.path.splitext(filename)[0]
|
||||||
|
file = os.path.splitext(file)[0]
|
||||||
|
MAP[file] = finalPath
|
||||||
|
|
||||||
|
def hasURL(prop):
|
||||||
|
if "URL" in prop:
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def handleURL(url):
|
||||||
|
newUrl = url
|
||||||
|
if "atp:" in url:
|
||||||
|
baseFilename = os.path.basename(url)
|
||||||
|
filename = os.path.splitext(baseFilename)[0]
|
||||||
|
newUrl = MAP[filename]
|
||||||
|
print newUrl
|
||||||
|
return newUrl
|
||||||
|
|
||||||
|
def handleOptions():
|
||||||
|
option = sys.argv[1]
|
||||||
|
if option == '--help' or option == '-h':
|
||||||
|
print 'Usage: convertToRelativePaths.py INPUT[json file you want to update the urls] INPUT[directory that the baked files are located in]'
|
||||||
|
sys.exit()
|
||||||
|
|
||||||
|
def main():
|
||||||
|
argsLength = len(sys.argv)
|
||||||
|
if argsLength == 3:
|
||||||
|
jsonFile = sys.argv[1]
|
||||||
|
gzipFile = jsonFile + '.gz'
|
||||||
|
assetDirectory = sys.argv[2]
|
||||||
|
createAssetMapping(assetDirectory)
|
||||||
|
f = open(jsonFile)
|
||||||
|
data = json.load(f)
|
||||||
|
f.close()
|
||||||
|
for entity in data['Entities']:
|
||||||
|
for prop in entity:
|
||||||
|
value = entity[prop]
|
||||||
|
if hasURL(prop):
|
||||||
|
value = handleURL(value)
|
||||||
|
if prop == "script":
|
||||||
|
value = handleURL(value)
|
||||||
|
if prop == "textures":
|
||||||
|
try:
|
||||||
|
tmp = json.loads(value)
|
||||||
|
for index in tmp:
|
||||||
|
tmp[index] = handleURL(tmp[index])
|
||||||
|
value = json.dumps(tmp)
|
||||||
|
except:
|
||||||
|
value = handleURL(value)
|
||||||
|
|
||||||
|
if prop == "serverScripts":
|
||||||
|
value = handleURL(value)
|
||||||
|
|
||||||
|
entity[prop] = value
|
||||||
|
|
||||||
|
|
||||||
|
jsonString = json.dumps(data)
|
||||||
|
jsonBytes= jsonString.encode('utf-8')
|
||||||
|
with gzip.GzipFile(gzipFile, 'w') as fout: # 4. gzip
|
||||||
|
fout.write(jsonBytes)
|
||||||
|
|
||||||
|
elif argsLength == 2:
|
||||||
|
handleOptions()
|
||||||
|
|
||||||
|
main()
|
Loading…
Reference in a new issue