Merge branch 'master' of github.com:highfidelity/hifi into set-model-entity-joints

This commit is contained in:
Seth Alves 2016-01-04 19:17:45 -08:00
commit afc1757adf
29 changed files with 598 additions and 309 deletions

View file

@ -23,14 +23,14 @@ var WANT_DEBUG = false;
// these tune time-averaging and "on" value for analog trigger // these tune time-averaging and "on" value for analog trigger
// //
var TRIGGER_SMOOTH_RATIO = 0.1; // Time averaging of trigger - 0.0 disables smoothing var TRIGGER_SMOOTH_RATIO = 0.1; // Time averaging of trigger - 0.0 disables smoothing
var TRIGGER_ON_VALUE = 0.4; // Squeezed just enough to activate search or near grab var TRIGGER_ON_VALUE = 0.4; // Squeezed just enough to activate search or near grab
var TRIGGER_GRAB_VALUE = 0.85; // Squeezed far enough to complete distant grab var TRIGGER_GRAB_VALUE = 0.85; // Squeezed far enough to complete distant grab
var TRIGGER_OFF_VALUE = 0.15; var TRIGGER_OFF_VALUE = 0.15;
var BUMPER_ON_VALUE = 0.5; var BUMPER_ON_VALUE = 0.5;
var HAND_HEAD_MIX_RATIO = 0.0; // 0 = only use hands for search/move. 1 = only use head for search/move. var HAND_HEAD_MIX_RATIO = 0.0; // 0 = only use hands for search/move. 1 = only use head for search/move.
// //
// distant manipulation // distant manipulation
@ -421,7 +421,7 @@ function MyController(hand) {
this.searchSphereOn = function(location, size, color) { this.searchSphereOn = function(location, size, color) {
if (this.searchSphere === null) { if (this.searchSphere === null) {
var sphereProperties = { var sphereProperties = {
position: location, position: location,
size: size, size: size,
color: color, color: color,
alpha: SEARCH_SPHERE_ALPHA, alpha: SEARCH_SPHERE_ALPHA,
@ -429,10 +429,15 @@ function MyController(hand) {
visible: true visible: true
} }
this.searchSphere = Overlays.addOverlay("sphere", sphereProperties); this.searchSphere = Overlays.addOverlay("sphere", sphereProperties);
} else { } else {
Overlays.editOverlay(this.searchSphere, { position: location, size: size, color: color, visible: true }); Overlays.editOverlay(this.searchSphere, {
position: location,
size: size,
color: color,
visible: true
});
} }
} }
this.overlayLineOn = function(closePoint, farPoint, color) { this.overlayLineOn = function(closePoint, farPoint, color) {
if (this.overlayLine === null) { if (this.overlayLine === null) {
@ -765,7 +770,7 @@ function MyController(hand) {
if (this.triggerSmoothedSqueezed() || this.bumperSqueezed()) { if (this.triggerSmoothedSqueezed() || this.bumperSqueezed()) {
this.lastPickTime = 0; this.lastPickTime = 0;
var controllerHandInput = (this.hand === RIGHT_HAND) ? Controller.Standard.RightHand : Controller.Standard.LeftHand; var controllerHandInput = (this.hand === RIGHT_HAND) ? Controller.Standard.RightHand : Controller.Standard.LeftHand;
this.startingHandRotation = Controller.getPoseValue(controllerHandInput).rotation; this.startingHandRotation = Controller.getPoseValue(controllerHandInput).rotation;
if (this.triggerSmoothedSqueezed()) { if (this.triggerSmoothedSqueezed()) {
this.setState(STATE_SEARCHING); this.setState(STATE_SEARCHING);
} else { } else {
@ -788,13 +793,19 @@ function MyController(hand) {
var controllerHandInput = (this.hand === RIGHT_HAND) ? Controller.Standard.RightHand : Controller.Standard.LeftHand; var controllerHandInput = (this.hand === RIGHT_HAND) ? Controller.Standard.RightHand : Controller.Standard.LeftHand;
var currentHandRotation = Controller.getPoseValue(controllerHandInput).rotation; var currentHandRotation = Controller.getPoseValue(controllerHandInput).rotation;
var handDeltaRotation = Quat.multiply(currentHandRotation, Quat.inverse(this.startingHandRotation)); var handDeltaRotation = Quat.multiply(currentHandRotation, Quat.inverse(this.startingHandRotation));
var distantPickRay = { var distantPickRay = {
origin: Camera.position, origin: Camera.position,
direction: Vec3.mix(Quat.getUp(this.getHandRotation()), Quat.getFront(Camera.orientation), HAND_HEAD_MIX_RATIO), direction: Vec3.mix(Quat.getUp(this.getHandRotation()), Quat.getFront(Camera.orientation), HAND_HEAD_MIX_RATIO),
length: PICK_MAX_DISTANCE length: PICK_MAX_DISTANCE
}; };
var searchVisualizationPickRay = {
origin: handPosition,
direction: Quat.getUp(this.getHandRotation()),
length: PICK_MAX_DISTANCE
};
// Pick at some maximum rate, not always // Pick at some maximum rate, not always
var pickRays = []; var pickRays = [];
var now = Date.now(); var now = Date.now();
@ -1015,6 +1026,11 @@ function MyController(hand) {
if (USE_PARTICLE_BEAM_FOR_SEARCHING === true) { if (USE_PARTICLE_BEAM_FOR_SEARCHING === true) {
this.handleParticleBeam(distantPickRay.origin, this.getHandRotation(), NO_INTERSECT_COLOR); this.handleParticleBeam(distantPickRay.origin, this.getHandRotation(), NO_INTERSECT_COLOR);
} }
if (USE_OVERLAY_LINES_FOR_SEARCHING === true) {
this.overlayLineOn(searchVisualizationPickRay.origin, Vec3.sum(searchVisualizationPickRay.origin, Vec3.multiply(searchVisualizationPickRay.direction, LINE_LENGTH)), NO_INTERSECT_COLOR);
}
if (this.intersectionDistance > 0) { if (this.intersectionDistance > 0) {
var SPHERE_INTERSECTION_SIZE = 0.011; var SPHERE_INTERSECTION_SIZE = 0.011;
var SEARCH_SPHERE_FOLLOW_RATE = 0.50; var SEARCH_SPHERE_FOLLOW_RATE = 0.50;

View file

@ -24,6 +24,9 @@ RaveStick = function(spawnPosition) {
green: 10, green: 10,
blue: 40 blue: 40
}]; }];
var stick = Entities.addEntity({ var stick = Entities.addEntity({
type: "Model", type: "Model",
name: "raveStick", name: "raveStick",
@ -40,23 +43,25 @@ RaveStick = function(spawnPosition) {
userData: JSON.stringify({ userData: JSON.stringify({
grabbableKey: { grabbableKey: {
spatialKey: { spatialKey: {
rightRelativePosition: { rightRelativePosition: {
x: 0.02, x: 0.02,
y: 0, y: 0,
z: 0 z: 0
},
leftRelativePosition: {
x: -0.02,
y: 0,
z: 0
},
relativeRotation: Quat.fromPitchYawRollDegrees(90, 90, 0)
}, },
leftRelativePosition: {
x: -0.02,
y: 0,
z: 0
},
relativeRotation: Quat.fromPitchYawRollDegrees(90, 90, 0)
},
invertSolidWhileHeld: true invertSolidWhileHeld: true
} }
}) })
}); });
var glowEmitter = createGlowEmitter();
var light = Entities.addEntity({ var light = Entities.addEntity({
type: 'Light', type: 'Light',
name: "raveLight", name: "raveLight",
@ -82,14 +87,76 @@ RaveStick = function(spawnPosition) {
green: 200, green: 200,
blue: 40 blue: 40
}; };
function cleanup() { function cleanup() {
Entities.deleteEntity(stick); Entities.deleteEntity(stick);
Entities.deleteEntity(light); Entities.deleteEntity(light);
Entities.deleteEntity(glowEmitter);
} }
this.cleanup = cleanup; this.cleanup = cleanup;
}
function createGlowEmitter() {
var props = Entities.getEntityProperties(stick, ["position", "rotation"]);
var forwardVec = Quat.getFront(props.rotation);
var forwardQuat = Quat.rotationBetween(Vec3.UNIT_Z, forwardVec);
var position = props.position;
var color = {
red: 150,
green: 20,
blue: 100
}
var props = {
type: "ParticleEffect",
name: "Rave Stick Glow Emitter",
position: position,
parentID: stick,
isEmitting: true,
colorStart: color,
color: {
red: 200,
green: 200,
blue: 255
},
colorFinish: color,
maxParticles: 100000,
lifespan: 0.8,
emitRate: 1000,
emitOrientation: forwardQuat,
emitSpeed: 0.2,
speedSpread: 0.0,
emitDimensions: {
x: 0,
y: 0,
z: 0
},
polarStart: 0,
polarFinish: 0,
azimuthStart: 0.1,
azimuthFinish: 0.01,
emitAcceleration: {
x: 0,
y: 0,
z: 0
},
accelerationSpread: {
x: 0.00,
y: 0.00,
z: 0.00
},
radiusStart: 0.01,
radiusFinish: 0.005,
alpha: 0.7,
alphaSpread: 0.1,
alphaStart: 0.1,
alphaFinish: 0.1,
textures: "https://s3.amazonaws.com/hifi-public/eric/textures/particleSprites/beamParticle.png",
emitterShouldTrail: false
}
var glowEmitter = Entities.addEntity(props);
return glowEmitter;
}
}

View file

@ -37,6 +37,13 @@
this.bulletForce = 10; this.bulletForce = 10;
this.showLaser = false; this.showLaser = false;
this.laserOffsets = {
y: 0.095
};
this.firingOffsets = {
z: 0.16
}
}; };
Pistol.prototype = { Pistol.prototype = {
@ -272,46 +279,12 @@
}); });
}, 100); }, 100);
Entities.editEntity(this.flash, { var flash = Entities.addEntity({
isEmitting: true
});
Script.setTimeout(function() {
Entities.editEntity(_this.flash, {
isEmitting: false
});
}, 100)
},
preload: function(entityID) {
this.entityID = entityID;
this.laser = Overlays.addOverlay("line3d", {
start: ZERO_VECTOR,
end: ZERO_VECTOR,
color: COLORS.RED,
alpha: 1,
visible: true,
lineWidth: 2
});
this.laserOffsets = {
y: 0.095
};
this.firingOffsets = {
z: 0.16
}
var gunProps = Entities.getEntityProperties(this.entityID, ['position', 'rotation']);
var position = gunProps.position;
var rotation = Quat.fromPitchYawRollDegrees(0, 0, 0);
this.firingDirection = Quat.getFront(rotation);
var upVec = Quat.getUp(rotation);
this.barrelPoint = Vec3.sum(position, Vec3.multiply(upVec, this.laserOffsets.y));
this.barrelPoint = Vec3.sum(this.barrelPoint, Vec3.multiply(this.firingDirection, this.firingOffsets.z))
this.flash = Entities.addEntity({
type: "ParticleEffect", type: "ParticleEffect",
position: this.barrelPoint, position: this.barrelPoint,
"name": "Muzzle Flash", "name": "Muzzle Flash",
isEmitting: false, lifetime: 4,
parentID: this.entityID,
"color": { "color": {
red: 228, red: 228,
green: 128, green: 128,
@ -363,14 +336,27 @@
"additiveBlending": true, "additiveBlending": true,
"textures": "http://ericrius1.github.io/PartiArt/assets/star.png" "textures": "http://ericrius1.github.io/PartiArt/assets/star.png"
}); });
Script.setTimeout(function() {
Entities.editEntity(flash, {
isEmitting: false
});
}, 100)
Script.setTimeout(function() { },
Entities.editEntity(_this.flash, {parentID: _this.entityID});
}, 500)
preload: function(entityID) {
this.entityID = entityID;
this.laser = Overlays.addOverlay("line3d", {
start: ZERO_VECTOR,
end: ZERO_VECTOR,
color: COLORS.RED,
alpha: 1,
visible: true,
lineWidth: 2
});
}, },
}; };
// entity scripts always need to return a newly constructed object of our type // entity scripts always need to return a newly constructed object of our type
return new Pistol(); return new Pistol();
}); });

View file

@ -84,6 +84,7 @@ Hifi.AvatarInputs {
Item { Item {
width: root.mirrorWidth width: root.mirrorWidth
height: 44 height: 44
visible: !root.isHMD
x: root.mirrorLeftPad x: root.mirrorLeftPad
y: root.mirrorVisible ? root.mirrorTopPad + root.mirrorHeight : 5 y: root.mirrorVisible ? root.mirrorTopPad + root.mirrorHeight : 5

View file

@ -49,7 +49,7 @@ VrDialog {
onUrlChanged: { onUrlChanged: {
var currentUrl = url.toString(); var currentUrl = url.toString();
var newUrl = urlHandler.fixupUrl(currentUrl); var newUrl = urlHandler.fixupUrl(currentUrl).toString();
if (newUrl != currentUrl) { if (newUrl != currentUrl) {
url = newUrl; url = newUrl;
} }

View file

@ -258,14 +258,14 @@ Item {
Text { Text {
color: root.fontColor; color: root.fontColor;
font.pixelSize: root.fontSize font.pixelSize: root.fontSize
visible: root.expanded visible: root.showAcuity
text: "LOD: " + root.lodStatus; text: "LOD: " + root.lodStatus;
} }
Text { Text {
color: root.fontColor; color: root.fontColor;
font.pixelSize: root.fontSize font.pixelSize: root.fontSize
visible: root.expanded visible: root.expanded
text: "Renderable avatars: " + root.avatarRenderableCount + " w/in " + root.avatarRenderDistance + "m"; text: root.lodStatsRenderText;
} }
} }
} }

View file

@ -1,7 +1,9 @@
import Hifi 1.0 as Hifi import Hifi 1.0 as Hifi
import QtQuick 2.4 import QtQuick 2.4
import QtQuick.Controls 1.3 import QtQuick.Controls 1.3
import QtQuick.Controls.Styles 1.3 import QtQuick.Controls.Styles 1.3
import "controls" import "controls"
import "styles" import "styles"
@ -21,15 +23,12 @@ Hifi.VrMenu {
property var models: [] property var models: []
property var columns: [] property var columns: []
onEnabledChanged: { onEnabledChanged: {
if (enabled && columns.length == 0) { if (enabled && columns.length == 0) {
pushColumn(rootMenu.items); pushColumn(rootMenu.items);
} }
opacity = enabled ? 1.0 : 0.0 opacity = enabled ? 1.0 : 0.0
if (enabled) { offscreenFlags.navigationFocused = enabled;
forceActiveFocus()
}
} }
// The actual animator // The actual animator
@ -49,13 +48,12 @@ Hifi.VrMenu {
} }
property var menuBuilder: Component { property var menuBuilder: Component {
Border { VrMenuView {
HifiConstants { id: hifi } property int menuDepth: root.models.length - 1
property int menuDepth model: root.models[menuDepth]
Component.onCompleted: { Component.onCompleted: {
menuDepth = root.models.length - 1 if (menuDepth === 0) {
if (menuDepth == 0) {
x = lastMousePosition.x - 20 x = lastMousePosition.x - 20
y = lastMousePosition.y - 20 y = lastMousePosition.y - 20
} else { } else {
@ -65,48 +63,8 @@ Hifi.VrMenu {
} }
} }
border.color: hifi.colors.hifiBlue onSelected: {
color: hifi.colors.window root.selectItem(menuDepth, item)
implicitHeight: listView.implicitHeight + 16
implicitWidth: listView.implicitWidth + 16
Column {
id: listView
property real minWidth: 0
anchors {
top: parent.top
topMargin: 8
left: parent.left
leftMargin: 8
right: parent.right
rightMargin: 8
}
Repeater {
model: root.models[menuDepth]
delegate: Loader {
id: loader
source: "VrMenuItem.qml"
Binding {
target: loader.item
property: "menuContainer"
value: root
when: loader.status == Loader.Ready
}
Binding {
target: loader.item
property: "source"
value: modelData
when: loader.status == Loader.Ready
}
Binding {
target: loader.item
property: "listView"
value: listView
when: loader.status == Loader.Ready
}
}
}
} }
} }
} }
@ -116,14 +74,14 @@ Hifi.VrMenu {
} }
function pushColumn(items) { function pushColumn(items) {
models.push(items) models.push(itemsToModel(items))
if (columns.length) { if (columns.length) {
var oldColumn = lastColumn(); var oldColumn = lastColumn();
//oldColumn.enabled = false //oldColumn.enabled = false
} }
var newColumn = menuBuilder.createObject(root); var newColumn = menuBuilder.createObject(root);
columns.push(newColumn); columns.push(newColumn);
newColumn.forceActiveFocus(); forceActiveFocus();
} }
function popColumn() { function popColumn() {
@ -145,13 +103,39 @@ Hifi.VrMenu {
curColumn.forceActiveFocus(); curColumn.forceActiveFocus();
} }
function selectItem(source) { function itemsToModel(items) {
var newListModel = Qt.createQmlObject('import QtQuick 2.2; ListModel {}', root);
for (var i = 0; i < items.length; ++i) {
var item = items[i];
switch (item.type) {
case 2:
newListModel.append({"type":item.type, "name": item.title, "item": item})
break;
case 1:
newListModel.append({"type":item.type, "name": item.text, "item": item})
break;
case 0:
newListModel.append({"type":item.type, "name": "-----", "item": item})
break;
}
}
return newListModel;
}
function selectItem(depth, source) {
var popped = false;
while (depth + 1 < columns.length) {
popColumn()
popped = true
}
switch (source.type) { switch (source.type) {
case 2: case 2:
lastColumn().enabled = false
pushColumn(source.items) pushColumn(source.items)
break; break;
case 1: case 1:
source.trigger() if (!popped) source.trigger()
enabled = false enabled = false
break; break;
case 0: case 0:
@ -165,14 +149,6 @@ Hifi.VrMenu {
} }
} }
Keys.onPressed: {
switch (event.key) {
case Qt.Key_Escape:
root.popColumn()
event.accepted = true;
}
}
MouseArea { MouseArea {
anchors.fill: parent anchors.fill: parent
id: mouseArea id: mouseArea
@ -206,4 +182,36 @@ Hifi.VrMenu {
function removeItem(menu, menuItem) { function removeItem(menu, menuItem) {
menu.removeItem(menuItem); menu.removeItem(menuItem);
} }
function previousItem() {
if (columns.length) {
lastColumn().incrementCurrentIndex()
}
}
function nextItem() {
if (columns.length) {
lastColumn().decrementCurrentIndex()
}
}
function selectCurrentItem() {
if (columns.length) {
var depth = columns.length - 1;
var index = lastColumn().currentIndex;
if (index >= 0) {
var model = models[depth];
var item = model.get(index).item;
selectItem(depth, item);
}
}
}
Keys.onDownPressed: previousItem();
Keys.onUpPressed: nextItem();
Keys.onSpacePressed: selectCurrentItem();
Keys.onReturnPressed: selectCurrentItem();
Keys.onRightPressed: selectCurrentItem();
Keys.onLeftPressed: popColumn();
Keys.onEscapePressed: popColumn();
} }

View file

@ -6,57 +6,18 @@ import "styles"
Item { Item {
id: root id: root
HifiConstants { HifiConstants {
id: hifi id: hifi
} }
// The model object
property alias text: label.text
property var source property var source
property var menuContainer
property var listView
MouseArea {
anchors.left: parent.left
anchors.right: tag.right
anchors.rightMargin: -4
anchors.top: parent.top
anchors.bottom: parent.bottom
acceptedButtons: Qt.LeftButton
hoverEnabled: true
Rectangle {
id: highlight
visible: false
anchors.fill: parent
color: "#7f0e7077"
}
onEntered: {
//if (source.type == 2 && enabled) {
// timer.start()
//}
highlight.visible = source.enabled
}
onExited: {
timer.stop()
highlight.visible = false
}
onClicked: {
select()
}
}
implicitHeight: source.visible ? label.implicitHeight * 1.5 : 0 implicitHeight: source.visible ? label.implicitHeight * 1.5 : 0
implicitWidth: label.implicitWidth + label.height * 2.5 implicitWidth: label.width + label.height * 2.5
visible: source.visible visible: source.visible
width: parent.width
Timer {
id: timer
interval: 1000
onTriggered: parent.select()
}
FontAwesome { FontAwesome {
clip: true clip: true
@ -84,32 +45,18 @@ Item {
Text { Text {
id: label id: label
text: typedText()
anchors.left: check.right anchors.left: check.right
anchors.leftMargin: 4 anchors.leftMargin: 4
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
verticalAlignment: Text.AlignVCenter verticalAlignment: Text.AlignVCenter
color: source.enabled ? hifi.colors.text : hifi.colors.disabledText color: source.enabled ? hifi.colors.text : hifi.colors.disabledText
enabled: source.enabled && source.visible enabled: source.visible && (source.type !== 0 ? source.enabled : false)
visible: source.visible visible: source.visible
function typedText() {
if (source) {
switch (source.type) {
case 2:
return source.title
case 1:
return source.text
case 0:
return "-----"
}
}
return ""
}
} }
FontAwesome { FontAwesome {
id: tag id: tag
x: listView.width - width - 4 x: root.parent.width - width
size: label.height size: label.height
width: implicitWidth width: implicitWidth
visible: source.visible && (source.type == 2) visible: source.visible && (source.type == 2)
@ -117,17 +64,4 @@ Item {
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
color: label.color color: label.color
} }
function select() {
//timer.stop();
var popped = false
while (columns.length - 1 > listView.parent.menuDepth) {
popColumn()
popped = true
}
if (!popped || source.type != 1) {
root.menuContainer.selectItem(source)
}
}
} }

View file

@ -0,0 +1,77 @@
import QtQuick 2.4
import QtQuick.Controls 1.3
import QtQuick.Controls.Styles 1.3
import "styles"
ListView {
id: root
HifiConstants { id: hifi }
width: 128
height: count * 32
onEnabledChanged: recalcSize();
onVisibleChanged: recalcSize();
onCountChanged: recalcSize();
signal selected(var item)
highlight: Rectangle {
width: root.currentItem ? root.currentItem.width : 0
height: root.currentItem ? root.currentItem.height : 0
color: "lightsteelblue"; radius: 3
}
delegate: VrMenuItem {
text: name
source: item
onImplicitHeightChanged: root.recalcSize()
onImplicitWidthChanged: root.recalcSize()
MouseArea {
anchors.fill: parent
hoverEnabled: true
onEntered: root.currentIndex = index
onClicked: root.selected(item)
}
}
function recalcSize() {
if (model.count !== count || !visible) {
return;
}
var originalIndex = currentIndex;
var maxWidth = width;
var newHeight = 0;
for (var i = 0; i < count; ++i) {
currentIndex = i;
if (!currentItem) {
continue;
}
if (currentItem && currentItem.implicitWidth > maxWidth) {
maxWidth = currentItem.implicitWidth
}
if (currentItem.visible) {
newHeight += currentItem.implicitHeight
}
}
if (maxWidth > width) {
width = maxWidth;
}
if (newHeight > height) {
height = newHeight
}
currentIndex = originalIndex;
}
Border {
id: border
anchors.fill: parent
anchors.margins: -8
z: parent.z - 1
border.color: hifi.colors.hifiBlue
color: hifi.colors.window
}
}

View file

@ -2990,7 +2990,11 @@ void Application::update(float deltaTime) {
bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings); bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings);
PerformanceWarning warn(showWarnings, "Application::update()"); PerformanceWarning warn(showWarnings, "Application::update()");
updateLOD(); if (DependencyManager::get<LODManager>()->getUseAcuity()) {
updateLOD();
} else {
DependencyManager::get<LODManager>()->updatePIDRenderDistance(getTargetFrameRate(), getLastInstanteousFps(), deltaTime, isThrottleRendering());
}
{ {
PerformanceTimer perfTimer("devices"); PerformanceTimer perfTimer("devices");

View file

@ -9,6 +9,7 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
// //
#include <avatar/AvatarManager.h>
#include <SettingHandle.h> #include <SettingHandle.h>
#include <Util.h> #include <Util.h>
@ -20,9 +21,30 @@
Setting::Handle<float> desktopLODDecreaseFPS("desktopLODDecreaseFPS", DEFAULT_DESKTOP_LOD_DOWN_FPS); Setting::Handle<float> desktopLODDecreaseFPS("desktopLODDecreaseFPS", DEFAULT_DESKTOP_LOD_DOWN_FPS);
Setting::Handle<float> hmdLODDecreaseFPS("hmdLODDecreaseFPS", DEFAULT_HMD_LOD_DOWN_FPS); Setting::Handle<float> hmdLODDecreaseFPS("hmdLODDecreaseFPS", DEFAULT_HMD_LOD_DOWN_FPS);
// There are two different systems in use, based on lodPreference:
// pid: renderDistance is adjusted by a PID such that frame rate targets are met.
// acuity: a pseudo-acuity target is held, or adjusted to match minimum frame rates (and a PID controlls avatar rendering distance)
// If unspecified, acuity is used only if user has specified non-default minumum frame rates.
Setting::Handle<int> lodPreference("lodPreference", (int)LODManager::LODPreference::unspecified);
const float SMALLEST_REASONABLE_HORIZON = 50.0f; // meters
Setting::Handle<float> renderDistanceInverseHighLimit("renderDistanceInverseHighLimit", 1.0f / SMALLEST_REASONABLE_HORIZON);
void LODManager::setRenderDistanceInverseHighLimit(float newValue) {
renderDistanceInverseHighLimit.set(newValue); // persist it, and tell all the controllers that use it
_renderDistanceController.setControlledValueHighLimit(newValue);
}
LODManager::LODManager() { LODManager::LODManager() {
calculateAvatarLODDistanceMultiplier(); calculateAvatarLODDistanceMultiplier();
setRenderDistanceInverseHighLimit(renderDistanceInverseHighLimit.get());
setRenderDistanceInverseLowLimit(1.0f / (float)TREE_SCALE);
// Advice for tuning parameters:
// See PIDController.h. There's a section on tuning in the reference.
// Turn on logging with the following (or from js with LODManager.setRenderDistanceControllerHistory("render pid", 240))
//setRenderDistanceControllerHistory("render pid", 60 * 4);
// Note that extra logging/hysteresis is turned off in Avatar.cpp when the above logging is on.
setRenderDistanceKP(0.000012f); // Usually about 0.6 of largest that doesn't oscillate when other parameters 0.
setRenderDistanceKI(0.00002f); // Big enough to bring us to target with the above KP.
} }
float LODManager::getLODDecreaseFPS() { float LODManager::getLODDecreaseFPS() {
@ -39,7 +61,6 @@ float LODManager::getLODIncreaseFPS() {
return getDesktopLODIncreaseFPS(); return getDesktopLODIncreaseFPS();
} }
void LODManager::autoAdjustLOD(float currentFPS) { void LODManager::autoAdjustLOD(float currentFPS) {
// NOTE: our first ~100 samples at app startup are completely all over the place, and we don't // NOTE: our first ~100 samples at app startup are completely all over the place, and we don't
@ -217,15 +238,58 @@ QString LODManager::getLODFeedbackText() {
return result; return result;
} }
static float renderDistance = (float)TREE_SCALE;
static int renderedCount = 0;
static int lastRenderedCount = 0;
bool LODManager::getUseAcuity() { return lodPreference.get() == (int)LODManager::LODPreference::acuity; }
void LODManager::setUseAcuity(bool newValue) { lodPreference.set(newValue ? (int)LODManager::LODPreference::acuity : (int)LODManager::LODPreference::pid); }
float LODManager::getRenderDistance() {
return renderDistance;
}
int LODManager::getRenderedCount() {
return lastRenderedCount;
}
QString LODManager::getLODStatsRenderText() {
QString label = getUseAcuity() ? "Renderable avatars: " : "Rendered objects: ";
return label + QString::number(getRenderedCount()) + " w/in " + QString::number((int)getRenderDistance()) + "m";
}
// compare audoAdjustLOD()
void LODManager::updatePIDRenderDistance(float targetFps, float measuredFps, float deltaTime, bool isThrottled) {
float distance;
if (!isThrottled) {
_renderDistanceController.setMeasuredValueSetpoint(targetFps / 2.0f); // No problem updating in flight.
// The PID controller raises the controlled value when the measured value goes up.
// The measured value is frame rate. When the controlled value (1 / render cutoff distance)
// goes up, the render cutoff distance gets closer, the number of rendered avatars is less, and frame rate
// goes up.
distance = 1.0f / _renderDistanceController.update(measuredFps, deltaTime);
} else {
// Here we choose to just use the maximum render cutoff distance if throttled.
distance = 1.0f / _renderDistanceController.getControlledValueLowLimit();
}
_renderDistanceAverage.updateAverage(distance);
renderDistance = _renderDistanceAverage.getAverage(); // average only once per cycle
lastRenderedCount = renderedCount;
renderedCount = 0;
}
bool LODManager::shouldRender(const RenderArgs* args, const AABox& bounds) { bool LODManager::shouldRender(const RenderArgs* args, const AABox& bounds) {
float distanceToCamera = glm::length(bounds.calcCenter() - args->_viewFrustum->getPosition());
float largestDimension = bounds.getLargestDimension();
if (!getUseAcuity()) {
const float scenerySize = 300; // meters
bool isRendered = (largestDimension > scenerySize) || // render scenery regardless of distance
(fabsf(distanceToCamera - largestDimension) < renderDistance);
renderedCount += isRendered ? 1 : 0;
return isRendered;
}
const float maxScale = (float)TREE_SCALE; const float maxScale = (float)TREE_SCALE;
const float octreeToMeshRatio = 4.0f; // must be this many times closer to a mesh than a voxel to see it. const float octreeToMeshRatio = 4.0f; // must be this many times closer to a mesh than a voxel to see it.
float octreeSizeScale = args->_sizeScale; float octreeSizeScale = args->_sizeScale;
int boundaryLevelAdjust = args->_boundaryLevelAdjust; int boundaryLevelAdjust = args->_boundaryLevelAdjust;
float visibleDistanceAtMaxScale = boundaryDistanceForRenderLevel(boundaryLevelAdjust, octreeSizeScale) / octreeToMeshRatio; float visibleDistanceAtMaxScale = boundaryDistanceForRenderLevel(boundaryLevelAdjust, octreeSizeScale) / octreeToMeshRatio;
float distanceToCamera = glm::length(bounds.calcCenter() - args->_viewFrustum->getPosition());
float largestDimension = bounds.getLargestDimension();
static bool shouldRenderTableNeedsBuilding = true; static bool shouldRenderTableNeedsBuilding = true;
static QMap<float, float> shouldRenderTable; static QMap<float, float> shouldRenderTable;
if (shouldRenderTableNeedsBuilding) { if (shouldRenderTableNeedsBuilding) {
@ -315,6 +379,12 @@ void LODManager::setBoundaryLevelAdjust(int boundaryLevelAdjust) {
void LODManager::loadSettings() { void LODManager::loadSettings() {
setDesktopLODDecreaseFPS(desktopLODDecreaseFPS.get()); setDesktopLODDecreaseFPS(desktopLODDecreaseFPS.get());
setHMDLODDecreaseFPS(hmdLODDecreaseFPS.get()); setHMDLODDecreaseFPS(hmdLODDecreaseFPS.get());
if (lodPreference.get() == (int)LODManager::LODPreference::unspecified) {
setUseAcuity((getDesktopLODDecreaseFPS() != DEFAULT_DESKTOP_LOD_DOWN_FPS) || (getHMDLODDecreaseFPS() != DEFAULT_HMD_LOD_DOWN_FPS));
}
Menu::getInstance()->getActionForOption(MenuOption::LodTools)->setEnabled(getUseAcuity());
Menu::getInstance()->getSubMenuFromName(MenuOption::RenderResolution, Menu::getInstance()->getSubMenuFromName("Render", Menu::getInstance()->getMenu("Developer")))->setEnabled(getUseAcuity());
} }
void LODManager::saveSettings() { void LODManager::saveSettings() {

View file

@ -15,6 +15,7 @@
#include <DependencyManager.h> #include <DependencyManager.h>
#include <NumericalConstants.h> #include <NumericalConstants.h>
#include <OctreeConstants.h> #include <OctreeConstants.h>
#include <PIDController.h>
#include <SimpleMovingAverage.h> #include <SimpleMovingAverage.h>
const float DEFAULT_DESKTOP_LOD_DOWN_FPS = 15.0; const float DEFAULT_DESKTOP_LOD_DOWN_FPS = 15.0;
@ -81,6 +82,27 @@ public:
Q_INVOKABLE float getLODDecreaseFPS(); Q_INVOKABLE float getLODDecreaseFPS();
Q_INVOKABLE float getLODIncreaseFPS(); Q_INVOKABLE float getLODIncreaseFPS();
enum class LODPreference {
pid = 0,
acuity,
unspecified
};
static bool getUseAcuity();
static void setUseAcuity(bool newValue);
Q_INVOKABLE void setRenderDistanceKP(float newValue) { _renderDistanceController.setKP(newValue); }
Q_INVOKABLE void setRenderDistanceKI(float newValue) { _renderDistanceController.setKI(newValue); }
Q_INVOKABLE void setRenderDistanceKD(float newValue) { _renderDistanceController.setKD(newValue); }
Q_INVOKABLE bool getRenderDistanceControllerIsLogging() { return _renderDistanceController.getIsLogging(); }
Q_INVOKABLE void setRenderDistanceControllerHistory(QString label, int size) { return _renderDistanceController.setHistorySize(label, size); }
Q_INVOKABLE float getRenderDistanceInverseLowLimit() { return _renderDistanceController.getControlledValueLowLimit(); }
Q_INVOKABLE void setRenderDistanceInverseLowLimit(float newValue) { _renderDistanceController.setControlledValueLowLimit(newValue); }
Q_INVOKABLE float getRenderDistanceInverseHighLimit() { return _renderDistanceController.getControlledValueHighLimit(); }
Q_INVOKABLE void setRenderDistanceInverseHighLimit(float newValue);
void updatePIDRenderDistance(float targetFps, float measuredFps, float deltaTime, bool isThrottled);
float getRenderDistance();
int getRenderedCount();
QString getLODStatsRenderText();
static bool shouldRender(const RenderArgs* args, const AABox& bounds); static bool shouldRender(const RenderArgs* args, const AABox& bounds);
bool shouldRenderMesh(float largestDimension, float distanceToCamera); bool shouldRenderMesh(float largestDimension, float distanceToCamera);
void autoAdjustLOD(float currentFPS); void autoAdjustLOD(float currentFPS);
@ -116,6 +138,9 @@ private:
bool _shouldRenderTableNeedsRebuilding = true; bool _shouldRenderTableNeedsRebuilding = true;
QMap<float, float> _shouldRenderTable; QMap<float, float> _shouldRenderTable;
PIDController _renderDistanceController{};
SimpleMovingAverage _renderDistanceAverage{ 10 };
}; };
#endif // hifi_LODManager_h #endif // hifi_LODManager_h

View file

@ -64,6 +64,7 @@ public:
void saveSettings(); void saveSettings();
MenuWrapper* getMenu(const QString& menuName); MenuWrapper* getMenu(const QString& menuName);
MenuWrapper* getSubMenuFromName(const QString& menuName, MenuWrapper* menu);
void triggerOption(const QString& menuOption); void triggerOption(const QString& menuOption);
QAction* getActionForOption(const QString& menuOption); QAction* getActionForOption(const QString& menuOption);
@ -130,7 +131,6 @@ private:
const QString& grouping = QString()); const QString& grouping = QString());
QAction* getActionFromName(const QString& menuName, MenuWrapper* menu); QAction* getActionFromName(const QString& menuName, MenuWrapper* menu);
MenuWrapper* getSubMenuFromName(const QString& menuName, MenuWrapper* menu);
MenuWrapper* getMenuParent(const QString& menuName, QString& finalMenuPart); MenuWrapper* getMenuParent(const QString& menuName, QString& finalMenuPart);
QAction* getMenuAction(const QString& menuName); QAction* getMenuAction(const QString& menuName);

View file

@ -184,26 +184,6 @@ void Avatar::simulate(float deltaTime) {
qCDebug(interfaceapp) << "Billboarding" << (isMyAvatar() ? "myself" : getSessionUUID()) << "for LOD" << getLODDistance(); qCDebug(interfaceapp) << "Billboarding" << (isMyAvatar() ? "myself" : getSessionUUID()) << "for LOD" << getLODDistance();
} }
const bool isControllerLogging = DependencyManager::get<AvatarManager>()->getRenderDistanceControllerIsLogging();
float renderDistance = DependencyManager::get<AvatarManager>()->getRenderDistance();
const float SKIP_HYSTERESIS_PROPORTION = isControllerLogging ? 0.0f : BILLBOARD_HYSTERESIS_PROPORTION;
float distance = glm::distance(qApp->getCamera()->getPosition(), getPosition());
if (_shouldSkipRender) {
if (distance < renderDistance * (1.0f - SKIP_HYSTERESIS_PROPORTION)) {
_shouldSkipRender = false;
_skeletonModel.setVisibleInScene(true, qApp->getMain3DScene());
if (!isControllerLogging) { // Test for isMyAvatar is prophylactic. Never occurs in current code.
qCDebug(interfaceapp) << "Rerendering" << (isMyAvatar() ? "myself" : getSessionUUID()) << "for distance" << renderDistance;
}
}
} else if (distance > renderDistance * (1.0f + SKIP_HYSTERESIS_PROPORTION)) {
_shouldSkipRender = true;
_skeletonModel.setVisibleInScene(false, qApp->getMain3DScene());
if (!isControllerLogging) {
qCDebug(interfaceapp) << "Unrendering" << (isMyAvatar() ? "myself" : getSessionUUID()) << "for distance" << renderDistance;
}
}
// simple frustum check // simple frustum check
float boundingRadius = getBillboardSize(); float boundingRadius = getBillboardSize();
bool inViewFrustum = qApp->getViewFrustum()->sphereInFrustum(getPosition(), boundingRadius) != bool inViewFrustum = qApp->getViewFrustum()->sphereInFrustum(getPosition(), boundingRadius) !=

View file

@ -76,13 +76,6 @@ AvatarManager::AvatarManager(QObject* parent) :
packetReceiver.registerListener(PacketType::AvatarBillboard, this, "processAvatarBillboardPacket"); packetReceiver.registerListener(PacketType::AvatarBillboard, this, "processAvatarBillboardPacket");
} }
const float SMALLEST_REASONABLE_HORIZON = 5.0f; // meters
Setting::Handle<float> avatarRenderDistanceInverseHighLimit("avatarRenderDistanceHighLimit", 1.0f / SMALLEST_REASONABLE_HORIZON);
void AvatarManager::setRenderDistanceInverseHighLimit(float newValue) {
avatarRenderDistanceInverseHighLimit.set(newValue);
_renderDistanceController.setControlledValueHighLimit(newValue);
}
void AvatarManager::init() { void AvatarManager::init() {
_myAvatar->init(); _myAvatar->init();
{ {
@ -98,19 +91,6 @@ void AvatarManager::init() {
_myAvatar->addToScene(_myAvatar, scene, pendingChanges); _myAvatar->addToScene(_myAvatar, scene, pendingChanges);
} }
scene->enqueuePendingChanges(pendingChanges); scene->enqueuePendingChanges(pendingChanges);
const float target_fps = qApp->getTargetFrameRate();
_renderDistanceController.setMeasuredValueSetpoint(target_fps);
_renderDistanceController.setControlledValueHighLimit(avatarRenderDistanceInverseHighLimit.get());
_renderDistanceController.setControlledValueLowLimit(1.0f / (float) TREE_SCALE);
// Advice for tuning parameters:
// See PIDController.h. There's a section on tuning in the reference.
// Turn on logging with the following (or from js with AvatarList.setRenderDistanceControllerHistory("avatar render", 300))
//_renderDistanceController.setHistorySize("avatar render", target_fps * 4);
// Note that extra logging/hysteresis is turned off in Avatar.cpp when the above logging is on.
_renderDistanceController.setKP(0.0008f); // Usually about 0.6 of largest that doesn't oscillate when other parameters 0.
_renderDistanceController.setKI(0.0006f); // Big enough to bring us to target with the above KP.
_renderDistanceController.setKD(0.000001f); // A touch of kd increases the speed by which we get there.
} }
void AvatarManager::updateMyAvatar(float deltaTime) { void AvatarManager::updateMyAvatar(float deltaTime) {
@ -145,23 +125,6 @@ void AvatarManager::updateOtherAvatars(float deltaTime) {
PerformanceTimer perfTimer("otherAvatars"); PerformanceTimer perfTimer("otherAvatars");
float distance;
if (!qApp->isThrottleRendering()) {
_renderDistanceController.setMeasuredValueSetpoint(qApp->getTargetFrameRate()); // No problem updating in flight.
// The PID controller raises the controlled value when the measured value goes up.
// The measured value is frame rate. When the controlled value (1 / render cutoff distance)
// goes up, the render cutoff distance gets closer, the number of rendered avatars is less, and frame rate
// goes up.
const float deduced = qApp->getLastUnsynchronizedFps();
distance = 1.0f / _renderDistanceController.update(deduced, deltaTime);
} else {
// Here we choose to just use the maximum render cutoff distance if throttled.
distance = 1.0f / _renderDistanceController.getControlledValueLowLimit();
}
_renderDistanceAverage.updateAverage(distance);
_renderDistance = _renderDistanceAverage.getAverage();
int renderableCount = 0;
// simulate avatars // simulate avatars
auto hashCopy = getHashCopy(); auto hashCopy = getHashCopy();
@ -179,14 +142,10 @@ void AvatarManager::updateOtherAvatars(float deltaTime) {
} else { } else {
avatar->startUpdate(); avatar->startUpdate();
avatar->simulate(deltaTime); avatar->simulate(deltaTime);
if (avatar->getShouldRender()) {
renderableCount++;
}
avatar->endUpdate(); avatar->endUpdate();
++avatarIterator; ++avatarIterator;
} }
} }
_renderedAvatarCount = renderableCount;
// simulate avatar fades // simulate avatar fades
simulateAvatarFades(deltaTime); simulateAvatarFades(deltaTime);

View file

@ -45,7 +45,6 @@ public:
void clearOtherAvatars(); void clearOtherAvatars();
bool shouldShowReceiveStats() const { return _shouldShowReceiveStats; } bool shouldShowReceiveStats() const { return _shouldShowReceiveStats; }
PIDController& getRenderDistanceController() { return _renderDistanceController; }
class LocalLight { class LocalLight {
public: public:
@ -68,19 +67,6 @@ public:
void addAvatarToSimulation(Avatar* avatar); void addAvatarToSimulation(Avatar* avatar);
// Expose results and parameter-tuning operations to other systems, such as stats and javascript.
Q_INVOKABLE float getRenderDistance() { return _renderDistance; }
Q_INVOKABLE float getRenderDistanceInverseLowLimit() { return _renderDistanceController.getControlledValueLowLimit(); }
Q_INVOKABLE float getRenderDistanceInverseHighLimit() { return _renderDistanceController.getControlledValueHighLimit(); }
Q_INVOKABLE int getNumberInRenderRange() { return _renderedAvatarCount; }
Q_INVOKABLE bool getRenderDistanceControllerIsLogging() { return _renderDistanceController.getIsLogging(); }
Q_INVOKABLE void setRenderDistanceControllerHistory(QString label, int size) { return _renderDistanceController.setHistorySize(label, size); }
Q_INVOKABLE void setRenderDistanceKP(float newValue) { _renderDistanceController.setKP(newValue); }
Q_INVOKABLE void setRenderDistanceKI(float newValue) { _renderDistanceController.setKI(newValue); }
Q_INVOKABLE void setRenderDistanceKD(float newValue) { _renderDistanceController.setKD(newValue); }
Q_INVOKABLE void setRenderDistanceInverseLowLimit(float newValue) { _renderDistanceController.setControlledValueLowLimit(newValue); }
Q_INVOKABLE void setRenderDistanceInverseHighLimit(float newValue);
public slots: public slots:
void setShouldShowReceiveStats(bool shouldShowReceiveStats) { _shouldShowReceiveStats = shouldShowReceiveStats; } void setShouldShowReceiveStats(bool shouldShowReceiveStats) { _shouldShowReceiveStats = shouldShowReceiveStats; }
void updateAvatarRenderStatus(bool shouldRenderAvatars); void updateAvatarRenderStatus(bool shouldRenderAvatars);
@ -106,10 +92,6 @@ private:
QVector<AvatarManager::LocalLight> _localLights; QVector<AvatarManager::LocalLight> _localLights;
bool _shouldShowReceiveStats = false; bool _shouldShowReceiveStats = false;
float _renderDistance { (float) TREE_SCALE };
int _renderedAvatarCount { 0 };
PIDController _renderDistanceController { };
SimpleMovingAverage _renderDistanceAverage { 10 };
SetOfAvatarMotionStates _avatarMotionStates; SetOfAvatarMotionStates _avatarMotionStates;
SetOfMotionStates _motionStatesToAdd; SetOfMotionStates _motionStatesToAdd;

View file

@ -86,7 +86,7 @@ void ApplicationOverlay::renderOverlay(RenderArgs* renderArgs) {
// Now render the overlay components together into a single texture // Now render the overlay components together into a single texture
renderDomainConnectionStatusBorder(renderArgs); // renders the connected domain line renderDomainConnectionStatusBorder(renderArgs); // renders the connected domain line
renderAudioScope(renderArgs); // audio scope in the very back renderAudioScope(renderArgs); // audio scope in the very back - NOTE: this is the debug audio scope, not the VU meter
renderRearView(renderArgs); // renders the mirror view selfie renderRearView(renderArgs); // renders the mirror view selfie
renderQmlUi(renderArgs); // renders a unit quad with the QML UI texture, and the text overlays from scripts renderQmlUi(renderArgs); // renders a unit quad with the QML UI texture, and the text overlays from scripts
renderOverlays(renderArgs); // renders Scripts Overlay and AudioScope renderOverlays(renderArgs); // renders Scripts Overlay and AudioScope
@ -158,7 +158,7 @@ void ApplicationOverlay::renderRearViewToFbo(RenderArgs* renderArgs) {
} }
void ApplicationOverlay::renderRearView(RenderArgs* renderArgs) { void ApplicationOverlay::renderRearView(RenderArgs* renderArgs) {
if (Menu::getInstance()->isOptionChecked(MenuOption::Mirror)) { if (!qApp->isHMDMode() && Menu::getInstance()->isOptionChecked(MenuOption::Mirror)) {
gpu::Batch& batch = *renderArgs->_batch; gpu::Batch& batch = *renderArgs->_batch;
auto geometryCache = DependencyManager::get<GeometryCache>(); auto geometryCache = DependencyManager::get<GeometryCache>();

View file

@ -66,6 +66,7 @@ void AvatarInputs::update() {
&& !Menu::getInstance()->isOptionChecked(MenuOption::FullscreenMirror)); && !Menu::getInstance()->isOptionChecked(MenuOption::FullscreenMirror));
AI_UPDATE(cameraEnabled, !Menu::getInstance()->isOptionChecked(MenuOption::NoFaceTracking)); AI_UPDATE(cameraEnabled, !Menu::getInstance()->isOptionChecked(MenuOption::NoFaceTracking));
AI_UPDATE(cameraMuted, Menu::getInstance()->isOptionChecked(MenuOption::MuteFaceTracking)); AI_UPDATE(cameraMuted, Menu::getInstance()->isOptionChecked(MenuOption::MuteFaceTracking));
AI_UPDATE(isHMD, qApp->isHMDMode());
auto audioIO = DependencyManager::get<AudioClient>(); auto audioIO = DependencyManager::get<AudioClient>();
const float AUDIO_METER_AVERAGING = 0.5; const float AUDIO_METER_AVERAGING = 0.5;

View file

@ -30,6 +30,7 @@ class AvatarInputs : public QQuickItem {
AI_PROPERTY(float, audioLevel, 0) AI_PROPERTY(float, audioLevel, 0)
AI_PROPERTY(bool, mirrorVisible, false) AI_PROPERTY(bool, mirrorVisible, false)
AI_PROPERTY(bool, mirrorZoomed, true) AI_PROPERTY(bool, mirrorZoomed, true)
AI_PROPERTY(bool, isHMD, false)
public: public:
static AvatarInputs* getInstance(); static AvatarInputs* getInstance();
@ -44,6 +45,7 @@ signals:
void audioLevelChanged(); void audioLevelChanged();
void mirrorVisibleChanged(); void mirrorVisibleChanged();
void mirrorZoomedChanged(); void mirrorZoomedChanged();
void isHMDChanged();
protected: protected:
Q_INVOKABLE void resetSensors(); Q_INVOKABLE void resetSensors();

View file

@ -48,6 +48,7 @@ PreferencesDialog::PreferencesDialog(QWidget* parent) :
connect(ui.buttonChangeAppearance, &QPushButton::clicked, this, &PreferencesDialog::openFullAvatarModelBrowser); connect(ui.buttonChangeAppearance, &QPushButton::clicked, this, &PreferencesDialog::openFullAvatarModelBrowser);
connect(ui.appearanceDescription, &QLineEdit::editingFinished, this, &PreferencesDialog::changeFullAvatarURL); connect(ui.appearanceDescription, &QLineEdit::editingFinished, this, &PreferencesDialog::changeFullAvatarURL);
connect(ui.useAcuityCheckBox, &QCheckBox::clicked, this, &PreferencesDialog::changeUseAcuity);
connect(qApp, &Application::fullAvatarURLChanged, this, &PreferencesDialog::fullAvatarURLChanged); connect(qApp, &Application::fullAvatarURLChanged, this, &PreferencesDialog::fullAvatarURLChanged);
@ -58,6 +59,16 @@ PreferencesDialog::PreferencesDialog(QWidget* parent) :
UIUtil::scaleWidgetFontSizes(this); UIUtil::scaleWidgetFontSizes(this);
} }
void PreferencesDialog::changeUseAcuity() {
bool useAcuity = ui.useAcuityCheckBox->isChecked();
ui.label_desktopMinimumFPSSpin->setEnabled(useAcuity);
ui.desktopMinimumFPSSpin->setEnabled(useAcuity);
ui.label_hmdMinimumFPSSpin->setEnabled(useAcuity);
ui.hmdMinimumFPSSpin->setEnabled(useAcuity);
ui.label_smallestReasonableRenderHorizon->setText(useAcuity ? "Minimum Avatar Display Distance (@half speed)" : "Minimum Display Distance (@half speed)");
Menu::getInstance()->getActionForOption(MenuOption::LodTools)->setEnabled(useAcuity);
Menu::getInstance()->getSubMenuFromName(MenuOption::RenderResolution, Menu::getInstance()->getSubMenuFromName("Render", Menu::getInstance()->getMenu("Developer")))->setEnabled(useAcuity);
}
void PreferencesDialog::changeFullAvatarURL() { void PreferencesDialog::changeFullAvatarURL() {
DependencyManager::get<AvatarManager>()->getMyAvatar()->useFullAvatarURL(ui.appearanceDescription->text(), ""); DependencyManager::get<AvatarManager>()->getMyAvatar()->useFullAvatarURL(ui.appearanceDescription->text(), "");
this->fullAvatarURLChanged(ui.appearanceDescription->text(), ""); this->fullAvatarURLChanged(ui.appearanceDescription->text(), "");
@ -212,9 +223,11 @@ void PreferencesDialog::loadPreferences() {
// LOD items // LOD items
auto lodManager = DependencyManager::get<LODManager>(); auto lodManager = DependencyManager::get<LODManager>();
ui.useAcuityCheckBox->setChecked(lodManager->getUseAcuity());
ui.desktopMinimumFPSSpin->setValue(lodManager->getDesktopLODDecreaseFPS()); ui.desktopMinimumFPSSpin->setValue(lodManager->getDesktopLODDecreaseFPS());
ui.hmdMinimumFPSSpin->setValue(lodManager->getHMDLODDecreaseFPS()); ui.hmdMinimumFPSSpin->setValue(lodManager->getHMDLODDecreaseFPS());
ui.avatarRenderSmallestReasonableHorizon->setValue(1.0f / DependencyManager::get<AvatarManager>()->getRenderDistanceInverseHighLimit()); ui.smallestReasonableRenderHorizon->setValue(1.0f / lodManager->getRenderDistanceInverseHighLimit());
changeUseAcuity();
} }
void PreferencesDialog::savePreferences() { void PreferencesDialog::savePreferences() {
@ -303,7 +316,8 @@ void PreferencesDialog::savePreferences() {
// LOD items // LOD items
auto lodManager = DependencyManager::get<LODManager>(); auto lodManager = DependencyManager::get<LODManager>();
lodManager->setUseAcuity(ui.useAcuityCheckBox->isChecked());
lodManager->setDesktopLODDecreaseFPS(ui.desktopMinimumFPSSpin->value()); lodManager->setDesktopLODDecreaseFPS(ui.desktopMinimumFPSSpin->value());
lodManager->setHMDLODDecreaseFPS(ui.hmdMinimumFPSSpin->value()); lodManager->setHMDLODDecreaseFPS(ui.hmdMinimumFPSSpin->value());
DependencyManager::get<AvatarManager>()->setRenderDistanceInverseHighLimit(1.0f / ui.avatarRenderSmallestReasonableHorizon->value()); lodManager->setRenderDistanceInverseHighLimit(1.0f / ui.smallestReasonableRenderHorizon->value());
} }

View file

@ -51,6 +51,7 @@ private slots:
void openScriptsLocationBrowser(); void openScriptsLocationBrowser();
void changeFullAvatarURL(); void changeFullAvatarURL();
void fullAvatarURLChanged(const QString& newValue, const QString& modelName); void fullAvatarURLChanged(const QString& newValue, const QString& modelName);
void changeUseAcuity();
}; };
#endif // hifi_PreferencesDialog_h #endif // hifi_PreferencesDialog_h

View file

@ -116,8 +116,6 @@ void Stats::updateStats(bool force) {
auto avatarManager = DependencyManager::get<AvatarManager>(); auto avatarManager = DependencyManager::get<AvatarManager>();
// we need to take one avatar out so we don't include ourselves // we need to take one avatar out so we don't include ourselves
STAT_UPDATE(avatarCount, avatarManager->size() - 1); STAT_UPDATE(avatarCount, avatarManager->size() - 1);
STAT_UPDATE(avatarRenderableCount, avatarManager->getNumberInRenderRange());
STAT_UPDATE(avatarRenderDistance, (int) round(avatarManager->getRenderDistance())); // deliberately truncating
STAT_UPDATE(serverCount, (int)nodeList->size()); STAT_UPDATE(serverCount, (int)nodeList->size());
STAT_UPDATE(renderrate, (int)qApp->getFps()); STAT_UPDATE(renderrate, (int)qApp->getFps());
if (qApp->getActiveDisplayPlugin()) { if (qApp->getActiveDisplayPlugin()) {
@ -285,7 +283,9 @@ void Stats::updateStats(bool force) {
STAT_UPDATE(localLeaves, (int)OctreeElement::getLeafNodeCount()); STAT_UPDATE(localLeaves, (int)OctreeElement::getLeafNodeCount());
// LOD Details // LOD Details
STAT_UPDATE(lodStatus, "You can see " + DependencyManager::get<LODManager>()->getLODFeedbackText()); STAT_UPDATE(lodStatus, "You can see " + DependencyManager::get<LODManager>()->getLODFeedbackText());
STAT_UPDATE(lodStatsRenderText, DependencyManager::get<LODManager>()->getLODStatsRenderText());
} }
STAT_UPDATE(showAcuity, (_expanded || force) && DependencyManager::get<LODManager>()->getUseAcuity());
bool performanceTimerIsActive = PerformanceTimer::isActive(); bool performanceTimerIsActive = PerformanceTimer::isActive();
bool displayPerf = _expanded && Menu::getInstance()->isOptionChecked(MenuOption::DisplayDebugTimingDetails); bool displayPerf = _expanded && Menu::getInstance()->isOptionChecked(MenuOption::DisplayDebugTimingDetails);

View file

@ -30,6 +30,7 @@ class Stats : public QQuickItem {
Q_PROPERTY(QString monospaceFont READ monospaceFont CONSTANT) Q_PROPERTY(QString monospaceFont READ monospaceFont CONSTANT)
Q_PROPERTY(float audioPacketlossUpstream READ getAudioPacketLossUpstream) Q_PROPERTY(float audioPacketlossUpstream READ getAudioPacketLossUpstream)
Q_PROPERTY(float audioPacketlossDownstream READ getAudioPacketLossDownstream) Q_PROPERTY(float audioPacketlossDownstream READ getAudioPacketLossDownstream)
Q_PROPERTY(bool showAcuity READ getShowAcuity WRITE setShowAcuity NOTIFY showAcuityChanged)
STATS_PROPERTY(int, serverCount, 0) STATS_PROPERTY(int, serverCount, 0)
STATS_PROPERTY(int, renderrate, 0) STATS_PROPERTY(int, renderrate, 0)
@ -37,8 +38,6 @@ class Stats : public QQuickItem {
STATS_PROPERTY(int, simrate, 0) STATS_PROPERTY(int, simrate, 0)
STATS_PROPERTY(int, avatarSimrate, 0) STATS_PROPERTY(int, avatarSimrate, 0)
STATS_PROPERTY(int, avatarCount, 0) STATS_PROPERTY(int, avatarCount, 0)
STATS_PROPERTY(int, avatarRenderableCount, 0)
STATS_PROPERTY(int, avatarRenderDistance, 0)
STATS_PROPERTY(int, packetInCount, 0) STATS_PROPERTY(int, packetInCount, 0)
STATS_PROPERTY(int, packetOutCount, 0) STATS_PROPERTY(int, packetOutCount, 0)
STATS_PROPERTY(float, mbpsIn, 0) STATS_PROPERTY(float, mbpsIn, 0)
@ -77,6 +76,7 @@ class Stats : public QQuickItem {
STATS_PROPERTY(QString, packetStats, QString()) STATS_PROPERTY(QString, packetStats, QString())
STATS_PROPERTY(QString, lodStatus, QString()) STATS_PROPERTY(QString, lodStatus, QString())
STATS_PROPERTY(QString, timingStats, QString()) STATS_PROPERTY(QString, timingStats, QString())
STATS_PROPERTY(QString, lodStatsRenderText, QString())
STATS_PROPERTY(int, serverElements, 0) STATS_PROPERTY(int, serverElements, 0)
STATS_PROPERTY(int, serverInternal, 0) STATS_PROPERTY(int, serverInternal, 0)
STATS_PROPERTY(int, serverLeaves, 0) STATS_PROPERTY(int, serverLeaves, 0)
@ -108,12 +108,15 @@ public:
emit expandedChanged(); emit expandedChanged();
} }
} }
bool getShowAcuity() { return _showAcuity; }
void setShowAcuity(bool newValue) { _showAcuity = newValue; }
public slots: public slots:
void forceUpdateStats() { updateStats(true); } void forceUpdateStats() { updateStats(true); }
signals: signals:
void expandedChanged(); void expandedChanged();
void showAcuityChanged();
void timingExpandedChanged(); void timingExpandedChanged();
void serverCountChanged(); void serverCountChanged();
void renderrateChanged(); void renderrateChanged();
@ -121,8 +124,7 @@ signals:
void simrateChanged(); void simrateChanged();
void avatarSimrateChanged(); void avatarSimrateChanged();
void avatarCountChanged(); void avatarCountChanged();
void avatarRenderableCountChanged(); void lodStatsRenderTextChanged();
void avatarRenderDistanceChanged();
void packetInCountChanged(); void packetInCountChanged();
void packetOutCountChanged(); void packetOutCountChanged();
void mbpsInChanged(); void mbpsInChanged();
@ -172,6 +174,7 @@ private:
int _recentMaxPackets{ 0 } ; // recent max incoming voxel packets to process int _recentMaxPackets{ 0 } ; // recent max incoming voxel packets to process
bool _resetRecentMaxPacketsSoon{ true }; bool _resetRecentMaxPacketsSoon{ true };
bool _expanded{ false }; bool _expanded{ false };
bool _showAcuity{ false };
bool _timingExpanded{ false }; bool _timingExpanded{ false };
QString _monospaceFont; QString _monospaceFont;
const AudioIOStats* _audioStats; const AudioIOStats* _audioStats;

View file

@ -742,6 +742,43 @@
</widget> </widget>
</item> </item>
<item>
<widget class="QCheckBox" name="useAcuityCheckBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Minimum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>32</width>
<height>28</height>
</size>
</property>
<property name="baseSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="font">
<font>
<family>Arial</family>
</font>
</property>
<property name="text">
<string>Render based on visual acuity</string>
</property>
<property name="iconSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
</widget>
</item>
<item> <item>
<layout class="QHBoxLayout" name="horizontalLayout_111x"> <layout class="QHBoxLayout" name="horizontalLayout_111x">
<property name="spacing"> <property name="spacing">
@ -757,7 +794,7 @@
<number>7</number> <number>7</number>
</property> </property>
<item> <item>
<widget class="QLabel" name="label_9x"> <widget class="QLabel" name="label_desktopMinimumFPSSpin">
<property name="font"> <property name="font">
<font> <font>
<family>Arial</family> <family>Arial</family>
@ -842,7 +879,7 @@
<number>7</number> <number>7</number>
</property> </property>
<item> <item>
<widget class="QLabel" name="label_9y"> <widget class="QLabel" name="label_hmdMinimumFPSSpin">
<property name="font"> <property name="font">
<font> <font>
<family>Arial</family> <family>Arial</family>
@ -927,7 +964,7 @@
<number>7</number> <number>7</number>
</property> </property>
<item> <item>
<widget class="QLabel" name="label_9yz"> <widget class="QLabel" name="label_smallestReasonableRenderHorizon">
<property name="font"> <property name="font">
<font> <font>
<family>Arial</family> <family>Arial</family>
@ -963,7 +1000,7 @@
</spacer> </spacer>
</item> </item>
<item> <item>
<widget class="QSpinBox" name="avatarRenderSmallestReasonableHorizon"> <widget class="QSpinBox" name="smallestReasonableRenderHorizon">
<property name="minimumSize"> <property name="minimumSize">
<size> <size>
<width>100</width> <width>100</width>

View file

@ -58,7 +58,7 @@ public:
const gpu::Stream::FormatPointer getVertexFormat() const { return _vertexFormat; } const gpu::Stream::FormatPointer getVertexFormat() const { return _vertexFormat; }
// BufferStream on the mesh vertices and attributes matching the vertex format // BufferStream on the mesh vertices and attributes matching the vertex format
const gpu::BufferStream getVertexStream() const { return _vertexStream; } const gpu::BufferStream& getVertexStream() const { return _vertexStream; }
// Index Buffer // Index Buffer
void setIndexBuffer(const BufferView& buffer); void setIndexBuffer(const BufferView& buffer);

View file

@ -819,8 +819,6 @@ model::MeshPointer DeferredLightingEffect::getSpotLightMesh() {
//DEBUG: model::Mesh::Part part(0, indices, 0, model::Mesh::LINE_STRIP); //DEBUG: model::Mesh::Part part(0, indices, 0, model::Mesh::LINE_STRIP);
_spotLightMesh->setPartBuffer(gpu::BufferView(new gpu::Buffer(sizeof(part), (gpu::Byte*) &part), gpu::Element::PART_DRAWCALL)); _spotLightMesh->setPartBuffer(gpu::BufferView(new gpu::Buffer(sizeof(part), (gpu::Byte*) &part), gpu::Element::PART_DRAWCALL));
_spotLightMesh->getVertexStream();
} }
return _spotLightMesh; return _spotLightMesh;
} }

View file

@ -31,7 +31,7 @@ public:
float update(float measuredValue, float dt, bool resetAccumulator = false); // returns the new computedValue float update(float measuredValue, float dt, bool resetAccumulator = false); // returns the new computedValue
void setHistorySize(QString label = QString(""), int size = 0) { _history.reserve(size); _history.resize(0); _label = label; } // non-empty does logging void setHistorySize(QString label = QString(""), int size = 0) { _history.reserve(size); _history.resize(0); _label = label; } // non-empty does logging
bool getIsLogging() { return _history.capacity(); } bool getIsLogging() { return !_label.isEmpty(); }
float getMeasuredValueSetpoint() const { return _measuredValueSetpoint; } float getMeasuredValueSetpoint() const { return _measuredValueSetpoint; }
// In normal operation (where we can easily reach setpoint), controlledValue is typcially pinned at max. // In normal operation (where we can easily reach setpoint), controlledValue is typcially pinned at max.
// Defaults to [0, max float], but for 1/LODdistance, it might be, say, [0, 0.2 or 0.1] // Defaults to [0, max float], but for 1/LODdistance, it might be, say, [0, 0.2 or 0.1]

View file

@ -170,10 +170,12 @@
function createRaveStick(position) { function createRaveStick(position) {
var modelURL = "http://hifi-content.s3.amazonaws.com/eric/models/raveStick.fbx"; var modelURL = "http://hifi-content.s3.amazonaws.com/eric/models/raveStick.fbx";
var rotation = Quat.fromPitchYawRollDegrees(0, 0, 0);
var stick = Entities.addEntity({ var stick = Entities.addEntity({
type: "Model", type: "Model",
name: "raveStick", name: "raveStick",
modelURL: modelURL, modelURL: modelURL,
rotation: rotation,
position: position, position: position,
shapeType: 'box', shapeType: 'box',
collisionsWillMove: true, collisionsWillMove: true,
@ -206,6 +208,66 @@
}) })
}); });
var forwardVec = Quat.getFront(rotation);
var forwardQuat = Quat.rotationBetween(Vec3.UNIT_Z, forwardVec);
var color = {
red: 150,
green: 20,
blue: 100
}
var raveGlowEmitter = Entities.addEntity({
type: "ParticleEffect",
name: "Rave Stick Glow Emitter",
position: position,
parentID: stick,
isEmitting: true,
colorStart: color,
color: {
red: 200,
green: 200,
blue: 255
},
colorFinish: color,
maxParticles: 100000,
lifespan: 0.8,
emitRate: 1000,
emitOrientation: forwardQuat,
emitSpeed: 0.2,
speedSpread: 0.0,
emitDimensions: {
x: 0,
y: 0,
z: 0
},
polarStart: 0,
polarFinish: 0,
azimuthStart: 0.1,
azimuthFinish: 0.01,
emitAcceleration: {
x: 0,
y: 0,
z: 0
},
accelerationSpread: {
x: 0.00,
y: 0.00,
z: 0.00
},
radiusStart: 0.01,
radiusFinish: 0.005,
alpha: 0.7,
alphaSpread: 0.1,
alphaStart: 0.1,
alphaFinish: 0.1,
textures: "https://s3.amazonaws.com/hifi-public/eric/textures/particleSprites/beamParticle.png",
emitterShouldTrail: false,
userData: JSON.stringify({
resetMe: {
resetMe: true
}
})
});
} }
function createGun(position) { function createGun(position) {

View file

@ -149,10 +149,12 @@ MasterReset = function() {
function createRaveStick(position) { function createRaveStick(position) {
var modelURL = "http://hifi-content.s3.amazonaws.com/eric/models/raveStick.fbx"; var modelURL = "http://hifi-content.s3.amazonaws.com/eric/models/raveStick.fbx";
var rotation = Quat.fromPitchYawRollDegrees(0, 0, 0);
var stick = Entities.addEntity({ var stick = Entities.addEntity({
type: "Model", type: "Model",
name: "raveStick", name: "raveStick",
modelURL: modelURL, modelURL: modelURL,
rotation: rotation,
position: position, position: position,
shapeType: 'box', shapeType: 'box',
collisionsWillMove: true, collisionsWillMove: true,
@ -189,6 +191,66 @@ MasterReset = function() {
} }
}) })
}); });
var forwardVec = Quat.getFront(rotation);
var forwardQuat = Quat.rotationBetween(Vec3.UNIT_Z, forwardVec);
var color = {
red: 150,
green: 20,
blue: 100
}
var raveGlowEmitter = Entities.addEntity({
type: "ParticleEffect",
name: "Rave Stick Glow Emitter",
position: position,
parentID: stick,
isEmitting: true,
colorStart: color,
color: {
red: 200,
green: 200,
blue: 255
},
colorFinish: color,
maxParticles: 100000,
lifespan: 0.8,
emitRate: 1000,
emitOrientation: forwardQuat,
emitSpeed: 0.2,
speedSpread: 0.0,
emitDimensions: {
x: 0,
y: 0,
z: 0
},
polarStart: 0,
polarFinish: 0,
azimuthStart: 0.1,
azimuthFinish: 0.01,
emitAcceleration: {
x: 0,
y: 0,
z: 0
},
accelerationSpread: {
x: 0.00,
y: 0.00,
z: 0.00
},
radiusStart: 0.01,
radiusFinish: 0.005,
alpha: 0.7,
alphaSpread: 0.1,
alphaStart: 0.1,
alphaFinish: 0.1,
textures: "https://s3.amazonaws.com/hifi-public/eric/textures/particleSprites/beamParticle.png",
emitterShouldTrail: false,
userData: JSON.stringify({
resetMe: {
resetMe: true
}
})
});
} }
function createGun(position) { function createGun(position) {
@ -1084,7 +1146,7 @@ MasterReset = function() {
y: 0, y: 0,
z: 0.06 z: 0.06
}, },
relativeRotation: Quat.fromPitchYawRollDegrees(0,-90, -90) relativeRotation: Quat.fromPitchYawRollDegrees(0, -90, -90)
}, },
invertSolidWhileHeld: true invertSolidWhileHeld: true
} }