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

This commit is contained in:
samcake 2016-04-01 17:20:05 -07:00
commit 5baaad9b6e
77 changed files with 3232 additions and 1050 deletions

View file

@ -0,0 +1,68 @@
// cowEntityScript.js
// examples/cows
//
// Created by Eric Levin on 3/25/16
// Copyright 2016 High Fidelity, Inc.
//
// This entity script handles the logic for untipping a cow after it collides with something
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
(function() {
Script.include("../libraries/utils.js");
var _this = this;
_this.COLLISION_COOLDOWN_TIME = 5000;
this.preload = function(entityID) {
print("EBL Preload!!");
_this.entityID = entityID;
_this.mooSound = SoundCache.getSound("https://s3-us-west-1.amazonaws.com/hifi-content/eric/Sounds/moo.wav")
_this.mooSoundOptions = {volume: 0.7, loop: false};
_this.timeSinceLastCollision = 0;
_this.shouldUntipCow = true;
}
this.collisionWithEntity = function(myID, otherID, collisionInfo) {
if(_this.shouldUntipCow) {
Script.setTimeout(function() {
_this.untipCow();
_this.shouldUntipCow = true;
}, _this.COLLISION_COOLDOWN_TIME);
}
_this.shouldUntipCow = false;
}
this.untipCow = function() {
// keep yaw but reset pitch and roll
var cowProps = Entities.getEntityProperties(_this.entityID, ["rotation", "position"]);
var eulerRotation = Quat.safeEulerAngles(cowProps.rotation);
eulerRotation.x = 0;
eulerRotation.z = 0;
var newRotation = Quat.fromVec3Degrees(eulerRotation);
Entities.editEntity(_this.entityID, {
rotation: newRotation,
velocity: {x: 0, y: 0, z: 0},
angularVelocity: {x: 0, y: 0, z:0}
});
_this.mooSoundOptions.position = cowProps.position;
if (!_this.soundInjector) {
_this.soundInjector = Audio.playSound(_this.mooSound, _this.mooSoundOptions);
} else {
_this.soundInjector.setOptions(_this.mooSoundOptions);
_this.soundInjector.restart();
}
}
});

View file

@ -0,0 +1,53 @@
// cowSpawner.js
// examples/cows
//
// Created by Eric Levin on 3/25/16
// Copyright 2016 High Fidelity, Inc.
//
// This spawns a cow which will untip itself
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
var orientation = MyAvatar.orientation;
orientation = Quat.safeEulerAngles(orientation);
orientation.x = 0;
orientation = Quat.fromVec3Degrees(orientation);
var center = Vec3.sum(MyAvatar.getHeadPosition(), Vec3.multiply(2, Quat.getFront(orientation)));
var SCRIPT_URL = Script.resolvePath("cowEntityScript.js?");
var cow = Entities.addEntity({
type: "Model",
modelURL: "http://hifi-content.s3.amazonaws.com/DomainContent/production/cow/newMooCow.fbx",
name: "playa_model_throwinCow",
position: center,
animation: {
currentFrame: 278,
running: true,
url: "http://hifi-content.s3.amazonaws.com/DomainContent/Junkyard/Playa/newMooCow.fbx"
},
dimensions: {
x: 0.739,
y: 1.613,
z: 2.529
},
dynamic: true,
gravity: {
x: 0,
y: -5,
z: 0
},
shapeType: "box",
script: SCRIPT_URL,
userData: "{\"grabbableKey\":{\"grabbable\":true}}"
});
function cleanup() {
Entities.deleteEntity(cow);
}
Script.scriptEnding.connect(cleanup);

View file

@ -112,7 +112,7 @@ function autoHideReticle() {
function checkReticleDepth() { function checkReticleDepth() {
var now = Date.now(); var now = Date.now();
var timeSinceLastDepthCheck = now - lastDepthCheckTime; var timeSinceLastDepthCheck = now - lastDepthCheckTime;
if (timeSinceLastDepthCheck > TIME_BETWEEN_DEPTH_CHECKS) { if (timeSinceLastDepthCheck > TIME_BETWEEN_DEPTH_CHECKS && Reticle.visible) {
var newDesiredDepth = desiredDepth; var newDesiredDepth = desiredDepth;
lastDepthCheckTime = now; lastDepthCheckTime = now;
var reticlePosition = Reticle.position; var reticlePosition = Reticle.position;
@ -160,7 +160,6 @@ function moveToDesiredDepth() {
} else { } else {
newDepth = Reticle.depth + distanceToAdjustThisCycle; newDepth = Reticle.depth + distanceToAdjustThisCycle;
} }
Reticle.setDepth(newDepth); Reticle.setDepth(newDepth);
} }
} }

View file

@ -0,0 +1,883 @@
/*
// edit-style.css
//
// Created by Ryan Huffman on 13 Nov 2014
// Copyright 2014 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
*/
@font-face {
font-family: Raleway-Regular;
src: url(../../resources/fonts/Raleway-Regular.ttf), /* Production */
url(../../interface/resources/fonts/Raleway-Regular.ttf); /* Development */
}
@font-face {
font-family: Raleway-Light;
src: url(../../resources/fonts/Raleway-Light.ttf),
url(../../interface/resources/fonts/Raleway-Light.ttf);
}
@font-face {
font-family: Raleway-Bold;
src: url(../../resources/fonts/Raleway-Bold.ttf),
url(../../interface/resources/fonts/Raleway-Bold.ttf);
}
@font-face {
font-family: Raleway-SemiBold;
src: url(../../resources/fonts/Raleway-SemiBold.ttf),
url(../../interface/resources/fonts/Raleway-SemiBold.ttf);
}
@font-face {
font-family: FiraSans-SemiBold;
src: url(../../resources/fonts/FiraSans-SemiBold.ttf),
url(../../interface/resources/fonts/FiraSans-SemiBold.ttf);
}
@font-face {
font-family: AnonymousPro-Regular;
src: url(../../resources/fonts/AnonymousPro-Regular.ttf),
url(../../interface/resources/fonts/AnonymousPro-Regular.ttf);
}
@font-face {
font-family: HiFi-Glyphs;
src: url(../../resources/fonts/hifi-glyphs.ttf),
url(../../interface/resources/fonts/hifi-glyphs.ttf);
}
* {
margin: 0;
padding: 0;
}
body {
padding: 24px 12px 24px 12px;
color: #afafaf;
background-color: #404040;
font-family: Raleway-Regular;
font-size: 15px;
-webkit-touch-callout: none;
-webkit-user-select: none;
-khtml-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
table {
font-family: FiraSans-SemiBold;
font-size: 15px;
color: #afafaf;
border-collapse: collapse;
width: 100%;
border: 2px solid #575757;
border-radius: 7px;
}
thead {
font-family: Raleway-Regular;
font-size: 12px;
text-transform: uppercase;
background-color: #1c1c1c;
padding: 1px 0px;
border-bottom: 1px solid #575757;
width: 100%;
}
tbody {
width: 100%;
}
tfoot {
font-family: Raleway-Light;
font-size: 13px;
background-color: #1c1c1c;
border-top: 1px solid #575757;
width: 100%;
}
tfoot tr {
background-color: #1c1cff;
}
thead tr {
height: 26px; /* 28px with thead padding */
}
thead th {
height: 26px;
background-color: #1c1c1c;
border-right: 1px solid #575757;
}
thead th:last-child {
border: none;
}
tbody td {
height: 26px;
}
tfoot td {
height: 18px;
width: 100%;
background-color: #1c1c1c;
margin-left: 12px;
}
tr {
width: 100%;
cursor: pointer;
}
tr:nth-child(odd) {
background-color: #2e2e2e;
}
tr:nth-child(even) {
background-color: #1c1c1c;
}
tr:focus {
outline: none;
}
tr.selected {
color: #000000;
background-color: #00b4ef;
}
th {
text-align: center;
word-wrap: nowrap;
white-space: nowrap;
padding-left: 12px;
padding-right: 12px;
}
td {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
word-wrap: nowrap;
padding-left: 12px;
padding-right: 12px;
}
td.url {
white-space: nowrap;
overflow: hidden;
}
input[type="text"], input[type="number"], textarea {
margin: 0;
padding: 0 12px;
color: #afafaf;
background-color: #252525;
border: none;
font-family: FiraSans-SemiBold;
font-size: 15px;
}
textarea {
font-family: AnonymousPro-Regular;
font-size: 16px;
padding-top: 5px;
padding-bottom: 5px;
min-height: 64px;
width: 100%;
resize: vertical;
}
input::-webkit-input-placeholder {
font-style: italic;
}
input:focus, textarea:focus {
color: #fff;
background-color: #000;
outline: 1px solid #00b4ef;
outline-offset: -1px;
}
input::selection, textarea::selection {
color: #000000;
background-color: #00b4ef;
}
input.search {
border-radius: 14px;
}
input:disabled, textarea:disabled {
background-color: #383838;
color: #afafaf;
}
input[type="text"] {
height: 28px;
width: 100%;
}
input[type="number"] {
position: relative;
height: 28px;
width: 120px;
}
input[type=number] {
padding-right: 6px;
}
input[type=number]::-webkit-inner-spin-button {
-webkit-appearance: none;
opacity: 1.0;
display: block;
position: relative;
width: 10px;
overflow: hidden;
font-family: hifi-glyphs;
font-size: 50px;
color: #afafaf;
cursor: pointer;
/*background-color: #000000;*/
}
input[type=number]::-webkit-inner-spin-button:before,
input[type=number]::-webkit-inner-spin-button:after {
position:absolute;
left: -21px;
line-height: 8px;
text-align: center;
}
input[type=number]::-webkit-inner-spin-button:before {
content: "6";
top: 5px;
}
input[type=number]::-webkit-inner-spin-button:after {
content: "5";
bottom: 6px;
}
input[type="number"]::-webkit-inner-spin-button:hover {
color: #ffffff;
}
input.no-spin::-webkit-outer-spin-button,
input.no-spin::-webkit-inner-spin-button {
display: none;
-webkit-appearance: none;
margin: 0; /* <-- Apparently some margin are still there even though it's hidden */
padding-right: 12px;
}
input[type=button] {
font-family: Raleway-Bold;
font-size: 13px;
text-transform: uppercase;
vertical-align: top;
height: 28px;
min-width: 120px;
padding: 0px 12px;
margin-right: 8px;
border-radius: 5px;
border: none;
color: #fff;
background-color: #000;
background: linear-gradient(#343434 20%, #000 100%);
cursor: pointer;
}
input[type=button].glyph {
font-family: HiFi-Glyphs;
font-size: 20px;
text-transform: none;
min-width: 32px;
padding: 0;
}
input[type=button].red {
color: #fff;
background-color: #94132e;
background: linear-gradient(#d42043 20%, #94132e 100%);
}
input[type=button].blue {
color: #fff;
background-color: #94132e;
background: linear-gradient(#00b4ef 20%, #1080b8 100%);
}
input[type=button]:enabled:hover {
background: linear-gradient(#000, #000);
border: none;
}
input[type=button].red:enabled:hover {
background: linear-gradient(#d42043, #d42043);
border: none;
}
input[type=button].blue:enabled:hover {
background: linear-gradient(#00b4ef, #00b4ef);
border: none;
}
input[type=button]:active {
background: linear-gradient(#343434, #343434);
}
input[type=button].red:active {
background: linear-gradient(#94132e, #94132e);
}
input[type=button].blue:active {
background: linear-gradient(#1080b8, #1080b8);
}
input[type=button]:disabled {
color: #252525;
background: linear-gradient(#575757 20%, #252525 100%);
}
input[type=checkbox] {
display: none;
}
input[type=checkbox] + label {
padding-left: 24px;
background-position-y: 6px;
background-repeat: no-repeat;
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAACpSURBVDhPY2xoaGD68+dPMSMjY9L////VgTQjAw4AlH8PxLOPHj1azWxjY1MBVNsBFBfBpwkEgNKcQGwtJyfHyATkF0KEiQdAzYlMQEIUyicFyDD9+/ePgRxMvsb///4zkIOZ/v0HmkAGHginYjGNGAzS+BpdkAj8mun/3//92DyPD//993cG88nTJ4+Zm5p/BSZeJYb/DEJADEzNOPF7hn8Mk69cvVIPAHN5pyfo70F5AAAAAElFTkSuQmCC);
}
input[type=checkbox]:enabled + label:hover {
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAClSURBVDhPY2hoaGD6//9/6b9//64D8T8gGycASr/7+/dv5/79+1kYgIxKqDjRAKiniRFIv2JgYBAFYlLAE0aQ66AckgDjjx8/yNP44cMH8jS+fPmSPI0PHz4kT+PNmzfJ03jp0iXyNJ46dYo8jYcPHyYnAbxm+vnzZz8wLhlIwd+/f5/BrKSkdExCQuLrnz9/lIBpUAiIQekXF34PTGmTT548WQ8AokXg+rhVtPYAAAAASUVORK5CYII=);
}
input[type=checkbox]:checked + label {
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAFLSURBVDhPjZK9SgNBFIXvbCaQRDQq7mIhQRPBRisJKcwLWOobaCsExEaxcEEQe0trGysfwg0EwWoDsbFIJUaIBJOwus547saNP3FlPzgzzJxzL5edFbZtG77v7wkhtrXWS9gFRQC/DZ07jnOYKJfL+8ie4n7mvyIGdhpay+VyQuK8y5dPZoHuVtbpZcLi4wjJ1x4t316R9dDgBlsSi8mGu7pJjyJFzVaH+r7iqyHSELSQzVADjS0UgjlDKUUsLzVO98+9kSLGV5qaHXhjU0GWNSxk3hCIwnsfeMNCjTArLmHeUBodoLiE+R+jxuHPUZP4elGE3teonx2S/Q7lJzOUlkYQ+A4/xzyegzNhXmJpwTMXry9IFjcoa84O0r+QXpcK1cugCLREZadyoA19Ergxwf96nKjd1KqlYqmLQ540TUNwItUmRWdu3T36AODjwgpY9xqqAAAAAElFTkSuQmCC);
}
input[type=checkbox]:checked + label:hover {
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAEySURBVDhPnZLPSsNAEMa/XVPBCE0RhNy0OarP4Av4AD6JB0GwVBA8efBBxHsgh4CQswcRoUIpiIpVAm3zZ5M4szFSbQPBH3xkJvNNZskOer2eLIriKM/ze1JOcS1UHmdZduF5ngEKjr/fN4Z6+oKerwA2gxC4HAFPEWVLsAzgZAvYt3Q6Enw6jg7uBAaTFMNwhpnKdbXCkAJdy8ROu4XrXW2HTJIErHcFDD6nC02Mom8PwymeE2gvS0ZRBBaTlsOXEmdlrfLLOI7Bakrl/zWxCT8T/904f9QW/b06qtrCUdtFCqdjYs2Q2jAPX8c2XQd7Kr/wfV8vwIPs4Ga1ixe5Xrr/YFLTYfKIvWzM6ZtwXZdX7lxXG0L+sxXHcW5t254opRzawQ0S72+dPmjTroIgOP0CQSMt5LDn1T8AAAAASUVORK5CYII=);
}
.selectable {
-webkit-touch-callout: text;
-webkit-user-select: text;
-khtml-user-select: text;
-moz-user-select: text;
-ms-user-select: text;
user-select: text;
cursor: text;
}
.color-box {
display: inline-block;
width: 15pt;
height: 15pt;
border: 0.75pt solid black;
margin: 1.5pt;
cursor: pointer;
}
.color-box.highlight {
width: 13.5pt;
height: 13.5pt;
border: 1.5pt solid black;
}
.section-header, .sub-section-header {
display: table;
width: 100%;
margin: 22px -12px 0 -12px;
padding: 14px 12px 0 12px;
font-family: Raleway-Regular;
font-size: 12px;
color: #afafaf;
height: 28px;
text-transform: uppercase;
outline: none;
}
.section-header {
position: relative;
background: #404040 url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAQAAAAqCAIAAAAbNW1vAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAmSURBVChTY1BFAgzhSIDBAQmMcoYHRwIJMCgjAQZ9JMBgBQdWVgBh5XmBV5A2FQAAAABJRU5ErkJggg==) repeat-x top left;
}
.sub-section-header, .no-collapse {
background: #404040 url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAQAAAAECAYAAACp8Z5+AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsQAAA7EAZUrDhsAAAAjSURBVBhXY1RVVf3PgARYjIyMoEwIYHRwcEBRwQSloYCBAQCwjgPMiI7W2QAAAABJRU5ErkJggg==) repeat-x top left;
}
.section-header:first-child {
margin-top: 0;
padding-top: 0;
background: none;
height: auto;
}
.sub-section-header {
margin-bottom: -10px;
}
.section-header span {
font-family: HiFi-Glyphs;
font-size: 30px;
float: right;
position: absolute;
top: 4px;
right: 6px;
}
.section-header[collapsed="true"] {
margin-bottom: -22px;
}
.text-group[collapsed="true"] ~ .text-group,
.zone-group[collapsed="true"] ~ .zone-group,
.web-group[collapsed="true"] ~ .web-group,
.hyperlink-group[collapsed="true"] ~ .hyperlink-group,
.spatial-group[collapsed="true"] ~ .spatial-group,
.physical-group[collapsed="true"] ~ .physical-group,
.behavior-group[collapsed="true"] ~ .behavior-group,
.model-group[collapsed="true"] ~ .model-group,
.light-group[collapsed="true"] ~ .light-group {
display: none !important;
}
.property {
display: table;
width: 100%;
margin-top: 22px;
min-height: 29px;
}
.property label {
display: table-cell;
vertical-align: middle;
font-family: Raleway-SemiBold;
font-size: 14px;
}
.value {
display: block;
min-height: 18px;
}
.value label {
display: inline-block;
vertical-align: top;
width: 48px;
}
.value span {
font-family: FiraSans-SemiBold;
font-size: 15px;
}
.checkbox + .checkbox {
margin-top: 0;
}
.checkbox-sub-props {
margin-top: 18px;
}
.property .number {
float: left;
}
.property .number + .number {
margin-left: 12px;
}
.text label, .url label, .number label, .textarea label, .rgb label, .xyz label, .pyr label, .dropdown label, .gen label {
float: left;
margin-bottom: 4px;
}
.number > input {
clear: both;
float: left;
}
.number > span {
clear: both;
float: left;
}
.xyz > div, .pyr > div, .gen > div {
clear: both;
}
.unit {
padding-left: 4px;
vertical-align: top;
position: relative;
top: 5px;
}
.dropdown {
position: relative;
margin-bottom: -17px;
}
.dropdown select {
clear: both;
}
.dropdown dl {
clear: both;
}
.dropdown dl {
font-family: FiraSans-SemiBold;
font-size: 15px;
width: 172px;
height: 28px;
padding: 0 28px 0 12px;
color: #afafaf;
background: linear-gradient(#7d7d7d 20%, #686a68 100%);
position: relative;
}
.dropdown dl[dropped="true"] {
color: #404040;
background: linear-gradient(#afafaf, #afafaf);
}
.dropdown dt {
height: 100%;
box-sizing: border-box;
border-right: 1px solid #121212;
width: 100%;
}
.dropdown dt:hover {
color: #404040;
}
.dropdown dt:focus {
outline: none;
}
.dropdown dt span:first-child {
display: inline-block;
position: relative;
top: 5px;
}
.dropdown dt span:last-child {
font-family: HiFi-Glyphs;
font-size: 42px;
float: right;
margin-right: -48px;
position: relative;
left: -12px;
top: -11px;
}
.dropdown dd {
position: absolute;
top: 28px;
left: 3px;
display: none;
}
.dropdown dl[dropped="true"] dd {
display: block;
}
.dropdown li {
list-style-type: none;
padding: 3px 0 1px 12px;
width: 200px;
height: auto;
font-family: FiraSans-SemiBold;
font-size: 15px;
color: #404040;
background-color: #afafaf
}
.dropdown li:hover {
background-color: #00b4ef;
}
div.refresh {
box-sizing: border-box;
padding-right: 44px;
}
div.refresh input[type="button"] {
float: right;
margin-right: -44px;
}
.color-picker {
box-sizing: border-box;
float: left;
margin-bottom: 12px;
width: 36px;
height: 36px;
border: 4px solid #afafaf;
border-radius: 4px;
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAABTSURBVChTjcxBDoAwCETRwTs33sFjwB6uaCE1Ggvav5qQF7CSqu40dllHjYiOT3gh3yV8Ii+Fb+RNMEP9hm3sKENmBhG5P1aImWMH/EMerSAAOAFgTC/R8ZXSXAAAAABJRU5ErkJggg==);
background-position: bottom right;
background-repeat: no-repeat;
}
.color-picker:focus {
outline: none;
}
.color-picker[active="true"] {
border-color: #000;
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAABDSURBVChTjcoLCkAhCETRNq0tf97Y5xGZ1gVJ45TH6njThIO+xk2UwhWFcEdH6JCqOuiQiMDi/hcii3crRRb/7ggAPvIMVihQwvSXAAAAAElFTkSuQmCC);
}
.rgb label {
float: left;
margin-top: 10px;
margin-left: 12px;
}
.rgb label + * {
clear: both;
}
.tuple {
width: 100%;
text-align: center;
}
.tuple div {
display: inline-block;
position: relative;
min-width: 120px;
min-height: 1px;
}
.tuple div:nth-child(1) {
float: left;
}
.tuple div:nth-child(2) {
}
.tuple div:nth-child(3) {
float: right;
}
.rgb .tuple input {
padding-left: 65px;
}
.xyz .tuple input {
padding-left: 25px;
}
.pyr .tuple input {
padding-left: 45px;
}
.tuple div > label:first-child {
float: left;
}
.tuple div > label + input {
clear: both;
float: left;
}
.tuple div input + label {
display: inline !important;
float: none !important;
position: absolute;
margin-top: 8px;
margin-left: 6px;
left: 0;
font-family: FiraSans-SemiBold;
font-size: 12px;
}
.tuple .red + label, .tuple .x + label, .tuple .pitch + label {
color: #e2334d;
}
.tuple .green + label, .tuple .y + label, .tuple .yaw + label {
color: #1ac567;
}
.tuple .blue + label, .tuple .z + label, .tuple .roll + label {
color: #1080b8;
}
.tuple .red:focus, .tuple .x:focus, .tuple .pitch:focus {
outline-color: #e2334d;
}
.tuple .green:focus, .tuple .y:focus, .tuple .yaw:focus {
outline-color: #1ac567;
}
tuple, .blue:focus, .tuple .z:focus, .tuple .roll:focus {
outline-color: #1080b8;
}
.xyz .buttons input {
margin-top: 12px;
}
.xyz .buttons span {
word-wrap: nowrap;
white-space: nowrap;
}
.row input {
float: left;
}
.row input[type=button] {
margin-left: 8px;
margin-right: 0;
}
::-webkit-scrollbar {
width: 10px;
height: 10px;
}
::-webkit-scrollbar-track {
background-color: #2e2e2e;
}
::-webkit-scrollbar-thumb {
background-color: #696969;
border: 2px solid #2e2e2e;
border-radius: 8px;
}
/* FIXME: Revisit textarea resizer/corner when move to Qt 5.6 or later: see if can get resizer/corner to always be visible and
have correct background color with and without scrollbars. */
textarea:enabled::-webkit-resizer {
background-size: 10px 10px;
background: #252525 url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAIAAAACUFjqAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAXSURBVChTY1RVVWXADZigNA4wMqUZGACS3gCD5UUtKAAAAABJRU5ErkJggg==) no-repeat bottom right;
}
textarea:focus::-webkit-resizer {
background-size: 10px 10px;
background: #000000 url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAACOSURBVChThdC5DQQhDAXQvyRI5LQxFdABARWQElAPogYkiqEWQhLYGe8xxzJaS5a/8AuQHwDG2n+Lvee0hBDQWlO+hRvy3mNZFjDG5vCDOOeIMaL3/guPKISAWiu9n+AVSSlhraXdF86Qcw6tNdoTvEOlFOScd6iUOv3JGEMopYQx9jNvaawnoHnNr8Z4AuRLPOq2gPgnAAAAAElFTkSuQmCC) no-repeat bottom right;
}
textarea:enabled[scrolling="true"]::-webkit-resizer {
background-size: 10px 10px;
background: #2e2e2e url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAACKSURBVChTjdAxDsMgDAXQT4UYuQIzCwsSKxsSJ4YDoByDY7AwUOG2aZMQqX+xhd9gzIwxA3/k8a7LCCFgraX+Fk4UY4RSCoyxNfwgzjlyzhhjXOEvSimhtUbvB3hGUkp472m2wxUKIaD3TnOCd6jWim3bvlBrfdjJOUeolEJoZj/4PMH83bl/BXgCWSs2Z09IjgoAAAAASUVORK5CYII=) no-repeat bottom right;
}
#entity-list-header {
margin-bottom: 24px;
}
#delete {
float: right;
margin-right: 0;
background-color: #ff0000;
}
#entity-list {
position: relative; /* New positioning context. */
}
#search-area {
padding-right: 148px;
padding-bottom: 24px;
}
#filter {
width: 98%;
}
#radius-and-unit {
float: right;
margin-right: -148px;
}
#entity-table-scroll {
/* Height is set by JavaScript. */
width: 100%;
overflow-x: hidden;
overflow-y: auto;
padding-top: 28px; /* Space for header and footer outside of scroll region. */
margin-top: 28px;
border-left: 2px solid #575757;
border-right: 2px solid #575757;
}
#entity-table-scroll, #entity-table {
background-color: #1c1c1c;
}
#entity-table {
margin-top: -28px;
margin-bottom: -18px;
table-layout: fixed;
border: none;
}
#entity-table thead {
border: 2px solid #575757;
border-top-left-radius: 7px;
border-top-right-radius: 7px;
border-bottom: 1px solid #575757;
}
#entity-table tfoot {
border: 2px solid #575757;
border-bottom-left-radius: 7px;
border-bottom-right-radius: 7px;
border-top: 1px solid #575757;
}
#entity-table thead tr, #entity-table thead tr th,
#entity-table tfoot tr, #entity-table tfoot tr td {
background: none;
}
#entity-table th:focus {
outline: none;
}
#col-type {
width: 16%;
}
#col-name {
width: 42%;
}
#col-url {
width: 42%;
}
#entity-table thead {
position: absolute;
top: 49px;
left: 0;
width: 100%;
}
#entity-table thead th {
padding: 0;
}
#entity-table tfoot {
position: absolute;
bottom: -21px;
left: 0;
width: 100%;
}
#no-entities {
display: none;
position: absolute;
top: 80px;
padding: 12px;
font-family: FiraSans-SemiBold;
font-size: 15px;
font-style: italic;
color: #afafaf;
}
#properties-list .property:first-child {
margin-top: 0;
}
#property-id::selection {
color: #000000;
background-color: #00b4ef;
}
input#dimension-rescale-button {
min-width: 50px;
margin-left: 6px;
}
.color-set label, .color-set span {
display: block;
}
.color-set span {
padding-top: 2px;
}

View file

@ -1,6 +1,16 @@
<!--
// entityList.html
//
// Created by Ryan Huffman on 19 Nov 2014
// Copyright 2014 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
-->
<html> <html>
<head> <head>
<link rel="stylesheet" type="text/css" href="style.css"> <link rel="stylesheet" type="text/css" href="edit-style.css">
<script src="list.min.js"></script> <script src="list.min.js"></script>
<script type="text/javascript" src="qrc:///qtwebchannel/qwebchannel.js"></script> <script type="text/javascript" src="qrc:///qtwebchannel/qwebchannel.js"></script>
<script type="text/javascript" src="eventBridgeLoader.js"></script> <script type="text/javascript" src="eventBridgeLoader.js"></script>
@ -26,8 +36,10 @@
elDelete = document.getElementById("delete"); elDelete = document.getElementById("delete");
elTeleport = document.getElementById("teleport"); elTeleport = document.getElementById("teleport");
elRadius = document.getElementById("radius"); elRadius = document.getElementById("radius");
elFooter = document.getElementById("footer-text");
elNoEntitiesMessage = document.getElementById("no-entities"); elNoEntitiesMessage = document.getElementById("no-entities");
elNoEntitiesRadius = document.getElementById("no-entities-radius"); elNoEntitiesRadius = document.getElementById("no-entities-radius");
elEntityTableScroll = document.getElementById("entity-table-scroll");
document.getElementById("entity-name").onclick = function() { document.getElementById("entity-name").onclick = function() {
setSortColumn('name'); setSortColumn('name');
@ -168,6 +180,17 @@
notFound = true; notFound = true;
} }
} }
if (selectedEntities.length > 1) {
elFooter.firstChild.nodeValue = selectedEntities.length + " entities selected";
} else if (selectedEntities.length === 1) {
elFooter.firstChild.nodeValue = "1 entity selected";
} else if (entityList.visibleItems.length === 1) {
elFooter.firstChild.nodeValue = "1 entity found";
} else {
elFooter.firstChild.nodeValue = entityList.visibleItems.length + " entities found";
}
return notFound; return notFound;
} }
@ -214,57 +237,90 @@
} else if (data.type == "update") { } else if (data.type == "update") {
var newEntities = data.entities; var newEntities = data.entities;
if (newEntities.length == 0) { if (newEntities.length == 0) {
elEntityTable.style.display = "none";
elNoEntitiesMessage.style.display = "block"; elNoEntitiesMessage.style.display = "block";
} else { } else {
elEntityTable.style.display = "table";
elNoEntitiesMessage.style.display = "none"; elNoEntitiesMessage.style.display = "none";
for (var i = 0; i < newEntities.length; i++) { for (var i = 0; i < newEntities.length; i++) {
var id = newEntities[i].id; var id = newEntities[i].id;
addEntity(id, newEntities[i].name, newEntities[i].type, newEntities[i].url); addEntity(id, newEntities[i].name, newEntities[i].type, newEntities[i].url);
} }
updateSelectedEntities(data.selectedIDs); updateSelectedEntities(data.selectedIDs);
resize();
} }
} }
}); });
setTimeout(refreshEntities, 1000); setTimeout(refreshEntities, 1000);
} }
function resize() {
// Take up available window space
elEntityTableScroll.style.height = window.innerHeight - 232;
// Update the widths of the header cells to match the body
var tds = document.querySelectorAll("#entity-table-body tr:first-child td");
var ths = document.querySelectorAll("#entity-table thead th");
if (tds.length >= ths.length) {
for (var i = 0; i < ths.length; i++) {
ths[i].style.width = tds[i].offsetWidth;
}
}
};
window.onresize = resize;
resize();
}); });
}
// Disable right-click context menu which is not visible in the HMD and makes it seem like the app has locked
document.addEventListener("contextmenu", function (event) {
event.preventDefault();
}, false);
}
</script> </script>
</head> </head>
<body onload='loaded();'> <body onload='loaded();'>
<div id="entity-list-header"> <div id="entity-list-header">
<input type="button" id="refresh" value="Refresh" /> <input type="button" class="glyph" id="refresh" value="F" />
<input type="button" id="teleport" value="Teleport" /> <input type="button" id="teleport" value="Jump To Selection" />
<input type="button" id="delete" style="background-color: rgb(244, 64, 64); float: right" value="Delete" /> <input type="button" class="red" id="delete" value="Delete" />
</div> </div>
<div id="entity-list"> <div id="entity-list">
<div id="search-area"> <div id="search-area">
<input type="text" class="search" id="filter" placeholder="Filter" /> <input type="text" class="search" id="filter" placeholder="Filter" />
<span id="radius-and-unit"><input type="number" id="radius" value="100" />&nbsp;m</span> <span id="radius-and-unit"><input type="number" id="radius" value="100" /><span class="unit">m</span></span>
</div>
<div id="entity-table-scroll">
<table id="entity-table">
<colgroup>
<col span="1" id="col-type" />
<col span="1" id="col-name" />
<col span="1" id="col-url" />
</colgroup>
<thead>
<tr>
<th id="entity-type" data-sort="type">Type <span class="sort-order" style="display: inline">&nbsp;&#x25BE;</span></th>
<th id="entity-name" data-sort="type">Name <span class="sort-order" style="display: none">&nbsp;&#x25BE;</span></th>
<th id="entity-url" data-sort="url">File <span class="sort-order" style="display: none">&nbsp;&#x25BE;</span></th>
</tr>
</thead>
<tbody class="list" id="entity-table-body">
<tr>
<td class="type">Type</td>
<td class="name">Name</td>
<td class="url"><div class='outer'><div class='inner'>URL</div></div></td>
<td class="id" style="display: none">Type</td>
</tr>
</tbody>
<tfoot>
<tr>
<td id="footer-text" colspan="3">Footer text</td>
</tr>
</tfoot>
</table>
<div id="no-entities">
No entities found within a <span id="no-entities-radius">100</span> meter radius. Try moving to a different location and refreshing.
</div>
</div> </div>
<table id="entity-table">
<thead>
<tr>
<th id="entity-type" data-sort="type">Type <span class="sort-order" style="display: inline">&nbsp;&#x25BE;</span></th>
<th id="entity-name" data-sort="type">Name <span class="sort-order" style="display: inline">&nbsp;&#x25BE;</span></th>
<th id="entity-url" data-sort="url">URL <span class="sort-order" style="display: none">&nbsp;&#x25BE;</span></th>
</tr>
</thead>
<tbody class="list" id="entity-table-body">
<tr>
<td class="id" style="display: none">Type</td>
<td class="type">Type</td>
<td class="name">Name</td>
<td class="url"><div class='outer'><div class='inner'>URL</div></div></td>
</tr>
</tbody>
</table>
</div>
<div id="no-entities">
No entities found within a <span id="no-entities-radius">100</span> meter radius. Try moving to a different location and refreshing.
</div> </div>
</body> </body>
</html> </html>

File diff suppressed because it is too large Load diff

View file

@ -1,6 +1,16 @@
<!--
// gridControls.html
//
// Created by Ryan Huffman on 6 Nov 2014
// Copyright 2014 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
-->
<html> <html>
<head> <head>
<link rel="stylesheet" type="text/css" href="style.css"> <link rel="stylesheet" type="text/css" href="edit-style.css">
<script type="text/javascript" src="qrc:///qtwebchannel/qwebchannel.js"></script> <script type="text/javascript" src="qrc:///qtwebchannel/qwebchannel.js"></script>
<script type="text/javascript" src="eventBridgeLoader.js"></script> <script type="text/javascript" src="eventBridgeLoader.js"></script>
<script> <script>
@ -109,61 +119,63 @@
EventBridge.emitWebEvent(JSON.stringify({ type: 'init' })); EventBridge.emitWebEvent(JSON.stringify({ type: 'init' }));
}); });
// Disable right-click context menu which is not visible in the HMD and makes it seem like the app has locked
document.addEventListener("contextmenu", function (event) {
event.preventDefault();
}, false);
} }
</script> </script>
</head> </head>
<body onload='loaded();'> <body onload='loaded();'>
<div class="grid-section"> <div id="grid-section">
<div class="property-section"> <div class="section-header">
<label>Visible</label> <label>Editing Grid</label>
</div>
<div class="property checkbox">
<input type='checkbox' id="horiz-grid-visible">
<label for="horiz-grid-visible">Visible</label>
</div>
<div class="property checkbox">
<input type="checkbox" id="snap-to-grid">
<label for="snap-to-grid">Snap entities to grid</label>
</div>
<div class="property">
<div class="number">
<label>Major grid size</label>
<span>
<input type="number" id="major-spacing" min="1" step="1" /><span class="unit">m</span>
</span>
</div>
<div class="number">
<label>Minor grid size</label>
<span>
<input type="number" id="minor-spacing" min="0.2" step="0.2" /><span class="unit">m</span>
</span>
</div>
</div>
<div class="property number">
<label>Position (Y axis)</label>
<span> <span>
<input type='checkbox' id="horiz-grid-visible"> <input type="number" id="horiz-y" step="0.1" /><span class="unit">m</span>
</span> </span>
</div> </div>
<div class="property-section"> <div class="property color-set">
<label>Snap to grid</label> <label>Grid line color</label>
<span>
<input type='checkbox' id="snap-to-grid">
</span>
</div>
<div id="horizontal-position" class="property-section">
<label>Position (Y Axis)</label>
<span>
<input type='number' id="horiz-y" class="number" step="0.1"></input>
</span>
</div>
<div class="property-section">
<label>Minor Grid Size</label>
<span>
<input type='number' id="minor-spacing" min="0.2" step="0.2"></input>
</span>
</div>
<div class="property-section">
<label>Major Grid Every</label>
<span>
<input type='number' id="major-spacing" min="1" step="1", ></input>
</span>
</div>
<div class="property-section">
<label>Grid Color</label>
<span id="grid-colors"></span> <span id="grid-colors"></span>
</div> </div>
<div class="property-section"> <div class="property">
<span> <span>
<input type="button" id="move-to-selection" value="Move to Selection"> <input type="button" id="move-to-selection" value="Align To Selection">
</span> <input type="button" id="move-to-avatar" value="Align To Avatar">
</div> </span>
<div class="property-section">
<span>
<input type="button" id="move-to-avatar" value="Move to Avatar">
</span>
</div> </div>
</div> </div>
</body> </body>

View file

@ -5,7 +5,7 @@ EntityListTool = function(opts) {
var url = ENTITY_LIST_HTML_URL; var url = ENTITY_LIST_HTML_URL;
var webView = new OverlayWebWindow({ var webView = new OverlayWebWindow({
title: 'Entities', source: url, toolWindow: true title: 'Entity List', source: url, toolWindow: true
}); });

View file

@ -20,6 +20,12 @@
<script type="text/javascript" src="../html/eventBridgeLoader.js"></script> <script type="text/javascript" src="../html/eventBridgeLoader.js"></script>
<script type="text/javascript" src="particleExplorer.js?v42"></script> <script type="text/javascript" src="particleExplorer.js?v42"></script>
<script> <script>
function loaded() {
// Disable right-click context menu which is not visible in the HMD and makes it seem like the app has locked
document.addEventListener("contextmenu", function (event) {
event.preventDefault();
}, false);
}
</script> </script>
<style> <style>
@ -60,7 +66,7 @@ body{
</style> </style>
</head> </head>
<body> <body onload="loaded();">
<div class="importer"> <div class="importer">
<input type='text' id="importer-input" placeholder="Import: Paste JSON here." onkeypress="handleInputKeyPress(event)"> <input type='text' id="importer-input" placeholder="Import: Paste JSON here." onkeypress="handleInputKeyPress(event)">
<div class = "exported-props-section"> <div class = "exported-props-section">

View file

@ -120,27 +120,95 @@ function menuItemEvent(menuItem) {
if (menuItem.endsWith(" for Output")) { if (menuItem.endsWith(" for Output")) {
var selectedDevice = menuItem.trimStartsWith("Use ").trimEndsWith(" for Output"); var selectedDevice = menuItem.trimStartsWith("Use ").trimEndsWith(" for Output");
print("output audio selection..." + selectedDevice); print("output audio selection..." + selectedDevice);
Menu.menuItemEvent.disconnect(menuItemEvent); Menu.menuItemEvent.disconnect(menuItemEvent);
Menu.setIsOptionChecked(selectedOutputMenu, false); Menu.setIsOptionChecked(selectedOutputMenu, false);
selectedOutputMenu = menuItem; selectedOutputMenu = menuItem;
Menu.setIsOptionChecked(selectedOutputMenu, true); Menu.setIsOptionChecked(selectedOutputMenu, true);
if (AudioDevice.setOutputDevice(selectedDevice)) { if (AudioDevice.setOutputDevice(selectedDevice)) {
Settings.setValue(OUTPUT_DEVICE_SETTING, selectedDevice); Settings.setValue(OUTPUT_DEVICE_SETTING, selectedDevice);
} }
Menu.menuItemEvent.connect(menuItemEvent); Menu.menuItemEvent.connect(menuItemEvent);
} else if (menuItem.endsWith(" for Input")) { } else if (menuItem.endsWith(" for Input")) {
var selectedDevice = menuItem.trimStartsWith("Use ").trimEndsWith(" for Input"); var selectedDevice = menuItem.trimStartsWith("Use ").trimEndsWith(" for Input");
print("input audio selection..." + selectedDevice); print("input audio selection..." + selectedDevice);
Menu.menuItemEvent.disconnect(menuItemEvent); Menu.menuItemEvent.disconnect(menuItemEvent);
Menu.setIsOptionChecked(selectedInputMenu, false); Menu.setIsOptionChecked(selectedInputMenu, false);
selectedInputMenu = menuItem; selectedInputMenu = menuItem;
Menu.setIsOptionChecked(selectedInputMenu, true); Menu.setIsOptionChecked(selectedInputMenu, true);
if (AudioDevice.setInputDevice(selectedDevice)) { if (AudioDevice.setInputDevice(selectedDevice)) {
Settings.setValue(INPUT_DEVICE_SETTING, selectedDevice); Settings.setValue(INPUT_DEVICE_SETTING, selectedDevice);
} }
Menu.menuItemEvent.connect(menuItemEvent); Menu.menuItemEvent.connect(menuItemEvent);
} }
} }
} }
Menu.menuItemEvent.connect(menuItemEvent); Menu.menuItemEvent.connect(menuItemEvent);
// Some HMDs (like Oculus CV1) have a built in audio device. If they
// do, then this function will handle switching to that device automatically
// when you goActive with the HMD active.
var wasHmdMounted = false; // assume it's un-mounted to start
var switchedAudioInputToHMD = false;
var switchedAudioOutputToHMD = false;
var previousSelectedInputAudioDevice = "";
var previousSelectedOutputAudioDevice = "";
function restoreAudio() {
if (switchedAudioInputToHMD) {
print("switching back from HMD preferred audio input to:" + previousSelectedInputAudioDevice);
menuItemEvent("Use " + previousSelectedInputAudioDevice + " for Input");
}
if (switchedAudioOutputToHMD) {
print("switching back from HMD preferred audio output to:" + previousSelectedOutputAudioDevice);
menuItemEvent("Use " + previousSelectedOutputAudioDevice + " for Output");
}
}
function checkHMDAudio() {
// Mounted state is changing... handle switching
if (HMD.mounted != wasHmdMounted) {
print("HMD mounted changed...");
// We're putting the HMD on... switch to those devices
if (HMD.mounted) {
print("NOW mounted...");
var hmdPreferredAudioInput = HMD.preferredAudioInput();
var hmdPreferredAudioOutput = HMD.preferredAudioOutput();
print("hmdPreferredAudioInput:" + hmdPreferredAudioInput);
print("hmdPreferredAudioOutput:" + hmdPreferredAudioOutput);
var hmdHasPreferredAudio = (hmdPreferredAudioInput !== "") || (hmdPreferredAudioOutput !== "");
if (hmdHasPreferredAudio) {
print("HMD has preferred audio!");
previousSelectedInputAudioDevice = Settings.getValue(INPUT_DEVICE_SETTING);
previousSelectedOutputAudioDevice = Settings.getValue(OUTPUT_DEVICE_SETTING);
print("previousSelectedInputAudioDevice:" + previousSelectedInputAudioDevice);
print("previousSelectedOutputAudioDevice:" + previousSelectedOutputAudioDevice);
if (hmdPreferredAudioInput != previousSelectedInputAudioDevice && hmdPreferredAudioInput !== "") {
print("switching to HMD preferred audio input to:" + hmdPreferredAudioInput);
switchedAudioInputToHMD = true;
menuItemEvent("Use " + hmdPreferredAudioInput + " for Input");
}
if (hmdPreferredAudioOutput != previousSelectedOutputAudioDevice && hmdPreferredAudioOutput !== "") {
print("switching to HMD preferred audio output to:" + hmdPreferredAudioOutput);
switchedAudioOutputToHMD = true;
menuItemEvent("Use " + hmdPreferredAudioOutput + " for Output");
}
}
} else {
print("HMD NOW un-mounted...");
restoreAudio();
}
}
wasHmdMounted = HMD.mounted;
}
Script.update.connect(checkHMDAudio);
Script.scriptEnding.connect(function () {
restoreAudio();
Menu.menuItemEvent.disconnect(menuItemEvent);
Script.update.disconnect(checkHMDAudio);
});

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 MiB

View file

@ -0,0 +1,41 @@
float aspect(vec2 v) {
return v.x / v.y;
}
vec3 aspectCorrectedTexture() {
vec2 uv = _position.xy;
uv += 0.5;
uv.y = 1.0 - uv.y;
float targetAspect = iWorldScale.x / iWorldScale.y;
float sourceAspect = aspect(iChannelResolution[0].xy);
float aspectCorrection = sourceAspect / targetAspect;
if (aspectCorrection > 1.0) {
float offset = aspectCorrection - 1.0;
float halfOffset = offset / 2.0;
uv.y -= halfOffset;
uv.y *= aspectCorrection;
} else {
float offset = 1.0 - aspectCorrection;
float halfOffset = offset / 2.0;
uv.x -= halfOffset;
uv.x /= aspectCorrection;
}
if (any(lessThan(uv, vec2(0.0)))) {
return vec3(0.0);
}
if (any(greaterThan(uv, vec2(1.0)))) {
return vec3(0.0);
}
vec4 color = texture(iChannel0, uv);
return color.rgb * max(0.5, sourceAspect) * max(0.9, fract(iWorldPosition.x));
}
float getProceduralColors(inout vec3 diffuse, inout vec3 specular, inout float shininess) {
specular = aspectCorrectedTexture();
return 1.0;
}

View file

@ -0,0 +1,67 @@
Script.include("https://s3.amazonaws.com/DreamingContent/scripts/Austin.js");
var ENTITY_SPAWN_LIMIT = 500;
var ENTITY_LIFETIME = 600;
var RADIUS = 1.0; // Spawn within this radius (square)
var TEST_ENTITY_NAME = "EntitySpawnTest";
var entities = [];
var textureIndex = 0;
var texture = Script.resolvePath('cube_texture.png');
var shader = Script.resolvePath('textureStress.fs');
var qml = Script.resolvePath('textureStress.qml');
qmlWindow = new OverlayWindow({
title: 'Test Qml',
source: qml,
height: 240,
width: 320,
toolWindow: false,
visible: true
});
function deleteItems(count) {
if (!count) {
var ids = Entities.findEntities(MyAvatar.position, 50);
ids.forEach(function(id) {
var properties = Entities.getEntityProperties(id, ["name"]);
if (properties.name === TEST_ENTITY_NAME) {
Entities.deleteEntity(id);
}
}, this);
entities = [];
return;
} else {
// FIXME... implement
}
}
function createItems(count) {
for (var i = 0; i < count; ++i) {
var newEntity = Entities.addEntity({
type: "Box",
name: TEST_ENTITY_NAME,
position: AUSTIN.avatarRelativePosition(AUSTIN.randomPositionXZ({ x: 0, y: 0, z: -2 }, RADIUS)),
color: { r: 255, g: 255, b: 255 },
dimensions: AUSTIN.randomDimensions(),
lifetime: ENTITY_LIFETIME,
userData: JSON.stringify({
ProceduralEntity: {
version: 2,
shaderUrl: shader,
channels: [ texture + "?" + textureIndex++ ]
}
})
});
entities.push(newEntity);
}
}
qmlWindow.fromQml.connect(function(message){
print(message);
if (message[0] === "create") {
var count = message[1] || 1;
createItems(message[1] || 1);
} else if (message[0] === "delete") {
deleteItems(message[1]);
}
});

View file

@ -0,0 +1,69 @@
import QtQuick 2.5
import QtQuick.Controls 1.4
Rectangle {
id: root
width: parent ? parent.width : 100
height: parent ? parent.height : 100
signal sendToScript(var message);
Text {
id: label
text: "GPU Texture Usage: "
}
Text {
id: usage
anchors.left: label.right
anchors.leftMargin: 8
text: "N/A"
Timer {
repeat: true
running: true
interval: 500
onTriggered: {
usage.text = Render.getConfig("Stats")["textureGPUMemoryUsage"];
}
}
}
Column {
anchors { left: parent.left; right: parent.right; top: label.bottom; topMargin: 8; bottom: parent.bottom }
spacing: 8
Button {
text: "Add 1"
onClicked: root.sendToScript(["create", 1]);
}
Button {
text: "Add 10"
onClicked: root.sendToScript(["create", 10]);
}
Button {
text: "Add 100"
onClicked: root.sendToScript(["create", 100]);
}
/*
Button {
text: "Delete 1"
onClicked: root.sendToScript(["delete", 1]);
}
Button {
text: "Delete 10"
onClicked: root.sendToScript(["delete", 10]);
}
Button {
text: "Delete 100"
onClicked: root.sendToScript(["delete", 100]);
}
*/
Button {
text: "Delete All"
onClicked: root.sendToScript(["delete", 0]);
}
}
}

View file

@ -106,6 +106,13 @@ elseif(WIN32)
# add an executable that also has the icon itself and the configured rc file as resources # add an executable that also has the icon itself and the configured rc file as resources
add_executable(${TARGET_NAME} WIN32 ${INTERFACE_SRCS} ${QM} ${CONFIGURE_ICON_RC_OUTPUT}) add_executable(${TARGET_NAME} WIN32 ${INTERFACE_SRCS} ${QM} ${CONFIGURE_ICON_RC_OUTPUT})
add_custom_command(
TARGET ${TARGET_NAME}
POST_BUILD
COMMAND "mt.exe" -manifest "${CMAKE_CURRENT_SOURCE_DIR}/interface.exe.manifest" -inputresource:"$<TARGET_FILE:${TARGET_NAME}>"\;\#1 -outputresource:"$<TARGET_FILE:${TARGET_NAME}>"\;\#1
COMMENT "Adding OS version support manifest to exe"
)
else() else()
add_executable(${TARGET_NAME} ${INTERFACE_SRCS} ${QM}) add_executable(${TARGET_NAME} ${INTERFACE_SRCS} ${QM})
endif() endif()

View file

@ -0,0 +1,15 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
<application>
<!--This Id value indicates the application supports Windows 7 functionality-->
<supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/>
<!--This Id value indicates the application supports Windows 8 functionality-->
<supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"/>
<!--This Id value indicates the application supports Windows 8.1 functionality-->
<supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/>
<!--This Id value indicates the application supports Windows 10 functionality-->
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/>
</application>
</compatibility>
</assembly>

View file

@ -416,7 +416,6 @@ Window {
anchors.top: assetDirectory.bottom anchors.top: assetDirectory.bottom
anchors.bottom: uploadSection.top anchors.bottom: uploadSection.top
anchors.margins: 12 anchors.margins: 12
anchors.bottomMargin: 0
anchors.left: parent.left anchors.left: parent.left
anchors.right: parent.right anchors.right: parent.right
@ -445,7 +444,7 @@ Window {
name: "Upload A File" name: "Upload A File"
spacing: hifi.dimensions.contentSpacing.y spacing: hifi.dimensions.contentSpacing.y
anchors.bottom: parent.bottom anchors.bottom: parent.bottom
height: 130 height: 92
Item { Item {
height: parent.height height: parent.height

View file

@ -1,13 +1,25 @@
//
// ToolWindow.qml
//
// Created by Bradley Austin Davis on 12 Jan 2016
// Copyright 2016 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
import QtQuick 2.5 import QtQuick 2.5
import QtQuick.Controls 1.4 import QtQuick.Controls 1.4
import QtQuick.Controls.Styles 1.4
import QtWebEngine 1.1 import QtWebEngine 1.1
import QtWebChannel 1.0 import QtWebChannel 1.0
import Qt.labs.settings 1.0 import Qt.labs.settings 1.0
import "windows" as Windows import "windows-uit"
import "controls" as Controls import "controls-uit"
import "styles-uit"
Windows.Window { Window {
id: toolWindow id: toolWindow
resizable: true resizable: true
objectName: "ToolWindow" objectName: "ToolWindow"
@ -15,9 +27,13 @@ Windows.Window {
destroyOnInvisible: false destroyOnInvisible: false
closable: true closable: true
visible: false visible: false
width: 384; height: 640; title: "Edit"
title: "Tools"
property alias tabView: tabView property alias tabView: tabView
implicitWidth: 520; implicitHeight: 695
minSize: Qt.vector2d(400, 500)
HifiConstants { id: hifi }
onParentChanged: { onParentChanged: {
if (parent) { if (parent) {
x = 120; x = 120;
@ -32,8 +48,11 @@ Windows.Window {
} }
TabView { TabView {
anchors.fill: parent
id: tabView; id: tabView;
width: pane.contentWidth
height: pane.scrollHeight // Pane height so that don't use Window's scrollbars otherwise tabs may be scrolled out of view.
property int tabCount: 0
Repeater { Repeater {
model: 4 model: 4
Tab { Tab {
@ -43,7 +62,7 @@ Windows.Window {
enabled: false enabled: false
property string originalUrl: ""; property string originalUrl: "";
Controls.WebView { WebView {
id: webView; id: webView;
anchors.fill: parent anchors.fill: parent
enabled: false enabled: false
@ -60,6 +79,61 @@ Windows.Window {
} }
} }
} }
style: TabViewStyle {
frame: Rectangle { // Background shown before content loads.
anchors.fill: parent
color: hifi.colors.baseGray
}
frameOverlap: 0
tab: Rectangle {
implicitWidth: text.width
implicitHeight: 3 * text.height
color: styleData.selected ? hifi.colors.black : hifi.colors.tabBackgroundDark
RalewayRegular {
id: text
text: styleData.title
font.capitalization: Font.AllUppercase
size: hifi.fontSizes.tabName
width: tabView.tabCount > 1 ? styleData.availableWidth / tabView.tabCount : implicitWidth + 2 * hifi.dimensions.contentSpacing.x
elide: Text.ElideRight
color: styleData.selected ? hifi.colors.primaryHighlight : hifi.colors.lightGrayText
horizontalAlignment: Text.AlignHCenter
anchors.centerIn: parent
}
Rectangle { // Separator.
width: 1
height: parent.height
color: hifi.colors.black
anchors.left: parent.left
anchors.top: parent.top
visible: styleData.index > 0
Rectangle {
width: 1
height: 1
color: hifi.colors.baseGray
anchors.left: parent.left
anchors.bottom: parent.bottom
}
}
Rectangle { // Active underline.
width: parent.width - (styleData.index > 0 ? 1 : 0)
height: 1
anchors.right: parent.right
anchors.bottom: parent.bottom
color: styleData.selected ? hifi.colors.primaryHighlight : hifi.colors.baseGray
}
}
tabOverlap: 0
}
} }
function updateVisiblity() { function updateVisiblity() {
@ -129,6 +203,7 @@ Windows.Window {
tab.originalUrl = ""; tab.originalUrl = "";
tab.item.url = "about:blank"; tab.item.url = "about:blank";
tab.item.enabled = false; tab.item.enabled = false;
tabView.tabCount--;
} }
function addWebTab(properties) { function addWebTab(properties) {
@ -150,6 +225,7 @@ Windows.Window {
return; return;
} }
if (properties.width) { if (properties.width) {
tabView.width = Math.min(Math.max(tabView.width, properties.width), toolWindow.maxSize.x); tabView.width = Math.min(Math.max(tabView.width, properties.width), toolWindow.maxSize.x);
} }
@ -169,6 +245,7 @@ Windows.Window {
var result = tab.item; var result = tab.item;
result.enabled = true; result.enabled = true;
tabView.tabCount++;
console.log("Setting event bridge: " + eventBridge); console.log("Setting event bridge: " + eventBridge);
result.eventBridgeWrapper.eventBridge = eventBridge; result.eventBridgeWrapper.eventBridge = eventBridge;
result.url = properties.source; result.url = properties.source;

View file

@ -46,7 +46,7 @@ Column {
Item { Item {
id: leadingSpace id: leadingSpace
width: 1 width: 1
height: isFirst ? hifi.dimensions.contentSpacing.y : hifi.dimensions.controlInterlineHeight height: isFirst ? hifi.dimensions.contentSpacing.y : 0
anchors.top: parent.top anchors.top: parent.top
} }
@ -97,11 +97,11 @@ Column {
HiFiGlyphs { HiFiGlyphs {
anchors { anchors {
verticalCenter: title.verticalCenter top: title.top
topMargin: -9
right: parent.right right: parent.right
rightMargin: -4 rightMargin: -4
} }
y: -2
size: hifi.fontSizes.disclosureButton size: hifi.fontSizes.disclosureButton
text: isCollapsed ? hifi.glyphs.disclosureButtonExpand : hifi.glyphs.disclosureButtonCollapse text: isCollapsed ? hifi.glyphs.disclosureButtonExpand : hifi.glyphs.disclosureButtonCollapse
color: hifi.colors.lightGrayText color: hifi.colors.lightGrayText

View file

@ -0,0 +1,74 @@
//
// WebView.qml
//
// Created by Bradley Austin Davis on 12 Jan 2016
// Copyright 2016 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
import QtQuick 2.5
import QtWebEngine 1.1
WebEngineView {
id: root
property var newUrl;
Component.onCompleted: {
console.log("Connecting JS messaging to Hifi Logging")
// Ensure the JS from the web-engine makes it to our logging
root.javaScriptConsoleMessage.connect(function(level, message, lineNumber, sourceID) {
console.log("Web Window JS message: " + sourceID + " " + lineNumber + " " + message);
});
}
// FIXME hack to get the URL with the auth token included. Remove when we move to Qt 5.6
Timer {
id: urlReplacementTimer
running: false
repeat: false
interval: 50
onTriggered: url = newUrl;
}
onUrlChanged: {
console.log("Url changed to " + url);
var originalUrl = url.toString();
newUrl = urlHandler.fixupUrl(originalUrl).toString();
if (newUrl !== originalUrl) {
root.stop();
if (urlReplacementTimer.running) {
console.warn("Replacement timer already running");
return;
}
urlReplacementTimer.start();
}
}
onFeaturePermissionRequested: {
grantFeaturePermission(securityOrigin, feature, true);
}
onLoadingChanged: {
// Required to support clicking on "hifi://" links
if (WebEngineView.LoadStartedStatus == loadRequest.status) {
var url = loadRequest.url.toString();
if (urlHandler.canHandleUrl(url)) {
if (urlHandler.handleUrl(url)) {
root.stop();
}
}
}
}
onNewViewRequested:{
var component = Qt.createComponent("../Browser.qml");
var newWindow = component.createObject(desktop);
request.openIn(newWindow.webView)
}
// This breaks the webchannel used for passing messages. Fixed in Qt 5.6
// See https://bugreports.qt.io/browse/QTBUG-49521
//profile: desktop.browserProfile
}

View file

@ -44,7 +44,7 @@ Preference {
children: [ contentContainer ] children: [ contentContainer ]
height: contentContainer.height + (root.isLast ? 2 * hifi.dimensions.contentSpacing.y : 0) height: contentContainer.height + (contentContainer.isCollapsed ? 0 : hifi.dimensions.controlInterlineHeight)
Component.onCompleted: d.buildPreferences(); Component.onCompleted: d.buildPreferences();
@ -111,7 +111,6 @@ Preference {
case Preference.Checkbox: case Preference.Checkbox:
checkBoxCount++; checkBoxCount++;
console.log("####### checkBoxCount = " + checkBoxCount);
builder = checkboxBuilder; builder = checkboxBuilder;
break; break;

View file

@ -120,7 +120,7 @@ Window {
} }
HifiControls.VerticalSpacer { HifiControls.VerticalSpacer {
height: 2 // Table view draws a little taller than it's height. height: hifi.dimensions.controlInterlineHeight + 2 // Table view draws a little taller than it's height.
} }
} }

View file

@ -58,6 +58,7 @@ Item {
readonly property color lightGrayText: "#afafaf" readonly property color lightGrayText: "#afafaf"
readonly property color faintGray: "#e3e3e3" readonly property color faintGray: "#e3e3e3"
readonly property color primaryHighlight: "#00b4ef" readonly property color primaryHighlight: "#00b4ef"
readonly property color blueHighlight: "#00b4ef"
readonly property color blueAccent: "#1080b8" readonly property color blueAccent: "#1080b8"
readonly property color redHighlight: "#e2334d" readonly property color redHighlight: "#e2334d"
readonly property color redAccent: "#b70a37" readonly property color redAccent: "#b70a37"
@ -75,12 +76,12 @@ Item {
readonly property color lightGrayText80: "#ccafafaf" readonly property color lightGrayText80: "#ccafafaf"
readonly property color faintGray80: "#cce3e3e3" readonly property color faintGray80: "#cce3e3e3"
readonly property color faintGray50: "#80e3e3e3" readonly property color faintGray50: "#80e3e3e3"
readonly property color locked: "#252525"
// Other colors // Other colors
readonly property color white: "#ffffff" readonly property color white: "#ffffff"
readonly property color gray: "#808080" readonly property color gray: "#808080"
readonly property color black: "#000000" readonly property color black: "#000000"
readonly property color locked: "#252525"
// Semitransparent // Semitransparent
readonly property color white50: "#80ffffff" readonly property color white50: "#80ffffff"
readonly property color white30: "#4dffffff" readonly property color white30: "#4dffffff"
@ -115,6 +116,8 @@ Item {
readonly property color dropDownDarkStart: "#7d7d7d" readonly property color dropDownDarkStart: "#7d7d7d"
readonly property color dropDownDarkFinish: "#6b6a6b" readonly property color dropDownDarkFinish: "#6b6a6b"
readonly property color textFieldLightBackground: "#d4d4d4" readonly property color textFieldLightBackground: "#d4d4d4"
readonly property color tabBackgroundDark: "#252525"
readonly property color tabBackgroundLight: "#d4d4d4"
} }
Item { Item {
@ -152,6 +155,7 @@ Item {
readonly property real sectionName: dimensions.largeScreen ? 12 : 10 readonly property real sectionName: dimensions.largeScreen ? 12 : 10
readonly property real inputLabel: dimensions.largeScreen ? 14 : 10 readonly property real inputLabel: dimensions.largeScreen ? 14 : 10
readonly property real textFieldInput: dimensions.largeScreen ? 15 : 12 readonly property real textFieldInput: dimensions.largeScreen ? 15 : 12
readonly property real textFieldInputLabel: dimensions.largeScreen ? 13 : 9
readonly property real tableText: dimensions.largeScreen ? 15 : 12 readonly property real tableText: dimensions.largeScreen ? 15 : 12
readonly property real buttonLabel: dimensions.largeScreen ? 13 : 9 readonly property real buttonLabel: dimensions.largeScreen ? 13 : 9
readonly property real iconButton: dimensions.largeScreen ? 13 : 9 readonly property real iconButton: dimensions.largeScreen ? 13 : 9
@ -164,7 +168,7 @@ Item {
readonly property real menuItem: dimensions.largeScreen ? 15 : 11 readonly property real menuItem: dimensions.largeScreen ? 15 : 11
readonly property real shortcutText: dimensions.largeScreen ? 13 : 9 readonly property real shortcutText: dimensions.largeScreen ? 13 : 9
readonly property real carat: dimensions.largeScreen ? 38 : 30 readonly property real carat: dimensions.largeScreen ? 38 : 30
readonly property real disclosureButton: dimensions.largeScreen ? 20 : 15 readonly property real disclosureButton: dimensions.largeScreen ? 30 : 22
} }
Item { Item {

View file

@ -53,7 +53,7 @@ Fadable {
property bool resizable: false property bool resizable: false
property vector2d minSize: Qt.vector2d(100, 100) property vector2d minSize: Qt.vector2d(100, 100)
property vector2d maxSize: Qt.vector2d(1280, 720) property vector2d maxSize: Qt.vector2d(1280, 800)
// The content to place inside the window, determined by the client // The content to place inside the window, determined by the client
default property var content default property var content

View file

@ -3215,6 +3215,25 @@ void Application::update(float deltaTime) {
updateLOD(); updateLOD();
if (!_physicsEnabled && _processOctreeStatsCounter > 0) {
// process octree stats packets are sent in between full sends of a scene.
// We keep physics disabled until we've recieved a full scene and everything near the avatar in that
// scene is ready to compute its collision shape.
if (nearbyEntitiesAreReadyForPhysics()) {
_physicsEnabled = true;
getMyAvatar()->updateMotionBehaviorFromMenu();
} else {
auto characterController = getMyAvatar()->getCharacterController();
if (characterController) {
// if we have a character controller, disable it here so the avatar doesn't get stuck due to
// a non-loading collision hull.
characterController->setEnabled(false);
}
}
}
{ {
PerformanceTimer perfTimer("devices"); PerformanceTimer perfTimer("devices");
DeviceTracker::updateAll(); DeviceTracker::updateAll();
@ -4260,22 +4279,7 @@ int Application::processOctreeStats(ReceivedMessage& message, SharedNodePointer
}); });
}); });
if (!_physicsEnabled) { _processOctreeStatsCounter++;
if (nearbyEntitiesAreReadyForPhysics()) {
// These stats packets are sent in between full sends of a scene.
// We keep physics disabled until we've recieved a full scene and everything near the avatar in that
// scene is ready to compute its collision shape.
_physicsEnabled = true;
getMyAvatar()->updateMotionBehaviorFromMenu();
} else {
auto characterController = getMyAvatar()->getCharacterController();
if (characterController) {
// if we have a character controller, disable it here so the avatar doesn't get stuck due to
// a non-loading collision hull.
characterController->setEnabled(false);
}
}
}
return statsMessageLength; return statsMessageLength;
} }

View file

@ -515,6 +515,8 @@ private:
std::map<void*, std::function<void()>> _preRenderLambdas; std::map<void*, std::function<void()>> _preRenderLambdas;
std::mutex _preRenderLambdasLock; std::mutex _preRenderLambdasLock;
std::atomic<uint32_t> _processOctreeStatsCounter { 0 };
}; };
#endif // hifi_Application_h #endif // hifi_Application_h

View file

@ -105,13 +105,18 @@ bool ModelPackager::loadModel() {
qWarning() << QString("ModelPackager::loadModel(): Could not open FBX file %1").arg(_fbxInfo.filePath()); qWarning() << QString("ModelPackager::loadModel(): Could not open FBX file %1").arg(_fbxInfo.filePath());
return false; return false;
} }
qCDebug(interfaceapp) << "Reading FBX file : " << _fbxInfo.filePath(); try {
QByteArray fbxContents = fbx.readAll(); qCDebug(interfaceapp) << "Reading FBX file : " << _fbxInfo.filePath();
QByteArray fbxContents = fbx.readAll();
_geometry.reset(readFBX(fbxContents, QVariantHash(), _fbxInfo.filePath())); _geometry.reset(readFBX(fbxContents, QVariantHash(), _fbxInfo.filePath()));
// make sure we have some basic mappings // make sure we have some basic mappings
populateBasicMapping(_mapping, _fbxInfo.filePath(), *_geometry); populateBasicMapping(_mapping, _fbxInfo.filePath(), *_geometry);
} catch (const QString& error) {
qCDebug(interfaceapp) << "Error reading " << _fbxInfo.filePath() << ": " << error;
return false;
}
return true; return true;
} }

View file

@ -31,6 +31,7 @@
#include <BugSplat.h> #include <BugSplat.h>
#endif #endif
int main(int argc, const char* argv[]) { int main(int argc, const char* argv[]) {
disableQtBearerPoll(); // Fixes wifi ping spikes disableQtBearerPoll(); // Fixes wifi ping spikes
@ -180,6 +181,8 @@ int main(int argc, const char* argv[]) {
mpSender.sendAdditionalFile(qPrintable(logPath)); mpSender.sendAdditionalFile(qPrintable(logPath));
#endif #endif
printSystemInformation();
QTranslator translator; QTranslator translator;
translator.load("i18n/interface_en"); translator.load("i18n/interface_en");
app.installTranslator(&translator); app.installTranslator(&translator);

View file

@ -97,3 +97,11 @@ bool HMDScriptingInterface::isMounted() const{
auto displayPlugin = qApp->getActiveDisplayPlugin(); auto displayPlugin = qApp->getActiveDisplayPlugin();
return (displayPlugin->isHmd() && displayPlugin->isDisplayVisible()); return (displayPlugin->isHmd() && displayPlugin->isDisplayVisible());
} }
QString HMDScriptingInterface::preferredAudioInput() const {
return qApp->getActiveDisplayPlugin()->getPreferredAudioInDevice();
}
QString HMDScriptingInterface::preferredAudioOutput() const {
return qApp->getActiveDisplayPlugin()->getPreferredAudioOutDevice();
}

View file

@ -34,6 +34,8 @@ public:
Q_INVOKABLE glm::vec2 sphericalToOverlay(const glm::vec2 & sphericalPos) const; Q_INVOKABLE glm::vec2 sphericalToOverlay(const glm::vec2 & sphericalPos) const;
Q_INVOKABLE glm::vec2 overlayToSpherical(const glm::vec2 & overlayPos) const; Q_INVOKABLE glm::vec2 overlayToSpherical(const glm::vec2 & overlayPos) const;
Q_INVOKABLE QString preferredAudioInput() const;
Q_INVOKABLE QString preferredAudioOutput() const;
public: public:
HMDScriptingInterface(); HMDScriptingInterface();

View file

@ -36,7 +36,7 @@ AnimationPointer AnimationCache::getAnimation(const QUrl& url) {
QSharedPointer<Resource> AnimationCache::createResource(const QUrl& url, const QSharedPointer<Resource>& fallback, QSharedPointer<Resource> AnimationCache::createResource(const QUrl& url, const QSharedPointer<Resource>& fallback,
bool delayLoad, const void* extra) { bool delayLoad, const void* extra) {
return QSharedPointer<Resource>(new Animation(url), &Resource::allReferencesCleared); return QSharedPointer<Resource>(new Animation(url), &Resource::deleter);
} }
Animation::Animation(const QUrl& url) : Resource(url) {} Animation::Animation(const QUrl& url) : Resource(url) {}

View file

@ -175,6 +175,50 @@ int numDestinationSamplesRequired(const QAudioFormat& sourceFormat, const QAudio
return (numSourceSamples * ratio) + 0.5f; return (numSourceSamples * ratio) + 0.5f;
} }
#ifdef Q_OS_WIN
QString friendlyNameForAudioDevice(IMMDevice* pEndpoint) {
QString deviceName;
IPropertyStore* pPropertyStore;
pEndpoint->OpenPropertyStore(STGM_READ, &pPropertyStore);
pEndpoint->Release();
pEndpoint = NULL;
PROPVARIANT pv;
PropVariantInit(&pv);
HRESULT hr = pPropertyStore->GetValue(PKEY_Device_FriendlyName, &pv);
pPropertyStore->Release();
pPropertyStore = NULL;
deviceName = QString::fromWCharArray((wchar_t*)pv.pwszVal);
if (!IsWindows8OrGreater()) {
// Windows 7 provides only the 31 first characters of the device name.
const DWORD QT_WIN7_MAX_AUDIO_DEVICENAME_LEN = 31;
deviceName = deviceName.left(QT_WIN7_MAX_AUDIO_DEVICENAME_LEN);
}
PropVariantClear(&pv);
return deviceName;
}
QString AudioClient::friendlyNameForAudioDevice(wchar_t* guid) {
QString deviceName;
HRESULT hr = S_OK;
CoInitialize(NULL);
IMMDeviceEnumerator* pMMDeviceEnumerator = NULL;
CoCreateInstance(__uuidof(MMDeviceEnumerator), NULL, CLSCTX_ALL, __uuidof(IMMDeviceEnumerator), (void**)&pMMDeviceEnumerator);
IMMDevice* pEndpoint;
hr = pMMDeviceEnumerator->GetDevice(guid, &pEndpoint);
if (hr == E_NOTFOUND) {
printf("Audio Error: device not found\n");
deviceName = QString("NONE");
} else {
deviceName = ::friendlyNameForAudioDevice(pEndpoint);
}
pMMDeviceEnumerator->Release();
pMMDeviceEnumerator = NULL;
CoUninitialize();
return deviceName;
}
#endif
QAudioDeviceInfo defaultAudioDeviceForMode(QAudio::Mode mode) { QAudioDeviceInfo defaultAudioDeviceForMode(QAudio::Mode mode) {
#ifdef __APPLE__ #ifdef __APPLE__
if (QAudioDeviceInfo::availableDevices(mode).size() > 1) { if (QAudioDeviceInfo::availableDevices(mode).size() > 1) {
@ -248,23 +292,7 @@ QAudioDeviceInfo defaultAudioDeviceForMode(QAudio::Mode mode) {
printf("Audio Error: device not found\n"); printf("Audio Error: device not found\n");
deviceName = QString("NONE"); deviceName = QString("NONE");
} else { } else {
IPropertyStore* pPropertyStore; deviceName = friendlyNameForAudioDevice(pEndpoint);
pEndpoint->OpenPropertyStore(STGM_READ, &pPropertyStore);
pEndpoint->Release();
pEndpoint = NULL;
PROPVARIANT pv;
PropVariantInit(&pv);
hr = pPropertyStore->GetValue(PKEY_Device_FriendlyName, &pv);
pPropertyStore->Release();
pPropertyStore = NULL;
deviceName = QString::fromWCharArray((wchar_t*)pv.pwszVal);
if (!IsWindows8OrGreater()) {
// Windows 7 provides only the 31 first characters of the device name.
const DWORD QT_WIN7_MAX_AUDIO_DEVICENAME_LEN = 31;
deviceName = deviceName.left(QT_WIN7_MAX_AUDIO_DEVICENAME_LEN);
}
qCDebug(audioclient) << (mode == QAudio::AudioOutput ? "output" : "input") << " device:" << deviceName;
PropVariantClear(&pv);
} }
pMMDeviceEnumerator->Release(); pMMDeviceEnumerator->Release();
pMMDeviceEnumerator = NULL; pMMDeviceEnumerator = NULL;

View file

@ -16,6 +16,7 @@
#include <memory> #include <memory>
#include <vector> #include <vector>
#include <QtCore/qsystemdetection.h>
#include <QtCore/QByteArray> #include <QtCore/QByteArray>
#include <QtCore/QElapsedTimer> #include <QtCore/QElapsedTimer>
#include <QtCore/QObject> #include <QtCore/QObject>
@ -126,6 +127,10 @@ public:
static const float CALLBACK_ACCELERATOR_RATIO; static const float CALLBACK_ACCELERATOR_RATIO;
#ifdef Q_OS_WIN
static QString friendlyNameForAudioDevice(wchar_t* guid);
#endif
public slots: public slots:
void start(); void start();
void stop(); void stop();

View file

@ -17,6 +17,7 @@
#include <QtCore/QDebug> #include <QtCore/QDebug>
#include <udt/PacketHeaders.h> #include <udt/PacketHeaders.h>
#include <LogHandler.h>
#include "AudioLogging.h" #include "AudioLogging.h"
@ -129,7 +130,10 @@ int AudioRingBuffer::writeData(const char* data, int maxSize) {
int samplesToDelete = samplesToCopy - samplesRoomFor; int samplesToDelete = samplesToCopy - samplesRoomFor;
_nextOutput = shiftedPositionAccomodatingWrap(_nextOutput, samplesToDelete); _nextOutput = shiftedPositionAccomodatingWrap(_nextOutput, samplesToDelete);
_overflowCount++; _overflowCount++;
qCDebug(audio) << "Overflowed ring buffer! Overwriting old data";
const QString RING_BUFFER_OVERFLOW_DEBUG { "AudioRingBuffer::writeData has overflown the buffer. Overwriting old data." };
static QString repeatedMessage = LogHandler::getInstance().addRepeatedMessageRegex(RING_BUFFER_OVERFLOW_DEBUG);
qCDebug(audio) << qPrintable(RING_BUFFER_OVERFLOW_DEBUG);
} }
if (_endOfLastWrite + samplesToCopy <= _buffer + _bufferLength) { if (_endOfLastWrite + samplesToCopy <= _buffer + _bufferLength) {

View file

@ -36,5 +36,5 @@ SharedSoundPointer SoundCache::getSound(const QUrl& url) {
QSharedPointer<Resource> SoundCache::createResource(const QUrl& url, const QSharedPointer<Resource>& fallback, QSharedPointer<Resource> SoundCache::createResource(const QUrl& url, const QSharedPointer<Resource>& fallback,
bool delayLoad, const void* extra) { bool delayLoad, const void* extra) {
qCDebug(audio) << "Requesting sound at" << url.toString(); qCDebug(audio) << "Requesting sound at" << url.toString();
return QSharedPointer<Resource>(new Sound(url), &Resource::allReferencesCleared); return QSharedPointer<Resource>(new Sound(url), &Resource::deleter);
} }

View file

@ -116,11 +116,18 @@ QVariantMap RenderableModelEntityItem::parseTexturesToMap(QString textures) {
QJsonParseError error; QJsonParseError error;
QJsonDocument texturesJson = QJsonDocument::fromJson(textures.toUtf8(), &error); QJsonDocument texturesJson = QJsonDocument::fromJson(textures.toUtf8(), &error);
// If textures are invalid, revert to original textures
if (error.error != QJsonParseError::NoError) { if (error.error != QJsonParseError::NoError) {
qCWarning(entitiesrenderer) << "Could not evaluate textures property value:" << textures; qCWarning(entitiesrenderer) << "Could not evaluate textures property value:" << textures;
return _originalTextures; return _originalTextures;
} }
QVariantMap texturesMap = texturesJson.toVariant().toMap();
// If textures are unset, revert to original textures
if (texturesMap.isEmpty()) {
return _originalTextures;
}
return texturesJson.toVariant().toMap(); return texturesJson.toVariant().toMap();
} }
@ -133,10 +140,8 @@ void RenderableModelEntityItem::remapTextures() {
return; // nothing to do if the model has not yet loaded return; // nothing to do if the model has not yet loaded
} }
auto& geometry = _model->getGeometry()->getGeometry();
if (!_originalTexturesRead) { if (!_originalTexturesRead) {
_originalTextures = geometry->getTextures(); _originalTextures = _model->getTextures();
_originalTexturesRead = true; _originalTexturesRead = true;
// Default to _originalTextures to avoid remapping immediately and lagging on load // Default to _originalTextures to avoid remapping immediately and lagging on load
@ -152,7 +157,7 @@ void RenderableModelEntityItem::remapTextures() {
auto newTextures = parseTexturesToMap(textures); auto newTextures = parseTexturesToMap(textures);
if (newTextures != _currentTextures) { if (newTextures != _currentTextures) {
geometry->setTextures(newTextures); _model->setTextures(newTextures);
_currentTextures = newTextures; _currentTextures = newTextures;
} }
} }
@ -366,41 +371,16 @@ void RenderableModelEntityItem::render(RenderArgs* args) {
assert(getType() == EntityTypes::Model); assert(getType() == EntityTypes::Model);
if (hasModel()) { if (hasModel()) {
if (_model) { // Prepare the current frame
render::ScenePointer scene = AbstractViewStateInterface::instance()->getMain3DScene();
// check to see if when we added our models to the scene they were ready, if they were not ready, then
// fix them up in the scene
bool shouldShowCollisionHull = (args->_debugFlags & (int)RenderArgs::RENDER_DEBUG_HULLS) > 0;
if (_model->needsFixupInScene() || _showCollisionHull != shouldShowCollisionHull) {
_showCollisionHull = shouldShowCollisionHull;
render::PendingChanges pendingChanges;
_model->removeFromScene(scene, pendingChanges);
render::Item::Status::Getters statusGetters;
makeEntityItemStatusGetters(getThisPointer(), statusGetters);
_model->addToScene(scene, pendingChanges, statusGetters, _showCollisionHull);
scene->enqueuePendingChanges(pendingChanges);
}
// FIXME: this seems like it could be optimized if we tracked our last known visible state in
// the renderable item. As it stands now the model checks it's visible/invisible state
// so most of the time we don't do anything in this function.
_model->setVisibleInScene(getVisible(), scene);
}
remapTextures();
{ {
// float alpha = getLocalRenderAlpha();
if (!_model || _needsModelReload) { if (!_model || _needsModelReload) {
// TODO: this getModel() appears to be about 3% of model render time. We should optimize // TODO: this getModel() appears to be about 3% of model render time. We should optimize
PerformanceTimer perfTimer("getModel"); PerformanceTimer perfTimer("getModel");
EntityTreeRenderer* renderer = static_cast<EntityTreeRenderer*>(args->_renderer); EntityTreeRenderer* renderer = static_cast<EntityTreeRenderer*>(args->_renderer);
getModel(renderer); getModel(renderer);
// Remap textures immediately after loading to avoid flicker
remapTextures();
} }
if (_model) { if (_model) {
@ -431,15 +411,40 @@ void RenderableModelEntityItem::render(RenderArgs* args) {
} }
}); });
updateModelBounds(); updateModelBounds();
}
}
// Check if the URL has changed // Enqueue updates for the next frame
// Do this last as the getModel is queued for the next frame, if (_model) {
// and we need to keep state directing the model to reinitialize
auto& currentURL = getParsedModelURL(); render::ScenePointer scene = AbstractViewStateInterface::instance()->getMain3DScene();
if (currentURL != _model->getURL()) {
// Defer setting the url to the render thread // FIXME: this seems like it could be optimized if we tracked our last known visible state in
getModel(_myRenderer); // the renderable item. As it stands now the model checks it's visible/invisible state
} // so most of the time we don't do anything in this function.
_model->setVisibleInScene(getVisible(), scene);
// Remap textures for the next frame to avoid flicker
remapTextures();
// check to see if when we added our models to the scene they were ready, if they were not ready, then
// fix them up in the scene
bool shouldShowCollisionHull = (args->_debugFlags & (int)RenderArgs::RENDER_DEBUG_HULLS) > 0;
if (_model->needsFixupInScene() || _showCollisionHull != shouldShowCollisionHull) {
_showCollisionHull = shouldShowCollisionHull;
render::PendingChanges pendingChanges;
render::Item::Status::Getters statusGetters;
makeEntityItemStatusGetters(getThisPointer(), statusGetters);
_model->addToScene(scene, pendingChanges, statusGetters, _showCollisionHull);
scene->enqueuePendingChanges(pendingChanges);
}
auto& currentURL = getParsedModelURL();
if (currentURL != _model->getURL()) {
// Defer setting the url to the render thread
getModel(_myRenderer);
} }
} }
} else { } else {

View file

@ -47,6 +47,9 @@ template<class T> QVariant readBinaryArray(QDataStream& in, int& position) {
in.readRawData(compressed.data() + sizeof(quint32), compressedLength); in.readRawData(compressed.data() + sizeof(quint32), compressedLength);
position += compressedLength; position += compressedLength;
QByteArray uncompressed = qUncompress(compressed); QByteArray uncompressed = qUncompress(compressed);
if (uncompressed.isEmpty()) { // answers empty byte array if corrupt
throw QString("corrupt fbx file");
}
QDataStream uncompressedIn(uncompressed); QDataStream uncompressedIn(uncompressed);
uncompressedIn.setByteOrder(QDataStream::LittleEndian); uncompressedIn.setByteOrder(QDataStream::LittleEndian);
uncompressedIn.setVersion(QDataStream::Qt_4_5); // for single/double precision switch uncompressedIn.setVersion(QDataStream::Qt_4_5); // for single/double precision switch

View file

@ -457,6 +457,14 @@ uint32 Texture::getStoredMipSize(uint16 level) const {
return 0; return 0;
} }
gpu::Resource::Size Texture::getStoredSize() const {
auto size = 0;
for (int level = 0; level < evalNumMips(); ++level) {
size += getStoredMipSize(level);
}
return size;
}
uint16 Texture::evalNumSamplesUsed(uint16 numSamplesTried) { uint16 Texture::evalNumSamplesUsed(uint16 numSamplesTried) {
uint16 sample = numSamplesTried; uint16 sample = numSamplesTried;
if (numSamplesTried <= 1) if (numSamplesTried <= 1)

View file

@ -288,9 +288,12 @@ public:
Stamp getStamp() const { return _stamp; } Stamp getStamp() const { return _stamp; }
Stamp getDataStamp() const { return _storage->getStamp(); } Stamp getDataStamp() const { return _storage->getStamp(); }
// The size in bytes of data stored in the texture // The theoretical size in bytes of data stored in the texture
Size getSize() const { return _size; } Size getSize() const { return _size; }
// The actual size in bytes of data stored in the texture
Size getStoredSize() const;
// Resize, unless auto mips mode would destroy all the sub mips // Resize, unless auto mips mode would destroy all the sub mips
Size resize1D(uint16 width, uint16 numSamples); Size resize1D(uint16 width, uint16 numSamples);
Size resize2D(uint16 width, uint16 height, uint16 numSamples); Size resize2D(uint16 width, uint16 height, uint16 numSamples);

View file

@ -29,6 +29,10 @@ public:
const QUrl& textureBaseUrl; const QUrl& textureBaseUrl;
}; };
QUrl resolveTextureBaseUrl(const QUrl& url, const QUrl& textureBaseUrl) {
return textureBaseUrl.isValid() ? textureBaseUrl : url;
}
class GeometryMappingResource : public GeometryResource { class GeometryMappingResource : public GeometryResource {
Q_OBJECT Q_OBJECT
public: public:
@ -52,30 +56,28 @@ void GeometryMappingResource::downloadFinished(const QByteArray& data) {
finishedLoading(false); finishedLoading(false);
} else { } else {
QUrl url = _url.resolved(filename); QUrl url = _url.resolved(filename);
QUrl textureBaseUrl;
QString texdir = mapping.value("texdir").toString(); QString texdir = mapping.value("texdir").toString();
if (!texdir.isNull()) { if (!texdir.isNull()) {
if (!texdir.endsWith('/')) { if (!texdir.endsWith('/')) {
texdir += '/'; texdir += '/';
} }
textureBaseUrl = _url.resolved(texdir); _textureBaseUrl = resolveTextureBaseUrl(url, _url.resolved(texdir));
} }
auto modelCache = DependencyManager::get<ModelCache>(); auto modelCache = DependencyManager::get<ModelCache>();
GeometryExtra extra{ mapping, textureBaseUrl }; GeometryExtra extra{ mapping, _textureBaseUrl };
// Get the raw GeometryResource, not the wrapped NetworkGeometry // Get the raw GeometryResource, not the wrapped NetworkGeometry
_geometryResource = modelCache->getResource(url, QUrl(), true, &extra).staticCast<GeometryResource>(); _geometryResource = modelCache->getResource(url, QUrl(), false, &extra).staticCast<GeometryResource>();
// Avoid caching nested resources - their references will be held by the parent
_geometryResource->_isCacheable = false;
if (_geometryResource->isLoaded()) { if (_geometryResource->isLoaded()) {
onGeometryMappingLoaded(!_geometryResource->getURL().isEmpty()); onGeometryMappingLoaded(!_geometryResource->getURL().isEmpty());
} else { } else {
connect(_geometryResource.data(), &Resource::finished, this, &GeometryMappingResource::onGeometryMappingLoaded); connect(_geometryResource.data(), &Resource::finished, this, &GeometryMappingResource::onGeometryMappingLoaded);
} }
// Avoid caching nested resources - their references will be held by the parent
_geometryResource->_isCacheable = false;
} }
} }
@ -86,6 +88,10 @@ void GeometryMappingResource::onGeometryMappingLoaded(bool success) {
_meshes = _geometryResource->_meshes; _meshes = _geometryResource->_meshes;
_materials = _geometryResource->_materials; _materials = _geometryResource->_materials;
} }
// Avoid holding onto extra references
_geometryResource.reset();
finishedLoading(success); finishedLoading(success);
} }
@ -157,7 +163,7 @@ class GeometryDefinitionResource : public GeometryResource {
Q_OBJECT Q_OBJECT
public: public:
GeometryDefinitionResource(const QUrl& url, const QVariantHash& mapping, const QUrl& textureBaseUrl) : GeometryDefinitionResource(const QUrl& url, const QVariantHash& mapping, const QUrl& textureBaseUrl) :
GeometryResource(url), _mapping(mapping), _textureBaseUrl(textureBaseUrl.isValid() ? textureBaseUrl : url) {} GeometryResource(url, resolveTextureBaseUrl(url, textureBaseUrl)), _mapping(mapping) {}
virtual void downloadFinished(const QByteArray& data) override; virtual void downloadFinished(const QByteArray& data) override;
@ -166,7 +172,6 @@ protected:
private: private:
QVariantHash _mapping; QVariantHash _mapping;
QUrl _textureBaseUrl;
}; };
void GeometryDefinitionResource::downloadFinished(const QByteArray& data) { void GeometryDefinitionResource::downloadFinished(const QByteArray& data) {
@ -220,13 +225,20 @@ QSharedPointer<Resource> ModelCache::createResource(const QUrl& url, const QShar
resource = new GeometryDefinitionResource(url, geometryExtra->mapping, geometryExtra->textureBaseUrl); resource = new GeometryDefinitionResource(url, geometryExtra->mapping, geometryExtra->textureBaseUrl);
} }
return QSharedPointer<Resource>(resource, &Resource::allReferencesCleared); return QSharedPointer<Resource>(resource, &Resource::deleter);
} }
std::shared_ptr<NetworkGeometry> ModelCache::getGeometry(const QUrl& url, const QVariantHash& mapping, const QUrl& textureBaseUrl) { std::shared_ptr<NetworkGeometry> ModelCache::getGeometry(const QUrl& url, const QVariantHash& mapping, const QUrl& textureBaseUrl) {
GeometryExtra geometryExtra = { mapping, textureBaseUrl }; GeometryExtra geometryExtra = { mapping, textureBaseUrl };
GeometryResource::Pointer resource = getResource(url, QUrl(), true, &geometryExtra).staticCast<GeometryResource>(); GeometryResource::Pointer resource = getResource(url, QUrl(), true, &geometryExtra).staticCast<GeometryResource>();
return std::make_shared<NetworkGeometry>(resource); if (resource) {
if (resource->isLoaded() && !resource->hasTextures()) {
resource->setTextures();
}
return std::make_shared<NetworkGeometry>(resource);
} else {
return NetworkGeometry::Pointer();
}
} }
const QVariantMap Geometry::getTextures() const { const QVariantMap Geometry::getTextures() const {
@ -270,6 +282,9 @@ void Geometry::setTextures(const QVariantMap& textureMap) {
material->setTextures(textureMap); material->setTextures(textureMap);
_areTexturesLoaded = false; _areTexturesLoaded = false;
// If we only use cached textures, they should all be loaded
areTexturesLoaded();
} }
} }
} else { } else {
@ -279,8 +294,6 @@ void Geometry::setTextures(const QVariantMap& textureMap) {
bool Geometry::areTexturesLoaded() const { bool Geometry::areTexturesLoaded() const {
if (!_areTexturesLoaded) { if (!_areTexturesLoaded) {
_hasTransparentTextures = false;
for (auto& material : _materials) { for (auto& material : _materials) {
// Check if material textures are loaded // Check if material textures are loaded
if (std::any_of(material->_textures.cbegin(), material->_textures.cend(), if (std::any_of(material->_textures.cbegin(), material->_textures.cend(),
@ -293,8 +306,6 @@ bool Geometry::areTexturesLoaded() const {
const auto albedoTexture = material->_textures[NetworkMaterial::MapChannel::ALBEDO_MAP]; const auto albedoTexture = material->_textures[NetworkMaterial::MapChannel::ALBEDO_MAP];
if (albedoTexture.texture && albedoTexture.texture->getGPUTexture()) { if (albedoTexture.texture && albedoTexture.texture->getGPUTexture()) {
material->resetOpacityMap(); material->resetOpacityMap();
_hasTransparentTextures |= material->getKey().isTranslucent();
} }
} }
@ -313,6 +324,21 @@ const std::shared_ptr<const NetworkMaterial> Geometry::getShapeMaterial(int shap
return nullptr; return nullptr;
} }
void GeometryResource::deleter() {
resetTextures();
Resource::deleter();
}
void GeometryResource::setTextures() {
for (const FBXMaterial& material : _geometry->materials) {
_materials.push_back(std::make_shared<NetworkMaterial>(material, _textureBaseUrl));
}
}
void GeometryResource::resetTextures() {
_materials.clear();
}
NetworkGeometry::NetworkGeometry(const GeometryResource::Pointer& networkGeometry) : _resource(networkGeometry) { NetworkGeometry::NetworkGeometry(const GeometryResource::Pointer& networkGeometry) : _resource(networkGeometry) {
connect(_resource.data(), &Resource::finished, this, &NetworkGeometry::resourceFinished); connect(_resource.data(), &Resource::finished, this, &NetworkGeometry::resourceFinished);
connect(_resource.data(), &Resource::onRefresh, this, &NetworkGeometry::resourceRefreshed); connect(_resource.data(), &Resource::onRefresh, this, &NetworkGeometry::resourceRefreshed);

View file

@ -74,9 +74,6 @@ public:
void setTextures(const QVariantMap& textureMap); void setTextures(const QVariantMap& textureMap);
virtual bool areTexturesLoaded() const; virtual bool areTexturesLoaded() const;
// Returns true if any albedo texture has a non-masking alpha channel.
// This can only be known after areTexturesLoaded().
bool hasTransparentTextures() const { return _hasTransparentTextures; }
protected: protected:
friend class GeometryMappingResource; friend class GeometryMappingResource;
@ -91,7 +88,6 @@ protected:
private: private:
mutable bool _areTexturesLoaded { false }; mutable bool _areTexturesLoaded { false };
mutable bool _hasTransparentTextures { false };
}; };
/// A geometry loaded from the network. /// A geometry loaded from the network.
@ -99,15 +95,25 @@ class GeometryResource : public Resource, public Geometry {
public: public:
using Pointer = QSharedPointer<GeometryResource>; using Pointer = QSharedPointer<GeometryResource>;
GeometryResource(const QUrl& url) : Resource(url) {} GeometryResource(const QUrl& url, const QUrl& textureBaseUrl = QUrl()) :
Resource(url), _textureBaseUrl(textureBaseUrl) {}
virtual bool areTexturesLoaded() const { return isLoaded() && Geometry::areTexturesLoaded(); } virtual bool areTexturesLoaded() const { return isLoaded() && Geometry::areTexturesLoaded(); }
virtual void deleter() override;
protected: protected:
friend class ModelCache;
friend class GeometryMappingResource; friend class GeometryMappingResource;
virtual bool isCacheable() const override { return _loaded && _isCacheable; } // Geometries may not hold onto textures while cached - that is for the texture cache
bool hasTextures() const { return !_materials.empty(); }
void setTextures();
void resetTextures();
QUrl _textureBaseUrl;
virtual bool isCacheable() const override { return _loaded && _isCacheable; }
bool _isCacheable { true }; bool _isCacheable { true };
}; };

View file

@ -28,6 +28,6 @@ NetworkShaderPointer ShaderCache::getShader(const QUrl& url) {
} }
QSharedPointer<Resource> ShaderCache::createResource(const QUrl& url, const QSharedPointer<Resource>& fallback, bool delayLoad, const void* extra) { QSharedPointer<Resource> ShaderCache::createResource(const QUrl& url, const QSharedPointer<Resource>& fallback, bool delayLoad, const void* extra) {
return QSharedPointer<Resource>(new NetworkShader(url, delayLoad), &Resource::allReferencesCleared); return QSharedPointer<Resource>(new NetworkShader(url, delayLoad), &Resource::deleter);
} }

View file

@ -166,12 +166,11 @@ gpu::TexturePointer TextureCache::getImageTexture(const QString& path) {
return texture; return texture;
} }
QSharedPointer<Resource> TextureCache::createResource(const QUrl& url, QSharedPointer<Resource> TextureCache::createResource(const QUrl& url,
const QSharedPointer<Resource>& fallback, bool delayLoad, const void* extra) { const QSharedPointer<Resource>& fallback, bool delayLoad, const void* extra) {
const TextureExtra* textureExtra = static_cast<const TextureExtra*>(extra); const TextureExtra* textureExtra = static_cast<const TextureExtra*>(extra);
return QSharedPointer<Resource>(new NetworkTexture(url, textureExtra->type, textureExtra->content), return QSharedPointer<Resource>(new NetworkTexture(url, textureExtra->type, textureExtra->content),
&Resource::allReferencesCleared); &Resource::deleter);
} }
NetworkTexture::NetworkTexture(const QUrl& url, TextureType type, const QByteArray& content) : NetworkTexture::NetworkTexture(const QUrl& url, TextureType type, const QByteArray& content) :
@ -339,8 +338,11 @@ void NetworkTexture::setImage(void* voidTexture, int originalWidth,
if (gpuTexture) { if (gpuTexture) {
_width = gpuTexture->getWidth(); _width = gpuTexture->getWidth();
_height = gpuTexture->getHeight(); _height = gpuTexture->getHeight();
setBytes(gpuTexture->getStoredSize());
} else { } else {
// FIXME: If !gpuTexture, we failed to load!
_width = _height = 0; _width = _height = 0;
qWarning() << "Texture did not load";
} }
finishedLoading(true); finishedLoading(true);

View file

@ -200,7 +200,7 @@ AssetUpload* AssetClient::createUpload(const QByteArray& data) {
return upload; return upload;
} }
bool AssetClient::getAsset(const QString& hash, DataOffset start, DataOffset end, MessageID AssetClient::getAsset(const QString& hash, DataOffset start, DataOffset end,
ReceivedAssetCallback callback, ProgressCallback progressCallback) { ReceivedAssetCallback callback, ProgressCallback progressCallback) {
if (hash.length() != SHA256_HASH_HEX_LENGTH) { if (hash.length() != SHA256_HASH_HEX_LENGTH) {
qCWarning(asset_client) << "Invalid hash size"; qCWarning(asset_client) << "Invalid hash size";
@ -230,17 +230,16 @@ bool AssetClient::getAsset(const QString& hash, DataOffset start, DataOffset end
_pendingRequests[assetServer][messageID] = { QSharedPointer<ReceivedMessage>(), callback, progressCallback }; _pendingRequests[assetServer][messageID] = { QSharedPointer<ReceivedMessage>(), callback, progressCallback };
return messageID;
return true;
} else { } else {
callback(false, AssetServerError::NoError, QByteArray()); callback(false, AssetServerError::NoError, QByteArray());
return false; return INVALID_MESSAGE_ID;
} }
} }
bool AssetClient::getAssetInfo(const QString& hash, GetInfoCallback callback) { MessageID AssetClient::getAssetInfo(const QString& hash, GetInfoCallback callback) {
auto nodeList = DependencyManager::get<NodeList>(); auto nodeList = DependencyManager::get<NodeList>();
SharedNodePointer assetServer = nodeList->soloNodeOfType(NodeType::AssetServer); SharedNodePointer assetServer = nodeList->soloNodeOfType(NodeType::AssetServer);
@ -257,10 +256,10 @@ bool AssetClient::getAssetInfo(const QString& hash, GetInfoCallback callback) {
_pendingInfoRequests[assetServer][messageID] = callback; _pendingInfoRequests[assetServer][messageID] = callback;
return true; return messageID;
} else { } else {
callback(false, AssetServerError::NoError, { "", 0 }); callback(false, AssetServerError::NoError, { "", 0 });
return false; return INVALID_MESSAGE_ID;
} }
} }
@ -352,7 +351,7 @@ void AssetClient::handleAssetGetReply(QSharedPointer<ReceivedMessage> message, S
} }
} }
bool AssetClient::getAssetMapping(const AssetPath& path, MappingOperationCallback callback) { MessageID AssetClient::getAssetMapping(const AssetPath& path, MappingOperationCallback callback) {
auto nodeList = DependencyManager::get<NodeList>(); auto nodeList = DependencyManager::get<NodeList>();
SharedNodePointer assetServer = nodeList->soloNodeOfType(NodeType::AssetServer); SharedNodePointer assetServer = nodeList->soloNodeOfType(NodeType::AssetServer);
@ -370,14 +369,14 @@ bool AssetClient::getAssetMapping(const AssetPath& path, MappingOperationCallbac
_pendingMappingRequests[assetServer][messageID] = callback; _pendingMappingRequests[assetServer][messageID] = callback;
return true; return messageID;
} else { } else {
callback(false, AssetServerError::NoError, QSharedPointer<ReceivedMessage>()); callback(false, AssetServerError::NoError, QSharedPointer<ReceivedMessage>());
return false; return INVALID_MESSAGE_ID;
} }
} }
bool AssetClient::getAllAssetMappings(MappingOperationCallback callback) { MessageID AssetClient::getAllAssetMappings(MappingOperationCallback callback) {
auto nodeList = DependencyManager::get<NodeList>(); auto nodeList = DependencyManager::get<NodeList>();
SharedNodePointer assetServer = nodeList->soloNodeOfType(NodeType::AssetServer); SharedNodePointer assetServer = nodeList->soloNodeOfType(NodeType::AssetServer);
@ -393,14 +392,14 @@ bool AssetClient::getAllAssetMappings(MappingOperationCallback callback) {
_pendingMappingRequests[assetServer][messageID] = callback; _pendingMappingRequests[assetServer][messageID] = callback;
return true; return messageID;
} else { } else {
callback(false, AssetServerError::NoError, QSharedPointer<ReceivedMessage>()); callback(false, AssetServerError::NoError, QSharedPointer<ReceivedMessage>());
return false; return INVALID_MESSAGE_ID;
} }
} }
bool AssetClient::deleteAssetMappings(const AssetPathList& paths, MappingOperationCallback callback) { MessageID AssetClient::deleteAssetMappings(const AssetPathList& paths, MappingOperationCallback callback) {
auto nodeList = DependencyManager::get<NodeList>(); auto nodeList = DependencyManager::get<NodeList>();
SharedNodePointer assetServer = nodeList->soloNodeOfType(NodeType::AssetServer); SharedNodePointer assetServer = nodeList->soloNodeOfType(NodeType::AssetServer);
@ -422,14 +421,14 @@ bool AssetClient::deleteAssetMappings(const AssetPathList& paths, MappingOperati
_pendingMappingRequests[assetServer][messageID] = callback; _pendingMappingRequests[assetServer][messageID] = callback;
return true; return messageID;
} else { } else {
callback(false, AssetServerError::NoError, QSharedPointer<ReceivedMessage>()); callback(false, AssetServerError::NoError, QSharedPointer<ReceivedMessage>());
return false; return INVALID_MESSAGE_ID;
} }
} }
bool AssetClient::setAssetMapping(const QString& path, const AssetHash& hash, MappingOperationCallback callback) { MessageID AssetClient::setAssetMapping(const QString& path, const AssetHash& hash, MappingOperationCallback callback) {
auto nodeList = DependencyManager::get<NodeList>(); auto nodeList = DependencyManager::get<NodeList>();
SharedNodePointer assetServer = nodeList->soloNodeOfType(NodeType::AssetServer); SharedNodePointer assetServer = nodeList->soloNodeOfType(NodeType::AssetServer);
@ -448,14 +447,14 @@ bool AssetClient::setAssetMapping(const QString& path, const AssetHash& hash, Ma
_pendingMappingRequests[assetServer][messageID] = callback; _pendingMappingRequests[assetServer][messageID] = callback;
return true; return messageID;
} else { } else {
callback(false, AssetServerError::NoError, QSharedPointer<ReceivedMessage>()); callback(false, AssetServerError::NoError, QSharedPointer<ReceivedMessage>());
return false; return INVALID_MESSAGE_ID;
} }
} }
bool AssetClient::renameAssetMapping(const AssetPath& oldPath, const AssetPath& newPath, MappingOperationCallback callback) { MessageID AssetClient::renameAssetMapping(const AssetPath& oldPath, const AssetPath& newPath, MappingOperationCallback callback) {
auto nodeList = DependencyManager::get<NodeList>(); auto nodeList = DependencyManager::get<NodeList>();
SharedNodePointer assetServer = nodeList->soloNodeOfType(NodeType::AssetServer); SharedNodePointer assetServer = nodeList->soloNodeOfType(NodeType::AssetServer);
@ -474,15 +473,53 @@ bool AssetClient::renameAssetMapping(const AssetPath& oldPath, const AssetPath&
_pendingMappingRequests[assetServer][messageID] = callback; _pendingMappingRequests[assetServer][messageID] = callback;
return true; return messageID;
} else { } else {
callback(false, AssetServerError::NoError, QSharedPointer<ReceivedMessage>()); callback(false, AssetServerError::NoError, QSharedPointer<ReceivedMessage>());
return false; return INVALID_MESSAGE_ID;
} }
} }
bool AssetClient::uploadAsset(const QByteArray& data, UploadResultCallback callback) { bool AssetClient::cancelMappingRequest(MessageID id) {
for (auto& kv : _pendingMappingRequests) {
if (kv.second.erase(id)) {
return true;
}
}
return false;
}
bool AssetClient::cancelGetAssetInfoRequest(MessageID id) {
for (auto& kv : _pendingInfoRequests) {
if (kv.second.erase(id)) {
return true;
}
}
return false;
}
bool AssetClient::cancelGetAssetRequest(MessageID id) {
// Search through each pending mapping request for id `id`
for (auto& kv : _pendingRequests) {
if (kv.second.erase(id)) {
return true;
}
}
return false;
}
bool AssetClient::cancelUploadAssetRequest(MessageID id) {
// Search through each pending mapping request for id `id`
for (auto& kv : _pendingUploads) {
if (kv.second.erase(id)) {
return true;
}
}
return false;
}
MessageID AssetClient::uploadAsset(const QByteArray& data, UploadResultCallback callback) {
auto nodeList = DependencyManager::get<NodeList>(); auto nodeList = DependencyManager::get<NodeList>();
SharedNodePointer assetServer = nodeList->soloNodeOfType(NodeType::AssetServer); SharedNodePointer assetServer = nodeList->soloNodeOfType(NodeType::AssetServer);
@ -500,10 +537,10 @@ bool AssetClient::uploadAsset(const QByteArray& data, UploadResultCallback callb
_pendingUploads[assetServer][messageID] = callback; _pendingUploads[assetServer][messageID] = callback;
return true; return messageID;
} else { } else {
callback(false, AssetServerError::NoError, QString()); callback(false, AssetServerError::NoError, QString());
return false; return INVALID_MESSAGE_ID;
} }
} }

View file

@ -60,6 +60,8 @@ public:
Q_INVOKABLE AssetUpload* createUpload(const QString& filename); Q_INVOKABLE AssetUpload* createUpload(const QString& filename);
Q_INVOKABLE AssetUpload* createUpload(const QByteArray& data); Q_INVOKABLE AssetUpload* createUpload(const QByteArray& data);
static const MessageID INVALID_MESSAGE_ID = 0;
public slots: public slots:
void init(); void init();
@ -75,16 +77,21 @@ private slots:
void handleNodeKilled(SharedNodePointer node); void handleNodeKilled(SharedNodePointer node);
private: private:
bool getAssetMapping(const AssetHash& hash, MappingOperationCallback callback); MessageID getAssetMapping(const AssetHash& hash, MappingOperationCallback callback);
bool getAllAssetMappings(MappingOperationCallback callback); MessageID getAllAssetMappings(MappingOperationCallback callback);
bool setAssetMapping(const QString& path, const AssetHash& hash, MappingOperationCallback callback); MessageID setAssetMapping(const QString& path, const AssetHash& hash, MappingOperationCallback callback);
bool deleteAssetMappings(const AssetPathList& paths, MappingOperationCallback callback); MessageID deleteAssetMappings(const AssetPathList& paths, MappingOperationCallback callback);
bool renameAssetMapping(const AssetPath& oldPath, const AssetPath& newPath, MappingOperationCallback callback); MessageID renameAssetMapping(const AssetPath& oldPath, const AssetPath& newPath, MappingOperationCallback callback);
bool getAssetInfo(const QString& hash, GetInfoCallback callback); MessageID getAssetInfo(const QString& hash, GetInfoCallback callback);
bool getAsset(const QString& hash, DataOffset start, DataOffset end, MessageID getAsset(const QString& hash, DataOffset start, DataOffset end,
ReceivedAssetCallback callback, ProgressCallback progressCallback); ReceivedAssetCallback callback, ProgressCallback progressCallback);
bool uploadAsset(const QByteArray& data, UploadResultCallback callback); MessageID uploadAsset(const QByteArray& data, UploadResultCallback callback);
bool cancelMappingRequest(MessageID id);
bool cancelGetAssetInfoRequest(MessageID id);
bool cancelGetAssetRequest(MessageID id);
bool cancelUploadAssetRequest(MessageID id);
struct GetAssetRequestData { struct GetAssetRequestData {
QSharedPointer<ReceivedMessage> message; QSharedPointer<ReceivedMessage> message;
@ -100,6 +107,7 @@ private:
friend class AssetRequest; friend class AssetRequest;
friend class AssetUpload; friend class AssetUpload;
friend class MappingRequest;
friend class GetMappingRequest; friend class GetMappingRequest;
friend class GetAllMappingsRequest; friend class GetAllMappingsRequest;
friend class SetMappingRequest; friend class SetMappingRequest;

View file

@ -25,6 +25,16 @@ AssetRequest::AssetRequest(const QString& hash) :
{ {
} }
AssetRequest::~AssetRequest() {
auto assetClient = DependencyManager::get<AssetClient>();
if (_assetRequestID) {
assetClient->cancelGetAssetRequest(_assetRequestID);
}
if (_assetInfoRequestID) {
assetClient->cancelGetAssetInfoRequest(_assetInfoRequestID);
}
}
void AssetRequest::start() { void AssetRequest::start() {
if (QThread::currentThread() != thread()) { if (QThread::currentThread() != thread()) {
QMetaObject::invokeMethod(this, "start", Qt::AutoConnection); QMetaObject::invokeMethod(this, "start", Qt::AutoConnection);
@ -60,7 +70,11 @@ void AssetRequest::start() {
_state = WaitingForInfo; _state = WaitingForInfo;
auto assetClient = DependencyManager::get<AssetClient>(); auto assetClient = DependencyManager::get<AssetClient>();
assetClient->getAssetInfo(_hash, [this](bool responseReceived, AssetServerError serverError, AssetInfo info) { _assetInfoRequestID = assetClient->getAssetInfo(_hash,
[this](bool responseReceived, AssetServerError serverError, AssetInfo info) {
_assetInfoRequestID = AssetClient::INVALID_MESSAGE_ID;
_info = info; _info = info;
if (!responseReceived) { if (!responseReceived) {
@ -92,8 +106,11 @@ void AssetRequest::start() {
int start = 0, end = _info.size; int start = 0, end = _info.size;
auto assetClient = DependencyManager::get<AssetClient>(); auto assetClient = DependencyManager::get<AssetClient>();
assetClient->getAsset(_hash, start, end, [this, start, end](bool responseReceived, AssetServerError serverError, _assetRequestID = assetClient->getAsset(_hash, start, end,
const QByteArray& data) { [this, start, end](bool responseReceived, AssetServerError serverError, const QByteArray& data) {
_assetRequestID = AssetClient::INVALID_MESSAGE_ID;
if (!responseReceived) { if (!responseReceived) {
_error = NetworkError; _error = NetworkError;
} else if (serverError != AssetServerError::NoError) { } else if (serverError != AssetServerError::NoError) {

View file

@ -41,6 +41,7 @@ public:
}; };
AssetRequest(const QString& hash); AssetRequest(const QString& hash);
virtual ~AssetRequest() override;
Q_INVOKABLE void start(); Q_INVOKABLE void start();
@ -61,6 +62,8 @@ private:
QString _hash; QString _hash;
QByteArray _data; QByteArray _data;
int _numPendingRequests { 0 }; int _numPendingRequests { 0 };
MessageID _assetRequestID { AssetClient::INVALID_MESSAGE_ID };
MessageID _assetInfoRequestID { AssetClient::INVALID_MESSAGE_ID };
}; };
#endif #endif

View file

@ -21,7 +21,7 @@ class AssetResourceRequest : public ResourceRequest {
Q_OBJECT Q_OBJECT
public: public:
AssetResourceRequest(const QUrl& url) : ResourceRequest(url) { } AssetResourceRequest(const QUrl& url) : ResourceRequest(url) { }
~AssetResourceRequest(); virtual ~AssetResourceRequest() override;
protected: protected:
virtual void doSend() override; virtual void doSend() override;

View file

@ -15,7 +15,12 @@
#include <DependencyManager.h> #include <DependencyManager.h>
#include "AssetClient.h" MappingRequest::~MappingRequest() {
auto assetClient = DependencyManager::get<AssetClient>();
if (_mappingRequestID) {
assetClient->cancelMappingRequest(_mappingRequestID);
}
}
void MappingRequest::start() { void MappingRequest::start() {
if (QThread::currentThread() != thread()) { if (QThread::currentThread() != thread()) {
@ -60,7 +65,10 @@ void GetMappingRequest::doStart() {
auto assetClient = DependencyManager::get<AssetClient>(); auto assetClient = DependencyManager::get<AssetClient>();
assetClient->getAssetMapping(_path, [this, assetClient](bool responseReceived, AssetServerError error, QSharedPointer<ReceivedMessage> message) { _mappingRequestID = assetClient->getAssetMapping(_path,
[this, assetClient](bool responseReceived, AssetServerError error, QSharedPointer<ReceivedMessage> message) {
_mappingRequestID = AssetClient::INVALID_MESSAGE_ID;
if (!responseReceived) { if (!responseReceived) {
_error = NetworkError; _error = NetworkError;
} else { } else {
@ -89,7 +97,11 @@ GetAllMappingsRequest::GetAllMappingsRequest() {
void GetAllMappingsRequest::doStart() { void GetAllMappingsRequest::doStart() {
auto assetClient = DependencyManager::get<AssetClient>(); auto assetClient = DependencyManager::get<AssetClient>();
assetClient->getAllAssetMappings([this, assetClient](bool responseReceived, AssetServerError error, QSharedPointer<ReceivedMessage> message) { _mappingRequestID = assetClient->getAllAssetMappings(
[this, assetClient](bool responseReceived, AssetServerError error, QSharedPointer<ReceivedMessage> message) {
_mappingRequestID = AssetClient::INVALID_MESSAGE_ID;
if (!responseReceived) { if (!responseReceived) {
_error = NetworkError; _error = NetworkError;
} else { } else {
@ -137,7 +149,10 @@ void SetMappingRequest::doStart() {
auto assetClient = DependencyManager::get<AssetClient>(); auto assetClient = DependencyManager::get<AssetClient>();
assetClient->setAssetMapping(_path, _hash, [this, assetClient](bool responseReceived, AssetServerError error, QSharedPointer<ReceivedMessage> message) { _mappingRequestID = assetClient->setAssetMapping(_path, _hash,
[this, assetClient](bool responseReceived, AssetServerError error, QSharedPointer<ReceivedMessage> message) {
_mappingRequestID = AssetClient::INVALID_MESSAGE_ID;
if (!responseReceived) { if (!responseReceived) {
_error = NetworkError; _error = NetworkError;
} else { } else {
@ -177,7 +192,10 @@ void DeleteMappingsRequest::doStart() {
auto assetClient = DependencyManager::get<AssetClient>(); auto assetClient = DependencyManager::get<AssetClient>();
assetClient->deleteAssetMappings(_paths, [this, assetClient](bool responseReceived, AssetServerError error, QSharedPointer<ReceivedMessage> message) { _mappingRequestID = assetClient->deleteAssetMappings(_paths,
[this, assetClient](bool responseReceived, AssetServerError error, QSharedPointer<ReceivedMessage> message) {
_mappingRequestID = AssetClient::INVALID_MESSAGE_ID;
if (!responseReceived) { if (!responseReceived) {
_error = NetworkError; _error = NetworkError;
} else { } else {
@ -216,9 +234,10 @@ void RenameMappingRequest::doStart() {
auto assetClient = DependencyManager::get<AssetClient>(); auto assetClient = DependencyManager::get<AssetClient>();
assetClient->renameAssetMapping(_oldPath, _newPath, [this, assetClient](bool responseReceived, _mappingRequestID = assetClient->renameAssetMapping(_oldPath, _newPath,
AssetServerError error, [this, assetClient](bool responseReceived, AssetServerError error, QSharedPointer<ReceivedMessage> message) {
QSharedPointer<ReceivedMessage> message) {
_mappingRequestID = AssetClient::INVALID_MESSAGE_ID;
if (!responseReceived) { if (!responseReceived) {
_error = NetworkError; _error = NetworkError;
} else { } else {

View file

@ -17,6 +17,7 @@
#include <QtCore/QObject> #include <QtCore/QObject>
#include "AssetUtils.h" #include "AssetUtils.h"
#include "AssetClient.h"
class MappingRequest : public QObject { class MappingRequest : public QObject {
Q_OBJECT Q_OBJECT
@ -31,12 +32,15 @@ public:
UnknownError UnknownError
}; };
virtual ~MappingRequest();
Q_INVOKABLE void start(); Q_INVOKABLE void start();
Error getError() const { return _error; } Error getError() const { return _error; }
Q_INVOKABLE QString getErrorString() const; Q_INVOKABLE QString getErrorString() const;
protected: protected:
Error _error { NoError }; Error _error { NoError };
MessageID _mappingRequestID { AssetClient::INVALID_MESSAGE_ID };
private: private:
virtual void doStart() = 0; virtual void doStart() = 0;

View file

@ -117,22 +117,22 @@ void ResourceCache::setUnusedResourceCacheSize(qint64 unusedResourcesMaxSize) {
} }
void ResourceCache::addUnusedResource(const QSharedPointer<Resource>& resource) { void ResourceCache::addUnusedResource(const QSharedPointer<Resource>& resource) {
if (resource->getBytesTotal() > _unusedResourcesMaxSize) { // If it doesn't fit or its size is unknown, leave the cache alone.
// If it doesn't fit anyway, let's leave whatever is already in the cache. if (resource->getBytes() == 0 || resource->getBytes() > _unusedResourcesMaxSize) {
resource->setCache(nullptr); resource->setCache(nullptr);
return; return;
} }
reserveUnusedResource(resource->getBytesTotal()); reserveUnusedResource(resource->getBytes());
resource->setLRUKey(++_lastLRUKey); resource->setLRUKey(++_lastLRUKey);
_unusedResources.insert(resource->getLRUKey(), resource); _unusedResources.insert(resource->getLRUKey(), resource);
_unusedResourcesSize += resource->getBytesTotal(); _unusedResourcesSize += resource->getBytes();
} }
void ResourceCache::removeUnusedResource(const QSharedPointer<Resource>& resource) { void ResourceCache::removeUnusedResource(const QSharedPointer<Resource>& resource) {
if (_unusedResources.contains(resource->getLRUKey())) { if (_unusedResources.contains(resource->getLRUKey())) {
_unusedResources.remove(resource->getLRUKey()); _unusedResources.remove(resource->getLRUKey());
_unusedResourcesSize -= resource->getBytesTotal(); _unusedResourcesSize -= resource->getBytes();
} }
} }
@ -142,7 +142,7 @@ void ResourceCache::reserveUnusedResource(qint64 resourceSize) {
// unload the oldest resource // unload the oldest resource
QMap<int, QSharedPointer<Resource> >::iterator it = _unusedResources.begin(); QMap<int, QSharedPointer<Resource> >::iterator it = _unusedResources.begin();
_unusedResourcesSize -= it.value()->getBytesTotal(); _unusedResourcesSize -= it.value()->getBytes();
it.value()->setCache(nullptr); it.value()->setCache(nullptr);
_unusedResources.erase(it); _unusedResources.erase(it);
} }
@ -399,7 +399,7 @@ void Resource::makeRequest() {
connect(_request, &ResourceRequest::progress, this, &Resource::handleDownloadProgress); connect(_request, &ResourceRequest::progress, this, &Resource::handleDownloadProgress);
connect(_request, &ResourceRequest::finished, this, &Resource::handleReplyFinished); connect(_request, &ResourceRequest::finished, this, &Resource::handleReplyFinished);
_bytesReceived = _bytesTotal = 0; _bytesReceived = _bytesTotal = _bytes = 0;
_request->send(); _request->send();
} }
@ -412,6 +412,8 @@ void Resource::handleDownloadProgress(uint64_t bytesReceived, uint64_t bytesTota
void Resource::handleReplyFinished() { void Resource::handleReplyFinished() {
Q_ASSERT_X(_request, "Resource::handleReplyFinished", "Request should not be null while in handleReplyFinished"); Q_ASSERT_X(_request, "Resource::handleReplyFinished", "Request should not be null while in handleReplyFinished");
_bytes = _bytesTotal;
if (!_request || _request != sender()) { if (!_request || _request != sender()) {
// This can happen in the edge case that a request is timed out, but a `finished` signal is emitted before it is deleted. // This can happen in the edge case that a request is timed out, but a `finished` signal is emitted before it is deleted.
qWarning(networking) << "Received signal Resource::handleReplyFinished from ResourceRequest that is not the current" qWarning(networking) << "Received signal Resource::handleReplyFinished from ResourceRequest that is not the current"

View file

@ -181,6 +181,9 @@ public:
/// For loading resources, returns the number of total bytes (<= zero if unknown). /// For loading resources, returns the number of total bytes (<= zero if unknown).
qint64 getBytesTotal() const { return _bytesTotal; } qint64 getBytesTotal() const { return _bytesTotal; }
/// For loaded resources, returns the number of actual bytes (defaults to total bytes if not explicitly set).
qint64 getBytes() const { return _bytes; }
/// For loading resources, returns the load progress. /// For loading resources, returns the load progress.
float getProgress() const { return (_bytesTotal <= 0) ? 0.0f : (float)_bytesReceived / _bytesTotal; } float getProgress() const { return (_bytesTotal <= 0) ? 0.0f : (float)_bytesReceived / _bytesTotal; }
@ -191,7 +194,7 @@ public:
void setCache(ResourceCache* cache) { _cache = cache; } void setCache(ResourceCache* cache) { _cache = cache; }
Q_INVOKABLE void allReferencesCleared(); virtual void deleter() { allReferencesCleared(); }
const QUrl& getURL() const { return _url; } const QUrl& getURL() const { return _url; }
@ -222,10 +225,15 @@ protected:
/// This should be overridden by subclasses that need to process the data once it is downloaded. /// This should be overridden by subclasses that need to process the data once it is downloaded.
virtual void downloadFinished(const QByteArray& data) { finishedLoading(true); } virtual void downloadFinished(const QByteArray& data) { finishedLoading(true); }
/// Called when the download is finished and processed, sets the number of actual bytes.
void setBytes(qint64 bytes) { _bytes = bytes; }
/// Called when the download is finished and processed. /// Called when the download is finished and processed.
/// This should be called by subclasses that override downloadFinished to mark the end of processing. /// This should be called by subclasses that override downloadFinished to mark the end of processing.
Q_INVOKABLE void finishedLoading(bool success); Q_INVOKABLE void finishedLoading(bool success);
Q_INVOKABLE void allReferencesCleared();
QUrl _url; QUrl _url;
QUrl _activeUrl; QUrl _activeUrl;
bool _startedLoading = false; bool _startedLoading = false;
@ -253,6 +261,7 @@ private:
QTimer* _replyTimer = nullptr; QTimer* _replyTimer = nullptr;
qint64 _bytesReceived = 0; qint64 _bytesReceived = 0;
qint64 _bytesTotal = 0; qint64 _bytesTotal = 0;
qint64 _bytes = 0;
int _attempts = 0; int _attempts = 0;
}; };

View file

@ -21,6 +21,7 @@ class ResourceRequest : public QObject {
Q_OBJECT Q_OBJECT
public: public:
ResourceRequest(const QUrl& url); ResourceRequest(const QUrl& url);
virtual ~ResourceRequest() = default;
enum State { enum State {
NotStarted = 0, NotStarted = 0,

View file

@ -10,10 +10,13 @@
// //
#include "SequenceNumberStats.h" #include "SequenceNumberStats.h"
#include "NetworkLogging.h"
#include <limits> #include <limits>
#include <LogHandler.h>
#include "NetworkLogging.h"
float PacketStreamStats::getLostRate() const { float PacketStreamStats::getLostRate() const {
return (_expectedReceived == 0) ? 0.0f : (float)_lost / (float)_expectedReceived; return (_expectedReceived == 0) ? 0.0f : (float)_lost / (float)_expectedReceived;
} }
@ -63,6 +66,7 @@ SequenceNumberStats::ArrivalInfo SequenceNumberStats::sequenceNumberReceived(qui
arrivalInfo._status = OnTime; arrivalInfo._status = OnTime;
_lastReceivedSequence = incoming; _lastReceivedSequence = incoming;
_stats._expectedReceived++; _stats._expectedReceived++;
} else { // out of order } else { // out of order
if (wantExtraDebugging) { if (wantExtraDebugging) {
@ -85,6 +89,9 @@ SequenceNumberStats::ArrivalInfo SequenceNumberStats::sequenceNumberReceived(qui
} else if (absGap > MAX_REASONABLE_SEQUENCE_GAP) { } else if (absGap > MAX_REASONABLE_SEQUENCE_GAP) {
arrivalInfo._status = Unreasonable; arrivalInfo._status = Unreasonable;
static const QString UNREASONABLE_SEQUENCE_REGEX { "unreasonable sequence number: \\d+ previous: \\d+" };
static QString repeatedMessage = LogHandler::getInstance().addRepeatedMessageRegex(UNREASONABLE_SEQUENCE_REGEX);
qCDebug(networking) << "unreasonable sequence number:" << incoming << "previous:" << _lastReceivedSequence; qCDebug(networking) << "unreasonable sequence number:" << incoming << "previous:" << _lastReceivedSequence;
_stats._unreasonable++; _stats._unreasonable++;
@ -147,6 +154,9 @@ SequenceNumberStats::ArrivalInfo SequenceNumberStats::sequenceNumberReceived(qui
arrivalInfo._status = Unreasonable; arrivalInfo._status = Unreasonable;
static const QString UNREASONABLE_SEQUENCE_REGEX { "unreasonable sequence number: \\d+ \\(possible duplicate\\)" };
static QString repeatedMessage = LogHandler::getInstance().addRepeatedMessageRegex(UNREASONABLE_SEQUENCE_REGEX);
qCDebug(networking) << "unreasonable sequence number:" << incoming << "(possible duplicate)"; qCDebug(networking) << "unreasonable sequence number:" << incoming << "(possible duplicate)";
_stats._unreasonable++; _stats._unreasonable++;

View file

@ -74,6 +74,9 @@ public:
/// whether the HMD is being worn /// whether the HMD is being worn
virtual bool isDisplayVisible() const { return false; } virtual bool isDisplayVisible() const { return false; }
virtual QString getPreferredAudioInDevice() const { return QString(); }
virtual QString getPreferredAudioOutDevice() const { return QString(); }
// Rendering support // Rendering support
/** /**

View file

@ -36,6 +36,6 @@ NetworkClipLoaderPointer ClipCache::getClipLoader(const QUrl& url) {
} }
QSharedPointer<Resource> ClipCache::createResource(const QUrl& url, const QSharedPointer<Resource>& fallback, bool delayLoad, const void* extra) { QSharedPointer<Resource> ClipCache::createResource(const QUrl& url, const QSharedPointer<Resource>& fallback, bool delayLoad, const void* extra) {
return QSharedPointer<Resource>(new NetworkClipLoader(url, delayLoad), &Resource::allReferencesCleared); return QSharedPointer<Resource>(new NetworkClipLoader(url, delayLoad), &Resource::deleter);
} }

View file

@ -76,14 +76,9 @@ AbstractViewStateInterface* Model::_viewState = NULL;
bool Model::needsFixupInScene() const { bool Model::needsFixupInScene() const {
if (readyToAddToScene()) { if (readyToAddToScene()) {
// Once textures are loaded, fixup if they are now transparent if (_needsUpdateTextures && _geometry->getGeometry()->areTexturesLoaded()) {
if (_needsUpdateTransparentTextures && _geometry->getGeometry()->areTexturesLoaded()) { _needsUpdateTextures = false;
_needsUpdateTransparentTextures = false; return true;
bool hasTransparentTextures = _geometry->getGeometry()->hasTransparentTextures();
if (_hasTransparentTextures != hasTransparentTextures) {
_hasTransparentTextures = hasTransparentTextures;
return true;
}
} }
if (!_readyWhenAdded) { if (!_readyWhenAdded) {
return true; return true;
@ -546,43 +541,6 @@ void Model::setVisibleInScene(bool newValue, std::shared_ptr<render::Scene> scen
} }
} }
bool Model::addToScene(std::shared_ptr<render::Scene> scene, render::PendingChanges& pendingChanges, bool showCollisionHull) {
if ((!_meshGroupsKnown || showCollisionHull != _showCollisionHull) && isLoaded()) {
_showCollisionHull = showCollisionHull;
segregateMeshGroups();
}
bool somethingAdded = false;
foreach (auto renderItem, _modelMeshRenderItemsSet) {
auto item = scene->allocateID();
auto renderPayload = std::make_shared<ModelMeshPartPayload::Payload>(renderItem);
pendingChanges.resetItem(item, renderPayload);
pendingChanges.updateItem<ModelMeshPartPayload>(item, [](ModelMeshPartPayload& data) {
data.notifyLocationChanged();
});
_modelMeshRenderItems.insert(item, renderPayload);
somethingAdded = true;
}
foreach (auto renderItem, _collisionRenderItemsSet) {
auto item = scene->allocateID();
auto renderPayload = std::make_shared<MeshPartPayload::Payload>(renderItem);
pendingChanges.resetItem(item, renderPayload);
pendingChanges.updateItem<MeshPartPayload>(item, [](MeshPartPayload& data) {
data.notifyLocationChanged();
});
_collisionRenderItems.insert(item, renderPayload);
somethingAdded = true;
}
_readyWhenAdded = readyToAddToScene();
return somethingAdded;
}
bool Model::addToScene(std::shared_ptr<render::Scene> scene, bool Model::addToScene(std::shared_ptr<render::Scene> scene,
render::PendingChanges& pendingChanges, render::PendingChanges& pendingChanges,
render::Item::Status::Getters& statusGetters, render::Item::Status::Getters& statusGetters,
@ -594,28 +552,48 @@ bool Model::addToScene(std::shared_ptr<render::Scene> scene,
bool somethingAdded = false; bool somethingAdded = false;
foreach (auto renderItem, _modelMeshRenderItemsSet) { if (_modelMeshRenderItems.size()) {
auto item = scene->allocateID(); for (auto item : _modelMeshRenderItems.keys()) {
auto renderPayload = std::make_shared<ModelMeshPartPayload::Payload>(renderItem); pendingChanges.updateItem<ModelMeshPartPayload>(item, [](ModelMeshPartPayload& data) {
renderPayload->addStatusGetters(statusGetters); data.notifyLocationChanged();
pendingChanges.resetItem(item, renderPayload); });
pendingChanges.updateItem<ModelMeshPartPayload>(item, [](ModelMeshPartPayload& data) { }
data.notifyLocationChanged(); } else {
}); for (auto renderItem : _modelMeshRenderItemsSet) {
_modelMeshRenderItems.insert(item, renderPayload); auto item = scene->allocateID();
somethingAdded = true; auto renderPayload = std::make_shared<ModelMeshPartPayload::Payload>(renderItem);
if (statusGetters.size()) {
renderPayload->addStatusGetters(statusGetters);
}
pendingChanges.resetItem(item, renderPayload);
pendingChanges.updateItem<ModelMeshPartPayload>(item, [](ModelMeshPartPayload& data) {
data.notifyLocationChanged();
});
_modelMeshRenderItems.insert(item, renderPayload);
somethingAdded = true;
}
} }
foreach (auto renderItem, _collisionRenderItemsSet) { if (_collisionRenderItems.size()) {
auto item = scene->allocateID(); for (auto item : _collisionRenderItems.keys()) {
auto renderPayload = std::make_shared<MeshPartPayload::Payload>(renderItem); pendingChanges.updateItem<MeshPartPayload>(item, [](MeshPartPayload& data) {
renderPayload->addStatusGetters(statusGetters); data.notifyLocationChanged();
pendingChanges.resetItem(item, renderPayload); });
pendingChanges.updateItem<MeshPartPayload>(item, [](MeshPartPayload& data) { }
data.notifyLocationChanged(); } else {
}); for (auto renderItem : _collisionRenderItemsSet) {
_collisionRenderItems.insert(item, renderPayload); auto item = scene->allocateID();
somethingAdded = true; auto renderPayload = std::make_shared<MeshPartPayload::Payload>(renderItem);
if (statusGetters.size()) {
renderPayload->addStatusGetters(statusGetters);
}
pendingChanges.resetItem(item, renderPayload);
pendingChanges.updateItem<MeshPartPayload>(item, [](MeshPartPayload& data) {
data.notifyLocationChanged();
});
_collisionRenderItems.insert(item, renderPayload);
somethingAdded = true;
}
} }
_readyWhenAdded = readyToAddToScene(); _readyWhenAdded = readyToAddToScene();
@ -791,6 +769,13 @@ int Model::getLastFreeJointIndex(int jointIndex) const {
return (isActive() && jointIndex != -1) ? getFBXGeometry().joints.at(jointIndex).freeLineage.last() : -1; return (isActive() && jointIndex != -1) ? getFBXGeometry().joints.at(jointIndex).freeLineage.last() : -1;
} }
void Model::setTextures(const QVariantMap& textures) {
if (isLoaded()) {
_needsUpdateTextures = true;
_geometry->getGeometry()->setTextures(textures);
}
}
void Model::setURL(const QUrl& url) { void Model::setURL(const QUrl& url) {
// don't recreate the geometry if it's the same URL // don't recreate the geometry if it's the same URL
if (_url == url && _geometry && _geometry->getURL() == url) { if (_url == url && _geometry && _geometry->getURL() == url) {
@ -807,8 +792,7 @@ void Model::setURL(const QUrl& url) {
} }
_needsReload = true; _needsReload = true;
_needsUpdateTransparentTextures = true; _needsUpdateTextures = true;
_hasTransparentTextures = false;
_meshGroupsKnown = false; _meshGroupsKnown = false;
invalidCalculatedMeshBoxes(); invalidCalculatedMeshBoxes();
deleteGeometry(); deleteGeometry();

View file

@ -87,7 +87,10 @@ public:
bool initWhenReady(render::ScenePointer scene); bool initWhenReady(render::ScenePointer scene);
bool addToScene(std::shared_ptr<render::Scene> scene, bool addToScene(std::shared_ptr<render::Scene> scene,
render::PendingChanges& pendingChanges, render::PendingChanges& pendingChanges,
bool showCollisionHull = false); bool showCollisionHull = false) {
auto getters = render::Item::Status::Getters(0);
return addToScene(scene, pendingChanges, getters, showCollisionHull);
}
bool addToScene(std::shared_ptr<render::Scene> scene, bool addToScene(std::shared_ptr<render::Scene> scene,
render::PendingChanges& pendingChanges, render::PendingChanges& pendingChanges,
render::Item::Status::Getters& statusGetters, render::Item::Status::Getters& statusGetters,
@ -129,6 +132,9 @@ public:
/// Returns a reference to the shared collision geometry. /// Returns a reference to the shared collision geometry.
const NetworkGeometry::Pointer& getCollisionGeometry() const { return _collisionGeometry; } const NetworkGeometry::Pointer& getCollisionGeometry() const { return _collisionGeometry; }
const QVariantMap getTextures() const { assert(isLoaded()); return _geometry->getGeometry()->getTextures(); }
void setTextures(const QVariantMap& textures);
/// Provided as a convenience, will crash if !isLoaded() /// Provided as a convenience, will crash if !isLoaded()
// And so that getGeometry() isn't chained everywhere // And so that getGeometry() isn't chained everywhere
const FBXGeometry& getFBXGeometry() const { assert(isLoaded()); return getGeometry()->getGeometry()->getGeometry(); } const FBXGeometry& getFBXGeometry() const { assert(isLoaded()); return getGeometry()->getGeometry()->getGeometry(); }
@ -385,9 +391,8 @@ protected:
bool _readyWhenAdded { false }; bool _readyWhenAdded { false };
bool _needsReload { true }; bool _needsReload { true };
bool _needsUpdateClusterMatrices { true }; bool _needsUpdateClusterMatrices { true };
mutable bool _needsUpdateTransparentTextures { true };
mutable bool _hasTransparentTextures { false };
bool _showCollisionHull { false }; bool _showCollisionHull { false };
mutable bool _needsUpdateTextures { true };
friend class ModelMeshPartPayload; friend class ModelMeshPartPayload;
RigPointer _rig; RigPointer _rig;

View file

@ -18,6 +18,7 @@
#include <NetworkAccessManager.h> #include <NetworkAccessManager.h>
#include <SharedUtil.h> #include <SharedUtil.h>
#include "ResourceManager.h" #include "ResourceManager.h"
#include "ScriptEngines.h"
BatchLoader::BatchLoader(const QList<QUrl>& urls) BatchLoader::BatchLoader(const QList<QUrl>& urls)
: QObject(), : QObject(),
@ -35,7 +36,8 @@ void BatchLoader::start() {
_started = true; _started = true;
for (const auto& url : _urls) { for (const auto& rawURL : _urls) {
QUrl url = expandScriptUrl(normalizeScriptURL(rawURL));
auto request = ResourceManager::createResourceRequest(this, url); auto request = ResourceManager::createResourceRequest(this, url);
if (!request) { if (!request) {
_data.insert(url, QString()); _data.insert(url, QString());

View file

@ -220,11 +220,10 @@ void ScriptEngine::loadURL(const QUrl& scriptURL, bool reload) {
return; return;
} }
_fileNameString = scriptURL.toString(); QUrl url = expandScriptUrl(scriptURL);
_fileNameString = url.toString();
_isReloading = reload; _isReloading = reload;
QUrl url(scriptURL);
bool isPending; bool isPending;
auto scriptCache = DependencyManager::get<ScriptCache>(); auto scriptCache = DependencyManager::get<ScriptCache>();
scriptCache->getScript(url, this, isPending, reload); scriptCache->getScript(url, this, isPending, reload);
@ -848,7 +847,7 @@ QUrl ScriptEngine::resolvePath(const QString& include) const {
QUrl url(include); QUrl url(include);
// first lets check to see if it's already a full URL // first lets check to see if it's already a full URL
if (!url.scheme().isEmpty()) { if (!url.scheme().isEmpty()) {
return url; return expandScriptUrl(url);
} }
// we apparently weren't a fully qualified url, so, let's assume we're relative // we apparently weren't a fully qualified url, so, let's assume we're relative
@ -865,7 +864,7 @@ QUrl ScriptEngine::resolvePath(const QString& include) const {
} }
// at this point we should have a legitimate fully qualified URL for our parent // at this point we should have a legitimate fully qualified URL for our parent
url = parentURL.resolved(url); url = expandScriptUrl(parentURL.resolved(url));
return url; return url;
} }

View file

@ -47,13 +47,6 @@ QUrl normalizeScriptURL(const QUrl& rawScriptURL) {
QUrl fullNormal = rawScriptURL; QUrl fullNormal = rawScriptURL;
QUrl defaultScriptLoc = defaultScriptsLocation(); QUrl defaultScriptLoc = defaultScriptsLocation();
#ifdef Q_OS_LINUX
#else
// Force lowercase on file scripts because of drive letter weirdness.
if (rawScriptURL.isLocalFile()) {
fullNormal.setPath(fullNormal.path().toLower());
}
#endif
// if this url is something "beneath" the default script url, replace the local path with ~ // if this url is something "beneath" the default script url, replace the local path with ~
if (fullNormal.scheme() == defaultScriptLoc.scheme() && if (fullNormal.scheme() == defaultScriptLoc.scheme() &&
fullNormal.host() == defaultScriptLoc.host() && fullNormal.host() == defaultScriptLoc.host() &&
@ -69,7 +62,8 @@ QUrl normalizeScriptURL(const QUrl& rawScriptURL) {
} }
} }
QUrl expandScriptUrl(const QUrl& normalizedScriptURL) { QUrl expandScriptUrl(const QUrl& rawScriptURL) {
QUrl normalizedScriptURL = normalizeScriptURL(rawScriptURL);
if (normalizedScriptURL.scheme() == "http" || if (normalizedScriptURL.scheme() == "http" ||
normalizedScriptURL.scheme() == "https" || normalizedScriptURL.scheme() == "https" ||
normalizedScriptURL.scheme() == "atp") { normalizedScriptURL.scheme() == "atp") {
@ -230,7 +224,7 @@ QVariantList ScriptEngines::getRunning() {
} }
QVariantMap resultNode; QVariantMap resultNode;
resultNode.insert("name", runningScriptURL.fileName()); resultNode.insert("name", runningScriptURL.fileName());
QUrl displayURL = expandScriptUrl(QUrl(runningScriptURL)); QUrl displayURL = expandScriptUrl(runningScriptURL);
QString displayURLString; QString displayURLString;
if (displayURL.isLocalFile()) { if (displayURL.isLocalFile()) {
displayURLString = displayURL.toLocalFile(); displayURLString = displayURL.toLocalFile();
@ -251,7 +245,7 @@ static const QString SETTINGS_KEY = "Settings";
void ScriptEngines::loadDefaultScripts() { void ScriptEngines::loadDefaultScripts() {
QUrl defaultScriptsLoc = defaultScriptsLocation(); QUrl defaultScriptsLoc = defaultScriptsLocation();
defaultScriptsLoc.setPath(defaultScriptsLoc.path() + "/scripts/defaultScripts.js"); defaultScriptsLoc.setPath(defaultScriptsLoc.path() + "/defaultScripts.js");
loadScript(defaultScriptsLoc.toString()); loadScript(defaultScriptsLoc.toString());
} }
@ -310,7 +304,12 @@ void ScriptEngines::saveScripts() {
QStringList ScriptEngines::getRunningScripts() { QStringList ScriptEngines::getRunningScripts() {
QReadLocker lock(&_scriptEnginesHashLock); QReadLocker lock(&_scriptEnginesHashLock);
return _scriptEnginesHash.keys(); QList<QUrl> urls = _scriptEnginesHash.keys();
QStringList result;
for (auto url : urls) {
result.append(url.toString());
}
return result;
} }
void ScriptEngines::stopAllScripts(bool restart) { void ScriptEngines::stopAllScripts(bool restart) {
@ -318,7 +317,7 @@ void ScriptEngines::stopAllScripts(bool restart) {
if (restart) { if (restart) {
// Delete all running scripts from cache so that they are re-downloaded when they are restarted // Delete all running scripts from cache so that they are re-downloaded when they are restarted
auto scriptCache = DependencyManager::get<ScriptCache>(); auto scriptCache = DependencyManager::get<ScriptCache>();
for (QHash<QString, ScriptEngine*>::const_iterator it = _scriptEnginesHash.constBegin(); for (QHash<QUrl, ScriptEngine*>::const_iterator it = _scriptEnginesHash.constBegin();
it != _scriptEnginesHash.constEnd(); it++) { it != _scriptEnginesHash.constEnd(); it++) {
if (!it.value()->isFinished()) { if (!it.value()->isFinished()) {
scriptCache->deleteScript(it.key()); scriptCache->deleteScript(it.key());
@ -327,7 +326,7 @@ void ScriptEngines::stopAllScripts(bool restart) {
} }
// Stop and possibly restart all currently running scripts // Stop and possibly restart all currently running scripts
for (QHash<QString, ScriptEngine*>::const_iterator it = _scriptEnginesHash.constBegin(); for (QHash<QUrl, ScriptEngine*>::const_iterator it = _scriptEnginesHash.constBegin();
it != _scriptEnginesHash.constEnd(); it++) { it != _scriptEnginesHash.constEnd(); it++) {
if (it.value()->isFinished()) { if (it.value()->isFinished()) {
continue; continue;
@ -350,21 +349,20 @@ bool ScriptEngines::stopScript(const QString& rawScriptURL, bool restart) {
if (!scriptURL.isValid()) { if (!scriptURL.isValid()) {
scriptURL = normalizeScriptURL(QUrl::fromLocalFile(rawScriptURL)); scriptURL = normalizeScriptURL(QUrl::fromLocalFile(rawScriptURL));
} }
const QString scriptURLString = scriptURL.toString();
QReadLocker lock(&_scriptEnginesHashLock); QReadLocker lock(&_scriptEnginesHashLock);
if (_scriptEnginesHash.contains(scriptURLString)) { if (_scriptEnginesHash.contains(scriptURL)) {
ScriptEngine* scriptEngine = _scriptEnginesHash[scriptURLString]; ScriptEngine* scriptEngine = _scriptEnginesHash[scriptURL];
if (restart) { if (restart) {
auto scriptCache = DependencyManager::get<ScriptCache>(); auto scriptCache = DependencyManager::get<ScriptCache>();
scriptCache->deleteScript(QUrl(scriptURLString)); scriptCache->deleteScript(scriptURL);
connect(scriptEngine, &ScriptEngine::finished, this, [this](QString scriptName, ScriptEngine* engine) { connect(scriptEngine, &ScriptEngine::finished, this, [this](QString scriptName, ScriptEngine* engine) {
reloadScript(scriptName); reloadScript(scriptName);
}); });
} }
scriptEngine->stop(); scriptEngine->stop();
stoppedScript = true; stoppedScript = true;
qCDebug(scriptengine) << "stopping script..." << scriptURLString; qCDebug(scriptengine) << "stopping script..." << scriptURL;
} }
} }
return stoppedScript; return stoppedScript;
@ -409,7 +407,7 @@ ScriptEngine* ScriptEngines::loadScript(const QUrl& scriptFilename, bool isUserL
scriptUrl = normalizeScriptURL(scriptFilename); scriptUrl = normalizeScriptURL(scriptFilename);
} }
auto scriptEngine = getScriptEngine(scriptUrl.toString()); auto scriptEngine = getScriptEngine(scriptUrl);
if (scriptEngine) { if (scriptEngine) {
return scriptEngine; return scriptEngine;
} }
@ -429,18 +427,18 @@ ScriptEngine* ScriptEngines::loadScript(const QUrl& scriptFilename, bool isUserL
connect(scriptEngine, &ScriptEngine::errorLoadingScript, this, &ScriptEngines::onScriptEngineError); connect(scriptEngine, &ScriptEngine::errorLoadingScript, this, &ScriptEngines::onScriptEngineError);
// get the script engine object to load the script at the designated script URL // get the script engine object to load the script at the designated script URL
scriptEngine->loadURL(QUrl(expandScriptUrl(scriptUrl.toString())), reload); scriptEngine->loadURL(scriptUrl, reload);
} }
return scriptEngine; return scriptEngine;
} }
ScriptEngine* ScriptEngines::getScriptEngine(const QString& rawScriptURL) { ScriptEngine* ScriptEngines::getScriptEngine(const QUrl& rawScriptURL) {
ScriptEngine* result = nullptr; ScriptEngine* result = nullptr;
{ {
QReadLocker lock(&_scriptEnginesHashLock); QReadLocker lock(&_scriptEnginesHashLock);
const QString scriptURLString = normalizeScriptURL(QUrl(rawScriptURL)).toString(); const QUrl scriptURL = normalizeScriptURL(rawScriptURL);
auto it = _scriptEnginesHash.find(scriptURLString); auto it = _scriptEnginesHash.find(scriptURL);
if (it != _scriptEnginesHash.end()) { if (it != _scriptEnginesHash.end()) {
result = it.value(); result = it.value();
} }
@ -459,8 +457,7 @@ void ScriptEngines::onScriptEngineLoaded(const QString& rawScriptURL) {
QWriteLocker lock(&_scriptEnginesHashLock); QWriteLocker lock(&_scriptEnginesHashLock);
QUrl url = QUrl(rawScriptURL); QUrl url = QUrl(rawScriptURL);
QUrl normalized = normalizeScriptURL(url); QUrl normalized = normalizeScriptURL(url);
const QString scriptURLString = normalized.toString(); _scriptEnginesHash.insertMulti(normalized, scriptEngine);
_scriptEnginesHash.insertMulti(scriptURLString, scriptEngine);
} }
emit scriptCountChanged(); emit scriptCountChanged();
} }
@ -486,8 +483,8 @@ void ScriptEngines::onScriptFinished(const QString& rawScriptURL, ScriptEngine*
bool removed = false; bool removed = false;
{ {
QWriteLocker lock(&_scriptEnginesHashLock); QWriteLocker lock(&_scriptEnginesHashLock);
const QString scriptURLString = normalizeScriptURL(QUrl(rawScriptURL)).toString(); const QUrl scriptURL = normalizeScriptURL(QUrl(rawScriptURL));
for (auto it = _scriptEnginesHash.find(scriptURLString); it != _scriptEnginesHash.end(); ++it) { for (auto it = _scriptEnginesHash.find(scriptURL); it != _scriptEnginesHash.end(); ++it) {
if (it.value() == engine) { if (it.value() == engine) {
_scriptEnginesHash.erase(it); _scriptEnginesHash.erase(it);
removed = true; removed = true;

View file

@ -45,7 +45,7 @@ public:
void loadDefaultScripts(); void loadDefaultScripts();
void setScriptsLocation(const QString& scriptsLocation); void setScriptsLocation(const QString& scriptsLocation);
QStringList getRunningScripts(); QStringList getRunningScripts();
ScriptEngine* getScriptEngine(const QString& scriptHash); ScriptEngine* getScriptEngine(const QUrl& scriptHash);
ScriptsModel* scriptsModel() { return &_scriptsModel; }; ScriptsModel* scriptsModel() { return &_scriptsModel; };
ScriptsModelFilter* scriptsModelFilter() { return &_scriptsModelFilter; }; ScriptsModelFilter* scriptsModelFilter() { return &_scriptsModelFilter; };
@ -86,7 +86,7 @@ protected:
Setting::Handle<bool> _firstRun { "firstRun", true }; Setting::Handle<bool> _firstRun { "firstRun", true };
QReadWriteLock _scriptEnginesHashLock; QReadWriteLock _scriptEnginesHashLock;
QHash<QString, ScriptEngine*> _scriptEnginesHash; QHash<QUrl, ScriptEngine*> _scriptEnginesHash;
QSet<ScriptEngine*> _allKnownScriptEngines; QSet<ScriptEngine*> _allKnownScriptEngines;
QMutex _allScriptsMutex; QMutex _allScriptsMutex;
std::atomic<bool> _stoppingAllScripts { false }; std::atomic<bool> _stoppingAllScripts { false };
@ -97,6 +97,6 @@ protected:
}; };
QUrl normalizeScriptURL(const QUrl& rawScriptURL); QUrl normalizeScriptURL(const QUrl& rawScriptURL);
QUrl expandScriptUrl(const QUrl& normalizedScriptURL); QUrl expandScriptUrl(const QUrl& rawScriptURL);
#endif // hifi_ScriptEngine_h #endif // hifi_ScriptEngine_h

View file

@ -25,7 +25,6 @@
#define __STR1__(x) __STR2__(x) #define __STR1__(x) __STR2__(x)
#define __LOC__ __FILE__ "(" __STR1__(__LINE__) ") : Warning Msg: " #define __LOC__ __FILE__ "(" __STR1__(__LINE__) ") : Warning Msg: "
static const QString MODELS_LOCATION = "scripts/";
static const QString PREFIX_PARAMETER_NAME = "prefix"; static const QString PREFIX_PARAMETER_NAME = "prefix";
static const QString MARKER_PARAMETER_NAME = "marker"; static const QString MARKER_PARAMETER_NAME = "marker";
static const QString IS_TRUNCATED_NAME = "IsTruncated"; static const QString IS_TRUNCATED_NAME = "IsTruncated";
@ -41,7 +40,7 @@ TreeNodeBase::TreeNodeBase(TreeNodeFolder* parent, const QString& name, TreeNode
TreeNodeScript::TreeNodeScript(const QString& localPath, const QString& fullPath, ScriptOrigin origin) : TreeNodeScript::TreeNodeScript(const QString& localPath, const QString& fullPath, ScriptOrigin origin) :
TreeNodeBase(NULL, localPath.split("/").last(), TREE_NODE_TYPE_SCRIPT), TreeNodeBase(NULL, localPath.split("/").last(), TREE_NODE_TYPE_SCRIPT),
_localPath(localPath), _localPath(localPath),
_fullPath(fullPath), _fullPath(expandScriptUrl(QUrl(fullPath)).toString()),
_origin(origin) { _origin(origin) {
}; };
@ -159,9 +158,11 @@ void ScriptsModel::requestDefaultFiles(QString marker) {
if (url.isLocalFile()) { if (url.isLocalFile()) {
// if the url indicates a local directory, use QDirIterator // if the url indicates a local directory, use QDirIterator
// QString localDir = url.toLocalFile() + "/scripts"; QString localDir = expandScriptUrl(url).toLocalFile();
QString localDir = expandScriptUrl(url).toLocalFile() + "/scripts";
int localDirPartCount = localDir.split("/").size(); int localDirPartCount = localDir.split("/").size();
if (localDir.endsWith("/")) {
localDirPartCount--;
}
#ifdef Q_OS_WIN #ifdef Q_OS_WIN
localDirPartCount++; // one for the drive letter localDirPartCount++; // one for the drive letter
#endif #endif
@ -176,7 +177,7 @@ void ScriptsModel::requestDefaultFiles(QString marker) {
} else { } else {
// the url indicates http(s), use QNetworkRequest // the url indicates http(s), use QNetworkRequest
QUrlQuery query; QUrlQuery query;
query.addQueryItem(PREFIX_PARAMETER_NAME, MODELS_LOCATION); query.addQueryItem(PREFIX_PARAMETER_NAME, ".");
if (!marker.isEmpty()) { if (!marker.isEmpty()) {
query.addQueryItem(MARKER_PARAMETER_NAME, marker); query.addQueryItem(MARKER_PARAMETER_NAME, marker);
} }
@ -240,7 +241,7 @@ bool ScriptsModel::parseXML(QByteArray xmlFile) {
if (jsRegex.exactMatch(xml.text().toString())) { if (jsRegex.exactMatch(xml.text().toString())) {
QString localPath = lastKey.split("/").mid(1).join("/"); QString localPath = lastKey.split("/").mid(1).join("/");
QUrl fullPath = defaultScriptsLocation(); QUrl fullPath = defaultScriptsLocation();
fullPath.setPath(fullPath.path() + "/" + lastKey); fullPath.setPath(fullPath.path() + lastKey);
const QString fullPathStr = normalizeScriptURL(fullPath).toString(); const QString fullPathStr = normalizeScriptURL(fullPath).toString();
_treeNodes.append(new TreeNodeScript(localPath, fullPathStr, SCRIPT_ORIGIN_DEFAULT)); _treeNodes.append(new TreeNodeScript(localPath, fullPathStr, SCRIPT_ORIGIN_DEFAULT));
} }
@ -309,6 +310,9 @@ void ScriptsModel::rebuildTree() {
QString hash; QString hash;
QStringList pathList = script->getLocalPath().split(tr("/")); QStringList pathList = script->getLocalPath().split(tr("/"));
pathList.removeLast(); pathList.removeLast();
if (pathList.isEmpty()) {
continue;
}
QStringList::const_iterator pathIterator; QStringList::const_iterator pathIterator;
for (pathIterator = pathList.constBegin(); pathIterator != pathList.constEnd(); ++pathIterator) { for (pathIterator = pathList.constBegin(); pathIterator != pathList.constEnd(); ++pathIterator) {
hash.append(*pathIterator + "/"); hash.append(*pathIterator + "/");

View file

@ -0,0 +1,75 @@
//
// CPUID.cpp
//
// Created by Ryan Huffman on 3/25/16.
// Copyright 2016 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "CPUID.h"
#ifdef Q_OS_WIN
const CPUID::CPUID_Internal CPUID::CPU_Rep;
std::vector<CPUID::Feature> CPUID::getAllFeatures() {
std::vector<CPUID::Feature> features;
features.push_back({ "3DNOW", CPUID::_3DNOW() });
features.push_back({ "3DNOWEXT", CPUID::_3DNOWEXT() });
features.push_back({ "ABM", CPUID::ABM() });
features.push_back({ "ADX", CPUID::ADX() });
features.push_back({ "AES", CPUID::AES() });
features.push_back({ "AVX", CPUID::AVX() });
features.push_back({ "AVX2", CPUID::AVX2() });
features.push_back({ "AVX512CD", CPUID::AVX512CD() });
features.push_back({ "AVX512ER", CPUID::AVX512ER() });
features.push_back({ "AVX512F", CPUID::AVX512F() });
features.push_back({ "AVX512PF", CPUID::AVX512PF() });
features.push_back({ "BMI1", CPUID::BMI1() });
features.push_back({ "BMI2", CPUID::BMI2() });
features.push_back({ "CLFSH", CPUID::CLFSH() });
features.push_back({ "CMPXCHG16B", CPUID::CMPXCHG16B() });
features.push_back({ "CX8", CPUID::CX8() });
features.push_back({ "ERMS", CPUID::ERMS() });
features.push_back({ "F16C", CPUID::F16C() });
features.push_back({ "FMA", CPUID::FMA() });
features.push_back({ "FSGSBASE", CPUID::FSGSBASE() });
features.push_back({ "FXSR", CPUID::FXSR() });
features.push_back({ "HLE", CPUID::HLE() });
features.push_back({ "INVPCID", CPUID::INVPCID() });
features.push_back({ "LAHF", CPUID::LAHF() });
features.push_back({ "LZCNT", CPUID::LZCNT() });
features.push_back({ "MMX", CPUID::MMX() });
features.push_back({ "MMXEXT", CPUID::MMXEXT() });
features.push_back({ "MONITOR", CPUID::MONITOR() });
features.push_back({ "MOVBE", CPUID::MOVBE() });
features.push_back({ "MSR", CPUID::MSR() });
features.push_back({ "OSXSAVE", CPUID::OSXSAVE() });
features.push_back({ "PCLMULQDQ", CPUID::PCLMULQDQ() });
features.push_back({ "POPCNT", CPUID::POPCNT() });
features.push_back({ "PREFETCHWT1", CPUID::PREFETCHWT1() });
features.push_back({ "RDRAND", CPUID::RDRAND() });
features.push_back({ "RDSEED", CPUID::RDSEED() });
features.push_back({ "RDTSCP", CPUID::RDTSCP() });
features.push_back({ "RTM", CPUID::RTM() });
features.push_back({ "SEP", CPUID::SEP() });
features.push_back({ "SHA", CPUID::SHA() });
features.push_back({ "SSE", CPUID::SSE() });
features.push_back({ "SSE2", CPUID::SSE2() });
features.push_back({ "SSE3", CPUID::SSE3() });
features.push_back({ "SSE4.1", CPUID::SSE41() });
features.push_back({ "SSE4.2", CPUID::SSE42() });
features.push_back({ "SSE4a", CPUID::SSE4a() });
features.push_back({ "SSSE3", CPUID::SSSE3() });
features.push_back({ "SYSCALL", CPUID::SYSCALL() });
features.push_back({ "TBM", CPUID::TBM() });
features.push_back({ "XOP", CPUID::XOP() });
features.push_back({ "XSAVE", CPUID::XSAVE() });
return features;
};
#endif

View file

@ -0,0 +1,212 @@
//
// CPUID.h
//
// Adapted from Microsoft's example for using the cpuid intrinsic,
// found at https://msdn.microsoft.com/en-us/library/hskdteyh.aspx
//
// Provides acccess to information provided by the CPUID opcode
//
// TODO: Generalize to work outside of Windows.
//
// Created by Ryan Huffman on 3/25/16.
// Copyright 2016 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#ifndef hifi_CPUID_h
#define hifi_CPUID_h
#include <QtCore/QtGlobal>
#include <vector>
#include <bitset>
#include <array>
#include <string>
#ifdef Q_OS_WIN
#include <intrin.h>
class CPUID
{
// forward declarations
class CPUID_Internal;
public:
struct Feature {
std::string name;
bool supported;
};
static std::vector<Feature> getAllFeatures();
static std::string Vendor(void) { return CPU_Rep.vendor_; }
static std::string Brand(void) { return CPU_Rep.brand_; }
static bool SSE3(void) { return CPU_Rep.f_1_ECX_[0]; }
static bool PCLMULQDQ(void) { return CPU_Rep.f_1_ECX_[1]; }
static bool MONITOR(void) { return CPU_Rep.f_1_ECX_[3]; }
static bool SSSE3(void) { return CPU_Rep.f_1_ECX_[9]; }
static bool FMA(void) { return CPU_Rep.f_1_ECX_[12]; }
static bool CMPXCHG16B(void) { return CPU_Rep.f_1_ECX_[13]; }
static bool SSE41(void) { return CPU_Rep.f_1_ECX_[19]; }
static bool SSE42(void) { return CPU_Rep.f_1_ECX_[20]; }
static bool MOVBE(void) { return CPU_Rep.f_1_ECX_[22]; }
static bool POPCNT(void) { return CPU_Rep.f_1_ECX_[23]; }
static bool AES(void) { return CPU_Rep.f_1_ECX_[25]; }
static bool XSAVE(void) { return CPU_Rep.f_1_ECX_[26]; }
static bool OSXSAVE(void) { return CPU_Rep.f_1_ECX_[27]; }
static bool AVX(void) { return CPU_Rep.f_1_ECX_[28]; }
static bool F16C(void) { return CPU_Rep.f_1_ECX_[29]; }
static bool RDRAND(void) { return CPU_Rep.f_1_ECX_[30]; }
static bool MSR(void) { return CPU_Rep.f_1_EDX_[5]; }
static bool CX8(void) { return CPU_Rep.f_1_EDX_[8]; }
static bool SEP(void) { return CPU_Rep.f_1_EDX_[11]; }
static bool CMOV(void) { return CPU_Rep.f_1_EDX_[15]; }
static bool CLFSH(void) { return CPU_Rep.f_1_EDX_[19]; }
static bool MMX(void) { return CPU_Rep.f_1_EDX_[23]; }
static bool FXSR(void) { return CPU_Rep.f_1_EDX_[24]; }
static bool SSE(void) { return CPU_Rep.f_1_EDX_[25]; }
static bool SSE2(void) { return CPU_Rep.f_1_EDX_[26]; }
static bool FSGSBASE(void) { return CPU_Rep.f_7_EBX_[0]; }
static bool BMI1(void) { return CPU_Rep.f_7_EBX_[3]; }
static bool HLE(void) { return CPU_Rep.isIntel_ && CPU_Rep.f_7_EBX_[4]; }
static bool AVX2(void) { return CPU_Rep.f_7_EBX_[5]; }
static bool BMI2(void) { return CPU_Rep.f_7_EBX_[8]; }
static bool ERMS(void) { return CPU_Rep.f_7_EBX_[9]; }
static bool INVPCID(void) { return CPU_Rep.f_7_EBX_[10]; }
static bool RTM(void) { return CPU_Rep.isIntel_ && CPU_Rep.f_7_EBX_[11]; }
static bool AVX512F(void) { return CPU_Rep.f_7_EBX_[16]; }
static bool RDSEED(void) { return CPU_Rep.f_7_EBX_[18]; }
static bool ADX(void) { return CPU_Rep.f_7_EBX_[19]; }
static bool AVX512PF(void) { return CPU_Rep.f_7_EBX_[26]; }
static bool AVX512ER(void) { return CPU_Rep.f_7_EBX_[27]; }
static bool AVX512CD(void) { return CPU_Rep.f_7_EBX_[28]; }
static bool SHA(void) { return CPU_Rep.f_7_EBX_[29]; }
static bool PREFETCHWT1(void) { return CPU_Rep.f_7_ECX_[0]; }
static bool LAHF(void) { return CPU_Rep.f_81_ECX_[0]; }
static bool LZCNT(void) { return CPU_Rep.isIntel_ && CPU_Rep.f_81_ECX_[5]; }
static bool ABM(void) { return CPU_Rep.isAMD_ && CPU_Rep.f_81_ECX_[5]; }
static bool SSE4a(void) { return CPU_Rep.isAMD_ && CPU_Rep.f_81_ECX_[6]; }
static bool XOP(void) { return CPU_Rep.isAMD_ && CPU_Rep.f_81_ECX_[11]; }
static bool TBM(void) { return CPU_Rep.isAMD_ && CPU_Rep.f_81_ECX_[21]; }
static bool SYSCALL(void) { return CPU_Rep.isIntel_ && CPU_Rep.f_81_EDX_[11]; }
static bool MMXEXT(void) { return CPU_Rep.isAMD_ && CPU_Rep.f_81_EDX_[22]; }
static bool RDTSCP(void) { return CPU_Rep.isIntel_ && CPU_Rep.f_81_EDX_[27]; }
static bool _3DNOWEXT(void) { return CPU_Rep.isAMD_ && CPU_Rep.f_81_EDX_[30]; }
static bool _3DNOW(void) { return CPU_Rep.isAMD_ && CPU_Rep.f_81_EDX_[31]; }
private:
static const CPUID_Internal CPU_Rep;
class CPUID_Internal
{
public:
CPUID_Internal()
: nIds_ { 0 },
nExIds_ { 0 },
isIntel_ { false },
isAMD_ { false },
f_1_ECX_ { 0 },
f_1_EDX_ { 0 },
f_7_EBX_ { 0 },
f_7_ECX_ { 0 },
f_81_ECX_ { 0 },
f_81_EDX_ { 0 },
data_ {},
extdata_ {}
{
//int cpuInfo[4] = {-1};
std::array<int, 4> cpui;
// Calling __cpuid with 0x0 as the function_id argument
// gets the number of the highest valid function ID.
__cpuid(cpui.data(), 0);
nIds_ = cpui[0];
for (int i = 0; i <= nIds_; ++i) {
__cpuidex(cpui.data(), i, 0);
data_.push_back(cpui);
}
// Capture vendor string
char vendor[0x20];
memset(vendor, 0, sizeof(vendor));
*reinterpret_cast<int*>(vendor) = data_[0][1];
*reinterpret_cast<int*>(vendor + 4) = data_[0][3];
*reinterpret_cast<int*>(vendor + 8) = data_[0][2];
vendor_ = vendor;
if (vendor_ == "GenuineIntel") {
isIntel_ = true;
} else if (vendor_ == "AuthenticAMD") {
isAMD_ = true;
}
// load bitset with flags for function 0x00000001
if (nIds_ >= 1) {
f_1_ECX_ = data_[1][2];
f_1_EDX_ = data_[1][3];
}
// load bitset with flags for function 0x00000007
if (nIds_ >= 7) {
f_7_EBX_ = data_[7][1];
f_7_ECX_ = data_[7][2];
}
// Calling __cpuid with 0x80000000 as the function_id argument
// gets the number of the highest valid extended ID.
__cpuid(cpui.data(), 0x80000000);
nExIds_ = cpui[0];
char brand[0x40];
memset(brand, 0, sizeof(brand));
for (int i = 0x80000000; i <= nExIds_; ++i) {
__cpuidex(cpui.data(), i, 0);
extdata_.push_back(cpui);
}
// load bitset with flags for function 0x80000001
if (nExIds_ >= 0x80000001) {
f_81_ECX_ = extdata_[1][2];
f_81_EDX_ = extdata_[1][3];
}
// Interpret CPU brand string if reported
if (nExIds_ >= 0x80000004) {
memcpy(brand, extdata_[2].data(), sizeof(cpui));
memcpy(brand + 16, extdata_[3].data(), sizeof(cpui));
memcpy(brand + 32, extdata_[4].data(), sizeof(cpui));
brand_ = brand;
}
};
int nIds_;
int nExIds_;
std::string vendor_;
std::string brand_;
bool isIntel_;
bool isAMD_;
std::bitset<32> f_1_ECX_;
std::bitset<32> f_1_EDX_;
std::bitset<32> f_7_EBX_;
std::bitset<32> f_7_ECX_;
std::bitset<32> f_81_ECX_;
std::bitset<32> f_81_EDX_;
std::vector<std::array<int, 4>> data_;
std::vector<std::array<int, 4>> extdata_;
};
};
#endif
#endif // hifi_CPUID_h

View file

@ -57,11 +57,11 @@ QString findMostRecentFileExtension(const QString& originalFileName, QVector<QSt
QUrl defaultScriptsLocation() { QUrl defaultScriptsLocation() {
#ifdef Q_OS_WIN #ifdef Q_OS_WIN
return QUrl(("file:///" + QCoreApplication::applicationDirPath()).toLower()); return QUrl(("file:///" + QCoreApplication::applicationDirPath()).toLower() + "/scripts");
#elif defined(Q_OS_OSX) #elif defined(Q_OS_OSX)
return QUrl(("file://" + QCoreApplication::applicationDirPath() + "/../Resources").toLower()); return QUrl(("file://" + QCoreApplication::applicationDirPath() + "/../Resources/scripts").toLower());
#else #else
// return "http://s3.amazonaws.com/hifi-public"; // return "http://s3.amazonaws.com/hifi-public";
return QUrl("file://" + QCoreApplication::applicationDirPath()); return QUrl("file://" + QCoreApplication::applicationDirPath() + "/scripts");
#endif #endif
} }

View file

@ -23,6 +23,11 @@
#include <windows.h> #include <windows.h>
#endif #endif
#ifdef Q_OS_WIN
#include "CPUID.h"
#endif
#ifdef __APPLE__ #ifdef __APPLE__
#include <CoreFoundation/CoreFoundation.h> #include <CoreFoundation/CoreFoundation.h>
#endif #endif
@ -30,6 +35,8 @@
#include <QtCore/QDebug> #include <QtCore/QDebug>
#include <QDateTime> #include <QDateTime>
#include <QElapsedTimer> #include <QElapsedTimer>
#include <QProcess>
#include <QSysInfo>
#include <QThread> #include <QThread>
#include "NumericalConstants.h" #include "NumericalConstants.h"
@ -692,3 +699,82 @@ void disableQtBearerPoll() {
qputenv("QT_BEARER_POLL_TIMEOUT", EXTREME_BEARER_POLL_TIMEOUT); qputenv("QT_BEARER_POLL_TIMEOUT", EXTREME_BEARER_POLL_TIMEOUT);
} }
void printSystemInformation() {
// Write system information to log
qDebug() << "Build Information";
qDebug().noquote() << "\tBuild ABI: " << QSysInfo::buildAbi();
qDebug().noquote() << "\tBuild CPU Architecture: " << QSysInfo::buildCpuArchitecture();
qDebug().noquote() << "System Information";
qDebug().noquote() << "\tProduct Name: " << QSysInfo::prettyProductName();
qDebug().noquote() << "\tCPU Architecture: " << QSysInfo::currentCpuArchitecture();
qDebug().noquote() << "\tKernel Type: " << QSysInfo::kernelType();
qDebug().noquote() << "\tKernel Version: " << QSysInfo::kernelVersion();
auto macVersion = QSysInfo::macVersion();
if (macVersion != QSysInfo::MV_None) {
qDebug() << "\tMac Version: " << macVersion;
}
auto windowsVersion = QSysInfo::windowsVersion();
if (windowsVersion != QSysInfo::WV_None) {
qDebug() << "\tWindows Version: " << windowsVersion;
}
#ifdef Q_OS_WIN
SYSTEM_INFO si;
GetNativeSystemInfo(&si);
qDebug() << "SYSTEM_INFO";
qDebug().noquote() << "\tOEM ID: " << si.dwOemId;
qDebug().noquote() << "\tProcessor Architecture: " << si.wProcessorArchitecture;
qDebug().noquote() << "\tProcessor Type: " << si.dwProcessorType;
qDebug().noquote() << "\tProcessor Level: " << si.wProcessorLevel;
qDebug().noquote() << "\tProcessor Revision: "
<< QString("0x%1").arg(si.wProcessorRevision, 4, 16, QChar('0'));
qDebug().noquote() << "\tNumber of Processors: " << si.dwNumberOfProcessors;
qDebug().noquote() << "\tPage size: " << si.dwPageSize << " Bytes";
qDebug().noquote() << "\tMin Application Address: "
<< QString("0x%1").arg(qulonglong(si.lpMinimumApplicationAddress), 16, 16, QChar('0'));
qDebug().noquote() << "\tMax Application Address: "
<< QString("0x%1").arg(qulonglong(si.lpMaximumApplicationAddress), 16, 16, QChar('0'));
const double BYTES_TO_MEGABYTE = 1.0 / (1024 * 1024);
qDebug() << "MEMORYSTATUSEX";
MEMORYSTATUSEX ms;
ms.dwLength = sizeof(ms);
if (GlobalMemoryStatusEx(&ms)) {
qDebug().noquote() << QString("\tCurrent System Memory Usage: %1%").arg(ms.dwMemoryLoad);
qDebug().noquote() << QString("\tAvail Physical Memory: %1 MB").arg(ms.ullAvailPhys * BYTES_TO_MEGABYTE, 20, 'f', 2);
qDebug().noquote() << QString("\tTotal Physical Memory: %1 MB").arg(ms.ullTotalPhys * BYTES_TO_MEGABYTE, 20, 'f', 2);
qDebug().noquote() << QString("\tAvail in Page File: %1 MB").arg(ms.ullAvailPageFile * BYTES_TO_MEGABYTE, 20, 'f', 2);
qDebug().noquote() << QString("\tTotal in Page File: %1 MB").arg(ms.ullTotalPageFile * BYTES_TO_MEGABYTE, 20, 'f', 2);
qDebug().noquote() << QString("\tAvail Virtual Memory: %1 MB").arg(ms.ullAvailVirtual * BYTES_TO_MEGABYTE, 20, 'f', 2);
qDebug().noquote() << QString("\tTotal Virtual Memory: %1 MB").arg(ms.ullTotalVirtual * BYTES_TO_MEGABYTE, 20, 'f', 2);
} else {
qDebug() << "\tFailed to retrieve memory status: " << GetLastError();
}
qDebug() << "CPUID";
qDebug() << "\tCPU Vendor: " << CPUID::Vendor().c_str();
qDebug() << "\tCPU Brand: " << CPUID::Brand().c_str();
for (auto& feature : CPUID::getAllFeatures()) {
qDebug().nospace().noquote() << "\t[" << (feature.supported ? "x" : " ") << "] " << feature.name.c_str();
}
#endif
qDebug() << "Environment Variables";
// List of env variables to include in the log. For privacy reasons we don't send all env variables.
const QStringList envWhitelist = {
"QTWEBENGINE_REMOTE_DEBUGGING"
};
auto envVariables = QProcessEnvironment::systemEnvironment();
for (auto& env : envWhitelist)
{
qDebug().noquote().nospace() << "\t" <<
(envVariables.contains(env) ? " = " + envVariables.value(env) : " NOT FOUND");
}
}

View file

@ -198,4 +198,6 @@ uint qHash(const std::shared_ptr<T>& ptr, uint seed = 0)
void disableQtBearerPoll(); void disableQtBearerPoll();
void printSystemInformation();
#endif // hifi_SharedUtil_h #endif // hifi_SharedUtil_h

View file

@ -122,13 +122,23 @@ void QmlWindowClass::initQml(QVariantMap properties) {
object->setProperty(SOURCE_PROPERTY, _source); object->setProperty(SOURCE_PROPERTY, _source);
// Forward messages received from QML on to the script // Forward messages received from QML on to the script
connect(_qmlWindow, SIGNAL(sendToScript(QVariant)), this, SIGNAL(fromQml(const QVariant&)), Qt::QueuedConnection); connect(_qmlWindow, SIGNAL(sendToScript(QVariant)), this, SLOT(qmlToScript(const QVariant&)), Qt::QueuedConnection);
}); });
} }
Q_ASSERT(_qmlWindow); Q_ASSERT(_qmlWindow);
Q_ASSERT(dynamic_cast<const QQuickItem*>(_qmlWindow.data())); Q_ASSERT(dynamic_cast<const QQuickItem*>(_qmlWindow.data()));
} }
void QmlWindowClass::qmlToScript(const QVariant& message) {
if (message.canConvert<QJSValue>()) {
emit fromQml(qvariant_cast<QJSValue>(message).toVariant());
} else if (message.canConvert<QString>()) {
emit fromQml(message.toString());
} else {
qWarning() << "Unsupported message type " << message;
}
}
void QmlWindowClass::sendToQml(const QVariant& message) { void QmlWindowClass::sendToQml(const QVariant& message) {
// Forward messages received from the script on to QML // Forward messages received from the script on to QML
QMetaObject::invokeMethod(asQuickItem(), "fromScript", Qt::QueuedConnection, Q_ARG(QVariant, message)); QMetaObject::invokeMethod(asQuickItem(), "fromScript", Qt::QueuedConnection, Q_ARG(QVariant, message));

View file

@ -63,6 +63,7 @@ signals:
protected slots: protected slots:
void hasClosed(); void hasClosed();
void qmlToScript(const QVariant& message);
protected: protected:
static QVariantMap parseArguments(QScriptContext* context); static QVariantMap parseArguments(QScriptContext* context);

View file

@ -12,8 +12,8 @@ if (WIN32)
add_definitions(-DGLEW_STATIC) add_definitions(-DGLEW_STATIC)
set(TARGET_NAME oculus) set(TARGET_NAME oculus)
setup_hifi_plugin() setup_hifi_plugin(Multimedia)
link_hifi_libraries(shared gl gpu controllers ui plugins display-plugins input-plugins) link_hifi_libraries(shared gl gpu controllers ui plugins display-plugins input-plugins audio-client networking)
include_hifi_library_headers(octree) include_hifi_library_headers(octree)
@ -21,5 +21,6 @@ if (WIN32)
find_package(LibOVR REQUIRED) find_package(LibOVR REQUIRED)
target_include_directories(${TARGET_NAME} PRIVATE ${LIBOVR_INCLUDE_DIRS}) target_include_directories(${TARGET_NAME} PRIVATE ${LIBOVR_INCLUDE_DIRS})
target_link_libraries(${TARGET_NAME} ${LIBOVR_LIBRARIES}) target_link_libraries(${TARGET_NAME} ${LIBOVR_LIBRARIES})
target_link_libraries(${TARGET_NAME} Winmm.lib)
endif() endif()

View file

@ -21,6 +21,7 @@ public:
// Stereo specific methods // Stereo specific methods
virtual void resetSensors() override final; virtual void resetSensors() override final;
virtual void beginFrameRender(uint32_t frameIndex) override; virtual void beginFrameRender(uint32_t frameIndex) override;
float getTargetFrameRate() override { return _hmdDesc.DisplayRefreshRate; }
protected: protected:

View file

@ -6,7 +6,14 @@
// 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 "OculusDisplayPlugin.h" #include "OculusDisplayPlugin.h"
// Odd ordering of header is required to avoid 'macro redinition warnings'
#include <AudioClient.h>
#include <OVR_CAPI_Audio.h>
#include <shared/NsightHelpers.h> #include <shared/NsightHelpers.h>
#include "OculusHelpers.h" #include "OculusHelpers.h"
const QString OculusDisplayPlugin::NAME("Oculus Rift"); const QString OculusDisplayPlugin::NAME("Oculus Rift");
@ -86,3 +93,26 @@ void OculusDisplayPlugin::hmdPresent() {
} }
} }
} }
bool OculusDisplayPlugin::isHmdMounted() const {
ovrSessionStatus status;
return (OVR_SUCCESS(ovr_GetSessionStatus(_session, &status)) &&
(ovrFalse != status.HmdMounted));
}
QString OculusDisplayPlugin::getPreferredAudioInDevice() const {
WCHAR buffer[OVR_AUDIO_MAX_DEVICE_STR_SIZE];
if (!OVR_SUCCESS(ovr_GetAudioDeviceInGuidStr(buffer))) {
return QString();
}
return AudioClient::friendlyNameForAudioDevice(buffer);
}
QString OculusDisplayPlugin::getPreferredAudioOutDevice() const {
WCHAR buffer[OVR_AUDIO_MAX_DEVICE_STR_SIZE];
if (!OVR_SUCCESS(ovr_GetAudioDeviceOutGuidStr(buffer))) {
return QString();
}
return AudioClient::friendlyNameForAudioDevice(buffer);
}

View file

@ -12,20 +12,18 @@
struct SwapFramebufferWrapper; struct SwapFramebufferWrapper;
using SwapFboPtr = QSharedPointer<SwapFramebufferWrapper>; using SwapFboPtr = QSharedPointer<SwapFramebufferWrapper>;
const float TARGET_RATE_Oculus = 75.0f;
class OculusDisplayPlugin : public OculusBaseDisplayPlugin { class OculusDisplayPlugin : public OculusBaseDisplayPlugin {
using Parent = OculusBaseDisplayPlugin; using Parent = OculusBaseDisplayPlugin;
public: public:
const QString& getName() const override { return NAME; } const QString& getName() const override { return NAME; }
float getTargetFrameRate() override { return TARGET_RATE_Oculus; } QString getPreferredAudioInDevice() const override;
QString getPreferredAudioOutDevice() const override;
protected: protected:
bool internalActivate() override; bool internalActivate() override;
void hmdPresent() override; void hmdPresent() override;
// FIXME update with Oculus API call once it's available in the SDK bool isHmdMounted() const override;
bool isHmdMounted() const override { return true; }
void customizeContext() override; void customizeContext() override;
void uncustomizeContext() override; void uncustomizeContext() override;
void cycleDebugOutput() override; void cycleDebugOutput() override;

View file

@ -220,6 +220,24 @@ ApplicationWindow {
} }
} }
Button {
text: "Add Tab 2"
onClicked: {
console.log(desktop.toolWindow);
desktop.toolWindow.addWebTab({ source: "Foo 2" });
desktop.toolWindow.showTabForUrl("Foo 2", true);
}
}
Button {
text: "Add Tab 3"
onClicked: {
console.log(desktop.toolWindow);
desktop.toolWindow.addWebTab({ source: "Foo 3" });
desktop.toolWindow.showTabForUrl("Foo 3", true);
}
}
Button { Button {
text: "Destroy Tab" text: "Destroy Tab"
onClicked: { onClicked: {

View file

@ -34,20 +34,24 @@ bool vhacd::VHACDUtil::loadFBX(const QString filename, FBXGeometry& result) {
return false; return false;
} }
std::cout << "Reading FBX.....\n"; std::cout << "Reading FBX.....\n";
try {
QByteArray fbxContents = fbx.readAll();
FBXGeometry* geom;
if (filename.toLower().endsWith(".obj")) {
geom = OBJReader().readOBJ(fbxContents, QVariantHash());
} else if (filename.toLower().endsWith(".fbx")) {
geom = readFBX(fbxContents, QVariantHash(), filename);
} else {
qDebug() << "unknown file extension";
return false;
}
result = *geom;
QByteArray fbxContents = fbx.readAll(); reSortFBXGeometryMeshes(result);
FBXGeometry* geom; } catch (const QString& error) {
if (filename.toLower().endsWith(".obj")) { qDebug() << "Error reading " << filename << ": " << error;
geom = OBJReader().readOBJ(fbxContents, QVariantHash());
} else if (filename.toLower().endsWith(".fbx")) {
geom = readFBX(fbxContents, QVariantHash(), filename);
} else {
qDebug() << "unknown file extension";
return false; return false;
} }
result = *geom;
reSortFBXGeometryMeshes(result);
return true; return true;
} }