diff --git a/README.md b/README.md
index a2eb058ae6..48e0de03af 100644
--- a/README.md
+++ b/README.md
@@ -8,14 +8,14 @@ like to get paid for your work, make sure you report the bug via a job on
[Worklist.net](https://worklist.net).
We're hiring! We're looking for skilled developers;
-send your resume to hiring@highfidelity.io
+send your resume to hiring@highfidelity.com
##### Chat with us
Come chat with us in [our Gitter](http://gitter.im/highfidelity/hifi) if you have any questions or just want to say hi!
Documentation
=========
-Documentation is available at [docs.highfidelity.io](http://docs.highfidelity.io), if something is missing, please suggest it via a new job on Worklist (add to the hifi-docs project).
+Documentation is available at [docs.highfidelity.com](http://docs.highfidelity.com), if something is missing, please suggest it via a new job on Worklist (add to the hifi-docs project).
Build Instructions
=========
diff --git a/assignment-client/src/audio/AudioMixer.cpp b/assignment-client/src/audio/AudioMixer.cpp
index 779307c19d..cc6c4930ff 100644
--- a/assignment-client/src/audio/AudioMixer.cpp
+++ b/assignment-client/src/audio/AudioMixer.cpp
@@ -369,14 +369,6 @@ void AudioMixer::sendAudioEnvironmentPacket(SharedNodePointer node) {
reverbTime = _zoneReverbSettings[i].reverbTime;
wetLevel = _zoneReverbSettings[i].wetLevel;
- // Modulate wet level with distance to wall
- float MIN_ATTENUATION_DISTANCE = 2.0f;
- float MAX_ATTENUATION = -12; // dB
- glm::vec3 distanceToWalls = (box.getDimensions() / 2.0f) - glm::abs(streamPosition - box.calcCenter());
- float distanceToClosestWall = glm::min(distanceToWalls.x, distanceToWalls.z);
- if (distanceToClosestWall < MIN_ATTENUATION_DISTANCE) {
- wetLevel += MAX_ATTENUATION * (1.0f - distanceToClosestWall / MIN_ATTENUATION_DISTANCE);
- }
break;
}
}
diff --git a/cmake/externals/openvr/CMakeLists.txt b/cmake/externals/openvr/CMakeLists.txt
index 3fe7df44d0..930a339d12 100644
--- a/cmake/externals/openvr/CMakeLists.txt
+++ b/cmake/externals/openvr/CMakeLists.txt
@@ -7,8 +7,8 @@ string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER)
ExternalProject_Add(
${EXTERNAL_NAME}
- URL https://github.com/ValveSoftware/openvr/archive/v0.9.15.zip
- URL_MD5 0ff8560b49b6da1150fcc47360e8ceca
+ URL https://github.com/ValveSoftware/openvr/archive/v0.9.19.zip
+ URL_MD5 843f9dde488584d8af1f3ecf2252b4e0
CONFIGURE_COMMAND ""
BUILD_COMMAND ""
INSTALL_COMMAND ""
diff --git a/domain-server/resources/describe-settings.json b/domain-server/resources/describe-settings.json
index 80ee32efa1..44a1796a8d 100644
--- a/domain-server/resources/describe-settings.json
+++ b/domain-server/resources/describe-settings.json
@@ -308,7 +308,7 @@
"name": "reverb",
"type": "table",
"label": "Reverb Settings",
- "help": "In this table you can set reverb levels for audio zones. For a medium-sized (e.g., 100 square meter) meeting room, try a decay time of around 1.5 seconds and a wet level of -10 db. For an airplane hangar or cathedral, try a decay time of 4 seconds and a wet level of -5 db.",
+ "help": "In this table you can set reverb levels for audio zones. For a medium-sized (e.g., 100 square meter) meeting room, try a decay time of around 1.5 seconds and a wet/dry mix of 25%. For an airplane hangar or cathedral, try a decay time of 4 seconds and a wet/dry mix of 50%.",
"numbered": true,
"columns": [
{
@@ -325,9 +325,9 @@
},
{
"name": "wet_level",
- "label": "Wet Level",
+ "label": "Wet/Dry Mix",
"can_set": true,
- "placeholder": "(in db)"
+ "placeholder": "(in percent)"
}
]
}
diff --git a/examples/airship/airship.js b/examples/airship/airship.js
new file mode 100644
index 0000000000..80f37603eb
--- /dev/null
+++ b/examples/airship/airship.js
@@ -0,0 +1,301 @@
+//
+// airship.js
+//
+// Animates a pirate airship that chases people and shoots cannonballs at them
+//
+// Created by Philip Rosedale on March 7, 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
+//
+
+(function () {
+ var entityID,
+ minimumDelay = 100, // milliseconds
+ distanceScale = 2.0, // distance at which 100% chance of hopping
+ timeScale = 300.0, // crash
+ hopStrength = 0.4, // meters / second
+ spotlight = null,
+ wantDebug = false,
+ timeoutID = undefined,
+ bullet = null,
+ particles = null,
+ nearbyAvatars = 0,
+ nearbyAvatarsInRange = 0,
+ averageAvatarLocation = { x: 0, y: 0, z: 0 },
+ properties,
+ lightTimer = 0,
+ lightTimeoutID = undefined,
+ audioInjector = null;
+
+ var HIFI_PUBLIC_BUCKET = "http://s3.amazonaws.com/hifi-public/";
+ var cannonSound = SoundCache.getSound(HIFI_PUBLIC_BUCKET + "philip/cannonShot.wav");
+ var explosionSound = SoundCache.getSound(HIFI_PUBLIC_BUCKET + "philip/explosion.wav");
+
+ var NO_SHOOT_COLOR = { red: 100, green: 100, blue: 100 };
+
+ var MAX_TARGET_RANGE = 200;
+
+ function printDebug(message) {
+ if (wantDebug) {
+ print(message);
+ }
+ }
+
+ var LIGHT_UPDATE_INTERVAL = 50;
+ var LIGHT_LIFETIME = 700;
+
+ function randomVector(size) {
+ return { x: (Math.random() - 0.5) * size,
+ y: (Math.random() - 0.5) * size,
+ z: (Math.random() - 0.5) * size };
+ }
+
+ function makeLight(parent, position, colorDivisor) {
+ // Create a flickering light somewhere for a while
+ if (spotlight !== null) {
+ // light still exists, do nothing
+ printDebug("light still there");
+ return;
+ }
+ printDebug("making light");
+
+ var colorIndex = 180 + Math.random() * 50;
+
+ spotlight = Entities.addEntity({
+ type: "Light",
+ name: "Test Light",
+ intensity: 50.0,
+ falloffRadius: 20.0,
+ dimensions: {
+ x: 150,
+ y: 150,
+ z: 150
+ },
+ position: position,
+ parentID: parent,
+ color: {
+ red: colorIndex,
+ green: colorIndex / colorDivisor,
+ blue: 0
+ },
+ lifetime: LIGHT_LIFETIME * 2
+ });
+
+ lightTimer = 0;
+ lightTimeoutID = Script.setTimeout(updateLight, LIGHT_UPDATE_INTERVAL);
+
+ };
+
+ function updateLight() {
+ lightTimer += LIGHT_UPDATE_INTERVAL;
+ if ((spotlight !== null) && (lightTimer > LIGHT_LIFETIME)) {
+ printDebug("deleting light!");
+ Entities.deleteEntity(spotlight);
+ spotlight = null;
+ } else {
+ Entities.editEntity(spotlight, {
+ intensity: 5 + Math.random() * 50,
+ falloffRadius: 5 + Math.random() * 10
+ });
+ lightTimeoutID = Script.setTimeout(updateLight, LIGHT_UPDATE_INTERVAL);
+ }
+ }
+
+ function move() {
+
+ var HOVER_DISTANCE = 30.0;
+ var RUN_TOWARD_STRENGTH = 0.002;
+ var VELOCITY_FOLLOW_RATE = 0.01;
+
+ var range = Vec3.distance(properties.position, averageAvatarLocation);
+ var impulse = { x: 0, y: 0, z: 0 };
+
+ // move in the XZ plane
+ var away = Vec3.subtract(properties.position, averageAvatarLocation);
+ away.y = 0.0;
+
+ if (range > HOVER_DISTANCE) {
+ impulse = Vec3.multiply(-RUN_TOWARD_STRENGTH, Vec3.normalize(away));
+ }
+
+ var rotation = Quat.rotationBetween(Vec3.UNIT_NEG_Z, properties.velocity);
+ Entities.editEntity(entityID, {velocity: Vec3.sum(properties.velocity, impulse), rotation: Quat.mix(properties.rotation, rotation, VELOCITY_FOLLOW_RATE)});
+ }
+
+
+ function countAvatars() {
+ var workQueue = AvatarList.getAvatarIdentifiers();
+ var averageLocation = {x: 0, y: 0, z: 0};
+ var summed = 0;
+ var inRange = 0;
+ for (var i = 0; i < workQueue.length; i++) {
+ var avatar = AvatarList.getAvatar(workQueue[i]), avatarPosition = avatar && avatar.position;
+ if (avatarPosition) {
+ averageLocation = Vec3.sum(averageLocation, avatarPosition);
+ summed++;
+ if (Vec3.distance(avatarPosition, properties.position) < MAX_TARGET_RANGE) {
+ inRange++;
+ }
+ }
+ }
+ if (summed > 0) {
+ averageLocation = Vec3.multiply(1 / summed, averageLocation);
+ }
+ nearbyAvatars = summed;
+ nearbyAvatarsInRange = inRange;
+ averageAvatarLocation = averageLocation;
+
+ //printDebug(" Avatars: " + summed + "in range: " + nearbyAvatarsInRange);
+ //Vec3.print(" location: ", averageAvatarLocation);
+
+ return;
+ }
+
+ function shoot() {
+ if (bullet !== null) {
+ return;
+ }
+ if (Vec3.distance(MyAvatar.position, properties.position) > MAX_TARGET_RANGE) {
+ return;
+ }
+
+ var SPEED = 7.5;
+ var GRAVITY = -2.0;
+ var DIAMETER = 0.5;
+ var direction = Vec3.subtract(MyAvatar.position, properties.position);
+ var range = Vec3.distance(MyAvatar.position, properties.position);
+ var timeOfFlight = range / SPEED;
+
+ var fall = 0.5 * -GRAVITY * (timeOfFlight * timeOfFlight);
+
+ var velocity = Vec3.multiply(SPEED, Vec3.normalize(direction));
+ velocity.y += 0.5 * fall / timeOfFlight * 2.0;
+
+ var DISTANCE_TO_DECK = 3;
+ var bulletStart = properties.position;
+ bulletStart.y -= DISTANCE_TO_DECK;
+
+ makeLight(entityID, Vec3.sum(properties.position, randomVector(10.0)), 2);
+
+ bullet = Entities.addEntity({
+ type: "Sphere",
+ name: "cannonball",
+ position: properties.position,
+ dimensions: { x: DIAMETER, y: DIAMETER, z: DIAMETER },
+ color: { red: 10, green: 10, blue: 10 },
+ velocity: velocity,
+ damping: 0.01,
+ dynamic: true,
+ ignoreForCollisions: true,
+ gravity: { x: 0, y: GRAVITY, z: 0 },
+ lifetime: timeOfFlight * 2
+ });
+
+ Audio.playSound(cannonSound, {
+ position: Vec3.sum(MyAvatar.position, velocity),
+ volume: 1.0
+ });
+ makeParticles(properties.position, bullet, timeOfFlight * 2);
+ Script.setTimeout(explode, timeOfFlight * 1000);
+ }
+
+ function explode() {
+ var properties = Entities.getEntityProperties(bullet);
+ var direction = Vec3.normalize(Vec3.subtract(MyAvatar.position, properties.position));
+ makeLight(null, properties.position, 10);
+ Audio.playSound(explosionSound, { position: Vec3.sum(MyAvatar.position, direction), volume: 1.0 });
+ bullet = null;
+ }
+
+
+ function maybe() { // every user checks their distance and tries to claim if close enough.
+ var PROBABILITY_OF_SHOOT = 0.015;
+ properties = Entities.getEntityProperties(entityID);
+ countAvatars();
+
+ if (nearbyAvatars && (Math.random() < 1 / nearbyAvatars)) {
+ move();
+ }
+
+ if (nearbyAvatarsInRange && (Math.random() < PROBABILITY_OF_SHOOT / nearbyAvatarsInRange)) {
+ shoot();
+ }
+
+ var TIME_TO_NEXT_CHECK = 33;
+ timeoutID = Script.setTimeout(maybe, TIME_TO_NEXT_CHECK);
+ }
+
+ this.preload = function (givenEntityID) {
+ printDebug("preload airship v1...");
+
+ entityID = givenEntityID;
+ properties = Entities.getEntityProperties(entityID);
+ timeoutID = Script.setTimeout(maybe, minimumDelay);
+ };
+
+ this.unload = function () {
+ printDebug("unload airship...");
+ if (timeoutID !== undefined) {
+ Script.clearTimeout(timeoutID);
+ }
+ if (lightTimeoutID !== undefined) {
+ Script.clearTimeout(lightTimeoutID);
+ }
+ if (spotlight !== null) {
+ Entities.deleteEntity(spotlight);
+ }
+ };
+
+ function makeParticles(position, parent, lifespan) {
+ particles = Entities.addEntity({
+ type: 'ParticleEffect',
+ position: position,
+ parentID: parent,
+ color: {
+ red: 70,
+ green: 70,
+ blue: 70
+ },
+ isEmitting: 1,
+ maxParticles: 1000,
+ lifetime: lifespan,
+ lifespan: lifespan / 3,
+ emitRate: 80,
+ emitSpeed: 0,
+ speedSpread: 1.0,
+ emitRadiusStart: 1,
+ polarStart: -Math.PI/8,
+ polarFinish: Math.PI/8,
+ azimuthStart: -Math.PI/4,
+ azimuthFinish: Math.PI/4,
+ emitAcceleration: { x: 0, y: 0, z: 0 },
+ particleRadius: 0.25,
+ radiusSpread: 0.1,
+ radiusStart: 0.3,
+ radiusFinish: 0.15,
+ colorSpread: {
+ red: 100,
+ green: 100,
+ blue: 0
+ },
+ colorStart: {
+ red: 125,
+ green: 125,
+ blue: 125
+ },
+ colorFinish: {
+ red: 10,
+ green: 10,
+ blue: 10
+ },
+ alpha: 0.5,
+ alphaSpread: 0,
+ alphaStart: 1,
+ alphaFinish: 0,
+ emitterShouldTrail: true,
+ textures: 'https://hifi-public.s3.amazonaws.com/alan/Particles/Particle-Sprite-Smoke-1.png'
+ });
+ }
+})
diff --git a/examples/airship/makeAirship.js b/examples/airship/makeAirship.js
new file mode 100644
index 0000000000..8a25ac6703
--- /dev/null
+++ b/examples/airship/makeAirship.js
@@ -0,0 +1,59 @@
+// makeAirship.js
+//
+// // Created by Philip Rosedale on March 7, 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
+//
+
+
+var SIZE = 0.2;
+var TYPE = "Model"; // Right now this can be "Box" or "Model" or "Sphere"
+
+var MODEL_URL = "https://s3.amazonaws.com/hifi-public/philip/airship_compact.fbx"
+
+var MODEL_DIMENSION = { x: 19.257, y: 24.094, z: 40.3122 };
+var ENTITY_URL = "https://s3.amazonaws.com/hifi-public/scripts/airship/airship.js";
+
+
+var LIFETIME = 3600 * 48;
+
+var GRAVITY = { x: 0, y: 0, z: 0 };
+var DAMPING = 0.05;
+var ANGULAR_DAMPING = 0.01;
+
+var collidable = true;
+var gravity = true;
+
+var HOW_FAR_IN_FRONT_OF_ME = 30;
+var HOW_FAR_ABOVE_ME = 15;
+
+var leaveBehind = true;
+
+var shipLocation = Vec3.sum(MyAvatar.position, Vec3.multiply(HOW_FAR_IN_FRONT_OF_ME, Quat.getFront(Camera.orientation)));
+shipLocation.y += HOW_FAR_ABOVE_ME;
+
+
+var airship = Entities.addEntity({
+ type: TYPE,
+ modelURL: MODEL_URL,
+ name: "airship",
+ position: shipLocation,
+ dimensions: (TYPE == "Model") ? MODEL_DIMENSION : { x: SIZE, y: SIZE, z: SIZE },
+ damping: DAMPING,
+ angularDamping: ANGULAR_DAMPING,
+ gravity: (gravity ? GRAVITY : { x: 0, y: 0, z: 0}),
+ dynamic: collidable,
+ lifetime: LIFETIME,
+ animation: {url: MODEL_URL, running: true, currentFrame: 0, loop: true},
+ script: ENTITY_URL
+ });
+
+function scriptEnding() {
+ if (!leaveBehind) {
+ Entities.deleteEntity(airship);
+ }
+}
+
+Script.scriptEnding.connect(scriptEnding);
\ No newline at end of file
diff --git a/examples/html/entityProperties.html b/examples/html/entityProperties.html
index e3eb19dc4a..225a1d7957 100644
--- a/examples/html/entityProperties.html
+++ b/examples/html/entityProperties.html
@@ -451,6 +451,37 @@
var elPreviewCameraButton = document.getElementById("preview-camera-button");
+ var urlUpdaters = document.getElementsByClassName("update-url-version");
+ var PARAM_REGEXP = /(?:\?)(\S+)/; // Check if this has any parameters.
+ var TIMESTAMP_REGEXP = /(&?HFTime=\d+)/;
+
+ var refreshEvent = function(event){
+ var urlElement = event.target.parentElement.getElementsByClassName("url")[0];
+ var content = urlElement.value;
+ var date = new Date();
+ var timeStamp = date.getTime();
+
+ if(content.length > 0){
+ if(PARAM_REGEXP.test(content)){
+ // Has params, so lets remove existing definition and append again.
+ content = content.replace(TIMESTAMP_REGEXP,"") + "&";
+ }else{
+ content += "?";
+ }
+ content = content.replace("?&","?");
+ urlElement.value = content + "HFTime=" + timeStamp;
+ }
+
+ var evt = document.createEvent("HTMLEvents");
+ evt.initEvent("change", true, true );
+ urlElement.dispatchEvent(evt);
+ };
+
+ for(var index = 0; index < urlUpdaters.length; index++){
+ var urlUpdater = urlUpdaters[index];
+ urlUpdater.addEventListener("click", refreshEvent);
+ }
+
if (window.EventBridge !== undefined) {
var properties;
EventBridge.scriptEventReceived.connect(function(data) {
@@ -1185,6 +1216,7 @@
Ambient URL
@@ -1262,6 +1294,7 @@
Skybox URL
@@ -1273,6 +1306,7 @@
Source URL
@@ -1286,12 +1320,14 @@
Href - Hifi://address
@@ -1375,16 +1411,19 @@
X-axis Texture URL
Y-axis Texture URL
Z-axis Texture URL
@@ -1566,6 +1605,7 @@
Collision Sound URL
@@ -1583,6 +1623,7 @@
@@ -1595,6 +1636,7 @@
Model URL
@@ -1613,12 +1655,14 @@
Compound Shape URL
diff --git a/examples/html/eventBridgeLoader.js b/examples/html/eventBridgeLoader.js
index ebfb6dc740..de6553ee1c 100644
--- a/examples/html/eventBridgeLoader.js
+++ b/examples/html/eventBridgeLoader.js
@@ -10,50 +10,11 @@
var EventBridge;
-EventBridgeConnectionProxy = function(parent) {
- this.parent = parent;
- this.realSignal = this.parent.realBridge.scriptEventReceived
- this.webWindowId = this.parent.webWindow.windowId;
-}
-
-EventBridgeConnectionProxy.prototype.connect = function(callback) {
- var that = this;
- this.realSignal.connect(function(id, message) {
- if (id === that.webWindowId) { callback(message); }
+openEventBridge = function(callback) {
+ new QWebChannel(qt.webChannelTransport, function(channel) {
+ console.log("uid " + EventBridgeUid);
+ EventBridge = channel.objects[EventBridgeUid];
+ callback(EventBridge);
});
}
-EventBridgeProxy = function(webWindow) {
- this.webWindow = webWindow;
- this.realBridge = this.webWindow.eventBridge;
- this.scriptEventReceived = new EventBridgeConnectionProxy(this);
-}
-
-EventBridgeProxy.prototype.emitWebEvent = function(data) {
- this.realBridge.emitWebEvent(data);
-}
-
-openEventBridge = function(callback) {
- EVENT_BRIDGE_URI = "ws://localhost:51016";
- socket = new WebSocket(this.EVENT_BRIDGE_URI);
-
- socket.onclose = function() {
- console.error("web channel closed");
- };
-
- socket.onerror = function(error) {
- console.error("web channel error: " + error);
- };
-
- socket.onopen = function() {
- channel = new QWebChannel(socket, function(channel) {
- console.log("Document url is " + document.URL);
- var webWindow = channel.objects[document.URL.toLowerCase()];
- console.log("WebWindow is " + webWindow)
- eventBridgeProxy = new EventBridgeProxy(webWindow);
- EventBridge = eventBridgeProxy;
- if (callback) { callback(eventBridgeProxy); }
- });
- }
-}
-
diff --git a/examples/html/qmlWebTest.html b/examples/html/qmlWebTest.html
index e59535701d..553ce83417 100644
--- a/examples/html/qmlWebTest.html
+++ b/examples/html/qmlWebTest.html
@@ -4,21 +4,17 @@
-
diff --git a/examples/html/style.css b/examples/html/style.css
index 83982dab15..3ae1c1739b 100644
--- a/examples/html/style.css
+++ b/examples/html/style.css
@@ -134,8 +134,18 @@ textarea {
resize: vertical;
}
+.update-url-version{
+ width:17px;
+ height:17px;
+ float:right;
+ background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABEAAAARCAQAAACRZI9xAAABAUlEQVQoz33RvyvEARjH8SdlUX6UbhFJfgwmE3aTxWEiitxqN/gH7IxCKTcymLiJFIOJsihiUKcrCYnuXoZv+F45n2d4lvfz6fPpCSFs6BO1JllvLmXk5F37dC0vJ5NGmlBxoFp7Rn6RDpRRtG5KtynrijhLoBBGQcllKkFWAXsyCbKEFxeGqmJmFZFLkE639vX+12hCSVft0nUR8RbLcRN/aSHGIqL2tVYPconL32qLtaiL88Rl0qtXc+pTDv1OPGMlidtgS8Wp2RR05FEFM98/GlRQ9mTHvB7jVt2rKGPAT9xh2z6qfnToHV1SjZpN23Tl051di1oco9W/pdttaBRfEhFXOZV7vEsAAAAASUVORK5CYII=);
+ padding:0 !important;
+ margin:0 2px 0 0 !important;
+}
+
input.url {
- width: 100%;
+ width:85%;
+ padding-right: 20px;
}
input.coord {
diff --git a/examples/particle_explorer/particleExplorer.js b/examples/particle_explorer/particleExplorer.js
index 307e361ff1..4fd0978a84 100644
--- a/examples/particle_explorer/particleExplorer.js
+++ b/examples/particle_explorer/particleExplorer.js
@@ -198,6 +198,16 @@ function createColorPicker(key) {
settings[key] = colorArray;
var controller = gui.addColor(settings, key);
controller.onChange(function(value) {
+ // Handle hex colors
+ if(_.isString(value) && value[0] === '#') {
+ const BASE_HEX = 16;
+ var colorRegExResult = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(value);
+ value = [
+ parseInt(colorRegExResult[1], BASE_HEX),
+ parseInt(colorRegExResult[2], BASE_HEX),
+ parseInt(colorRegExResult[3], BASE_HEX)
+ ];
+ }
var obj = {};
obj[key] = convertColorArrayToObject(value);
writeVec3ToInterface(obj);
diff --git a/examples/tests/qmlWebTest.js b/examples/tests/qmlWebTest.js
index 5faa68668d..d29f2ba002 100644
--- a/examples/tests/qmlWebTest.js
+++ b/examples/tests/qmlWebTest.js
@@ -8,26 +8,14 @@ webWindow.eventBridge.webEventReceived.connect(function(data) {
print("JS Side event received: " + data);
});
-var titles = ["A", "B", "C"];
-var titleIndex = 0;
-
Script.setInterval(function() {
- webWindow.eventBridge.emitScriptEvent("JS Event sent");
- var size = webWindow.size;
- var position = webWindow.position;
- print("Window url: " + webWindow.url)
- print("Window visible: " + webWindow.visible)
- print("Window size: " + size.x + "x" + size.y)
- print("Window pos: " + position.x + "x" + position.y)
- webWindow.setVisible(!webWindow.visible);
- webWindow.setTitle(titles[titleIndex]);
- webWindow.setSize(320 + Math.random() * 100, 240 + Math.random() * 100);
- titleIndex += 1;
- titleIndex %= titles.length;
-}, 2 * 1000);
+ var message = [ Math.random(), Math.random() ];
+ print("JS Side sending: " + message);
+ webWindow.emitScriptEvent(message);
+}, 5 * 1000);
-Script.setTimeout(function() {
- print("Closing script");
+Script.scriptEnding.connect(function(){
webWindow.close();
- Script.stop();
-}, 15 * 1000)
+ webWindow.deleteLater();
+});
+
diff --git a/interface/resources/qml/QmlWebWindow.qml b/interface/resources/qml/QmlWebWindow.qml
index fd4e629568..0058770462 100644
--- a/interface/resources/qml/QmlWebWindow.qml
+++ b/interface/resources/qml/QmlWebWindow.qml
@@ -1,6 +1,7 @@
import QtQuick 2.3
import QtQuick.Controls 1.2
import QtWebEngine 1.1
+import QtWebChannel 1.0
import "windows" as Windows
import "controls" as Controls
@@ -15,11 +16,22 @@ Windows.Window {
// Don't destroy on close... otherwise the JS/C++ will have a dangling pointer
destroyOnCloseButton: false
property alias source: webview.url
+ property alias webChannel: webview.webChannel
+ // A unique identifier to let the HTML JS find the event bridge
+ // object (our C++ wrapper)
+ property string uid;
+
+ // This is for JS/QML communication, which is unused in a WebWindow,
+ // but not having this here results in spurious warnings about a
+ // missing signal
+ signal sendToScript(var message);
Controls.WebView {
id: webview
url: "about:blank"
anchors.fill: parent
focus: true
+ onUrlChanged: webview.runJavaScript("EventBridgeUid = \"" + uid + "\";");
+ Component.onCompleted: webview.runJavaScript("EventBridgeUid = \"" + uid + "\";");
}
} // dialog
diff --git a/interface/resources/qml/ToolWindow.qml b/interface/resources/qml/ToolWindow.qml
index 75aa50aa34..5313ca23e9 100644
--- a/interface/resources/qml/ToolWindow.qml
+++ b/interface/resources/qml/ToolWindow.qml
@@ -37,14 +37,33 @@ Windows.Window {
Repeater {
model: 4
Tab {
+ // Force loading of the content even if the tab is not visible
+ // (required for letting the C++ code access the webview)
active: true
- enabled: false;
- // we need to store the original url here for future identification
+ enabled: false
property string originalUrl: "";
- onEnabledChanged: toolWindow.updateVisiblity();
+
Controls.WebView {
id: webView;
+ // we need to store the original url here for future identification
+ // A unique identifier to let the HTML JS find the event bridge
+ // object (our C++ wrapper)
+ property string uid;
anchors.fill: parent
+ enabled: false
+
+ // This is for JS/QML communication, which is unused in a WebWindow,
+ // but not having this here results in spurious warnings about a
+ // missing signal
+ signal sendToScript(var message);
+
+ onUrlChanged: webView.runJavaScript("EventBridgeUid = \"" + uid + "\";");
+ onEnabledChanged: toolWindow.updateVisiblity();
+ onLoadingChanged: {
+ if (loadRequest.status == WebEngineView.LoadSucceededStatus) {
+ webView.runJavaScript("EventBridgeUid = \"" + uid + "\";");
+ }
+ }
}
}
}
@@ -113,20 +132,23 @@ Windows.Window {
var tab = tabView.getTab(index);
tab.title = "";
- tab.originalUrl = "";
tab.enabled = false;
+ tab.originalUrl = "";
+ tab.item.url = "about:blank";
+ tab.item.enabled = false;
}
function addWebTab(properties) {
if (!properties.source) {
- console.warn("Attempted to open Web Tool Pane without URL")
+ console.warn("Attempted to open Web Tool Pane without URL");
return;
}
var existingTabIndex = findIndexForUrl(properties.source);
if (existingTabIndex >= 0) {
- console.log("Existing tab " + existingTabIndex + " found with URL " + properties.source)
- return tabView.getTab(existingTabIndex);
+ console.log("Existing tab " + existingTabIndex + " found with URL " + properties.source);
+ var tab = tabView.getTab(existingTabIndex);
+ return tab.item;
}
var freeTabIndex = findFreeTab();
@@ -135,25 +157,22 @@ Windows.Window {
return;
}
- var newTab = tabView.getTab(freeTabIndex);
- newTab.title = properties.title || "Unknown";
- newTab.originalUrl = properties.source;
- newTab.item.url = properties.source;
- newTab.active = true;
-
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);
}
if (properties.height) {
- tabView.height = Math.min(Math.max(tabView.height, properties.height),
- toolWindow.maxSize.y);
+ tabView.height = Math.min(Math.max(tabView.height, properties.height), toolWindow.maxSize.y);
}
- console.log("Updating visibility based on child tab added");
- newTab.enabledChanged.connect(updateVisiblity)
- updateVisiblity();
- return newTab
+ var tab = tabView.getTab(freeTabIndex);
+ tab.title = properties.title || "Unknown";
+ tab.enabled = true;
+ tab.originalUrl = properties.source;
+
+ var result = tab.item;
+ result.enabled = true;
+ result.url = properties.source;
+ return result;
}
}
diff --git a/interface/resources/qml/controls-uit/ComboBox.qml b/interface/resources/qml/controls-uit/ComboBox.qml
old mode 100644
new mode 100755
index 888e8ccbc1..d25a8c5940
--- a/interface/resources/qml/controls-uit/ComboBox.qml
+++ b/interface/resources/qml/controls-uit/ComboBox.qml
@@ -162,6 +162,30 @@ FocusScope {
height: 480
width: root.width + 4
+ style: ScrollViewStyle {
+ decrementControl: Item {
+ visible: false
+ }
+ incrementControl: Item {
+ visible: false
+ }
+ scrollBarBackground: Rectangle{
+ implicitWidth: 14
+ color: hifi.colors.baseGray
+ }
+
+ handle:
+ Rectangle {
+ implicitWidth: 8
+ anchors.left: parent.left
+ anchors.leftMargin: 3
+ anchors.top: parent.top
+ anchors.bottom: parent.bottom
+ radius: 3
+ color: hifi.colors.lightGrayText
+ }
+ }
+
ListView {
id: listView
height: textField.height * count * 1.4
diff --git a/interface/resources/qml/controls-uit/SpinBox.qml b/interface/resources/qml/controls-uit/SpinBox.qml
old mode 100644
new mode 100755
index 5dac1710f0..ec7821851f
--- a/interface/resources/qml/controls-uit/SpinBox.qml
+++ b/interface/resources/qml/controls-uit/SpinBox.qml
@@ -35,7 +35,6 @@ SpinBox {
style: SpinBoxStyle {
id: spinStyle
background: Rectangle {
- id: backgrondRec
color: isLightColorScheme
? (spinBox.focus ? hifi.colors.white : hifi.colors.lightGray)
: (spinBox.focus ? hifi.colors.black : hifi.colors.baseGrayShadow)
@@ -91,4 +90,27 @@ SpinBox {
color: spinBox.colorLabelInside
visible: spinBox.labelInside != ""
}
+
+ MouseArea {
+ anchors.fill: parent
+ propagateComposedEvents: true
+ onWheel: {
+ if(spinBox.focus)
+ wheel.accepted = false
+ else
+ wheel.accepted = true
+ }
+ onPressed: {
+ mouse.accepted = false
+ }
+ onReleased: {
+ mouse.accepted = false
+ }
+ onClicked: {
+ mouse.accepted = false
+ }
+ onDoubleClicked: {
+ mouse.accepted = false
+ }
+ }
}
diff --git a/interface/resources/qml/controls/WebView.qml b/interface/resources/qml/controls/WebView.qml
index 18080cd448..1361e6e322 100644
--- a/interface/resources/qml/controls/WebView.qml
+++ b/interface/resources/qml/controls/WebView.qml
@@ -59,6 +59,7 @@ WebEngineView {
request.openIn(newWindow.webView)
}
-
- profile: desktop.browserProfile
+ // This breaks the webchannel used for passing messages. Fixed in Qt 5.6
+ // See https://bugreports.qt.io/browse/QTBUG-49521
+ //profile: desktop.browserProfile
}
diff --git a/interface/resources/qml/hifi/dialogs/AttachmentsDialog.qml b/interface/resources/qml/hifi/dialogs/AttachmentsDialog.qml
old mode 100644
new mode 100755
index f82ef8d86c..01be4ddbde
--- a/interface/resources/qml/hifi/dialogs/AttachmentsDialog.qml
+++ b/interface/resources/qml/hifi/dialogs/AttachmentsDialog.qml
@@ -4,18 +4,14 @@ import QtQuick.Dialogs 1.2 as OriginalDialogs
import Qt.labs.settings 1.0
import QtQuick.Controls.Styles 1.4
-//import "../../windows"
-
-
import "../../styles-uit"
import "../../controls-uit" as HifiControls
import "../../windows-uit"
-
import "attachments"
Window {
id: root
- title: "Attachments Dialog"
+ title: "Attachments"
objectName: "AttachmentsDialog"
width: 600
height: 600
@@ -43,6 +39,7 @@ Window {
listView.model.append({});
}
}
+
Column {
width: pane.contentWidth
@@ -55,14 +52,22 @@ Window {
Rectangle {
id: attachmentsBackground
anchors { left: parent.left; right: parent.right; top: parent.top; bottom: newAttachmentButton.top; margins: 8 }
- color: hifi.colors.lightGrayText
+ color: hifi.colors.baseGrayShadow
radius: 4
- ScrollView{
+ ScrollView {
id: scrollView
anchors.fill: parent
anchors.margins: 4
+
style: ScrollViewStyle {
+
+ padding {
+ top: 0
+ right: 0
+ bottom: 0
+ }
+
decrementControl: Item {
visible: false
}
@@ -72,16 +77,29 @@ Window {
scrollBarBackground: Rectangle{
implicitWidth: 14
color: hifi.colors.baseGray
- }
+ radius: 4
+ Rectangle {
+ // Make top left corner of scrollbar appear square
+ width: 8
+ height: 4
+ color: hifi.colors.baseGray
+ anchors.top: parent.top
+ anchors.horizontalCenter: parent.left
+ }
+ }
handle:
Rectangle {
implicitWidth: 8
- anchors.left: parent.left
- anchors.leftMargin: 3
- anchors.top: parent.top
- anchors.bottom: parent.bottom
- radius: 3
+ anchors {
+ left: parent.left
+ leftMargin: 3
+ top: parent.top
+ topMargin: 3
+ bottom: parent.bottom
+ bottomMargin: 4
+ }
+ radius: 4
color: hifi.colors.lightGrayText
}
}
@@ -90,8 +108,9 @@ Window {
id: listView
model: ListModel {}
delegate: Item {
+ id: attachmentDelegate
implicitHeight: attachmentView.height + 8;
- implicitWidth: attachmentView.width;
+ implicitWidth: attachmentView.width
Attachment {
id: attachmentView
width: scrollView.width
@@ -113,7 +132,7 @@ Window {
anchors { left: parent.left; right: parent.right; bottom: buttonRow.top; margins: 8 }
text: "New Attachment"
color: hifi.buttons.black
- colorScheme: hifi.colorSchemes.dark
+ colorScheme: hifi.colorSchemes.dark
onClicked: {
var template = {
modelUrl: "",
@@ -133,15 +152,15 @@ Window {
id: buttonRow
spacing: 8
anchors { right: parent.right; bottom: parent.bottom; margins: 8 }
- HifiControls.Button {
- action: cancelAction
- color: hifi.buttons.black
- colorScheme: hifi.colorSchemes.dark
- }
HifiControls.Button {
action: okAction
color: hifi.buttons.black
- colorScheme: hifi.colorSchemes.dark
+ colorScheme: hifi.colorSchemes.dark
+ }
+ HifiControls.Button {
+ action: cancelAction
+ color: hifi.buttons.black
+ colorScheme: hifi.colorSchemes.dark
}
}
diff --git a/interface/resources/qml/hifi/dialogs/attachments/Attachment.qml b/interface/resources/qml/hifi/dialogs/attachments/Attachment.qml
old mode 100644
new mode 100755
index c88e991ed3..1277c459ce
--- a/interface/resources/qml/hifi/dialogs/attachments/Attachment.qml
+++ b/interface/resources/qml/hifi/dialogs/attachments/Attachment.qml
@@ -4,11 +4,8 @@ import QtQuick.Controls.Styles 1.4
import QtQuick.Dialogs 1.2 as OriginalDialogs
import Qt.labs.settings 1.0
-//import "../../../windows"
-//import "../../../controls" as VrControls
import "."
import ".."
-
import "../../../styles-uit"
import "../../../controls-uit" as HifiControls
import "../../../windows-uit"
@@ -33,13 +30,13 @@ Item {
Column {
y: 8
id: column
- anchors { left: parent.left; right: parent.right; margins: 8 }
+ anchors { left: parent.left; right: parent.right; margins: 20 }
spacing: 8
Item {
- height: modelChooserButton.height + urlLabel.height
+ height: modelChooserButton.height + urlLabel.height + 4
anchors { left: parent.left; right: parent.right;}
- Text { id: urlLabel; color: hifi.colors.lightGrayText; text: "Model URL:"; width: 80; anchors.top: parent.top;}
+ HifiControls.Label { id: urlLabel; color: hifi.colors.lightGrayText; text: "Model URL"; anchors.top: parent.top;}
HifiControls.TextField {
id: modelUrl;
height: jointChooser.height;
@@ -60,12 +57,12 @@ Item {
colorScheme: hifi.colorSchemes.dark
anchors { right: parent.right; verticalCenter: modelUrl.verticalCenter }
Component {
- id: modelBrowserBuiler;
+ id: modelBrowserBuilder;
ModelBrowserDialog {}
}
onClicked: {
- var browser = modelBrowserBuiler.createObject(desktop);
+ var browser = modelBrowserBuilder.createObject(desktop);
browser.selected.connect(function(newModelUrl){
modelUrl.text = newModelUrl;
})
@@ -74,12 +71,11 @@ Item {
}
Item {
- height: jointChooser.height + jointLabel.height
+ height: jointChooser.height + jointLabel.height + 4
anchors { left: parent.left; right: parent.right; }
- Text {
+ HifiControls.Label {
id: jointLabel;
- width: 80;
- text: "Joint:";
+ text: "Joint";
color: hifi.colors.lightGrayText;
anchors.top: parent.top
}
@@ -99,9 +95,9 @@ Item {
}
Item {
- height: translation.height + translationLabel.height
+ height: translation.height + translationLabel.height + 4
anchors { left: parent.left; right: parent.right; }
- Text { id: translationLabel; width: 80; color: hifi.colors.lightGrayText; text: "Translation:"; anchors.top: parent.top; }
+ HifiControls.Label { id: translationLabel; color: hifi.colors.lightGrayText; text: "Translation"; anchors.top: parent.top; }
Translation {
id: translation;
anchors { left: parent.left; right: parent.right; bottom: parent.bottom}
@@ -116,9 +112,9 @@ Item {
}
Item {
- height: rotation.height + rotationLabel.height
+ height: rotation.height + rotationLabel.height + 4
anchors { left: parent.left; right: parent.right; }
- Text { id: rotationLabel; width: 80; color: hifi.colors.lightGrayText; text: "Rotation:"; anchors.top: parent.top; }
+ HifiControls.Label { id: rotationLabel; color: hifi.colors.lightGrayText; text: "Rotation"; anchors.top: parent.top; }
Rotation {
id: rotation;
anchors { left: parent.left; right: parent.right; bottom: parent.bottom; }
@@ -133,45 +129,58 @@ Item {
}
Item {
- height: scaleSpinner.height + scaleLabel.height
+ height: scaleItem.height
anchors { left: parent.left; right: parent.right; }
- Text { id: scaleLabel; width: 80; color: hifi.colors.lightGrayText; text: "Scale:"; anchors.top: parent.top; }
- HifiControls.SpinBox {
- id: scaleSpinner;
- anchors { left: parent.left; right: parent.right; bottom: parent.bottom; }
- decimals: 1;
- minimumValue: 0.1
- maximumValue: 10
- stepSize: 0.1;
- value: attachment ? attachment.scale : 1.0
- colorScheme: hifi.colorSchemes.dark
- onValueChanged: {
- if (completed && attachment && attachment.scale !== value) {
- attachment.scale = value;
- updateAttachment();
+
+ Item {
+ id: scaleItem
+ height: scaleSpinner.height + scaleLabel.height + 4
+ width: parent.width / 3 - 8
+ anchors { right: parent.right; }
+ HifiControls.Label { id: scaleLabel; color: hifi.colors.lightGrayText; text: "Scale"; anchors.top: parent.top; }
+ HifiControls.SpinBox {
+ id: scaleSpinner;
+ anchors { left: parent.left; right: parent.right; bottom: parent.bottom; }
+ decimals: 1;
+ minimumValue: 0.1
+ maximumValue: 10
+ stepSize: 0.1;
+ value: attachment ? attachment.scale : 1.0
+ colorScheme: hifi.colorSchemes.dark
+ onValueChanged: {
+ if (completed && attachment && attachment.scale !== value) {
+ attachment.scale = value;
+ updateAttachment();
+ }
+ }
+ }
+ }
+
+ Item {
+ id: isSoftItem
+ height: scaleSpinner.height
+ anchors {
+ left: parent.left
+ bottom: parent.bottom
+ }
+ HifiControls.CheckBox {
+ id: soft
+ text: "Is soft"
+ anchors {
+ left: parent.left
+ verticalCenter: parent.verticalCenter
+ }
+ checked: attachment ? attachment.soft : false
+ colorScheme: hifi.colorSchemes.dark
+ onCheckedChanged: {
+ if (completed && attachment && attachment.soft !== checked) {
+ attachment.soft = checked;
+ updateAttachment();
+ }
}
}
}
}
-
- Item {
- height: soft.height
- anchors { left: parent.left; right: parent.right; }
- Text { id: softLabel; width: 80; color: hifi.colors.lightGrayText; text: "Is soft"; anchors.left: soft.right; anchors.leftMargin: 8; }
- HifiControls.CheckBox {
- id: soft;
- anchors { left: parent.left; bottom: parent.bottom;}
- checked: attachment ? attachment.soft : false
- colorScheme: hifi.colorSchemes.dark
- onCheckedChanged: {
- if (completed && attachment && attachment.soft !== checked) {
- attachment.soft = checked;
- updateAttachment();
- }
- }
- }
- }
-
HifiControls.Button {
color: hifi.buttons.black
colorScheme: hifi.colorSchemes.dark
diff --git a/interface/resources/qml/hifi/dialogs/attachments/Translation.qml b/interface/resources/qml/hifi/dialogs/attachments/Translation.qml
index f3a90cdf94..39ac6da55a 100644
--- a/interface/resources/qml/hifi/dialogs/attachments/Translation.qml
+++ b/interface/resources/qml/hifi/dialogs/attachments/Translation.qml
@@ -1,7 +1,7 @@
import "."
Vector3 {
- decimals: 2;
+ decimals: 3;
stepSize: 0.01;
maximumValue: 10
minimumValue: -10
diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp
index 65a8f83871..b9effa444b 100644
--- a/interface/src/Application.cpp
+++ b/interface/src/Application.cpp
@@ -195,6 +195,7 @@ static const QString FBX_EXTENSION = ".fbx";
static const QString OBJ_EXTENSION = ".obj";
static const QString AVA_JSON_EXTENSION = ".ava.json";
+static const int MSECS_PER_SEC = 1000;
static const int MIRROR_VIEW_TOP_PADDING = 5;
static const int MIRROR_VIEW_LEFT_PADDING = 10;
static const int MIRROR_VIEW_WIDTH = 265;
@@ -239,7 +240,7 @@ class DeadlockWatchdogThread : public QThread {
public:
static const unsigned long HEARTBEAT_CHECK_INTERVAL_SECS = 1;
static const unsigned long HEARTBEAT_UPDATE_INTERVAL_SECS = 1;
- static const unsigned long MAX_HEARTBEAT_AGE_USECS = 10 * USECS_PER_SECOND;
+ static const unsigned long MAX_HEARTBEAT_AGE_USECS = 15 * USECS_PER_SECOND;
// Set the heartbeat on launch
DeadlockWatchdogThread() {
@@ -365,7 +366,7 @@ bool setupEssentials(int& argc, char** argv) {
Setting::preInit();
- CrashHandler::checkForAndHandleCrash();
+ bool previousSessionCrashed = CrashHandler::checkForAndHandleCrash();
CrashHandler::writeRunningMarkerFiler();
qAddPostRoutine(CrashHandler::deleteRunningMarkerFile);
@@ -427,7 +428,7 @@ bool setupEssentials(int& argc, char** argv) {
DependencyManager::set
();
DependencyManager::set(true, qApp, qApp);
DependencyManager::set();
- return true;
+ return previousSessionCrashed;
}
// FIXME move to header, or better yet, design some kind of UI manager
@@ -448,10 +449,13 @@ PluginContainer* _pluginContainer;
OffscreenGLCanvas* _chromiumShareContext { nullptr };
Q_GUI_EXPORT void qt_gl_set_global_share_context(QOpenGLContext *context);
+Setting::Handle sessionRunTime{ "sessionRunTime", 0 };
+
Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) :
QApplication(argc, argv),
_window(new MainWindow(desktop())),
- _dependencyManagerIsSetup(setupEssentials(argc, argv)),
+ _sessionRunTimer(startupTimer),
+ _previousSessionCrashed(setupEssentials(argc, argv)),
_undoStackScriptingInterface(&_undoStack),
_frameCount(0),
_fps(60.0f),
@@ -601,7 +605,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) :
connect(&domainHandler, SIGNAL(disconnectedFromDomain()), SLOT(clearDomainOctreeDetails()));
// update our location every 5 seconds in the metaverse server, assuming that we are authenticated with one
- const qint64 DATA_SERVER_LOCATION_CHANGE_UPDATE_MSECS = 5 * 1000;
+ const qint64 DATA_SERVER_LOCATION_CHANGE_UPDATE_MSECS = 5 * MSECS_PER_SEC;
auto discoverabilityManager = DependencyManager::get();
connect(&locationUpdateTimer, &QTimer::timeout, discoverabilityManager.data(), &DiscoverabilityManager::updateLocation);
@@ -623,7 +627,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) :
// connect to appropriate slots on AccountManager
AccountManager& accountManager = AccountManager::getInstance();
- const qint64 BALANCE_UPDATE_INTERVAL_MSECS = 5 * 1000;
+ const qint64 BALANCE_UPDATE_INTERVAL_MSECS = 5 * MSECS_PER_SEC;
connect(&balanceUpdateTimer, &QTimer::timeout, &accountManager, &AccountManager::updateBalance);
balanceUpdateTimer.start(BALANCE_UPDATE_INTERVAL_MSECS);
@@ -638,7 +642,9 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) :
accountManager.setIsAgent(true);
accountManager.setAuthURL(NetworkingConstants::METAVERSE_SERVER_URL);
- UserActivityLogger::getInstance().launch(applicationVersion());
+ // sessionRunTime will be reset soon by loadSettings. Grab it now to get previous session value.
+ // The value will be 0 if the user blew away settings this session, which is both a feature and a bug.
+ UserActivityLogger::getInstance().launch(applicationVersion(), _previousSessionCrashed, sessionRunTime.get());
// once the event loop has started, check and signal for an access token
QMetaObject::invokeMethod(&accountManager, "checkAndSignalForAccessToken", Qt::QueuedConnection);
@@ -1409,7 +1415,7 @@ void Application::paintGL() {
_lastFramesPerSecondUpdate = now;
}
- PROFILE_RANGE(__FUNCTION__);
+ PROFILE_RANGE_EX(__FUNCTION__, 0xff0000ff, (uint64_t)_frameCount);
PerformanceTimer perfTimer("paintGL");
if (nullptr == _displayPlugin) {
@@ -2548,11 +2554,12 @@ void Application::idle(uint64_t now) {
return;
}
+ PROFILE_RANGE(__FUNCTION__);
+
// We're going to execute idle processing, so restart the last idle timer
_lastTimeUpdated.start();
{
- PROFILE_RANGE(__FUNCTION__);
static uint64_t lastIdleStart{ now };
uint64_t idleStartToStartDuration = now - lastIdleStart;
if (idleStartToStartDuration != 0) {
@@ -2804,6 +2811,7 @@ bool Application::exportEntities(const QString& filename, float x, float y, floa
void Application::loadSettings() {
+ sessionRunTime.set(0); // Just clean living. We're about to saveSettings, which will update value.
DependencyManager::get()->loadSettings();
DependencyManager::get()->loadSettings();
@@ -2817,6 +2825,7 @@ void Application::loadSettings() {
}
void Application::saveSettings() {
+ sessionRunTime.set(_sessionRunTimer.elapsed() / MSECS_PER_SEC);
DependencyManager::get()->saveSettings();
DependencyManager::get()->saveSettings();
@@ -3138,6 +3147,9 @@ void Application::updateDialogs(float deltaTime) {
}
void Application::update(float deltaTime) {
+
+ PROFILE_RANGE_EX(__FUNCTION__, 0xffff0000, (uint64_t)_frameCount + 1);
+
bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings);
PerformanceWarning warn(showWarnings, "Application::update()");
@@ -3228,7 +3240,9 @@ void Application::update(float deltaTime) {
controller::Pose leftHandPose = userInputMapper->getPoseState(controller::Action::LEFT_HAND);
controller::Pose rightHandPose = userInputMapper->getPoseState(controller::Action::RIGHT_HAND);
auto myAvatarMatrix = createMatFromQuatAndPos(myAvatar->getOrientation(), myAvatar->getPosition());
- myAvatar->setHandControllerPosesInWorldFrame(leftHandPose.transform(myAvatarMatrix), rightHandPose.transform(myAvatarMatrix));
+ auto worldToSensorMatrix = glm::inverse(myAvatar->getSensorToWorldMatrix());
+ auto avatarToSensorMatrix = worldToSensorMatrix * myAvatarMatrix;
+ myAvatar->setHandControllerPosesInSensorFrame(leftHandPose.transform(avatarToSensorMatrix), rightHandPose.transform(avatarToSensorMatrix));
updateThreads(deltaTime); // If running non-threaded, then give the threads some time to process...
updateDialogs(deltaTime); // update various stats dialogs if present
@@ -3236,9 +3250,13 @@ void Application::update(float deltaTime) {
QSharedPointer avatarManager = DependencyManager::get();
if (_physicsEnabled) {
+ PROFILE_RANGE_EX("Physics", 0xffff0000, (uint64_t)getActiveDisplayPlugin()->presentCount());
+
PerformanceTimer perfTimer("physics");
{
+ PROFILE_RANGE_EX("UpdateStats", 0xffffff00, (uint64_t)getActiveDisplayPlugin()->presentCount());
+
PerformanceTimer perfTimer("updateStates)");
static VectorOfMotionStates motionStates;
_entitySimulation.getObjectsToRemoveFromPhysics(motionStates);
@@ -3271,12 +3289,14 @@ void Application::update(float deltaTime) {
});
}
{
+ PROFILE_RANGE_EX("StepSimulation", 0xffff8000, (uint64_t)getActiveDisplayPlugin()->presentCount());
PerformanceTimer perfTimer("stepSimulation");
getEntities()->getTree()->withWriteLock([&] {
_physicsEngine->stepSimulation();
});
}
{
+ PROFILE_RANGE_EX("HarvestChanges", 0xffffff00, (uint64_t)getActiveDisplayPlugin()->presentCount());
PerformanceTimer perfTimer("havestChanges");
if (_physicsEngine->hasOutgoingChanges()) {
getEntities()->getTree()->withWriteLock([&] {
@@ -3311,14 +3331,24 @@ void Application::update(float deltaTime) {
qApp->setAvatarSimrateSample(1.0f / deltaTime);
- avatarManager->updateOtherAvatars(deltaTime);
+ {
+ PROFILE_RANGE_EX("OtherAvatars", 0xffff00ff, (uint64_t)getActiveDisplayPlugin()->presentCount());
+ avatarManager->updateOtherAvatars(deltaTime);
+ }
qApp->updateMyAvatarLookAtPosition();
- avatarManager->updateMyAvatar(deltaTime);
+ // update sensorToWorldMatrix for camera and hand controllers
+ myAvatar->updateSensorToWorldMatrix();
+
+ {
+ PROFILE_RANGE_EX("MyAvatar", 0xffff00ff, (uint64_t)getActiveDisplayPlugin()->presentCount());
+ avatarManager->updateMyAvatar(deltaTime);
+ }
}
{
+ PROFILE_RANGE_EX("Overlays", 0xffff0000, (uint64_t)getActiveDisplayPlugin()->presentCount());
PerformanceTimer perfTimer("overlays");
_overlays.update(deltaTime);
}
@@ -3338,6 +3368,7 @@ void Application::update(float deltaTime) {
// Update my voxel servers with my current voxel query...
{
+ PROFILE_RANGE_EX("QueryOctree", 0xffff0000, (uint64_t)getActiveDisplayPlugin()->presentCount());
PerformanceTimer perfTimer("queryOctree");
quint64 sinceLastQuery = now - _lastQueriedTime;
const quint64 TOO_LONG_SINCE_LAST_QUERY = 3 * USECS_PER_SECOND;
@@ -3374,9 +3405,6 @@ void Application::update(float deltaTime) {
QMetaObject::invokeMethod(DependencyManager::get().data(), "sendDownstreamAudioStatsPacket", Qt::QueuedConnection);
}
}
-
- // update sensorToWorldMatrix for rendering camera.
- myAvatar->updateSensorToWorldMatrix();
}
@@ -4667,13 +4695,18 @@ qreal Application::getDevicePixelRatio() {
}
DisplayPlugin* Application::getActiveDisplayPlugin() {
- std::unique_lock lock(_displayPluginLock);
- if (nullptr == _displayPlugin && QThread::currentThread() == thread()) {
- updateDisplayMode();
- Q_ASSERT(_displayPlugin);
+ DisplayPlugin* result = nullptr;
+ if (QThread::currentThread() == thread()) {
+ if (nullptr == _displayPlugin) {
+ updateDisplayMode();
+ Q_ASSERT(_displayPlugin);
+ }
+ result = _displayPlugin.get();
+ } else {
+ std::unique_lock lock(_displayPluginLock);
+ result = _displayPlugin.get();
}
-
- return _displayPlugin.get();
+ return result;
}
const DisplayPlugin* Application::getActiveDisplayPlugin() const {
@@ -4791,20 +4824,26 @@ void Application::updateDisplayMode() {
return;
}
- if (_displayPlugin) {
- _displayPlugin->deactivate();
- }
-
auto offscreenUi = DependencyManager::get();
- // FIXME probably excessive and useless context switching
- _offscreenContext->makeCurrent();
- newDisplayPlugin->activate();
- _offscreenContext->makeCurrent();
- offscreenUi->resize(fromGlm(newDisplayPlugin->getRecommendedUiSize()));
- _offscreenContext->makeCurrent();
- getApplicationCompositor().setDisplayPlugin(newDisplayPlugin);
- _displayPlugin = newDisplayPlugin;
+ // Make the switch atomic from the perspective of other threads
+ {
+ std::unique_lock lock(_displayPluginLock);
+
+ if (_displayPlugin) {
+ _displayPlugin->deactivate();
+ }
+
+ // FIXME probably excessive and useless context switching
+ _offscreenContext->makeCurrent();
+ newDisplayPlugin->activate();
+ _offscreenContext->makeCurrent();
+ offscreenUi->resize(fromGlm(newDisplayPlugin->getRecommendedUiSize()));
+ _offscreenContext->makeCurrent();
+ getApplicationCompositor().setDisplayPlugin(newDisplayPlugin);
+ _displayPlugin = newDisplayPlugin;
+ }
+
emit activeDisplayPluginChanged();
diff --git a/interface/src/Application.h b/interface/src/Application.h
index 3b906a7d17..d21e647bc7 100644
--- a/interface/src/Application.h
+++ b/interface/src/Application.h
@@ -377,12 +377,13 @@ private:
void maybeToggleMenuVisible(QMouseEvent* event);
MainWindow* _window;
+ QElapsedTimer& _sessionRunTimer;
- bool _dependencyManagerIsSetup;
+ bool _previousSessionCrashed;
OffscreenGLCanvas* _offscreenContext { nullptr };
DisplayPluginPointer _displayPlugin;
- std::recursive_mutex _displayPluginLock;
+ std::mutex _displayPluginLock;
InputPluginList _activeInputPlugins;
bool _activatingDisplayPlugin { false };
diff --git a/interface/src/CrashHandler.cpp b/interface/src/CrashHandler.cpp
index 5a194973b4..50cca15c88 100644
--- a/interface/src/CrashHandler.cpp
+++ b/interface/src/CrashHandler.cpp
@@ -27,7 +27,7 @@
static const QString RUNNING_MARKER_FILENAME = "Interface.running";
-void CrashHandler::checkForAndHandleCrash() {
+bool CrashHandler::checkForAndHandleCrash() {
QFile runningMarkerFile(runningMarkerFilePath());
if (runningMarkerFile.exists()) {
QSettings::setDefaultFormat(QSettings::IniFormat);
@@ -42,7 +42,9 @@ void CrashHandler::checkForAndHandleCrash() {
handleCrash(action);
}
}
+ return true;
}
+ return false;
}
CrashHandler::Action CrashHandler::promptUserForAction() {
diff --git a/interface/src/CrashHandler.h b/interface/src/CrashHandler.h
index 61361b6107..18c935b595 100644
--- a/interface/src/CrashHandler.h
+++ b/interface/src/CrashHandler.h
@@ -17,7 +17,7 @@
class CrashHandler {
public:
- static void checkForAndHandleCrash();
+ static bool checkForAndHandleCrash();
static void writeRunningMarkerFiler();
static void deleteRunningMarkerFile();
diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp
index 35bc2aa696..63c67f6b9f 100644
--- a/interface/src/Menu.cpp
+++ b/interface/src/Menu.cpp
@@ -107,7 +107,7 @@ Menu::Menu() {
auto scriptEngines = DependencyManager::get();
// Edit > Stop All Scripts... [advanced]
- addActionToQMenuAndActionHash(editMenu, MenuOption::StopAllScripts, 0,
+ addActionToQMenuAndActionHash(editMenu, MenuOption::StopAllScripts, 0,
scriptEngines.data(), SLOT(stopAllScripts()),
QAction::NoRole, UNSPECIFIED_POSITION, "Advanced");
@@ -140,7 +140,7 @@ Menu::Menu() {
// Edit > Reload All Content [advanced]
addActionToQMenuAndActionHash(editMenu, MenuOption::ReloadContent, 0, qApp, SLOT(reloadResourceCaches()),
QAction::NoRole, UNSPECIFIED_POSITION, "Advanced");
-
+
// Edit > Package Model... [advanced]
addActionToQMenuAndActionHash(editMenu, MenuOption::PackageModel, 0,
@@ -153,7 +153,7 @@ Menu::Menu() {
auto audioIO = DependencyManager::get();
// Audio > Mute
- addCheckableActionToQMenuAndActionHash(audioMenu, MenuOption::MuteAudio, Qt::CTRL | Qt::Key_M, false,
+ addCheckableActionToQMenuAndActionHash(audioMenu, MenuOption::MuteAudio, Qt::CTRL | Qt::Key_M, false,
audioIO.data(), SLOT(toggleMute()));
// Audio > Show Level Meter
@@ -458,7 +458,7 @@ Menu::Menu() {
avatar, SLOT(setEnableMeshVisible(bool)));
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::DisableEyelidAdjustment, 0, false);
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::TurnWithHead, 0, false);
- addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::UseAnimPreAndPostRotations, 0, false,
+ addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::UseAnimPreAndPostRotations, 0, true,
avatar, SLOT(setUseAnimPreAndPostRotations(bool)));
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::EnableInverseKinematics, 0, true,
avatar, SLOT(setEnableInverseKinematics(bool)));
@@ -534,7 +534,7 @@ Menu::Menu() {
// Developer > Audio >>>
MenuWrapper* audioDebugMenu = developerMenu->addMenu("Audio");
- addCheckableActionToQMenuAndActionHash(audioDebugMenu, MenuOption::AudioNoiseReduction, 0, true,
+ addCheckableActionToQMenuAndActionHash(audioDebugMenu, MenuOption::AudioNoiseReduction, 0, true,
audioIO.data(), SLOT(toggleAudioNoiseReduction()));
addCheckableActionToQMenuAndActionHash(audioDebugMenu, MenuOption::EchoServerAudio, 0, false,
audioIO.data(), SLOT(toggleServerEcho()));
@@ -617,7 +617,7 @@ Menu::Menu() {
QAction::NoRole, UNSPECIFIED_POSITION, "Advanced");
- addCheckableActionToQMenuAndActionHash(avatarMenu, MenuOption::NamesAboveHeads, 0, true,
+ addCheckableActionToQMenuAndActionHash(avatarMenu, MenuOption::NamesAboveHeads, 0, true,
NULL, NULL, UNSPECIFIED_POSITION, "Advanced");
#endif
}
@@ -651,7 +651,7 @@ void Menu::addMenuItem(const MenuItemProperties& properties) {
} else if (properties.isCheckable) {
menuItemAction = addCheckableActionToQMenuAndActionHash(menuObj, properties.menuItemName,
properties.shortcutKeySequence, properties.isChecked,
- MenuScriptingInterface::getInstance(), SLOT(menuItemTriggered()),
+ MenuScriptingInterface::getInstance(), SLOT(menuItemTriggered()),
requestedPosition, properties.grouping);
} else {
menuItemAction = addActionToQMenuAndActionHash(menuObj, properties.menuItemName, properties.shortcutKeySequence,
diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp
index 7b63b7fc5a..f722210a8e 100644
--- a/interface/src/avatar/Avatar.cpp
+++ b/interface/src/avatar/Avatar.cpp
@@ -187,7 +187,7 @@ void Avatar::simulate(float deltaTime) {
// simple frustum check
float boundingRadius = getBoundingRadius();
- bool inView = qApp->getViewFrustum()->sphereIntersectsFrustum(getPosition(), boundingRadius);
+ bool inView = qApp->getDisplayViewFrustum()->sphereIntersectsFrustum(getPosition(), boundingRadius);
if (_shouldAnimate && !_shouldSkipRender && inView) {
{
diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp
index 4405204b47..8f11c635e9 100644
--- a/interface/src/avatar/MyAvatar.cpp
+++ b/interface/src/avatar/MyAvatar.cpp
@@ -418,7 +418,7 @@ void MyAvatar::updateFromHMDSensorMatrix(const glm::mat4& hmdSensorMatrix) {
_hmdSensorFacing = getFacingDir2D(_hmdSensorOrientation);
}
-// best called at end of main loop, just before rendering.
+// best called at end of main loop, after physics.
// update sensor to world matrix from current body position and hmd sensor.
// This is so the correct camera can be used for rendering.
void MyAvatar::updateSensorToWorldMatrix() {
@@ -1087,24 +1087,32 @@ static controller::Pose applyLowVelocityFilter(const controller::Pose& oldPose,
return finalPose;
}
-void MyAvatar::setHandControllerPosesInWorldFrame(const controller::Pose& left, const controller::Pose& right) {
+void MyAvatar::setHandControllerPosesInSensorFrame(const controller::Pose& left, const controller::Pose& right) {
if (controller::InputDevice::getLowVelocityFilter()) {
- auto oldLeftPose = getLeftHandControllerPoseInWorldFrame();
- auto oldRightPose = getRightHandControllerPoseInWorldFrame();
- _leftHandControllerPoseInWorldFrameCache.set(applyLowVelocityFilter(oldLeftPose, left));
- _rightHandControllerPoseInWorldFrameCache.set(applyLowVelocityFilter(oldRightPose, right));
+ auto oldLeftPose = getLeftHandControllerPoseInSensorFrame();
+ auto oldRightPose = getRightHandControllerPoseInSensorFrame();
+ _leftHandControllerPoseInSensorFrameCache.set(applyLowVelocityFilter(oldLeftPose, left));
+ _rightHandControllerPoseInSensorFrameCache.set(applyLowVelocityFilter(oldRightPose, right));
} else {
- _leftHandControllerPoseInWorldFrameCache.set(left);
- _rightHandControllerPoseInWorldFrameCache.set(right);
+ _leftHandControllerPoseInSensorFrameCache.set(left);
+ _rightHandControllerPoseInSensorFrameCache.set(right);
}
}
+controller::Pose MyAvatar::getLeftHandControllerPoseInSensorFrame() const {
+ return _leftHandControllerPoseInSensorFrameCache.get();
+}
+
+controller::Pose MyAvatar::getRightHandControllerPoseInSensorFrame() const {
+ return _rightHandControllerPoseInSensorFrameCache.get();
+}
+
controller::Pose MyAvatar::getLeftHandControllerPoseInWorldFrame() const {
- return _leftHandControllerPoseInWorldFrameCache.get();
+ return _leftHandControllerPoseInSensorFrameCache.get().transform(getSensorToWorldMatrix());
}
controller::Pose MyAvatar::getRightHandControllerPoseInWorldFrame() const {
- return _rightHandControllerPoseInWorldFrameCache.get();
+ return _rightHandControllerPoseInSensorFrameCache.get().transform(getSensorToWorldMatrix());
}
controller::Pose MyAvatar::getLeftHandControllerPoseInAvatarFrame() const {
diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h
index 3554d9b8bc..92bf9e7614 100644
--- a/interface/src/avatar/MyAvatar.h
+++ b/interface/src/avatar/MyAvatar.h
@@ -247,7 +247,9 @@ public:
virtual void rebuildCollisionShape() override;
- void setHandControllerPosesInWorldFrame(const controller::Pose& left, const controller::Pose& right);
+ void setHandControllerPosesInSensorFrame(const controller::Pose& left, const controller::Pose& right);
+ controller::Pose getLeftHandControllerPoseInSensorFrame() const;
+ controller::Pose getRightHandControllerPoseInSensorFrame() const;
controller::Pose getLeftHandControllerPoseInWorldFrame() const;
controller::Pose getRightHandControllerPoseInWorldFrame() const;
controller::Pose getLeftHandControllerPoseInAvatarFrame() const;
@@ -451,9 +453,9 @@ private:
bool _hoverReferenceCameraFacingIsCaptured { false };
glm::vec3 _hoverReferenceCameraFacing { 0.0f, 0.0f, -1.0f }; // hmd sensor space
- // These are stored in WORLD frame
- ThreadSafeValueCache _leftHandControllerPoseInWorldFrameCache { controller::Pose() };
- ThreadSafeValueCache _rightHandControllerPoseInWorldFrameCache { controller::Pose() };
+ // These are stored in SENSOR frame
+ ThreadSafeValueCache _leftHandControllerPoseInSensorFrameCache { controller::Pose() };
+ ThreadSafeValueCache _rightHandControllerPoseInSensorFrameCache { controller::Pose() };
float AVATAR_MOVEMENT_ENERGY_CONSTANT { 0.001f };
float AUDIO_ENERGY_CONSTANT { 0.000001f };
diff --git a/libraries/animation/src/AnimClip.cpp b/libraries/animation/src/AnimClip.cpp
index e8f429f22c..a5747e4f96 100644
--- a/libraries/animation/src/AnimClip.cpp
+++ b/libraries/animation/src/AnimClip.cpp
@@ -13,7 +13,7 @@
#include "AnimationLogging.h"
#include "AnimUtil.h"
-bool AnimClip::usePreAndPostPoseFromAnim = false;
+bool AnimClip::usePreAndPostPoseFromAnim = true;
AnimClip::AnimClip(const QString& id, const QString& url, float startFrame, float endFrame, float timeScale, bool loopFlag, bool mirrorFlag) :
AnimNode(AnimNode::Type::Clip, id),
diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp
index ae9adc71c2..a2b664d064 100644
--- a/libraries/animation/src/Rig.cpp
+++ b/libraries/animation/src/Rig.cpp
@@ -20,6 +20,7 @@
#include
#include
#include
+#include
#include "AnimationLogging.h"
#include "AnimClip.h"
@@ -852,6 +853,8 @@ void Rig::updateAnimationStateHandlers() { // called on avatar update thread (wh
void Rig::updateAnimations(float deltaTime, glm::mat4 rootTransform) {
+ PROFILE_RANGE_EX(__FUNCTION__, 0xffff00ff, 0);
+
setModelOffset(rootTransform);
if (_animNode) {
diff --git a/libraries/audio-client/src/AudioClient.cpp b/libraries/audio-client/src/AudioClient.cpp
index 4d44a771f7..7e01196dc7 100644
--- a/libraries/audio-client/src/AudioClient.cpp
+++ b/libraries/audio-client/src/AudioClient.cpp
@@ -565,10 +565,10 @@ void AudioClient::updateReverbOptions() {
_zoneReverbOptions.setReverbTime(_receivedAudioStream.getRevebTime());
reverbChanged = true;
}
- //if (_zoneReverbOptions.getWetLevel() != _receivedAudioStream.getWetLevel()) {
- // _zoneReverbOptions.setWetLevel(_receivedAudioStream.getWetLevel());
- // reverbChanged = true;
- //}
+ if (_zoneReverbOptions.getWetDryMix() != _receivedAudioStream.getWetLevel()) {
+ _zoneReverbOptions.setWetDryMix(_receivedAudioStream.getWetLevel());
+ reverbChanged = true;
+ }
if (_reverbOptions != &_zoneReverbOptions) {
_reverbOptions = &_zoneReverbOptions;
diff --git a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp
index ca36ab35f0..d842dc553b 100644
--- a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp
+++ b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp
@@ -21,7 +21,7 @@
#include
#include
#include
-
+#include
#include
#include
#include
@@ -404,7 +404,11 @@ void OpenGLDisplayPlugin::submitOverlayTexture(const gpu::TexturePointer& overla
void OpenGLDisplayPlugin::updateTextures() {
// FIXME intrduce a GPU wait instead of a CPU/GPU sync point?
+#if THREADED_PRESENT
if (_sceneTextureEscrow.fetchSignaledAndRelease(_currentSceneTexture)) {
+#else
+ if (_sceneTextureEscrow.fetchAndReleaseWithGpuWait(_currentSceneTexture)) {
+#endif
updateFrameData();
}
@@ -527,6 +531,9 @@ void OpenGLDisplayPlugin::internalPresent() {
void OpenGLDisplayPlugin::present() {
incrementPresentCount();
+
+ PROFILE_RANGE_EX(__FUNCTION__, 0xff00ff00, (uint64_t)presentCount())
+
updateTextures();
if (_currentSceneTexture) {
// Write all layers to a local framebuffer
diff --git a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.h b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.h
index 501232f7e7..7295b07ad3 100644
--- a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.h
+++ b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.h
@@ -17,9 +17,9 @@
#include
#include
#include
-#include
#define THREADED_PRESENT 1
+#include
class OpenGLDisplayPlugin : public DisplayPlugin {
protected:
diff --git a/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.cpp
index c3782b907f..b022b10887 100644
--- a/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.cpp
+++ b/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.cpp
@@ -19,6 +19,7 @@
#include
#include
#include
+#include
#include "../Logging.h"
#include "../CompositorHelper.h"
@@ -106,6 +107,9 @@ void HmdDisplayPlugin::compositePointer() {
}
void HmdDisplayPlugin::internalPresent() {
+
+ PROFILE_RANGE_EX(__FUNCTION__, 0xff00ff00, (uint64_t)presentCount())
+
// Composite together the scene, overlay and mouse cursor
hmdPresent();
@@ -149,6 +153,8 @@ void HmdDisplayPlugin::internalPresent() {
});
swapBuffers();
}
+
+ postPreview();
}
void HmdDisplayPlugin::setEyeRenderPose(uint32_t frameIndex, Eye eye, const glm::mat4& pose) {
diff --git a/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.h b/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.h
index 659a3a16fa..fede16c3a5 100644
--- a/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.h
+++ b/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.h
@@ -31,6 +31,7 @@ public:
protected:
virtual void hmdPresent() = 0;
virtual bool isHmdMounted() const = 0;
+ virtual void postPreview() {};
void internalActivate() override;
void compositeOverlay() override;
diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp
index ff4ed28150..98559a56a4 100644
--- a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp
+++ b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp
@@ -113,14 +113,18 @@ QVariantMap RenderableModelEntityItem::parseTexturesToMap(QString textures) {
return _originalTexturesMap;
}
- QString jsonTextures = "{\"" + textures.replace(":\"", "\":\"").replace(",\n", ",\"") + "}";
+ // Legacy: a ,\n-delimited list of filename:"texturepath"
+ if (*textures.cbegin() != '{') {
+ textures = "{\"" + textures.replace(":\"", "\":\"").replace(",\n", ",\"") + "}";
+ }
+
QJsonParseError error;
- QJsonDocument texturesAsJson = QJsonDocument::fromJson(jsonTextures.toUtf8(), &error);
+ QJsonDocument texturesJson = QJsonDocument::fromJson(textures.toUtf8(), &error);
if (error.error != QJsonParseError::NoError) {
qCWarning(entitiesrenderer) << "Could not evaluate textures property value:" << _textures;
+ return _originalTexturesMap;
}
- QJsonObject texturesAsJsonObject = texturesAsJson.object();
- return texturesAsJsonObject.toVariantMap();
+ return texturesJson.object().toVariantMap();
}
void RenderableModelEntityItem::remapTextures() {
diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.h b/libraries/entities-renderer/src/RenderableModelEntityItem.h
index 03226342da..069b7385b5 100644
--- a/libraries/entities-renderer/src/RenderableModelEntityItem.h
+++ b/libraries/entities-renderer/src/RenderableModelEntityItem.h
@@ -38,12 +38,6 @@ public:
EntityPropertyFlags& propertyFlags, bool overwriteLocalData,
bool& somethingChanged) override;
- virtual void somethingChangedNotification() override {
- // FIX ME: this is overly aggressive. We only really need to simulate() if something about
- // the world space transform has changed and/or if some animation is occurring.
- _needsInitialSimulation = true;
- }
-
virtual bool readyToAddToScene(RenderArgs* renderArgs = nullptr);
virtual bool addToScene(EntityItemPointer self, std::shared_ptr scene, render::PendingChanges& pendingChanges) override;
virtual void removeFromScene(EntityItemPointer self, std::shared_ptr scene, render::PendingChanges& pendingChanges) override;
diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp
index 21919922b9..bbc94e9910 100644
--- a/libraries/entities/src/EntityItem.cpp
+++ b/libraries/entities/src/EntityItem.cpp
@@ -516,7 +516,7 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef
EntityTreePointer tree = getTree();
if (tree && tree->isDeletedEntity(_id)) {
#ifdef WANT_DEBUG
- qDebug() << "Recieved packet for previously deleted entity [" << _id << "] ignoring. "
+ qDebug() << "Received packet for previously deleted entity [" << _id << "] ignoring. "
"(inside " << __FUNCTION__ << ")";
#endif
ignoreServerPacket = true;
diff --git a/libraries/entities/src/EntityTreeElement.cpp b/libraries/entities/src/EntityTreeElement.cpp
index 6f9880171b..7f87c4e0b1 100644
--- a/libraries/entities/src/EntityTreeElement.cpp
+++ b/libraries/entities/src/EntityTreeElement.cpp
@@ -956,8 +956,10 @@ int EntityTreeElement::readElementDataFromBuffer(const unsigned char* data, int
entityItem->recordCreationTime();
}
} else {
- qDebug() << "Recieved packet for previously deleted entity [" <<
+ #ifdef WANT_DEBUG
+ qDebug() << "Received packet for previously deleted entity [" <<
entityItem->getID() << "] ignoring. (inside " << __FUNCTION__ << ")";
+ #endif
}
}
}
diff --git a/libraries/fbx/src/FBXReader.cpp b/libraries/fbx/src/FBXReader.cpp
index de8c001566..65acedfc96 100644
--- a/libraries/fbx/src/FBXReader.cpp
+++ b/libraries/fbx/src/FBXReader.cpp
@@ -1102,8 +1102,9 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS
diffuseTextures.insert(getID(connection.properties, 2), getID(connection.properties, 1));
} else if (type.contains("tex_color_map")) {
diffuseTextures.insert(getID(connection.properties, 2), getID(connection.properties, 1));
- } else if (type.contains("transparentcolor")) { // it should be TransparentColor...
- // THis is how Maya assign a texture that affect diffuse color AND transparency ?
+ } else if (type.contains("transparentcolor")) { // Maya way of passing TransparentMap
+ transparentTextures.insert(getID(connection.properties, 2), getID(connection.properties, 1));
+ } else if (type.contains("transparencyfactor")) { // Blender way of passing TransparentMap
transparentTextures.insert(getID(connection.properties, 2), getID(connection.properties, 1));
} else if (type.contains("bump")) {
bumpTextures.insert(getID(connection.properties, 2), getID(connection.properties, 1));
diff --git a/libraries/fbx/src/FBXReader_Material.cpp b/libraries/fbx/src/FBXReader_Material.cpp
index fb272a1af9..11c6dad2f2 100644
--- a/libraries/fbx/src/FBXReader_Material.cpp
+++ b/libraries/fbx/src/FBXReader_Material.cpp
@@ -102,16 +102,20 @@ void FBXReader::consolidateFBXMaterials() {
detectDifferentUVs = (diffuseTexture.texcoordSet != 0) || (!diffuseTexture.transform.isIdentity());
}
-
FBXTexture transparentTexture;
QString transparentTextureID = transparentTextures.value(material.materialID);
+ // If PBS Material, systematically bind the albedo texture as transparency texture and check for the alpha channel
+ if (material.isPBSMaterial) {
+ transparentTextureID = diffuseTextureID;
+ }
if (!transparentTextureID.isNull()) {
transparentTexture = getTexture(transparentTextureID);
-
material.opacityTexture = transparentTexture;
detectDifferentUVs |= (transparentTexture.texcoordSet != 0) || (!transparentTexture.transform.isIdentity());
}
+
+
FBXTexture normalTexture;
QString bumpTextureID = bumpTextures.value(material.materialID);
QString normalTextureID = normalTextures.value(material.materialID);
diff --git a/libraries/gl/src/gl/OffscreenQmlSurface.cpp b/libraries/gl/src/gl/OffscreenQmlSurface.cpp
index d3699b54e6..e224adad07 100644
--- a/libraries/gl/src/gl/OffscreenQmlSurface.cpp
+++ b/libraries/gl/src/gl/OffscreenQmlSurface.cpp
@@ -23,6 +23,7 @@
#include
#include
#include
+#include
#include "OffscreenGLCanvas.h"
#include "GLEscrow.h"
@@ -84,6 +85,7 @@ protected:
Queue _queue;
QMutex _mutex;
QWaitCondition _waitCondition;
+ std::atomic _rendering { false };
private:
// Event-driven methods
@@ -271,15 +273,25 @@ void OffscreenQmlRenderThread::resize() {
}
void OffscreenQmlRenderThread::render() {
- if (_surface->_paused) {
+ // Ensure we always release the main thread
+ Finally releaseMainThread([this] {
_waitCondition.wakeOne();
+ });
+
+ if (_surface->_paused) {
return;
}
- QMutexLocker locker(&_mutex);
- _renderControl->sync();
- _waitCondition.wakeOne();
- locker.unlock();
+ _rendering = true;
+ Finally unmarkRenderingFlag([this] {
+ _rendering = false;
+ });
+
+ {
+ QMutexLocker locker(&_mutex);
+ _renderControl->sync();
+ releaseMainThread.trigger();
+ }
using namespace oglplus;
@@ -292,6 +304,7 @@ void OffscreenQmlRenderThread::render() {
_fbo->AttachTexture(Framebuffer::Target::Draw, FramebufferAttachment::Color, *texture, 0);
_fbo->Complete(Framebuffer::Target::Draw);
{
+ PROFILE_RANGE("qml_render->rendercontrol")
_renderControl->render();
// FIXME The web browsers seem to be leaving GL in an error state.
// Need a debug context with sync logging to figure out why.
@@ -380,8 +393,6 @@ void OffscreenQmlSurface::resize(const QSize& newSize_) {
std::max(static_cast(scale * newSize.height()), 10));
}
-
-
QSize currentSize = _renderer->_quickWindow->geometry().size();
if (newSize == currentSize) {
return;
@@ -492,7 +503,12 @@ QObject* OffscreenQmlSurface::finishQmlLoad(std::functionallowNewFrame(_maxFps)) {
+ // If we're
+ // a) not set up
+ // b) already rendering a frame
+ // c) rendering too fast
+ // then skip this
+ if (!_renderer || _renderer->_rendering || !_renderer->allowNewFrame(_maxFps)) {
return;
}
@@ -502,11 +518,11 @@ void OffscreenQmlSurface::updateQuick() {
}
if (_render) {
+ PROFILE_RANGE(__FUNCTION__);
// Lock the GUI size while syncing
QMutexLocker locker(&(_renderer->_mutex));
_renderer->_queue.add(RENDER);
_renderer->_waitCondition.wait(&(_renderer->_mutex));
-
_render = false;
}
diff --git a/libraries/model-networking/src/model-networking/ModelCache.cpp b/libraries/model-networking/src/model-networking/ModelCache.cpp
index 86aa20fa1c..b0b769d5e9 100644
--- a/libraries/model-networking/src/model-networking/ModelCache.cpp
+++ b/libraries/model-networking/src/model-networking/ModelCache.cpp
@@ -139,7 +139,7 @@ bool NetworkGeometry::isLoadedWithTextures() const {
}
if (!_isLoadedWithTextures) {
- _hasTransparentTextures = true;
+ _hasTransparentTextures = false;
for (auto&& material : _materials) {
if ((material->albedoTexture && !material->albedoTexture->isLoaded()) ||
@@ -152,12 +152,11 @@ bool NetworkGeometry::isLoadedWithTextures() const {
return false;
}
if (material->albedoTexture && material->albedoTexture->getGPUTexture()) {
- // Reset the materialKey transparentTexture key only, as it is albedoTexture-dependent
+ // Reassign the texture to make sure that itsalbedo alpha channel material key is detected correctly
+ material->_material->setTextureMap(model::MaterialKey::ALBEDO_MAP, material->_material->getTextureMap(model::MaterialKey::ALBEDO_MAP));
const auto& usage = material->albedoTexture->getGPUTexture()->getUsage();
bool isTransparentTexture = usage.isAlpha() && !usage.isAlphaMask();
- material->_material->setTransparentTexture(isTransparentTexture);
- // FIXME: Materials with *some* transparent textures seem to give all *other* textures alphas of 0.
- _hasTransparentTextures = isTransparentTexture && _hasTransparentTextures;
+ _hasTransparentTextures |= isTransparentTexture;
}
}
@@ -177,9 +176,9 @@ void NetworkGeometry::setTextureWithNameToURL(const QString& name, const QUrl& u
auto albedoMap = model::TextureMapPointer(new model::TextureMap());
albedoMap->setTextureSource(material->albedoTexture->_textureSource);
- albedoMap->setTextureTransform(
- oldTextureMaps[model::MaterialKey::ALBEDO_MAP]->getTextureTransform());
-
+ albedoMap->setTextureTransform(oldTextureMaps[model::MaterialKey::ALBEDO_MAP]->getTextureTransform());
+ // when reassigning the albedo texture we also check for the alpha channel used as opacity
+ albedoMap->setUseAlphaChannel(true);
networkMaterial->setTextureMap(model::MaterialKey::ALBEDO_MAP, albedoMap);
} else if (material->normalTextureName == name) {
material->normalTexture = textureCache->getTexture(url);
@@ -212,10 +211,10 @@ void NetworkGeometry::setTextureWithNameToURL(const QString& name, const QUrl& u
networkMaterial->setTextureMap(model::MaterialKey::EMISSIVE_MAP, emissiveMap);
} else if (material->lightmapTextureName == name) {
- material->emissiveTexture = textureCache->getTexture(url, LIGHTMAP_TEXTURE);
+ material->lightmapTexture = textureCache->getTexture(url, LIGHTMAP_TEXTURE);
auto lightmapMap = model::TextureMapPointer(new model::TextureMap());
- lightmapMap->setTextureSource(material->emissiveTexture->_textureSource);
+ lightmapMap->setTextureSource(material->lightmapTexture->_textureSource);
lightmapMap->setTextureTransform(
oldTextureMaps[model::MaterialKey::LIGHTMAP_MAP]->getTextureTransform());
glm::vec2 oldOffsetScale =
@@ -380,9 +379,20 @@ static NetworkMaterial* buildNetworkMaterial(NetworkGeometry* geometry, const FB
auto albedoMap = setupNetworkTextureMap(geometry, textureBaseUrl, material.albedoTexture, DEFAULT_TEXTURE,
networkMaterial->albedoTexture, networkMaterial->albedoTextureName);
albedoMap->setTextureTransform(material.albedoTexture.transform);
+
+ if (!material.opacityTexture.filename.isEmpty()) {
+ if (material.albedoTexture.filename == material.opacityTexture.filename) {
+ // Best case scenario, just indicating that the albedo map contains transparency
+ albedoMap->setUseAlphaChannel(true);
+ } else {
+ // Opacity Map is different from the Abledo map, not supported
+ }
+ }
+
material._material->setTextureMap(model::MaterialKey::ALBEDO_MAP, albedoMap);
}
+
if (!material.normalTexture.filename.isEmpty()) {
auto normalMap = setupNetworkTextureMap(geometry, textureBaseUrl, material.normalTexture,
(material.normalTexture.isBumpmap ? BUMP_TEXTURE : NORMAL_TEXTURE),
diff --git a/libraries/model-networking/src/model-networking/ModelCache.h b/libraries/model-networking/src/model-networking/ModelCache.h
index 7f01bdafaa..1c76a0b878 100644
--- a/libraries/model-networking/src/model-networking/ModelCache.h
+++ b/libraries/model-networking/src/model-networking/ModelCache.h
@@ -199,7 +199,6 @@ public:
QSharedPointer occlusionTexture;
QString lightmapTextureName;
QSharedPointer lightmapTexture;
-
};
diff --git a/libraries/model-networking/src/model-networking/TextureCache.cpp b/libraries/model-networking/src/model-networking/TextureCache.cpp
index 58a82d5f11..28f4882b86 100644
--- a/libraries/model-networking/src/model-networking/TextureCache.cpp
+++ b/libraries/model-networking/src/model-networking/TextureCache.cpp
@@ -320,13 +320,12 @@ void ImageReader::run() {
}
QMetaObject::invokeMethod(texture.data(), "setImage",
- Q_ARG(const QImage&, image),
Q_ARG(void*, theTexture),
Q_ARG(int, originalWidth), Q_ARG(int, originalHeight));
QThread::currentThread()->setPriority(originalPriority);
}
-void NetworkTexture::setImage(const QImage& image, void* voidTexture, int originalWidth,
+void NetworkTexture::setImage(void* voidTexture, int originalWidth,
int originalHeight) {
_originalWidth = originalWidth;
_originalHeight = originalHeight;
diff --git a/libraries/model-networking/src/model-networking/TextureCache.h b/libraries/model-networking/src/model-networking/TextureCache.h
index 6fb0cc3177..858a40de36 100644
--- a/libraries/model-networking/src/model-networking/TextureCache.h
+++ b/libraries/model-networking/src/model-networking/TextureCache.h
@@ -136,7 +136,7 @@ protected:
Q_INVOKABLE void loadContent(const QByteArray& content);
// FIXME: This void* should be a gpu::Texture* but i cannot get it to work for now, moving on...
- Q_INVOKABLE void setImage(const QImage& image, void* texture, int originalWidth, int originalHeight);
+ Q_INVOKABLE void setImage(void* texture, int originalWidth, int originalHeight);
private:
diff --git a/libraries/model/src/model/Material.cpp b/libraries/model/src/model/Material.cpp
index 306867c204..0a6a4bb695 100755
--- a/libraries/model/src/model/Material.cpp
+++ b/libraries/model/src/model/Material.cpp
@@ -51,7 +51,7 @@ void Material::setEmissive(const Color& emissive, bool isSRGB) {
}
void Material::setOpacity(float opacity) {
- _key.setTransparent((opacity < 1.0f));
+ _key.setTranslucentFactor((opacity < 1.0f));
_schemaBuffer.edit()._key = (uint32)_key._flags.to_ulong();
_schemaBuffer.edit()._opacity = opacity;
}
@@ -80,19 +80,52 @@ void Material::setMetallic(float metallic) {
_schemaBuffer.edit()._metallic = metallic;
}
-void Material::setTransparentTexture(bool isTransparent) {
- _key.setTransparentTexture(isTransparent);
- _schemaBuffer.edit()._key = (uint32)_key._flags.to_ulong();
-}
-
void Material::setTextureMap(MapChannel channel, const TextureMapPointer& textureMap) {
if (textureMap) {
_key.setMapChannel(channel, (true));
- _schemaBuffer.edit()._key = (uint32)_key._flags.to_ulong();
+
+ if (channel == MaterialKey::ALBEDO_MAP) {
+ // clear the previous flags whatever they were:
+ _key.setOpacityMaskMap(false);
+ _key.setTranslucentMap(false);
+
+ if (textureMap->useAlphaChannel() && textureMap->isDefined() && textureMap->getTextureView().isValid()) {
+ auto usage = textureMap->getTextureView()._texture->getUsage();
+ if (usage.isAlpha()) {
+ // Texture has alpha, is not just a mask or a true transparent channel
+ if (usage.isAlphaMask()) {
+ _key.setOpacityMaskMap(true);
+ _key.setTranslucentMap(false);
+ } else {
+ _key.setOpacityMaskMap(false);
+ _key.setTranslucentMap(true);
+ }
+ }
+ }
+ }
+
_textureMaps[channel] = textureMap;
} else {
_key.setMapChannel(channel, (false));
- _schemaBuffer.edit()._key = (uint32)_key._flags.to_ulong();
+
+ if (channel == MaterialKey::ALBEDO_MAP) {
+ _key.setOpacityMaskMap(false);
+ _key.setTranslucentMap(false);
+ }
+
_textureMaps.erase(channel);
}
+
+ _schemaBuffer.edit()._key = (uint32)_key._flags.to_ulong();
+
+}
+
+
+const TextureMapPointer Material::getTextureMap(MapChannel channel) const {
+ auto result = _textureMaps.find(channel);
+ if (result != _textureMaps.end()) {
+ return (result->second);
+ } else {
+ return TextureMapPointer();
+ }
}
diff --git a/libraries/model/src/model/Material.h b/libraries/model/src/model/Material.h
index 078fc6499a..5a7b919994 100755
--- a/libraries/model/src/model/Material.h
+++ b/libraries/model/src/model/Material.h
@@ -31,14 +31,15 @@ public:
ALBEDO_VAL_BIT,
METALLIC_VAL_BIT,
GLOSSY_VAL_BIT,
- TRANSPARENT_VAL_BIT,
- TRANSPARENT_TEX_VAL_BIT,
+ OPACITY_VAL_BIT,
+ OPACITY_MASK_MAP_BIT, // OPacity Map and Opacity MASK map are mutually exclusive
+ OPACITY_TRANSLUCENT_MAP_BIT,
+ // THe map bits must be in the smae sequence as the enum names for the map channels
EMISSIVE_MAP_BIT,
ALBEDO_MAP_BIT,
METALLIC_MAP_BIT,
ROUGHNESS_MAP_BIT,
- TRANSPARENT_MAP_BIT,
NORMAL_MAP_BIT,
OCCLUSION_MAP_BIT,
LIGHTMAP_MAP_BIT,
@@ -52,7 +53,6 @@ public:
ALBEDO_MAP,
METALLIC_MAP,
ROUGHNESS_MAP,
- TRANSPARENT_MAP,
NORMAL_MAP,
OCCLUSION_MAP,
LIGHTMAP_MAP,
@@ -77,13 +77,15 @@ public:
Builder& withAlbedo() { _flags.set(ALBEDO_VAL_BIT); return (*this); }
Builder& withMetallic() { _flags.set(METALLIC_VAL_BIT); return (*this); }
Builder& withGlossy() { _flags.set(GLOSSY_VAL_BIT); return (*this); }
- Builder& withTransparent() { _flags.set(TRANSPARENT_VAL_BIT); return (*this); }
+ Builder& withTranslucentFactor() { _flags.set(OPACITY_VAL_BIT); return (*this); }
Builder& withEmissiveMap() { _flags.set(EMISSIVE_MAP_BIT); return (*this); }
Builder& withAlbedoMap() { _flags.set(ALBEDO_MAP_BIT); return (*this); }
Builder& withMetallicMap() { _flags.set(METALLIC_MAP_BIT); return (*this); }
Builder& withRoughnessMap() { _flags.set(ROUGHNESS_MAP_BIT); return (*this); }
- Builder& withTransparentMap() { _flags.set(TRANSPARENT_MAP_BIT); return (*this); }
+
+ Builder& withTranslucentMap() { _flags.set(OPACITY_TRANSLUCENT_MAP_BIT); return (*this); }
+ Builder& withMaskMap() { _flags.set(OPACITY_MASK_MAP_BIT); return (*this); }
Builder& withNormalMap() { _flags.set(NORMAL_MAP_BIT); return (*this); }
Builder& withOcclusionMap() { _flags.set(OCCLUSION_MAP_BIT); return (*this); }
@@ -102,9 +104,6 @@ public:
void setAlbedo(bool value) { _flags.set(ALBEDO_VAL_BIT, value); }
bool isAlbedo() const { return _flags[ALBEDO_VAL_BIT]; }
- void setTransparentTexture(bool value) { _flags.set(TRANSPARENT_TEX_VAL_BIT, value); }
- bool isTransparentTexture() const { return _flags[TRANSPARENT_TEX_VAL_BIT]; }
-
void setAlbedoMap(bool value) { _flags.set(ALBEDO_MAP_BIT, value); }
bool isAlbedoMap() const { return _flags[ALBEDO_MAP_BIT]; }
@@ -121,13 +120,15 @@ public:
void setRoughnessMap(bool value) { _flags.set(ROUGHNESS_MAP_BIT, value); }
bool isRoughnessMap() const { return _flags[ROUGHNESS_MAP_BIT]; }
- void setTransparent(bool value) { _flags.set(TRANSPARENT_VAL_BIT, value); }
- bool isTransparent() const { return _flags[TRANSPARENT_VAL_BIT]; }
- bool isOpaque() const { return !_flags[TRANSPARENT_VAL_BIT]; }
+ void setTranslucentFactor(bool value) { _flags.set(OPACITY_VAL_BIT, value); }
+ bool isTranslucentFactor() const { return _flags[OPACITY_VAL_BIT]; }
- void setTransparentMap(bool value) { _flags.set(TRANSPARENT_MAP_BIT, value); }
- bool isTransparentMap() const { return _flags[TRANSPARENT_MAP_BIT]; }
+ void setTranslucentMap(bool value) { _flags.set(OPACITY_TRANSLUCENT_MAP_BIT, value); }
+ bool isTranslucentMap() const { return _flags[OPACITY_TRANSLUCENT_MAP_BIT]; }
+ void setOpacityMaskMap(bool value) { _flags.set(OPACITY_MASK_MAP_BIT, value); }
+ bool isOpacityMaskMap() const { return _flags[OPACITY_MASK_MAP_BIT]; }
+
void setNormalMap(bool value) { _flags.set(NORMAL_MAP_BIT, value); }
bool isNormalMap() const { return _flags[NORMAL_MAP_BIT]; }
@@ -140,6 +141,12 @@ public:
void setMapChannel(MapChannel channel, bool value) { _flags.set(EMISSIVE_MAP_BIT + channel, value); }
bool isMapChannel(MapChannel channel) const { return _flags[EMISSIVE_MAP_BIT + channel]; }
+
+ // Translucency and Opacity Heuristics are combining several flags:
+ bool isTranslucent() const { return isTranslucentFactor() || isTranslucentMap(); }
+ bool isOpaque() const { return !isTranslucent(); }
+ bool isSurfaceOpaque() const { return isOpaque() && !isOpacityMaskMap(); }
+ bool isTexelOpaque() const { return isOpaque() && isOpacityMaskMap(); }
};
@@ -168,9 +175,6 @@ public:
Builder& withoutAlbedo() { _value.reset(MaterialKey::ALBEDO_VAL_BIT); _mask.set(MaterialKey::ALBEDO_VAL_BIT); return (*this); }
Builder& withAlbedo() { _value.set(MaterialKey::ALBEDO_VAL_BIT); _mask.set(MaterialKey::ALBEDO_VAL_BIT); return (*this); }
- Builder& withoutTransparentTexture() { _value.reset(MaterialKey::TRANSPARENT_TEX_VAL_BIT); _mask.set(MaterialKey::TRANSPARENT_TEX_VAL_BIT); return (*this); }
- Builder& withTransparentTexture() { _value.set(MaterialKey::TRANSPARENT_TEX_VAL_BIT); _mask.set(MaterialKey::TRANSPARENT_TEX_VAL_BIT); return (*this); }
-
Builder& withoutAlbedoMap() { _value.reset(MaterialKey::ALBEDO_MAP_BIT); _mask.set(MaterialKey::ALBEDO_MAP_BIT); return (*this); }
Builder& withAlbedoMap() { _value.set(MaterialKey::ALBEDO_MAP_BIT); _mask.set(MaterialKey::ALBEDO_MAP_BIT); return (*this); }
@@ -186,11 +190,14 @@ public:
Builder& withoutRoughnessMap() { _value.reset(MaterialKey::ROUGHNESS_MAP_BIT); _mask.set(MaterialKey::ROUGHNESS_MAP_BIT); return (*this); }
Builder& withRoughnessMap() { _value.set(MaterialKey::ROUGHNESS_MAP_BIT); _mask.set(MaterialKey::ROUGHNESS_MAP_BIT); return (*this); }
- Builder& withoutTransparent() { _value.reset(MaterialKey::TRANSPARENT_VAL_BIT); _mask.set(MaterialKey::TRANSPARENT_VAL_BIT); return (*this); }
- Builder& withTransparent() { _value.set(MaterialKey::TRANSPARENT_VAL_BIT); _mask.set(MaterialKey::TRANSPARENT_VAL_BIT); return (*this); }
+ Builder& withoutTranslucentFactor() { _value.reset(MaterialKey::OPACITY_VAL_BIT); _mask.set(MaterialKey::OPACITY_VAL_BIT); return (*this); }
+ Builder& withTranslucentFactor() { _value.set(MaterialKey::OPACITY_VAL_BIT); _mask.set(MaterialKey::OPACITY_VAL_BIT); return (*this); }
- Builder& withoutTransparentMap() { _value.reset(MaterialKey::TRANSPARENT_MAP_BIT); _mask.set(MaterialKey::TRANSPARENT_MAP_BIT); return (*this); }
- Builder& withTransparentMap() { _value.set(MaterialKey::TRANSPARENT_MAP_BIT); _mask.set(MaterialKey::TRANSPARENT_MAP_BIT); return (*this); }
+ Builder& withoutTranslucentMap() { _value.reset(MaterialKey::OPACITY_TRANSLUCENT_MAP_BIT); _mask.set(MaterialKey::OPACITY_TRANSLUCENT_MAP_BIT); return (*this); }
+ Builder& withTranslucentMap() { _value.set(MaterialKey::OPACITY_TRANSLUCENT_MAP_BIT); _mask.set(MaterialKey::OPACITY_TRANSLUCENT_MAP_BIT); return (*this); }
+
+ Builder& withoutMaskMap() { _value.reset(MaterialKey::OPACITY_MASK_MAP_BIT); _mask.set(MaterialKey::OPACITY_MASK_MAP_BIT); return (*this); }
+ Builder& withMaskMap() { _value.set(MaterialKey::OPACITY_MASK_MAP_BIT); _mask.set(MaterialKey::OPACITY_MASK_MAP_BIT); return (*this); }
Builder& withoutNormalMap() { _value.reset(MaterialKey::NORMAL_MAP_BIT); _mask.set(MaterialKey::NORMAL_MAP_BIT); return (*this); }
Builder& withNormalMap() { _value.set(MaterialKey::NORMAL_MAP_BIT); _mask.set(MaterialKey::NORMAL_MAP_BIT); return (*this); }
@@ -202,7 +209,7 @@ public:
Builder& withLightmapMap() { _value.set(MaterialKey::LIGHTMAP_MAP_BIT); _mask.set(MaterialKey::LIGHTMAP_MAP_BIT); return (*this); }
// Convenient standard keys that we will keep on using all over the place
- static MaterialFilter opaqueAlbedo() { return Builder().withAlbedo().withoutTransparent().build(); }
+ static MaterialFilter opaqueAlbedo() { return Builder().withAlbedo().withoutTranslucentFactor().build(); }
};
// Item Filter operator testing if a key pass the filter
@@ -255,7 +262,6 @@ public:
void setRoughness(float roughness);
float getRoughness() const { return _schemaBuffer.get()._roughness; }
- void setTransparentTexture(bool isTransparent);
// Schema to access the attribute values of the material
class Schema {
@@ -283,6 +289,7 @@ public:
// The texture map to channel association
void setTextureMap(MapChannel channel, const TextureMapPointer& textureMap);
const TextureMaps& getTextureMaps() const { return _textureMaps; }
+ const TextureMapPointer getTextureMap(MapChannel channel) const;
// conversion from legacy material properties to PBR equivalent
static float shininessToRoughness(float shininess) { return 1.0f - shininess / 100.0f; }
diff --git a/libraries/model/src/model/Material.slh b/libraries/model/src/model/Material.slh
index da185ddf0b..28f9769a8b 100644
--- a/libraries/model/src/model/Material.slh
+++ b/libraries/model/src/model/Material.slh
@@ -39,21 +39,21 @@ float getMaterialShininess(Material m) { return 1.0 - getMaterialRoughness(m); }
int getMaterialKey(Material m) { return floatBitsToInt(m._spareKey.w); }
-const int EMISSIVE_VAL_BIT = 0x00000001;
-const int ALBEDO_VAL_BIT = 0x00000002;
-const int METALLIC_VAL_BIT = 0x00000004;
-const int GLOSSY_VAL_BIT = 0x00000008;
-const int TRANSPARENT_VAL_BIT = 0x00000010;
-const int TRANSPARENT_TEX_VAL_BIT = 0x00000020;
+const int EMISSIVE_VAL_BIT = 0x00000001;
+const int ALBEDO_VAL_BIT = 0x00000002;
+const int METALLIC_VAL_BIT = 0x00000004;
+const int GLOSSY_VAL_BIT = 0x00000008;
+const int OPACITY_VAL_BIT = 0x00000010;
+const int OPACITY_MASK_MAP_BIT = 0x00000020;
+const int OPACITY_TRANSLUCENT_MAP_BIT = 0x00000040;
-const int EMISSIVE_MAP_BIT = 0x00000040;
-const int ALBEDO_MAP_BIT = 0x00000080;
-const int METALLIC_MAP_BIT = 0x00000100;
-const int ROUGHNESS_MAP_BIT = 0x00000200;
-const int TRANSPARENT_MAP_BIT = 0x00000400;
-const int NORMAL_MAP_BIT = 0x00000800;
-const int OCCLUSION_MAP_BIT = 0x00001000;
+const int EMISSIVE_MAP_BIT = 0x00000080;
+const int ALBEDO_MAP_BIT = 0x00000100;
+const int METALLIC_MAP_BIT = 0x00000200;
+const int ROUGHNESS_MAP_BIT = 0x00000400;
+const int NORMAL_MAP_BIT = 0x00000800;
+const int OCCLUSION_MAP_BIT = 0x00001000;
+const int LIGHTMAP_MAP_BIT = 0x00002000;
-const int LIGHTMAP_MAP_BIT = 0x00002000;
<@endif@>
diff --git a/libraries/model/src/model/TextureMap.h b/libraries/model/src/model/TextureMap.h
index 228adb25e6..e845aebb81 100755
--- a/libraries/model/src/model/TextureMap.h
+++ b/libraries/model/src/model/TextureMap.h
@@ -56,6 +56,9 @@ public:
void setTextureTransform(const Transform& texcoordTransform);
const Transform& getTextureTransform() const { return _texcoordTransform; }
+ void setUseAlphaChannel(bool useAlpha) { _useAlphaChannel = useAlpha; }
+ bool useAlphaChannel() const { return _useAlphaChannel; }
+
void setLightmapOffsetScale(float offset, float scale);
const glm::vec2& getLightmapOffsetScale() const { return _lightmapOffsetScale; }
@@ -64,6 +67,8 @@ protected:
Transform _texcoordTransform;
glm::vec2 _lightmapOffsetScale{ 0.0f, 1.0f };
+
+ bool _useAlphaChannel{ false };
};
typedef std::shared_ptr< TextureMap > TextureMapPointer;
diff --git a/libraries/networking/src/ResourceCache.cpp b/libraries/networking/src/ResourceCache.cpp
index 295af8c5ee..3de94ed839 100644
--- a/libraries/networking/src/ResourceCache.cpp
+++ b/libraries/networking/src/ResourceCache.cpp
@@ -216,17 +216,13 @@ Resource* ResourceCacheSharedItems::getHighestPendingRequest() {
bool ResourceCache::attemptRequest(Resource* resource) {
auto sharedItems = DependencyManager::get();
- // Disable request limiting for ATP
- if (resource->getURL().scheme() != URL_SCHEME_ATP) {
- if (_requestsActive >= _requestLimit) {
- // wait until a slot becomes available
- sharedItems->appendPendingRequest(resource);
- return false;
- }
-
- ++_requestsActive;
+ if (_requestsActive >= _requestLimit) {
+ // wait until a slot becomes available
+ sharedItems->appendPendingRequest(resource);
+ return false;
}
-
+
+ ++_requestsActive;
sharedItems->appendActiveRequest(resource);
resource->makeRequest();
return true;
@@ -235,9 +231,7 @@ bool ResourceCache::attemptRequest(Resource* resource) {
void ResourceCache::requestCompleted(Resource* resource) {
auto sharedItems = DependencyManager::get();
sharedItems->removeRequest(resource);
- if (resource->getURL().scheme() != URL_SCHEME_ATP) {
- --_requestsActive;
- }
+ --_requestsActive;
attemptHighestPriorityRequest();
}
@@ -380,6 +374,7 @@ void Resource::finishedLoading(bool success) {
_failedToLoad = true;
}
_loadPriorities.clear();
+ emit finished(success);
}
void Resource::reinsert() {
diff --git a/libraries/networking/src/ResourceCache.h b/libraries/networking/src/ResourceCache.h
index ed938f6cf4..77878794b5 100644
--- a/libraries/networking/src/ResourceCache.h
+++ b/libraries/networking/src/ResourceCache.h
@@ -201,6 +201,9 @@ signals:
/// This can be used instead of downloadFinished to access data before it is processed.
void loaded(const QByteArray& request);
+ /// Fired when the resource has finished loading.
+ void finished(bool success);
+
/// Fired when the resource failed to load.
void failed(QNetworkReply::NetworkError error);
@@ -224,9 +227,6 @@ protected:
/// This should be called by subclasses that override downloadFinished to mark the end of processing.
Q_INVOKABLE void finishedLoading(bool success);
- /// Reinserts this resource into the cache.
- virtual void reinsert();
-
QUrl _url;
QUrl _activeUrl;
bool _startedLoading = false;
@@ -246,6 +246,7 @@ private:
void makeRequest();
void retry();
+ void reinsert();
friend class ResourceCache;
diff --git a/libraries/networking/src/UserActivityLogger.cpp b/libraries/networking/src/UserActivityLogger.cpp
index f2019ba9a9..2e74a5166c 100644
--- a/libraries/networking/src/UserActivityLogger.cpp
+++ b/libraries/networking/src/UserActivityLogger.cpp
@@ -78,11 +78,15 @@ void UserActivityLogger::requestError(QNetworkReply& errorReply) {
qCDebug(networking) << errorReply.error() << "-" << errorReply.errorString();
}
-void UserActivityLogger::launch(QString applicationVersion) {
+void UserActivityLogger::launch(QString applicationVersion, bool previousSessionCrashed, int previousSessionRuntime) {
const QString ACTION_NAME = "launch";
QJsonObject actionDetails;
QString VERSION_KEY = "version";
+ QString CRASH_KEY = "previousSessionCrashed";
+ QString RUNTIME_KEY = "previousSessionRuntime";
actionDetails.insert(VERSION_KEY, applicationVersion);
+ actionDetails.insert(CRASH_KEY, previousSessionCrashed);
+ actionDetails.insert(RUNTIME_KEY, previousSessionRuntime);
logAction(ACTION_NAME, actionDetails);
}
diff --git a/libraries/networking/src/UserActivityLogger.h b/libraries/networking/src/UserActivityLogger.h
index 2811be86a8..8eda086521 100644
--- a/libraries/networking/src/UserActivityLogger.h
+++ b/libraries/networking/src/UserActivityLogger.h
@@ -29,7 +29,7 @@ public slots:
void disable(bool disable);
void logAction(QString action, QJsonObject details = QJsonObject(), JSONCallbackParameters params = JSONCallbackParameters());
- void launch(QString applicationVersion);
+ void launch(QString applicationVersion, bool previousSessionCrashed, int previousSessionRuntime);
void changedDisplayName(QString displayName);
void changedModel(QString typeOfModel, QString modelURL);
diff --git a/libraries/networking/src/udt/CongestionControl.cpp b/libraries/networking/src/udt/CongestionControl.cpp
index 1d1a6628fe..d30be2c139 100644
--- a/libraries/networking/src/udt/CongestionControl.cpp
+++ b/libraries/networking/src/udt/CongestionControl.cpp
@@ -201,7 +201,7 @@ void DefaultCC::onTimeout() {
void DefaultCC::stopSlowStart() {
_slowStart = false;
-
+
if (_receiveRate > 0) {
// Set the sending rate to the receiving rate.
setPacketSendPeriod(USECS_PER_SECOND / _receiveRate);
diff --git a/libraries/networking/src/udt/Connection.cpp b/libraries/networking/src/udt/Connection.cpp
index e5f3508b81..af70295840 100644
--- a/libraries/networking/src/udt/Connection.cpp
+++ b/libraries/networking/src/udt/Connection.cpp
@@ -103,6 +103,7 @@ SendQueue& Connection::getSendQueue() {
QObject::connect(_sendQueue.get(), &SendQueue::packetRetransmitted, this, &Connection::recordRetransmission);
QObject::connect(_sendQueue.get(), &SendQueue::queueInactive, this, &Connection::queueInactive);
QObject::connect(_sendQueue.get(), &SendQueue::timeout, this, &Connection::queueTimeout);
+ QObject::connect(_sendQueue.get(), &SendQueue::shortCircuitLoss, this, &Connection::queueShortCircuitLoss);
// set defaults on the send queue from our congestion control object and estimatedTimeout()
_sendQueue->setPacketSendPeriod(_congestionControl->_packetSendPeriod);
@@ -140,6 +141,12 @@ void Connection::queueTimeout() {
});
}
+void Connection::queueShortCircuitLoss(quint32 sequenceNumber) {
+ updateCongestionControlAndSendQueue([this, sequenceNumber]{
+ _congestionControl->onLoss(SequenceNumber { sequenceNumber }, SequenceNumber { sequenceNumber });
+ });
+}
+
void Connection::sendReliablePacket(std::unique_ptr packet) {
Q_ASSERT_X(packet->isReliable(), "Connection::send", "Trying to send an unreliable packet reliably.");
getSendQueue().queuePacket(std::move(packet));
diff --git a/libraries/networking/src/udt/Connection.h b/libraries/networking/src/udt/Connection.h
index 4f5a8793e7..08a2df9b97 100644
--- a/libraries/networking/src/udt/Connection.h
+++ b/libraries/networking/src/udt/Connection.h
@@ -87,6 +87,7 @@ private slots:
void recordRetransmission();
void queueInactive();
void queueTimeout();
+ void queueShortCircuitLoss(quint32 sequenceNumber);
private:
void sendACK(bool wasCausedBySyncTimeout = true);
diff --git a/libraries/networking/src/udt/SendQueue.cpp b/libraries/networking/src/udt/SendQueue.cpp
index 9701561ec7..2ffa42cb82 100644
--- a/libraries/networking/src/udt/SendQueue.cpp
+++ b/libraries/networking/src/udt/SendQueue.cpp
@@ -128,13 +128,13 @@ void SendQueue::stop() {
_emptyCondition.notify_one();
}
-void SendQueue::sendPacket(const Packet& packet) {
- _socket->writeDatagram(packet.getData(), packet.getDataSize(), _destination);
+int SendQueue::sendPacket(const Packet& packet) {
+ return _socket->writeDatagram(packet.getData(), packet.getDataSize(), _destination);
}
void SendQueue::ack(SequenceNumber ack) {
// this is a response from the client, re-set our timeout expiry and our last response time
- _lastReceiverResponse = uint64_t(QDateTime::currentMSecsSinceEpoch());
+ _lastReceiverResponse = QDateTime::currentMSecsSinceEpoch();
if (_lastACKSequenceNumber == (uint32_t) ack) {
return;
@@ -164,7 +164,7 @@ void SendQueue::ack(SequenceNumber ack) {
void SendQueue::nak(SequenceNumber start, SequenceNumber end) {
// this is a response from the client, re-set our timeout expiry
- _lastReceiverResponse = uint64_t(QDateTime::currentMSecsSinceEpoch());
+ _lastReceiverResponse = QDateTime::currentMSecsSinceEpoch();
{
std::lock_guard nakLocker(_naksLock);
@@ -177,8 +177,8 @@ void SendQueue::nak(SequenceNumber start, SequenceNumber end) {
void SendQueue::overrideNAKListFromPacket(ControlPacket& packet) {
// this is a response from the client, re-set our timeout expiry
- _lastReceiverResponse = uint64_t(QDateTime::currentMSecsSinceEpoch());
-
+ _lastReceiverResponse = QDateTime::currentMSecsSinceEpoch();
+
{
std::lock_guard nakLocker(_naksLock);
_naks.clear();
@@ -232,15 +232,16 @@ SequenceNumber SendQueue::getNextSequenceNumber() {
return _currentSequenceNumber;
}
-void SendQueue::sendNewPacketAndAddToSentList(std::unique_ptr newPacket, SequenceNumber sequenceNumber) {
+bool SendQueue::sendNewPacketAndAddToSentList(std::unique_ptr newPacket, SequenceNumber sequenceNumber) {
// write the sequence number and send the packet
newPacket->writeSequenceNumber(sequenceNumber);
- sendPacket(*newPacket);
-
+
// Save packet/payload size before we move it
auto packetSize = newPacket->getDataSize();
auto payloadSize = newPacket->getPayloadSize();
+ auto bytesWritten = sendPacket(*newPacket);
+
{
// Insert the packet we have just sent in the sent list
QWriteLocker locker(&_sentLock);
@@ -249,8 +250,24 @@ void SendQueue::sendNewPacketAndAddToSentList(std::unique_ptr newPacket,
entry.second.swap(newPacket);
}
Q_ASSERT_X(!newPacket, "SendQueue::sendNewPacketAndAddToSentList()", "Overriden packet in sent list");
-
+
emit packetSent(packetSize, payloadSize);
+
+ if (bytesWritten < 0) {
+ // this is a short-circuit loss - we failed to put this packet on the wire
+ // so immediately add it to the loss list
+
+ {
+ std::lock_guard nakLocker(_naksLock);
+ _naks.append(sequenceNumber);
+ }
+
+ emit shortCircuitLoss(quint32(sequenceNumber));
+
+ return false;
+ } else {
+ return true;
+ }
}
void SendQueue::run() {
@@ -285,12 +302,14 @@ void SendQueue::run() {
auto nextPacketTimestamp = p_high_resolution_clock::now();
while (_state == State::Running) {
- bool sentAPacket = maybeResendPacket();
+ bool attemptedToSendPacket = maybeResendPacket();
// if we didn't find a packet to re-send AND we think we can fit a new packet on the wire
// (this is according to the current flow window size) then we send out a new packet
- if (!sentAPacket) {
- sentAPacket = maybeSendNewPacket();
+ auto newPacketCount = 0;
+ if (!attemptedToSendPacket) {
+ newPacketCount = maybeSendNewPacket();
+ attemptedToSendPacket = (newPacketCount > 0);
}
// since we're a while loop, give the thread a chance to process events
@@ -300,12 +319,13 @@ void SendQueue::run() {
// If the send queue has been innactive, skip the sleep for
// Either _isRunning will have been set to false and we'll break
// Or something happened and we'll keep going
- if (_state != State::Running || isInactive(sentAPacket)) {
+ if (_state != State::Running || isInactive(attemptedToSendPacket)) {
return;
}
// push the next packet timestamp forwards by the current packet send period
- nextPacketTimestamp += std::chrono::microseconds(_packetSendPeriod);
+ auto nextPacketDelta = (newPacketCount == 2 ? 2 : 1) * _packetSendPeriod;
+ nextPacketTimestamp += std::chrono::microseconds(nextPacketDelta);
// sleep as long as we need until next packet send, if we can
const auto timeToSleep = duration_cast(nextPacketTimestamp - p_high_resolution_clock::now());
@@ -314,7 +334,7 @@ void SendQueue::run() {
}
}
-bool SendQueue::maybeSendNewPacket() {
+int SendQueue::maybeSendNewPacket() {
if (!isFlowWindowFull()) {
// we didn't re-send a packet, so time to send a new one
@@ -324,38 +344,43 @@ bool SendQueue::maybeSendNewPacket() {
// grab the first packet we will send
std::unique_ptr firstPacket = _packets.takePacket();
Q_ASSERT(firstPacket);
-
- std::unique_ptr secondPacket;
- bool shouldSendPairTail = false;
-
- if (((uint32_t) nextNumber & 0xF) == 0) {
- // the first packet is the first in a probe pair - every 16 (rightmost 16 bits = 0) packets
- // pull off a second packet if we can before we unlock
- shouldSendPairTail = true;
-
- secondPacket = _packets.takePacket();
+
+
+ // attempt to send the first packet
+ if (sendNewPacketAndAddToSentList(move(firstPacket), nextNumber)) {
+ std::unique_ptr secondPacket;
+ bool shouldSendPairTail = false;
+
+ if (((uint32_t) nextNumber & 0xF) == 0) {
+ // the first packet is the first in a probe pair - every 16 (rightmost 16 bits = 0) packets
+ // pull off a second packet if we can before we unlock
+ shouldSendPairTail = true;
+
+ secondPacket = _packets.takePacket();
+ }
+
+ // do we have a second in a pair to send as well?
+ if (secondPacket) {
+ sendNewPacketAndAddToSentList(move(secondPacket), getNextSequenceNumber());
+ } else if (shouldSendPairTail) {
+ // we didn't get a second packet to send in the probe pair
+ // send a control packet of type ProbePairTail so the receiver can still do
+ // proper bandwidth estimation
+ static auto pairTailPacket = ControlPacket::create(ControlPacket::ProbeTail);
+ _socket->writeBasePacket(*pairTailPacket, _destination);
+ }
+
+ // we attempted to send two packets, return 2
+ return 2;
+ } else {
+ // we attempted to send a single packet, return 1
+ return 1;
}
-
- // definitely send the first packet
- sendNewPacketAndAddToSentList(move(firstPacket), nextNumber);
-
- // do we have a second in a pair to send as well?
- if (secondPacket) {
- sendNewPacketAndAddToSentList(move(secondPacket), getNextSequenceNumber());
- } else if (shouldSendPairTail) {
- // we didn't get a second packet to send in the probe pair
- // send a control packet of type ProbePairTail so the receiver can still do
- // proper bandwidth estimation
- static auto pairTailPacket = ControlPacket::create(ControlPacket::ProbeTail);
- _socket->writeBasePacket(*pairTailPacket, _destination);
- }
-
- // We sent our packet(s), return here
- return true;
}
}
+
// No packets were sent
- return false;
+ return 0;
}
bool SendQueue::maybeResendPacket() {
@@ -375,8 +400,9 @@ bool SendQueue::maybeResendPacket() {
// see if we can find the packet to re-send
auto it = _sentPackets.find(resendNumber);
-
+
if (it != _sentPackets.end()) {
+
auto& entry = it->second;
// we found the packet - grab it
auto& resendPacket = *(entry.second);
@@ -437,7 +463,7 @@ bool SendQueue::maybeResendPacket() {
return false;
}
-bool SendQueue::isInactive(bool sentAPacket) {
+bool SendQueue::isInactive(bool attemptedToSendPacket) {
// check for connection timeout first
// that will be the case if we have had 16 timeouts since hearing back from the client, and it has been
@@ -447,7 +473,8 @@ bool SendQueue::isInactive(bool sentAPacket) {
auto sinceLastResponse = (QDateTime::currentMSecsSinceEpoch() - _lastReceiverResponse);
- if (sinceLastResponse >= quint64(NUM_TIMEOUTS_BEFORE_INACTIVE * (_estimatedTimeout / USECS_PER_MSEC)) &&
+ if (sinceLastResponse > 0 &&
+ sinceLastResponse >= int64_t(NUM_TIMEOUTS_BEFORE_INACTIVE * (_estimatedTimeout / USECS_PER_MSEC)) &&
_lastReceiverResponse > 0 &&
sinceLastResponse > MIN_MS_BEFORE_INACTIVE) {
// If the flow window has been full for over CONSIDER_INACTIVE_AFTER,
@@ -462,7 +489,7 @@ bool SendQueue::isInactive(bool sentAPacket) {
return true;
}
- if (!sentAPacket) {
+ if (!attemptedToSendPacket) {
// During our processing above we didn't send any packets
// If that is still the case we should use a condition_variable_any to sleep until we have data to handle.
diff --git a/libraries/networking/src/udt/SendQueue.h b/libraries/networking/src/udt/SendQueue.h
index 9400ae8352..21f6141c3c 100644
--- a/libraries/networking/src/udt/SendQueue.h
+++ b/libraries/networking/src/udt/SendQueue.h
@@ -79,6 +79,7 @@ signals:
void queueInactive();
+ void shortCircuitLoss(quint32 sequenceNumber);
void timeout();
private slots:
@@ -91,13 +92,13 @@ private:
void sendHandshake();
- void sendPacket(const Packet& packet);
- void sendNewPacketAndAddToSentList(std::unique_ptr newPacket, SequenceNumber sequenceNumber);
+ int sendPacket(const Packet& packet);
+ bool sendNewPacketAndAddToSentList(std::unique_ptr newPacket, SequenceNumber sequenceNumber);
- bool maybeSendNewPacket(); // Figures out what packet to send next
+ int maybeSendNewPacket(); // Figures out what packet to send next
bool maybeResendPacket(); // Determines whether to resend a packet and which one
- bool isInactive(bool sentAPacket);
+ bool isInactive(bool attemptedToSendPacket);
void deactivate(); // makes the queue inactive and cleans it up
bool isFlowWindowFull() const;
@@ -122,7 +123,7 @@ private:
std::atomic _estimatedTimeout { 0 }; // Estimated timeout, set from CC
std::atomic _syncInterval { udt::DEFAULT_SYN_INTERVAL_USECS }; // Sync interval, set from CC
- std::atomic _lastReceiverResponse { 0 }; // Timestamp for the last time we got new data from the receiver (ACK/NAK)
+ std::atomic _lastReceiverResponse { 0 }; // Timestamp for the last time we got new data from the receiver (ACK/NAK)
std::atomic _flowWindowSize { 0 }; // Flow control window size (number of packets that can be on wire) - set from CC
diff --git a/libraries/physics/src/ShapeFactory.cpp b/libraries/physics/src/ShapeFactory.cpp
index f956e562cd..de3e9cc794 100644
--- a/libraries/physics/src/ShapeFactory.cpp
+++ b/libraries/physics/src/ShapeFactory.cpp
@@ -120,3 +120,21 @@ btCollisionShape* ShapeFactory::createShapeFromInfo(const ShapeInfo& info) {
}
return shape;
}
+
+void ShapeFactory::deleteShape(btCollisionShape* shape) {
+ assert(shape);
+ if (shape->getShapeType() == (int)COMPOUND_SHAPE_PROXYTYPE) {
+ btCompoundShape* compoundShape = static_cast(shape);
+ const int numChildShapes = compoundShape->getNumChildShapes();
+ for (int i = 0; i < numChildShapes; i ++) {
+ btCollisionShape* childShape = compoundShape->getChildShape(i);
+ if (childShape->getShapeType() == (int)COMPOUND_SHAPE_PROXYTYPE) {
+ // recurse
+ ShapeFactory::deleteShape(childShape);
+ } else {
+ delete childShape;
+ }
+ }
+ }
+ delete shape;
+}
diff --git a/libraries/physics/src/ShapeFactory.h b/libraries/physics/src/ShapeFactory.h
index f6a6dfb3e6..1ba2bdb619 100644
--- a/libraries/physics/src/ShapeFactory.h
+++ b/libraries/physics/src/ShapeFactory.h
@@ -22,6 +22,7 @@
namespace ShapeFactory {
btConvexHullShape* createConvexHull(const QVector& points);
btCollisionShape* createShapeFromInfo(const ShapeInfo& info);
+ void deleteShape(btCollisionShape* shape);
};
#endif // hifi_ShapeFactory_h
diff --git a/libraries/physics/src/ShapeManager.cpp b/libraries/physics/src/ShapeManager.cpp
index 7a3065dfff..4231d1eb60 100644
--- a/libraries/physics/src/ShapeManager.cpp
+++ b/libraries/physics/src/ShapeManager.cpp
@@ -23,7 +23,7 @@ ShapeManager::~ShapeManager() {
int numShapes = _shapeMap.size();
for (int i = 0; i < numShapes; ++i) {
ShapeReference* shapeRef = _shapeMap.getAtIndex(i);
- delete shapeRef->shape;
+ ShapeFactory::deleteShape(shapeRef->shape);
}
_shapeMap.clear();
}
@@ -32,13 +32,14 @@ btCollisionShape* ShapeManager::getShape(const ShapeInfo& info) {
if (info.getType() == SHAPE_TYPE_NONE) {
return NULL;
}
- // Very small or large objects are not supported.
- float diagonal = 4.0f * glm::length2(info.getHalfExtents());
- const float MIN_SHAPE_DIAGONAL_SQUARED = 3.0e-4f; // 1 cm cube
- //const float MAX_SHAPE_DIAGONAL_SQUARED = 3.0e6f; // 1000 m cube
- if (diagonal < MIN_SHAPE_DIAGONAL_SQUARED /* || diagonal > MAX_SHAPE_DIAGONAL_SQUARED*/ ) {
- // qCDebug(physics) << "ShapeManager::getShape -- not making shape due to size" << diagonal;
- return NULL;
+ if (info.getType() != SHAPE_TYPE_COMPOUND) {
+ // Very small or large non-compound objects are not supported.
+ float diagonal = 4.0f * glm::length2(info.getHalfExtents());
+ const float MIN_SHAPE_DIAGONAL_SQUARED = 3.0e-4f; // 1 cm cube
+ if (diagonal < MIN_SHAPE_DIAGONAL_SQUARED) {
+ // qCDebug(physics) << "ShapeManager::getShape -- not making shape due to size" << diagonal;
+ return NULL;
+ }
}
DoubleHashKey key = info.getHash();
ShapeReference* shapeRef = _shapeMap.find(key);
@@ -58,14 +59,14 @@ btCollisionShape* ShapeManager::getShape(const ShapeInfo& info) {
}
// private helper method
-bool ShapeManager::releaseShape(const DoubleHashKey& key) {
+bool ShapeManager::releaseShapeByKey(const DoubleHashKey& key) {
ShapeReference* shapeRef = _shapeMap.find(key);
if (shapeRef) {
if (shapeRef->refCount > 0) {
shapeRef->refCount--;
if (shapeRef->refCount == 0) {
_pendingGarbage.push_back(key);
- const int MAX_GARBAGE_CAPACITY = 127;
+ const int MAX_GARBAGE_CAPACITY = 255;
if (_pendingGarbage.size() > MAX_GARBAGE_CAPACITY) {
collectGarbage();
}
@@ -82,16 +83,12 @@ bool ShapeManager::releaseShape(const DoubleHashKey& key) {
return false;
}
-bool ShapeManager::releaseShape(const ShapeInfo& info) {
- return releaseShape(info.getHash());
-}
-
bool ShapeManager::releaseShape(const btCollisionShape* shape) {
int numShapes = _shapeMap.size();
for (int i = 0; i < numShapes; ++i) {
ShapeReference* shapeRef = _shapeMap.getAtIndex(i);
if (shape == shapeRef->shape) {
- return releaseShape(shapeRef->key);
+ return releaseShapeByKey(shapeRef->key);
}
}
return false;
@@ -103,17 +100,7 @@ void ShapeManager::collectGarbage() {
DoubleHashKey& key = _pendingGarbage[i];
ShapeReference* shapeRef = _shapeMap.find(key);
if (shapeRef && shapeRef->refCount == 0) {
- // if the shape we're about to delete is compound, delete the children first.
- if (shapeRef->shape->getShapeType() == COMPOUND_SHAPE_PROXYTYPE) {
- const btCompoundShape* compoundShape = static_cast(shapeRef->shape);
- const int numChildShapes = compoundShape->getNumChildShapes();
- for (int i = 0; i < numChildShapes; i ++) {
- const btCollisionShape* childShape = compoundShape->getChildShape(i);
- delete childShape;
- }
- }
-
- delete shapeRef->shape;
+ ShapeFactory::deleteShape(shapeRef->shape);
_shapeMap.remove(key);
}
}
diff --git a/libraries/physics/src/ShapeManager.h b/libraries/physics/src/ShapeManager.h
index e04fa1280c..0c411f5f62 100644
--- a/libraries/physics/src/ShapeManager.h
+++ b/libraries/physics/src/ShapeManager.h
@@ -29,7 +29,6 @@ public:
btCollisionShape* getShape(const ShapeInfo& info);
/// \return true if shape was found and released
- bool releaseShape(const ShapeInfo& info);
bool releaseShape(const btCollisionShape* shape);
/// delete shapes that have zero references
@@ -39,10 +38,10 @@ public:
int getNumShapes() const { return _shapeMap.size(); }
int getNumReferences(const ShapeInfo& info) const;
int getNumReferences(const btCollisionShape* shape) const;
- bool hasShape(const btCollisionShape* shape) const;
+ bool hasShape(const btCollisionShape* shape) const;
private:
- bool releaseShape(const DoubleHashKey& key);
+ bool releaseShapeByKey(const DoubleHashKey& key);
struct ShapeReference {
int refCount;
diff --git a/libraries/render-utils/src/MaterialTextures.slh b/libraries/render-utils/src/MaterialTextures.slh
index 8904ae34b2..34bb7c429b 100644
--- a/libraries/render-utils/src/MaterialTextures.slh
+++ b/libraries/render-utils/src/MaterialTextures.slh
@@ -59,7 +59,7 @@ float fetchOcclusionMap(vec2 uv) {
<@func fetchMaterialTextures(matKey, texcoord0, albedo, roughness, normal, metallic, emissive, occlusion)@>
<@if albedo@>
- vec4 <$albedo$> = (((<$matKey$> & ALBEDO_MAP_BIT) != 0) ? fetchAlbedoMap(<$texcoord0$>) : vec4(1.0));
+ vec4 <$albedo$> = (((<$matKey$> & (ALBEDO_MAP_BIT | OPACITY_MASK_MAP_BIT | OPACITY_TRANSLUCENT_MAP_BIT)) != 0) ? fetchAlbedoMap(<$texcoord0$>) : vec4(1.0));
<@endif@>
<@if roughness@>
float <$roughness$> = (((<$matKey$> & ROUGHNESS_MAP_BIT) != 0) ? fetchRoughnessMap(<$texcoord0$>) : 1.0);
@@ -112,6 +112,23 @@ vec3 fetchLightmapMap(vec2 uv) {
}
<@endfunc@>
+<@func evalMaterialOpacity(fetchedOpacity, materialOpacity, matKey, opacity)@>
+{
+ const float OPACITY_MASK_THRESHOLD = 0.5;
+ <$opacity$> = (((<$matKey$> & (OPACITY_TRANSLUCENT_MAP_BIT | OPACITY_MASK_MAP_BIT)) != 0) ?
+ (((<$matKey$> & OPACITY_MASK_MAP_BIT) != 0) ? step(OPACITY_MASK_THRESHOLD, <$fetchedOpacity$>) : <$fetchedOpacity$>) :
+ 1.0) * <$materialOpacity$>;
+}
+<@endfunc@>
+
+<@func $discardTransparent(opacity)@>
+{
+ if (<$opacity$> < 1.0) {
+ discard;
+ }
+}
+<@endfunc@>
+
<@func evalMaterialRoughness(fetchedRoughness, materialRoughness, matKey, roughness)@>
{
<$roughness$> = (((<$matKey$> & ROUGHNESS_MAP_BIT) != 0) ? <$fetchedRoughness$> : <$materialRoughness$>);
diff --git a/libraries/render-utils/src/MeshPartPayload.cpp b/libraries/render-utils/src/MeshPartPayload.cpp
index 4ae5b4532a..363def05a1 100644
--- a/libraries/render-utils/src/MeshPartPayload.cpp
+++ b/libraries/render-utils/src/MeshPartPayload.cpp
@@ -81,7 +81,7 @@ ItemKey MeshPartPayload::getKey() const {
if (_drawMaterial) {
auto matKey = _drawMaterial->getKey();
- if (matKey.isTransparent() || matKey.isTransparentTexture() || matKey.isTransparentMap()) {
+ if (matKey.isTranslucent()) {
builder.withTransparent();
}
}
@@ -100,7 +100,7 @@ ShapeKey MeshPartPayload::getShapeKey() const {
}
ShapeKey::Builder builder;
- if (drawMaterialKey.isTransparent() || drawMaterialKey.isTransparentTexture() || drawMaterialKey.isTransparentMap()) {
+ if (drawMaterialKey.isTranslucent()) {
builder.withTranslucent();
}
if (drawMaterialKey.isNormalMap()) {
@@ -365,7 +365,7 @@ ItemKey ModelMeshPartPayload::getKey() const {
if (_drawMaterial) {
auto matKey = _drawMaterial->getKey();
- if (matKey.isTransparent() || matKey.isTransparentTexture() || matKey.isTransparentMap()) {
+ if (matKey.isTranslucent()) {
builder.withTransparent();
}
}
@@ -412,8 +412,7 @@ ShapeKey ModelMeshPartPayload::getShapeKey() const {
drawMaterialKey = _drawMaterial->getKey();
}
- bool isTranslucent =
- drawMaterialKey.isTransparent() || drawMaterialKey.isTransparentTexture() || drawMaterialKey.isTransparentMap();
+ bool isTranslucent = drawMaterialKey.isTranslucent();
bool hasTangents = drawMaterialKey.isNormalMap() && !mesh.tangents.isEmpty();
bool hasSpecular = drawMaterialKey.isMetallicMap();
bool hasLightmap = drawMaterialKey.isLightmapMap();
diff --git a/libraries/render-utils/src/model.slf b/libraries/render-utils/src/model.slf
index c9ca6d9eb7..ddfd83d1d4 100755
--- a/libraries/render-utils/src/model.slf
+++ b/libraries/render-utils/src/model.slf
@@ -29,6 +29,10 @@ void main(void) {
int matKey = getMaterialKey(mat);
<$fetchMaterialTextures(matKey, _texCoord0, albedoTex, roughnessTex, _SCRIBE_NULL, _SCRIBE_NULL, emissiveTex, occlusionTex)$>
+ float opacity = 1.0;
+ <$evalMaterialOpacity(albedoTex.a, opacity, matKey, opacity)&>;
+ <$discardTransparent(opacity)$>;
+
vec3 albedo = getMaterialAlbedo(mat);
<$evalMaterialAlbedo(albedoTex, albedo, matKey, albedo)$>;
albedo *= _color;
@@ -41,7 +45,7 @@ void main(void) {
packDeferredFragment(
normalize(_normal.xyz),
- evalOpaqueFinalAlpha(getMaterialOpacity(mat), albedoTex.a),
+ opacity,
albedo,
roughness,
getMaterialMetallic(mat),
diff --git a/libraries/render-utils/src/model_normal_map.slf b/libraries/render-utils/src/model_normal_map.slf
index a584427005..10ae6ee880 100755
--- a/libraries/render-utils/src/model_normal_map.slf
+++ b/libraries/render-utils/src/model_normal_map.slf
@@ -30,6 +30,10 @@ void main(void) {
int matKey = getMaterialKey(mat);
<$fetchMaterialTextures(matKey, _texCoord0, albedoTex, roughnessTex, normalTex, _SCRIBE_NULL, emissiveTex, occlusionTex)$>
+ float opacity = 1.0;
+ <$evalMaterialOpacity(albedoTex.a, opacity, matKey, opacity)&>;
+ <$discardTransparent(opacity)$>;
+
vec3 albedo = getMaterialAlbedo(mat);
<$evalMaterialAlbedo(albedoTex, albedo, matKey, albedo)$>;
albedo *= _color;
@@ -45,7 +49,7 @@ void main(void) {
packDeferredFragment(
viewNormal,
- evalOpaqueFinalAlpha(getMaterialOpacity(mat), albedoTex.a),
+ opacity,
albedo,
roughness,
getMaterialMetallic(mat),
diff --git a/libraries/render-utils/src/model_normal_specular_map.slf b/libraries/render-utils/src/model_normal_specular_map.slf
index cf461db7ef..2529596818 100755
--- a/libraries/render-utils/src/model_normal_specular_map.slf
+++ b/libraries/render-utils/src/model_normal_specular_map.slf
@@ -30,6 +30,10 @@ void main(void) {
int matKey = getMaterialKey(mat);
<$fetchMaterialTextures(matKey, _texCoord0, albedoTex, roughnessTex, normalTex, metallicTex, emissiveTex, occlusionTex)$>
+ float opacity = 1.0;
+ <$evalMaterialOpacity(albedoTex.a, opacity, matKey, opacity)&>;
+ <$discardTransparent(opacity)$>;
+
vec3 albedo = getMaterialAlbedo(mat);
<$evalMaterialAlbedo(albedoTex, albedo, matKey, albedo)$>;
albedo *= _color;
@@ -49,7 +53,7 @@ void main(void) {
packDeferredFragment(
normalize(viewNormal.xyz),
- evalOpaqueFinalAlpha(getMaterialOpacity(mat), albedoTex.a),
+ opacity,
albedo,
roughness,
metallic,
diff --git a/libraries/render-utils/src/model_specular_map.slf b/libraries/render-utils/src/model_specular_map.slf
index 32e5823430..9b2f4ae640 100755
--- a/libraries/render-utils/src/model_specular_map.slf
+++ b/libraries/render-utils/src/model_specular_map.slf
@@ -30,6 +30,10 @@ void main(void) {
int matKey = getMaterialKey(mat);
<$fetchMaterialTextures(matKey, _texCoord0, albedoTex, roughnessTex, _SCRIBE_NULL, metallicTex, emissiveTex, occlusionTex)$>
+ float opacity = 1.0;
+ <$evalMaterialOpacity(albedoTex.a, opacity, matKey, opacity)&>;
+ <$discardTransparent(opacity)$>;
+
vec3 albedo = getMaterialAlbedo(mat);
<$evalMaterialAlbedo(albedoTex, albedo, matKey, albedo)$>;
albedo *= _color;
@@ -45,7 +49,7 @@ void main(void) {
packDeferredFragment(
normalize(_normal),
- evalOpaqueFinalAlpha(getMaterialOpacity(mat), albedoTex.a),
+ opacity,
albedo,
roughness,
metallic,
diff --git a/libraries/render-utils/src/model_translucent.slf b/libraries/render-utils/src/model_translucent.slf
index b99b5c8a2a..12a7b9299f 100755
--- a/libraries/render-utils/src/model_translucent.slf
+++ b/libraries/render-utils/src/model_translucent.slf
@@ -36,6 +36,10 @@ void main(void) {
int matKey = getMaterialKey(mat);
<$fetchMaterialTextures(matKey, _texCoord0, albedoTex, roughnessTex, _SCRIBE_NULL, _SCRIBE_NULL, emissiveTex, occlusionTex)$>
+ float opacity = getMaterialOpacity(mat) * _alpha;
+ <$evalMaterialOpacity(albedoTex.a, opacity, matKey, opacity)&>;
+ <$discardTransparent(opacity)$>;
+
vec3 albedo = getMaterialAlbedo(mat);
<$evalMaterialAlbedo(albedoTex, albedo, matKey, albedo)$>;
albedo *= _color;
@@ -52,8 +56,6 @@ void main(void) {
vec3 fragPosition = _position.xyz;
vec3 fragNormal = normalize(_normal);
- float fragOpacity = getMaterialOpacity(mat) * albedoTex.a * _alpha;
-
TransformCamera cam = getTransformCamera();
_fragColor = vec4(evalAmbientSphereGlobalColor(
@@ -66,5 +68,5 @@ void main(void) {
metallic,
emissive,
roughness),
- fragOpacity);
+ opacity);
}
diff --git a/libraries/render/src/render/CullTask.cpp b/libraries/render/src/render/CullTask.cpp
index 484f049944..56805e8f83 100644
--- a/libraries/render/src/render/CullTask.cpp
+++ b/libraries/render/src/render/CullTask.cpp
@@ -114,6 +114,7 @@ void FetchSpatialTree::run(const SceneContextPointer& sceneContext, const Render
void CullSpatialSelection::configure(const Config& config) {
_justFrozeFrustum = _justFrozeFrustum || (config.freezeFrustum && !_freezeFrustum);
_freezeFrustum = config.freezeFrustum;
+ _skipCulling = config.skipCulling;
}
void CullSpatialSelection::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext,
@@ -191,60 +192,112 @@ void CullSpatialSelection::run(const SceneContextPointer& sceneContext, const Re
// visibility cull if partially selected ( octree cell contianing it was partial)
// distance cull if was a subcell item ( octree cell is way bigger than the item bound itself, so now need to test per item)
- // inside & fit items: easy, just filter
- {
- PerformanceTimer perfTimer("insideFitItems");
- for (auto id : inSelection.insideItems) {
- auto& item = scene->getItem(id);
- if (_filter.test(item.getKey())) {
- ItemBound itemBound(id, item.getBound());
- outItems.emplace_back(itemBound);
- }
- }
- }
-
- // inside & subcell items: filter & distance cull
- {
- PerformanceTimer perfTimer("insideSmallItems");
- for (auto id : inSelection.insideSubcellItems) {
- auto& item = scene->getItem(id);
- if (_filter.test(item.getKey())) {
- ItemBound itemBound(id, item.getBound());
- if (test.solidAngleTest(itemBound.bound)) {
+ if (_skipCulling) {
+ // inside & fit items: filter only, culling is disabled
+ {
+ PerformanceTimer perfTimer("insideFitItems");
+ for (auto id : inSelection.insideItems) {
+ auto& item = scene->getItem(id);
+ if (_filter.test(item.getKey())) {
+ ItemBound itemBound(id, item.getBound());
outItems.emplace_back(itemBound);
}
}
}
- }
- // partial & fit items: filter & frustum cull
- {
- PerformanceTimer perfTimer("partialFitItems");
- for (auto id : inSelection.partialItems) {
- auto& item = scene->getItem(id);
- if (_filter.test(item.getKey())) {
- ItemBound itemBound(id, item.getBound());
- if (test.frustumTest(itemBound.bound)) {
+ // inside & subcell items: filter only, culling is disabled
+ {
+ PerformanceTimer perfTimer("insideSmallItems");
+ for (auto id : inSelection.insideSubcellItems) {
+ auto& item = scene->getItem(id);
+ if (_filter.test(item.getKey())) {
+ ItemBound itemBound(id, item.getBound());
outItems.emplace_back(itemBound);
}
}
}
- }
- // partial & subcell items:: filter & frutum cull & solidangle cull
- {
- PerformanceTimer perfTimer("partialSmallItems");
- for (auto id : inSelection.partialSubcellItems) {
- auto& item = scene->getItem(id);
- if (_filter.test(item.getKey())) {
- ItemBound itemBound(id, item.getBound());
- if (test.frustumTest(itemBound.bound)) {
+ // partial & fit items: filter only, culling is disabled
+ {
+ PerformanceTimer perfTimer("partialFitItems");
+ for (auto id : inSelection.partialItems) {
+ auto& item = scene->getItem(id);
+ if (_filter.test(item.getKey())) {
+ ItemBound itemBound(id, item.getBound());
+ outItems.emplace_back(itemBound);
+ }
+ }
+ }
+
+ // partial & subcell items: filter only, culling is disabled
+ {
+ PerformanceTimer perfTimer("partialSmallItems");
+ for (auto id : inSelection.partialSubcellItems) {
+ auto& item = scene->getItem(id);
+ if (_filter.test(item.getKey())) {
+ ItemBound itemBound(id, item.getBound());
+ outItems.emplace_back(itemBound);
+ }
+ }
+ }
+
+ } else {
+
+ // inside & fit items: easy, just filter
+ {
+ PerformanceTimer perfTimer("insideFitItems");
+ for (auto id : inSelection.insideItems) {
+ auto& item = scene->getItem(id);
+ if (_filter.test(item.getKey())) {
+ ItemBound itemBound(id, item.getBound());
+ outItems.emplace_back(itemBound);
+ }
+ }
+ }
+
+ // inside & subcell items: filter & distance cull
+ {
+ PerformanceTimer perfTimer("insideSmallItems");
+ for (auto id : inSelection.insideSubcellItems) {
+ auto& item = scene->getItem(id);
+ if (_filter.test(item.getKey())) {
+ ItemBound itemBound(id, item.getBound());
if (test.solidAngleTest(itemBound.bound)) {
outItems.emplace_back(itemBound);
}
}
}
}
+
+ // partial & fit items: filter & frustum cull
+ {
+ PerformanceTimer perfTimer("partialFitItems");
+ for (auto id : inSelection.partialItems) {
+ auto& item = scene->getItem(id);
+ if (_filter.test(item.getKey())) {
+ ItemBound itemBound(id, item.getBound());
+ if (test.frustumTest(itemBound.bound)) {
+ outItems.emplace_back(itemBound);
+ }
+ }
+ }
+ }
+
+ // partial & subcell items:: filter & frutum cull & solidangle cull
+ {
+ PerformanceTimer perfTimer("partialSmallItems");
+ for (auto id : inSelection.partialSubcellItems) {
+ auto& item = scene->getItem(id);
+ if (_filter.test(item.getKey())) {
+ ItemBound itemBound(id, item.getBound());
+ if (test.frustumTest(itemBound.bound)) {
+ if (test.solidAngleTest(itemBound.bound)) {
+ outItems.emplace_back(itemBound);
+ }
+ }
+ }
+ }
+ }
}
details._rendered += (int)outItems.size();
diff --git a/libraries/render/src/render/CullTask.h b/libraries/render/src/render/CullTask.h
index a6a32e4561..e84f018e91 100644
--- a/libraries/render/src/render/CullTask.h
+++ b/libraries/render/src/render/CullTask.h
@@ -70,14 +70,16 @@ namespace render {
Q_OBJECT
Q_PROPERTY(int numItems READ getNumItems)
Q_PROPERTY(bool freezeFrustum MEMBER freezeFrustum WRITE setFreezeFrustum)
+ Q_PROPERTY(bool skipCulling MEMBER skipCulling WRITE setSkipCulling)
public:
int numItems{ 0 };
int getNumItems() { return numItems; }
bool freezeFrustum{ false };
+ bool skipCulling{ false };
public slots:
void setFreezeFrustum(bool enabled) { freezeFrustum = enabled; emit dirty(); }
-
+ void setSkipCulling(bool enabled) { skipCulling = enabled; emit dirty(); }
signals:
void dirty();
};
@@ -85,6 +87,7 @@ namespace render {
class CullSpatialSelection {
bool _freezeFrustum{ false }; // initialized by Config
bool _justFrozeFrustum{ false };
+ bool _skipCulling{ false };
ViewFrustum _frozenFrutstum;
public:
using Config = CullSpatialSelectionConfig;
diff --git a/libraries/shared/src/Finally.h b/libraries/shared/src/Finally.h
index 59e8be0228..9070d49647 100644
--- a/libraries/shared/src/Finally.h
+++ b/libraries/shared/src/Finally.h
@@ -20,6 +20,10 @@ public:
template
Finally(F f) : _f(f) {}
~Finally() { _f(); }
+ void trigger() {
+ _f();
+ _f = [] {};
+ }
private:
std::function _f;
};
diff --git a/libraries/shared/src/ShapeInfo.h b/libraries/shared/src/ShapeInfo.h
index a3fbe55f36..79390b6680 100644
--- a/libraries/shared/src/ShapeInfo.h
+++ b/libraries/shared/src/ShapeInfo.h
@@ -63,7 +63,7 @@ public:
void appendToPoints (const QVector& newPoints) { _points << newPoints; }
float computeVolume() const;
-
+
/// Returns whether point is inside the shape
/// For compound shapes it will only return whether it is inside the bounding box
bool contains(const glm::vec3& point) const;
diff --git a/libraries/shared/src/shared/NsightHelpers.cpp b/libraries/shared/src/shared/NsightHelpers.cpp
index e48e228588..2539ff8864 100644
--- a/libraries/shared/src/shared/NsightHelpers.cpp
+++ b/libraries/shared/src/shared/NsightHelpers.cpp
@@ -8,6 +8,7 @@
#include "NsightHelpers.h"
+#ifdef _WIN32
#if defined(NSIGHT_FOUND)
#include "nvToolsExt.h"
@@ -15,8 +16,28 @@ ProfileRange::ProfileRange(const char *name) {
nvtxRangePush(name);
}
+ProfileRange::ProfileRange(const char *name, uint32_t argbColor, uint64_t payload) {
+
+ nvtxEventAttributes_t eventAttrib = {0};
+ eventAttrib.version = NVTX_VERSION;
+ eventAttrib.size = NVTX_EVENT_ATTRIB_STRUCT_SIZE;
+ eventAttrib.colorType = NVTX_COLOR_ARGB;
+ eventAttrib.color = argbColor;
+ eventAttrib.messageType = NVTX_MESSAGE_TYPE_ASCII;
+ eventAttrib.message.ascii = name;
+ eventAttrib.payload.llValue = payload;
+ eventAttrib.payloadType = NVTX_PAYLOAD_TYPE_UNSIGNED_INT64;
+
+ nvtxRangePushEx(&eventAttrib);
+}
+
ProfileRange::~ProfileRange() {
nvtxRangePop();
}
+#else
+ProfileRange::ProfileRange(const char *name) {}
+ProfileRange::ProfileRange(const char *name, uint32_t argbColor, uint64_t payload) {}
+ProfileRange::~ProfileRange() {}
#endif
+#endif // _WIN32
diff --git a/libraries/shared/src/shared/NsightHelpers.h b/libraries/shared/src/shared/NsightHelpers.h
index 3acdf14411..9853171b34 100644
--- a/libraries/shared/src/shared/NsightHelpers.h
+++ b/libraries/shared/src/shared/NsightHelpers.h
@@ -9,16 +9,21 @@
#ifndef hifi_gl_NsightHelpers_h
#define hifi_gl_NsightHelpers_h
-#if defined(NSIGHT_FOUND)
- class ProfileRange {
- public:
- ProfileRange(const char *name);
- ~ProfileRange();
- };
+#ifdef _WIN32
+#include
+
+class ProfileRange {
+public:
+ ProfileRange(const char *name);
+ ProfileRange(const char *name, uint32_t argbColor, uint64_t payload);
+ ~ProfileRange();
+};
+
#define PROFILE_RANGE(name) ProfileRange profileRangeThis(name);
+#define PROFILE_RANGE_EX(name, argbColor, payload) ProfileRange profileRangeThis(name, argbColor, payload);
#else
#define PROFILE_RANGE(name)
+#define PROFILE_RANGE_EX(name, argbColor, payload)
#endif
-
-#endif
\ No newline at end of file
+#endif
diff --git a/libraries/ui/src/QmlWebWindowClass.cpp b/libraries/ui/src/QmlWebWindowClass.cpp
index f12fb51b19..43fd5a64df 100644
--- a/libraries/ui/src/QmlWebWindowClass.cpp
+++ b/libraries/ui/src/QmlWebWindowClass.cpp
@@ -14,6 +14,8 @@
#include
+#include
+
#include
#include
@@ -31,19 +33,39 @@ static const char* const URL_PROPERTY = "source";
// Method called by Qt scripts to create a new web window in the overlay
QScriptValue QmlWebWindowClass::constructor(QScriptContext* context, QScriptEngine* engine) {
return QmlWindowClass::internalConstructor("QmlWebWindow.qml", context, engine,
- [&](QObject* object) { return new QmlWebWindowClass(object); });
+ [&](QObject* object) { return new QmlWebWindowClass(object); });
}
QmlWebWindowClass::QmlWebWindowClass(QObject* qmlWindow) : QmlWindowClass(qmlWindow) {
+ _uid = QUuid::createUuid().toString();
+ asQuickItem()->setProperty("uid", _uid);
+ auto webchannelVar = qmlWindow->property("webChannel");
+ _webchannel = qvariant_cast(webchannelVar);
+ Q_ASSERT(_webchannel);
+ _webchannel->registerObject(_uid, this);
}
+void QmlWebWindowClass::emitScriptEvent(const QVariant& scriptMessage) {
+ if (QThread::currentThread() != thread()) {
+ QMetaObject::invokeMethod(this, "emitScriptEvent", Qt::QueuedConnection, Q_ARG(QVariant, scriptMessage));
+ } else {
+ emit scriptEventReceived(scriptMessage);
+ }
+}
-// FIXME remove.
-void QmlWebWindowClass::handleNavigation(const QString& url) {
+void QmlWebWindowClass::emitWebEvent(const QVariant& webMessage) {
+ if (QThread::currentThread() != thread()) {
+ QMetaObject::invokeMethod(this, "emitWebEvent", Qt::QueuedConnection, Q_ARG(QVariant, webMessage));
+ } else {
+ emit webEventReceived(webMessage);
+ }
}
QString QmlWebWindowClass::getURL() const {
QVariant result = DependencyManager::get()->returnFromUiThread([&]()->QVariant {
+ if (_qmlWindow.isNull()) {
+ return QVariant();
+ }
return _qmlWindow->property(URL_PROPERTY);
});
return result.toString();
@@ -54,6 +76,8 @@ extern QString fixupHifiUrl(const QString& urlString);
void QmlWebWindowClass::setURL(const QString& urlString) {
DependencyManager::get()->executeOnUiThread([=] {
- _qmlWindow->setProperty(URL_PROPERTY, fixupHifiUrl(urlString));
+ if (!_qmlWindow.isNull()) {
+ _qmlWindow->setProperty(URL_PROPERTY, fixupHifiUrl(urlString));
+ }
});
}
diff --git a/libraries/ui/src/QmlWebWindowClass.h b/libraries/ui/src/QmlWebWindowClass.h
index 14e533c7b4..35322ef0ea 100644
--- a/libraries/ui/src/QmlWebWindowClass.h
+++ b/libraries/ui/src/QmlWebWindowClass.h
@@ -11,10 +11,13 @@
#include "QmlWindowClass.h"
+class QWebChannel;
+
// FIXME refactor this class to be a QQuickItem derived type and eliminate the needless wrapping
class QmlWebWindowClass : public QmlWindowClass {
Q_OBJECT
Q_PROPERTY(QString url READ getURL CONSTANT)
+ Q_PROPERTY(QString uid READ getUid CONSTANT)
public:
static QScriptValue constructor(QScriptContext* context, QScriptEngine* engine);
@@ -23,12 +26,18 @@ public:
public slots:
QString getURL() const;
void setURL(const QString& url);
+ const QString& getUid() const { return _uid; }
+ void emitScriptEvent(const QVariant& scriptMessage);
+ void emitWebEvent(const QVariant& webMessage);
signals:
void urlChanged();
+ void scriptEventReceived(const QVariant& message);
+ void webEventReceived(const QVariant& message);
-private slots:
- void handleNavigation(const QString& url);
+private:
+ QString _uid;
+ QWebChannel* _webchannel { nullptr };
};
#endif
diff --git a/libraries/ui/src/QmlWindowClass.cpp b/libraries/ui/src/QmlWindowClass.cpp
index 679e86f4ae..37d461acd0 100644
--- a/libraries/ui/src/QmlWindowClass.cpp
+++ b/libraries/ui/src/QmlWindowClass.cpp
@@ -26,10 +26,6 @@
#include "OffscreenUi.h"
-QWebSocketServer* QmlWindowClass::_webChannelServer { nullptr };
-static QWebChannel webChannel;
-static const uint16_t WEB_CHANNEL_PORT = 51016;
-static std::atomic nextWindowId;
static const char* const SOURCE_PROPERTY = "source";
static const char* const TITLE_PROPERTY = "title";
static const char* const WIDTH_PROPERTY = "width";
@@ -37,54 +33,6 @@ static const char* const HEIGHT_PROPERTY = "height";
static const char* const VISIBILE_PROPERTY = "visible";
static const char* const TOOLWINDOW_PROPERTY = "toolWindow";
-void QmlScriptEventBridge::emitWebEvent(const QString& data) {
- QMetaObject::invokeMethod(this, "webEventReceived", Qt::QueuedConnection, Q_ARG(QString, data));
-}
-
-void QmlScriptEventBridge::emitScriptEvent(const QString& data) {
- QMetaObject::invokeMethod(this, "scriptEventReceived", Qt::QueuedConnection,
- Q_ARG(int, _webWindow->getWindowId()), Q_ARG(QString, data));
-}
-
-class QmlWebTransport : public QWebChannelAbstractTransport {
- Q_OBJECT
-public:
- QmlWebTransport(QWebSocket* webSocket) : _webSocket(webSocket) {
- // Translate from the websocket layer to the webchannel layer
- connect(webSocket, &QWebSocket::textMessageReceived, [this](const QString& message) {
- QJsonParseError error;
- QJsonDocument document = QJsonDocument::fromJson(message.toUtf8(), &error);
- if (error.error || !document.isObject()) {
- qWarning() << "Unable to parse incoming JSON message" << message;
- return;
- }
- emit messageReceived(document.object(), this);
- });
- }
-
- virtual void sendMessage(const QJsonObject &message) override {
- // Translate from the webchannel layer to the websocket layer
- _webSocket->sendTextMessage(QJsonDocument(message).toJson(QJsonDocument::Compact));
- }
-
-private:
- QWebSocket* const _webSocket;
-};
-
-
-void QmlWindowClass::setupServer() {
- if (!_webChannelServer) {
- _webChannelServer = new QWebSocketServer("EventBridge Server", QWebSocketServer::NonSecureMode);
- if (!_webChannelServer->listen(QHostAddress::LocalHost, WEB_CHANNEL_PORT)) {
- qFatal("Failed to open web socket server.");
- }
-
- QObject::connect(_webChannelServer, &QWebSocketServer::newConnection, [] {
- webChannel.connectTo(new QmlWebTransport(_webChannelServer->nextPendingConnection()));
- });
- }
-}
-
QScriptValue QmlWindowClass::internalConstructor(const QString& qmlSource,
QScriptContext* context, QScriptEngine* engine,
std::function builder)
@@ -168,10 +116,8 @@ QScriptValue QmlWindowClass::internalConstructor(const QString& qmlSource,
}
offscreenUi->returnFromUiThread([&] {
- setupServer();
retVal = builder(newTab);
retVal->_toolWindow = true;
- registerObject(url.toLower(), retVal);
return QVariant();
});
} else {
@@ -179,10 +125,8 @@ QScriptValue QmlWindowClass::internalConstructor(const QString& qmlSource,
QMetaObject::invokeMethod(offscreenUi.data(), "load", Qt::BlockingQueuedConnection,
Q_ARG(const QString&, qmlSource),
Q_ARG(std::function, [&](QQmlContext* context, QObject* object) {
- setupServer();
retVal = builder(object);
context->engine()->setObjectOwnership(retVal->_qmlWindow, QQmlEngine::CppOwnership);
- registerObject(url.toLower(), retVal);
if (!title.isEmpty()) {
retVal->setTitle(title);
}
@@ -209,12 +153,9 @@ QScriptValue QmlWindowClass::constructor(QScriptContext* context, QScriptEngine*
});
}
-QmlWindowClass::QmlWindowClass(QObject* qmlWindow)
- : _windowId(++nextWindowId), _qmlWindow(qmlWindow)
-{
- qDebug() << "Created window with ID " << _windowId;
+QmlWindowClass::QmlWindowClass(QObject* qmlWindow) : _qmlWindow(qmlWindow) {
Q_ASSERT(_qmlWindow);
- Q_ASSERT(dynamic_cast(_qmlWindow));
+ Q_ASSERT(dynamic_cast(_qmlWindow.data()));
// Forward messages received from QML on to the script
connect(_qmlWindow, SIGNAL(sendToScript(QVariant)), this, SIGNAL(fromQml(const QVariant&)), Qt::QueuedConnection);
}
@@ -228,19 +169,11 @@ QmlWindowClass::~QmlWindowClass() {
close();
}
-void QmlWindowClass::registerObject(const QString& name, QObject* object) {
- webChannel.registerObject(name, object);
-}
-
-void QmlWindowClass::deregisterObject(QObject* object) {
- webChannel.deregisterObject(object);
-}
-
QQuickItem* QmlWindowClass::asQuickItem() const {
if (_toolWindow) {
return DependencyManager::get()->getToolWindow();
}
- return dynamic_cast(_qmlWindow);
+ return _qmlWindow.isNull() ? nullptr : dynamic_cast(_qmlWindow.data());
}
void QmlWindowClass::setVisible(bool visible) {
@@ -260,32 +193,34 @@ void QmlWindowClass::setVisible(bool visible) {
bool QmlWindowClass::isVisible() const {
// The tool window itself has special logic based on whether any tabs are enabled
- if (_toolWindow) {
- auto targetTab = dynamic_cast(_qmlWindow);
- return DependencyManager::get()->returnFromUiThread([&] {
- return QVariant::fromValue(targetTab->isEnabled());
- }).toBool();
- } else {
- QQuickItem* targetWindow = asQuickItem();
- return DependencyManager::get()->returnFromUiThread([&] {
- return QVariant::fromValue(targetWindow->isVisible());
- }).toBool();
- }
+ return DependencyManager::get()->returnFromUiThread([&] {
+ if (_qmlWindow.isNull()) {
+ return QVariant::fromValue(false);
+ }
+ if (_toolWindow) {
+ return QVariant::fromValue(dynamic_cast(_qmlWindow.data())->isEnabled());
+ } else {
+ return QVariant::fromValue(asQuickItem()->isVisible());
+ }
+ }).toBool();
}
glm::vec2 QmlWindowClass::getPosition() const {
- QQuickItem* targetWindow = asQuickItem();
QVariant result = DependencyManager::get()->returnFromUiThread([&]()->QVariant {
- return targetWindow->position();
+ if (_qmlWindow.isNull()) {
+ return QVariant(QPointF(0, 0));
+ }
+ return asQuickItem()->position();
});
return toGlm(result.toPointF());
}
void QmlWindowClass::setPosition(const glm::vec2& position) {
- QQuickItem* targetWindow = asQuickItem();
DependencyManager::get()->executeOnUiThread([=] {
- targetWindow->setPosition(QPointF(position.x, position.y));
+ if (!_qmlWindow.isNull()) {
+ asQuickItem()->setPosition(QPointF(position.x, position.y));
+ }
});
}
@@ -299,17 +234,21 @@ glm::vec2 toGlm(const QSizeF& size) {
}
glm::vec2 QmlWindowClass::getSize() const {
- QQuickItem* targetWindow = asQuickItem();
QVariant result = DependencyManager::get()->returnFromUiThread([&]()->QVariant {
+ if (_qmlWindow.isNull()) {
+ return QVariant(QSizeF(0, 0));
+ }
+ QQuickItem* targetWindow = asQuickItem();
return QSizeF(targetWindow->width(), targetWindow->height());
});
return toGlm(result.toSizeF());
}
void QmlWindowClass::setSize(const glm::vec2& size) {
- QQuickItem* targetWindow = asQuickItem();
DependencyManager::get()->executeOnUiThread([=] {
- targetWindow->setSize(QSizeF(size.x, size.y));
+ if (!_qmlWindow.isNull()) {
+ asQuickItem()->setSize(QSizeF(size.x, size.y));
+ }
});
}
@@ -318,9 +257,10 @@ void QmlWindowClass::setSize(int width, int height) {
}
void QmlWindowClass::setTitle(const QString& title) {
- QQuickItem* targetWindow = asQuickItem();
DependencyManager::get()->executeOnUiThread([=] {
- targetWindow->setProperty(TITLE_PROPERTY, title);
+ if (!_qmlWindow.isNull()) {
+ asQuickItem()->setProperty(TITLE_PROPERTY, title);
+ }
});
}
@@ -345,7 +285,12 @@ void QmlWindowClass::hasClosed() {
}
void QmlWindowClass::raise() {
- QMetaObject::invokeMethod(asQuickItem(), "raise", Qt::QueuedConnection);
+ auto offscreenUi = DependencyManager::get();
+ offscreenUi->executeOnUiThread([=] {
+ if (!_qmlWindow.isNull()) {
+ QMetaObject::invokeMethod(asQuickItem(), "raise", Qt::DirectConnection);
+ }
+ });
}
#include "QmlWindowClass.moc"
diff --git a/libraries/ui/src/QmlWindowClass.h b/libraries/ui/src/QmlWindowClass.h
index 26152b1f24..eda6ce674c 100644
--- a/libraries/ui/src/QmlWindowClass.h
+++ b/libraries/ui/src/QmlWindowClass.h
@@ -10,42 +10,21 @@
#define hifi_ui_QmlWindowClass_h
#include
-#include
+#include
#include
#include
-#include
+
+#include
class QScriptEngine;
class QScriptContext;
-class QmlWindowClass;
-class QWebSocketServer;
-class QWebSocket;
-
-class QmlScriptEventBridge : public QObject {
- Q_OBJECT
-public:
- QmlScriptEventBridge(const QmlWindowClass* webWindow) : _webWindow(webWindow) {}
-
-public slots :
- void emitWebEvent(const QString& data);
- void emitScriptEvent(const QString& data);
-
-signals:
- void webEventReceived(const QString& data);
- void scriptEventReceived(int windowId, const QString& data);
-
-private:
- const QmlWindowClass* _webWindow { nullptr };
- QWebSocket *_socket { nullptr };
-};
// FIXME refactor this class to be a QQuickItem derived type and eliminate the needless wrapping
class QmlWindowClass : public QObject {
Q_OBJECT
Q_PROPERTY(QObject* eventBridge READ getEventBridge CONSTANT)
- Q_PROPERTY(int windowId READ getWindowId CONSTANT)
- Q_PROPERTY(glm::vec2 position READ getPosition WRITE setPosition)
- Q_PROPERTY(glm::vec2 size READ getSize WRITE setSize)
+ Q_PROPERTY(glm::vec2 position READ getPosition WRITE setPosition NOTIFY positionChanged)
+ Q_PROPERTY(glm::vec2 size READ getSize WRITE setSize NOTIFY sizeChanged)
Q_PROPERTY(bool visible READ isVisible WRITE setVisible NOTIFY visibilityChanged)
public:
@@ -64,21 +43,19 @@ public slots:
glm::vec2 getSize() const;
void setSize(const glm::vec2& size);
void setSize(int width, int height);
-
void setTitle(const QString& title);
-
- // Ugh.... do not want to do
Q_INVOKABLE void raise();
Q_INVOKABLE void close();
- Q_INVOKABLE int getWindowId() const { return _windowId; };
- Q_INVOKABLE QmlScriptEventBridge* getEventBridge() const { return _eventBridge; };
+ Q_INVOKABLE QObject* getEventBridge() { return this; };
// Scripts can use this to send a message to the QML object
void sendToQml(const QVariant& message);
signals:
void visibilityChanged(bool visible); // Tool window
+ void positionChanged();
+ void sizeChanged();
void moved(glm::vec2 position);
void resized(QSizeF size);
void closed();
@@ -92,19 +69,13 @@ protected:
static QScriptValue internalConstructor(const QString& qmlSource,
QScriptContext* context, QScriptEngine* engine,
std::function function);
- static void setupServer();
- static void registerObject(const QString& name, QObject* object);
- static void deregisterObject(QObject* object);
- static QWebSocketServer* _webChannelServer;
QQuickItem* asQuickItem() const;
- QmlScriptEventBridge* const _eventBridge { new QmlScriptEventBridge(this) };
// FIXME needs to be initialized in the ctor once we have support
// for tool window panes in QML
bool _toolWindow { false };
- const int _windowId;
- QObject* _qmlWindow;
+ QPointer _qmlWindow;
QString _source;
};
diff --git a/plugins/oculus/src/OculusDisplayPlugin.cpp b/plugins/oculus/src/OculusDisplayPlugin.cpp
index 5ab56e1659..71a858e1e8 100644
--- a/plugins/oculus/src/OculusDisplayPlugin.cpp
+++ b/plugins/oculus/src/OculusDisplayPlugin.cpp
@@ -6,6 +6,7 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "OculusDisplayPlugin.h"
+#include
#include "OculusHelpers.h"
const QString OculusDisplayPlugin::NAME("Oculus Rift");
@@ -54,6 +55,9 @@ void OculusDisplayPlugin::updateFrameData() {
}
void OculusDisplayPlugin::hmdPresent() {
+
+ PROFILE_RANGE_EX(__FUNCTION__, 0xff00ff00, (uint64_t)_currentRenderFrameIndex)
+
if (!_currentSceneTexture) {
return;
}
diff --git a/plugins/openvr/src/OpenVrDisplayPlugin.cpp b/plugins/openvr/src/OpenVrDisplayPlugin.cpp
index 0cd9bac15f..0e7541066e 100644
--- a/plugins/openvr/src/OpenVrDisplayPlugin.cpp
+++ b/plugins/openvr/src/OpenVrDisplayPlugin.cpp
@@ -21,7 +21,7 @@
#include
#include
#include
-
+#include
#include "OpenVrHelpers.h"
Q_DECLARE_LOGGING_CATEGORY(displayplugins)
@@ -69,6 +69,9 @@ void OpenVrDisplayPlugin::internalActivate() {
_compositor = vr::VRCompositor();
Q_ASSERT(_compositor);
+ // enable async time warp
+ // _compositor->ForceInterleavedReprojectionOn(true);
+
// set up default sensor space such that the UI overlay will align with the front of the room.
auto chaperone = vr::VRChaperone();
if (chaperone) {
@@ -119,14 +122,11 @@ void OpenVrDisplayPlugin::updateHeadPose(uint32_t frameIndex) {
float vsyncToPhotons = _system->GetFloatTrackedDeviceProperty(vr::k_unTrackedDeviceIndex_Hmd, vr::Prop_SecondsFromVsyncToPhotons_Float);
#if THREADED_PRESENT
- // TODO: this seems awfuly long, 44ms total, but it produced the best results.
+ // 3 frames of prediction + vsyncToPhotons = 44ms total
const float NUM_PREDICTION_FRAMES = 3.0f;
float predictedSecondsFromNow = NUM_PREDICTION_FRAMES * frameDuration + vsyncToPhotons;
#else
- uint64_t frameCounter;
- float timeSinceLastVsync;
- _system->GetTimeSinceLastVsync(&timeSinceLastVsync, &frameCounter);
- float predictedSecondsFromNow = 3.0f * frameDuration - timeSinceLastVsync + vsyncToPhotons;
+ float predictedSecondsFromNow = frameDuration + vsyncToPhotons;
#endif
vr::TrackedDevicePose_t predictedTrackedDevicePose[vr::k_unMaxTrackedDeviceCount];
@@ -144,6 +144,9 @@ void OpenVrDisplayPlugin::updateHeadPose(uint32_t frameIndex) {
}
void OpenVrDisplayPlugin::hmdPresent() {
+
+ PROFILE_RANGE_EX(__FUNCTION__, 0xff00ff00, (uint64_t)_currentRenderFrameIndex)
+
// Flip y-axis since GL UV coords are backwards.
static vr::VRTextureBounds_t leftBounds{ 0, 0, 0.5f, 1 };
static vr::VRTextureBounds_t rightBounds{ 0.5f, 0, 1, 1 };
@@ -152,6 +155,10 @@ void OpenVrDisplayPlugin::hmdPresent() {
_compositor->Submit(vr::Eye_Left, &texture, &leftBounds);
_compositor->Submit(vr::Eye_Right, &texture, &rightBounds);
+}
+
+void OpenVrDisplayPlugin::postPreview() {
+ PROFILE_RANGE_EX(__FUNCTION__, 0xff00ff00, (uint64_t)_currentRenderFrameIndex)
vr::TrackedDevicePose_t currentTrackedDevicePose[vr::k_unMaxTrackedDeviceCount];
_compositor->WaitGetPoses(currentTrackedDevicePose, vr::k_unMaxTrackedDeviceCount, nullptr, 0);
diff --git a/plugins/openvr/src/OpenVrDisplayPlugin.h b/plugins/openvr/src/OpenVrDisplayPlugin.h
index caaf75a4d0..78b76cb78d 100644
--- a/plugins/openvr/src/OpenVrDisplayPlugin.h
+++ b/plugins/openvr/src/OpenVrDisplayPlugin.h
@@ -35,6 +35,7 @@ protected:
void hmdPresent() override;
bool isHmdMounted() const override;
+ void postPreview() override;
private:
vr::IVRSystem* _system { nullptr };
diff --git a/plugins/openvr/src/OpenVrHelpers.cpp b/plugins/openvr/src/OpenVrHelpers.cpp
index d2c3649933..8ddf028dd2 100644
--- a/plugins/openvr/src/OpenVrHelpers.cpp
+++ b/plugins/openvr/src/OpenVrHelpers.cpp
@@ -51,7 +51,7 @@ vr::IVRSystem* acquireOpenVrSystem() {
if (!activeHmd) {
qCDebug(displayplugins) << "openvr: No vr::IVRSystem instance active, building";
vr::EVRInitError eError = vr::VRInitError_None;
- activeHmd = vr::VR_Init(&eError);
+ activeHmd = vr::VR_Init(&eError, vr::VRApplication_Scene);
qCDebug(displayplugins) << "openvr display: HMD is " << activeHmd << " error is " << eError;
}
if (activeHmd) {
diff --git a/server-console/src/log.js b/server-console/src/log.js
index e45848e5a5..3634eaeaa7 100644
--- a/server-console/src/log.js
+++ b/server-console/src/log.js
@@ -44,6 +44,14 @@ ready = function() {
var domainServer = remote.getGlobal('domainServer');
var acMonitor = remote.getGlobal('acMonitor');
+ var pendingLines = {
+ 'ds': new Array(),
+ 'ac': new Array()
+ };
+
+ var UPDATE_INTERVAL = 16; // Update log at ~60 fps
+ var interval = setInterval(flushPendingLines, UPDATE_INTERVAL);
+
var logWatchers = {
'ds': {
},
@@ -83,7 +91,7 @@ ready = function() {
var logTail = new Tail(cleanFilePath, '\n', { start: start, interval: 500 });
logTail.on('line', function(msg) {
- appendLogMessage(msg, stream);
+ pendingLines[stream].push(msg);
});
logTail.on('error', function(error) {
@@ -107,6 +115,7 @@ ready = function() {
}
window.onbeforeunload = function(e) {
+ clearInterval(interval);
domainServer.removeListener('logs-updated', updateLogFiles);
acMonitor.removeListener('logs-updated', updateLogFiles);
};
@@ -164,14 +173,23 @@ ready = function() {
return !filter || message.toLowerCase().indexOf(filter) >= 0;
}
- function appendLogMessage(msg, name) {
+ function appendLogMessages(name) {
+ var array = pendingLines[name];
+ if (array.length === 0) {
+ return;
+ }
+ if (array.length > maxLogLines) {
+ array = array.slice(-maxLogLines);
+ }
+
+ console.log(name, array.length);
+
var id = name == "ds" ? "domain-server" : "assignment-client";
var $pidLog = $('#' + id);
- var size = ++tabStates[id].size;
+ var size = tabStates[id].size + array.length;
if (size > maxLogLines) {
- $pidLog.find('div.log-line:first').remove();
- removed = true;
+ $pidLog.find('div.log-line:lt(' + (size - maxLogLines) + ')').remove();
}
var wasAtBottom = false;
@@ -179,17 +197,25 @@ ready = function() {
wasAtBottom = $pidLog[0].scrollTop >= ($pidLog[0].scrollHeight - $pidLog.height());
}
- var $logLine = $('').text(msg);
- if (!shouldDisplayLogMessage(msg)) {
- $logLine.hide();
+ for (line in array) {
+ var $logLine = $('
').text(array[line]);
+ if (!shouldDisplayLogMessage(array[line])) {
+ $logLine.hide();
+ }
+
+ $pidLog.append($logLine);
}
- $pidLog.append($logLine);
+ delete pendingLines[name];
+ pendingLines[name] = new Array();
if (wasAtBottom) {
$pidLog.scrollTop($pidLog[0].scrollHeight);
}
-
+ }
+ function flushPendingLines() {
+ appendLogMessages('ds');
+ appendLogMessages('ac');
}
// handle filtering of table rows on input change
diff --git a/tests/physics/src/BulletTestUtils.h b/tests/physics/src/BulletTestUtils.h
index 9166f80ba1..c047a2c1ce 100644
--- a/tests/physics/src/BulletTestUtils.h
+++ b/tests/physics/src/BulletTestUtils.h
@@ -14,6 +14,8 @@
#include
+#include
+
// Implements functionality in QTestExtensions.h for glm types
// There are 3 functions in here (which need to be defined for all types that use them):
//
diff --git a/tests/physics/src/MeshMassPropertiesTests.cpp b/tests/physics/src/MeshMassPropertiesTests.cpp
index 825721641f..0b22ea1223 100644
--- a/tests/physics/src/MeshMassPropertiesTests.cpp
+++ b/tests/physics/src/MeshMassPropertiesTests.cpp
@@ -33,7 +33,7 @@ void MeshMassPropertiesTests::testParallelAxisTheorem() {
// verity we can compute the inertia tensor of a box in two different ways:
// (a) as one box
// (b) as a combination of two partial boxes.
-
+
btScalar bigBoxX = 7.0f;
btScalar bigBoxY = 9.0f;
btScalar bigBoxZ = 11.0f;
@@ -62,9 +62,9 @@ void MeshMassPropertiesTests::testParallelAxisTheorem() {
}
void MeshMassPropertiesTests::testTetrahedron(){
- // given the four vertices of a tetrahedron verify the analytic formula for inertia
+ // given the four vertices of a tetrahedron verify the analytic formula for inertia
// agrees with expected results
-
+
// these numbers from the Tonon paper:
btVector3 points[4];
points[0] = btVector3(8.33220f, -11.86875f, 0.93355f);
@@ -102,14 +102,14 @@ void MeshMassPropertiesTests::testTetrahedron(){
}
btMatrix3x3 inertia;
computeTetrahedronInertia(volume, points, inertia);
-
+
QCOMPARE_WITH_ABS_ERROR(volume, expectedVolume, acceptableRelativeError * volume);
-
+
QCOMPARE_WITH_RELATIVE_ERROR(inertia, expectedInertia, acceptableRelativeError);
}
void MeshMassPropertiesTests::testOpenTetrahedonMesh() {
- // given the simplest possible mesh (open, with one triangle)
+ // given the simplest possible mesh (open, with one triangle)
// verify MeshMassProperties computes the right nubers
// these numbers from the Tonon paper:
@@ -155,7 +155,7 @@ void MeshMassPropertiesTests::testOpenTetrahedonMesh() {
void MeshMassPropertiesTests::testClosedTetrahedronMesh() {
// given a tetrahedron as a closed mesh of four tiangles
// verify MeshMassProperties computes the right nubers
-
+
// these numbers from the Tonon paper:
VectorOfPoints points;
points.push_back(btVector3(8.33220f, -11.86875f, 0.93355f));
@@ -186,7 +186,7 @@ void MeshMassPropertiesTests::testClosedTetrahedronMesh() {
// compute mass properties
MeshMassProperties mesh(points, triangles);
-
+
// verify
QCOMPARE_WITH_ABS_ERROR(mesh._volume, expectedVolume, acceptableRelativeError * expectedVolume);
QCOMPARE_WITH_ABS_ERROR(mesh._centerOfMass, expectedCenterOfMass, acceptableAbsoluteError);
@@ -210,7 +210,7 @@ void MeshMassPropertiesTests::testClosedTetrahedronMesh() {
void MeshMassPropertiesTests::testBoxAsMesh() {
// verify that a mesh box produces the same mass properties as the analytic box.
-
+
// build a box:
// /
// y
@@ -265,7 +265,7 @@ void MeshMassPropertiesTests::testBoxAsMesh() {
MeshMassProperties mesh(points, triangles);
// verify
-
+
QCOMPARE_WITH_ABS_ERROR(mesh._volume, expectedVolume, acceptableRelativeError * expectedVolume);
QCOMPARE_WITH_ABS_ERROR(mesh._centerOfMass, expectedCenterOfMass, acceptableAbsoluteError);
diff --git a/tests/physics/src/ShapeManagerTests.cpp b/tests/physics/src/ShapeManagerTests.cpp
index 1ee7cd561e..66ac9d0c4a 100644
--- a/tests/physics/src/ShapeManagerTests.cpp
+++ b/tests/physics/src/ShapeManagerTests.cpp
@@ -21,7 +21,7 @@ void ShapeManagerTests::testShapeAccounting() {
ShapeManager shapeManager;
ShapeInfo info;
info.setBox(glm::vec3(1.0f, 1.0f, 1.0f));
-
+
int numReferences = shapeManager.getNumReferences(info);
QCOMPARE(numReferences, 0);
@@ -42,10 +42,10 @@ void ShapeManagerTests::testShapeAccounting() {
QCOMPARE(numReferences, expectedNumReferences);
// release all references
- bool released = shapeManager.releaseShape(info);
+ bool released = shapeManager.releaseShape(shape);
numReferences--;
while (numReferences > 0) {
- released = shapeManager.releaseShape(info) && released;
+ released = shapeManager.releaseShape(shape) && released;
numReferences--;
}
QCOMPARE(released, true);
@@ -69,7 +69,7 @@ void ShapeManagerTests::testShapeAccounting() {
QCOMPARE(numReferences, 1);
// release reference and verify that it is collected as garbage
- released = shapeManager.releaseShape(info);
+ released = shapeManager.releaseShape(shape);
shapeManager.collectGarbage();
QCOMPARE(shapeManager.getNumShapes(), 0);
QCOMPARE(shapeManager.hasShape(shape), false);
@@ -183,3 +183,58 @@ void ShapeManagerTests::addCapsuleShape() {
QCOMPARE(shape, otherShape);
*/
}
+
+void ShapeManagerTests::addCompoundShape() {
+ // initialize some points for generating tetrahedral convex hulls
+ QVector tetrahedron;
+ tetrahedron.push_back(glm::vec3(1.0f, 1.0f, 1.0f));
+ tetrahedron.push_back(glm::vec3(1.0f, -1.0f, -1.0f));
+ tetrahedron.push_back(glm::vec3(-1.0f, 1.0f, -1.0f));
+ tetrahedron.push_back(glm::vec3(-1.0f, -1.0f, 1.0f));
+ int numHullPoints = tetrahedron.size();
+
+ // compute the points of the hulls
+ QVector< QVector > hulls;
+ int numHulls = 5;
+ glm::vec3 offsetNormal(1.0f, 0.0f, 0.0f);
+ for (int i = 0; i < numHulls; ++i) {
+ glm::vec3 offset = (float)(i - numHulls/2) * offsetNormal;
+ QVector hull;
+ float radius = (float)(i + 1);
+ for (int j = 0; j < numHullPoints; ++j) {
+ glm::vec3 point = radius * tetrahedron[j] + offset;
+ hull.push_back(point);
+ }
+ hulls.push_back(hull);
+ }
+
+ // create the ShapeInfo
+ ShapeInfo info;
+ info.setConvexHulls(hulls);
+
+ // create the shape
+ ShapeManager shapeManager;
+ btCollisionShape* shape = shapeManager.getShape(info);
+ QVERIFY(shape != nullptr);
+
+ // verify the shape is correct type
+ QCOMPARE(shape->getShapeType(), (int)COMPOUND_SHAPE_PROXYTYPE);
+
+ // verify the shape has correct number of children
+ btCompoundShape* compoundShape = static_cast(shape);
+ QCOMPARE(compoundShape->getNumChildShapes(), numHulls);
+
+ // verify manager has only one shape
+ QCOMPARE(shapeManager.getNumShapes(), 1);
+ QCOMPARE(shapeManager.getNumReferences(info), 1);
+
+ // release the shape
+ shapeManager.releaseShape(shape);
+ QCOMPARE(shapeManager.getNumShapes(), 1);
+ QCOMPARE(shapeManager.getNumReferences(info), 0);
+
+ // collect garbage
+ shapeManager.collectGarbage();
+ QCOMPARE(shapeManager.getNumShapes(), 0);
+ QCOMPARE(shapeManager.getNumReferences(info), 0);
+}
diff --git a/tests/physics/src/ShapeManagerTests.h b/tests/physics/src/ShapeManagerTests.h
index a4b7fbecd1..cff02dcfa4 100644
--- a/tests/physics/src/ShapeManagerTests.h
+++ b/tests/physics/src/ShapeManagerTests.h
@@ -16,7 +16,7 @@
class ShapeManagerTests : public QObject {
Q_OBJECT
-
+
private slots:
void testShapeAccounting();
void addManyShapes();
@@ -24,6 +24,7 @@ private slots:
void addSphereShape();
void addCylinderShape();
void addCapsuleShape();
+ void addCompoundShape();
};
#endif // hifi_ShapeManagerTests_h
diff --git a/tools/udt-test/src/UDTTest.cpp b/tools/udt-test/src/UDTTest.cpp
index 533e6371e9..2b5e306b09 100644
--- a/tools/udt-test/src/UDTTest.cpp
+++ b/tools/udt-test/src/UDTTest.cpp
@@ -77,7 +77,7 @@ UDTTest::UDTTest(int& argc, char** argv) :
// randomize the seed for packet size randomization
srand(time(NULL));
-
+
_socket.bind(QHostAddress::AnyIPv4, _argumentParser.value(PORT_OPTION).toUInt());
qDebug() << "Test socket is listening on" << _socket.localPort();