mirror of
https://github.com/JulianGro/overte.git
synced 2025-04-25 21:35:04 +02:00
first cut, pretty ugly
This commit is contained in:
parent
5bef6ea029
commit
51246a2e45
3 changed files with 73 additions and 40 deletions
|
@ -31,6 +31,7 @@ Item {
|
||||||
property real displayNameTextPixelSize: 18
|
property real displayNameTextPixelSize: 18
|
||||||
property int usernameTextHeight: 12
|
property int usernameTextHeight: 12
|
||||||
property real audioLevel: 0.0
|
property real audioLevel: 0.0
|
||||||
|
property real avgAudioLevel: 0.0
|
||||||
property bool isMyCard: false
|
property bool isMyCard: false
|
||||||
property bool selected: false
|
property bool selected: false
|
||||||
property bool isAdmin: false
|
property bool isAdmin: false
|
||||||
|
@ -335,7 +336,7 @@ Item {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Per-Avatar Gain Slider
|
// Per-Avatar Gain Slider
|
||||||
Slider {
|
Slider {
|
||||||
id: gainSlider
|
id: gainSlider
|
||||||
// Size
|
// Size
|
||||||
|
@ -369,7 +370,7 @@ Item {
|
||||||
mouse.accepted = false
|
mouse.accepted = false
|
||||||
}
|
}
|
||||||
onReleased: {
|
onReleased: {
|
||||||
// the above mouse.accepted seems to make this
|
// the above mouse.accepted seems to make this
|
||||||
// never get called, nonetheless...
|
// never get called, nonetheless...
|
||||||
mouse.accepted = false
|
mouse.accepted = false
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
// Pal.qml
|
// Pal.qml
|
||||||
// qml/hifi
|
// qml/hifi
|
||||||
//
|
//
|
||||||
// People Action List
|
// People Action List
|
||||||
//
|
//
|
||||||
// Created by Howard Stearns on 12/12/2016
|
// Created by Howard Stearns on 12/12/2016
|
||||||
// Copyright 2016 High Fidelity, Inc.
|
// Copyright 2016 High Fidelity, Inc.
|
||||||
|
@ -29,8 +29,8 @@ Rectangle {
|
||||||
property int myCardHeight: 90
|
property int myCardHeight: 90
|
||||||
property int rowHeight: 70
|
property int rowHeight: 70
|
||||||
property int actionButtonWidth: 55
|
property int actionButtonWidth: 55
|
||||||
property int nameCardWidth: palContainer.width - actionButtonWidth*(iAmAdmin ? 4 : 2) - 4 - hifi.dimensions.scrollbarBackgroundWidth
|
property int nameCardWidth: palContainer.width - actionButtonWidth*(iAmAdmin ? 5 : 3) - 4 - hifi.dimensions.scrollbarBackgroundWidth
|
||||||
property var myData: ({displayName: "", userName: "", audioLevel: 0.0, admin: true}) // valid dummy until set
|
property var myData: ({displayName: "", userName: "", audioLevel: 0.0, avgAudioLevel: 0.0, admin: true}) // valid dummy until set
|
||||||
property var ignored: ({}); // Keep a local list of ignored avatars & their data. Necessary because HashMap is slow to respond after ignoring.
|
property var ignored: ({}); // Keep a local list of ignored avatars & their data. Necessary because HashMap is slow to respond after ignoring.
|
||||||
property var userModelData: [] // This simple list is essentially a mirror of the userModel listModel without all the extra complexities.
|
property var userModelData: [] // This simple list is essentially a mirror of the userModel listModel without all the extra complexities.
|
||||||
property bool iAmAdmin: false
|
property bool iAmAdmin: false
|
||||||
|
@ -86,6 +86,7 @@ Rectangle {
|
||||||
displayName: myData.displayName
|
displayName: myData.displayName
|
||||||
userName: myData.userName
|
userName: myData.userName
|
||||||
audioLevel: myData.audioLevel
|
audioLevel: myData.audioLevel
|
||||||
|
avgAudioLevel: myData.avgAudioLevel
|
||||||
isMyCard: true
|
isMyCard: true
|
||||||
// Size
|
// Size
|
||||||
width: nameCardWidth
|
width: nameCardWidth
|
||||||
|
@ -184,6 +185,13 @@ Rectangle {
|
||||||
movable: false
|
movable: false
|
||||||
resizable: false
|
resizable: false
|
||||||
}
|
}
|
||||||
|
TableViewColumn {
|
||||||
|
role: "avgAudioLevel"
|
||||||
|
title: "VOL"
|
||||||
|
width: actionButtonWidth
|
||||||
|
movable: false
|
||||||
|
resizable: false
|
||||||
|
}
|
||||||
TableViewColumn {
|
TableViewColumn {
|
||||||
visible: iAmAdmin
|
visible: iAmAdmin
|
||||||
role: "mute"
|
role: "mute"
|
||||||
|
@ -218,6 +226,7 @@ Rectangle {
|
||||||
id: itemCell
|
id: itemCell
|
||||||
property bool isCheckBox: styleData.role === "personalMute" || styleData.role === "ignore"
|
property bool isCheckBox: styleData.role === "personalMute" || styleData.role === "ignore"
|
||||||
property bool isButton: styleData.role === "mute" || styleData.role === "kick"
|
property bool isButton: styleData.role === "mute" || styleData.role === "kick"
|
||||||
|
property bool isText: styleData.role == "avgAudioLevel"
|
||||||
// This NameCard refers to the cell that contains an avatar's
|
// This NameCard refers to the cell that contains an avatar's
|
||||||
// DisplayName and UserName
|
// DisplayName and UserName
|
||||||
NameCard {
|
NameCard {
|
||||||
|
@ -226,7 +235,8 @@ Rectangle {
|
||||||
displayName: styleData.value
|
displayName: styleData.value
|
||||||
userName: model ? model.userName : ""
|
userName: model ? model.userName : ""
|
||||||
audioLevel: model ? model.audioLevel : 0.0
|
audioLevel: model ? model.audioLevel : 0.0
|
||||||
visible: !isCheckBox && !isButton
|
avgAudioLevel: model ? model.avgAudioLevel : 0.0
|
||||||
|
visible: !isCheckBox && !isButton && !isText
|
||||||
uuid: model ? model.sessionId : ""
|
uuid: model ? model.sessionId : ""
|
||||||
selected: styleData.selected
|
selected: styleData.selected
|
||||||
isAdmin: model && model.admin
|
isAdmin: model && model.admin
|
||||||
|
@ -236,7 +246,16 @@ Rectangle {
|
||||||
// Anchors
|
// Anchors
|
||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
}
|
}
|
||||||
|
Text {
|
||||||
|
id: avgAudioVolume
|
||||||
|
text: model ? model.avgAudioLevel : 0.0
|
||||||
|
visible: isText
|
||||||
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
verticalAlignment: Text.AlignVCenter
|
||||||
|
width: parent.width
|
||||||
|
height: parent.height
|
||||||
|
}
|
||||||
|
|
||||||
// This CheckBox belongs in the columns that contain the stateful action buttons ("Mute" & "Ignore" for now)
|
// This CheckBox belongs in the columns that contain the stateful action buttons ("Mute" & "Ignore" for now)
|
||||||
// KNOWN BUG with the Checkboxes: When clicking in the center of the sorting header, the checkbox
|
// KNOWN BUG with the Checkboxes: When clicking in the center of the sorting header, the checkbox
|
||||||
// will appear in the "hovered" state. Hovering over the checkbox will fix it.
|
// will appear in the "hovered" state. Hovering over the checkbox will fix it.
|
||||||
|
@ -272,7 +291,7 @@ Rectangle {
|
||||||
checked = Qt.binding(function() { return (model[styleData.role])})
|
checked = Qt.binding(function() { return (model[styleData.role])})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// This Button belongs in the columns that contain the stateless action buttons ("Silence" & "Ban" for now)
|
// This Button belongs in the columns that contain the stateless action buttons ("Silence" & "Ban" for now)
|
||||||
HifiControls.Button {
|
HifiControls.Button {
|
||||||
id: actionButton
|
id: actionButton
|
||||||
|
@ -542,23 +561,28 @@ Rectangle {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 'updateAudioLevel':
|
case 'updateAudioLevel':
|
||||||
for (var userId in message.params) {
|
for (var userId in message.params) {
|
||||||
var audioLevel = message.params[userId];
|
var audioLevel = message.params[userId][0];
|
||||||
|
var avgAudioLevel = message.params[userId][1];
|
||||||
// If the userId is 0, we're updating "myData".
|
// If the userId is 0, we're updating "myData".
|
||||||
if (userId == 0) {
|
if (userId == 0) {
|
||||||
myData.audioLevel = audioLevel;
|
myData.audioLevel = audioLevel;
|
||||||
myCard.audioLevel = audioLevel; // Defensive programming
|
myCard.audioLevel = audioLevel; // Defensive programming
|
||||||
|
myData.avgAudioLevel = avgAudioLevel;
|
||||||
|
myCard.avgAudioLevel = avgAudioLevel;
|
||||||
} else {
|
} else {
|
||||||
var userIndex = findSessionIndex(userId);
|
var userIndex = findSessionIndex(userId);
|
||||||
if (userIndex != -1) {
|
if (userIndex != -1) {
|
||||||
userModel.setProperty(userIndex, "audioLevel", audioLevel);
|
userModel.setProperty(userIndex, "audioLevel", audioLevel);
|
||||||
userModelData[userIndex].audioLevel = audioLevel; // Defensive programming
|
userModelData[userIndex].audioLevel = audioLevel; // Defensive programming
|
||||||
|
userModel.setProperty(userIndex, "avgAudioLevel", avgAudioLevel);
|
||||||
|
userModelData[userIndex].avgAudioLevel = avgAudioLevel;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 'clearLocalQMLData':
|
case 'clearLocalQMLData':
|
||||||
ignored = {};
|
ignored = {};
|
||||||
gainSliderValueDB = {};
|
gainSliderValueDB = {};
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -13,7 +13,7 @@
|
||||||
|
|
||||||
(function() { // BEGIN LOCAL_SCOPE
|
(function() { // BEGIN LOCAL_SCOPE
|
||||||
|
|
||||||
// hardcoding these as it appears we cannot traverse the originalTextures in overlays??? Maybe I've missed
|
// hardcoding these as it appears we cannot traverse the originalTextures in overlays??? Maybe I've missed
|
||||||
// something, will revisit as this is sorta horrible.
|
// something, will revisit as this is sorta horrible.
|
||||||
const UNSELECTED_TEXTURES = {"idle-D": Script.resolvePath("./assets/models/Avatar-Overlay-v1.fbx/Avatar-Overlay-v1.fbm/avatar-overlay-idle.png"),
|
const UNSELECTED_TEXTURES = {"idle-D": Script.resolvePath("./assets/models/Avatar-Overlay-v1.fbx/Avatar-Overlay-v1.fbm/avatar-overlay-idle.png"),
|
||||||
"idle-E": Script.resolvePath("./assets/models/Avatar-Overlay-v1.fbx/Avatar-Overlay-v1.fbm/avatar-overlay-idle.png")
|
"idle-E": Script.resolvePath("./assets/models/Avatar-Overlay-v1.fbx/Avatar-Overlay-v1.fbm/avatar-overlay-idle.png")
|
||||||
|
@ -87,7 +87,7 @@ ExtendedOverlay.prototype.hover = function (hovering) {
|
||||||
} else {
|
} else {
|
||||||
lastHoveringId = 0;
|
lastHoveringId = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.editOverlay({color: color(this.selected, hovering, this.audioLevel)});
|
this.editOverlay({color: color(this.selected, hovering, this.audioLevel)});
|
||||||
if (this.model) {
|
if (this.model) {
|
||||||
this.model.editOverlay({textures: textures(this.selected, hovering)});
|
this.model.editOverlay({textures: textures(this.selected, hovering)});
|
||||||
|
@ -104,7 +104,7 @@ ExtendedOverlay.prototype.select = function (selected) {
|
||||||
if (this.selected === selected) {
|
if (this.selected === selected) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
UserActivityLogger.palAction(selected ? "avatar_selected" : "avatar_deselected", this.key);
|
UserActivityLogger.palAction(selected ? "avatar_selected" : "avatar_deselected", this.key);
|
||||||
|
|
||||||
this.editOverlay({color: color(selected, this.hovering, this.audioLevel)});
|
this.editOverlay({color: color(selected, this.hovering, this.audioLevel)});
|
||||||
|
@ -273,7 +273,7 @@ function sendToQml(message) {
|
||||||
//
|
//
|
||||||
function addAvatarNode(id) {
|
function addAvatarNode(id) {
|
||||||
var selected = ExtendedOverlay.isSelected(id);
|
var selected = ExtendedOverlay.isSelected(id);
|
||||||
return new ExtendedOverlay(id, "sphere", {
|
return new ExtendedOverlay(id, "sphere", {
|
||||||
drawInFront: true,
|
drawInFront: true,
|
||||||
solid: true,
|
solid: true,
|
||||||
alpha: 0.8,
|
alpha: 0.8,
|
||||||
|
@ -290,6 +290,7 @@ function populateUserList(selectData) {
|
||||||
userName: '',
|
userName: '',
|
||||||
sessionId: id || '',
|
sessionId: id || '',
|
||||||
audioLevel: 0.0,
|
audioLevel: 0.0,
|
||||||
|
avgAudioLevel: 0.0,
|
||||||
admin: false,
|
admin: false,
|
||||||
personalMute: !!id && Users.getPersonalMuteStatus(id), // expects proper boolean, not null
|
personalMute: !!id && Users.getPersonalMuteStatus(id), // expects proper boolean, not null
|
||||||
ignore: !!id && Users.getIgnoreStatus(id) // ditto
|
ignore: !!id && Users.getIgnoreStatus(id) // ditto
|
||||||
|
@ -341,7 +342,7 @@ function updateOverlays() {
|
||||||
var target = avatar.position;
|
var target = avatar.position;
|
||||||
var distance = Vec3.distance(target, eye);
|
var distance = Vec3.distance(target, eye);
|
||||||
var offset = 0.2;
|
var offset = 0.2;
|
||||||
|
|
||||||
// base offset on 1/2 distance from hips to head if we can
|
// base offset on 1/2 distance from hips to head if we can
|
||||||
var headIndex = avatar.getJointIndex("Head");
|
var headIndex = avatar.getJointIndex("Head");
|
||||||
if (headIndex > 0) {
|
if (headIndex > 0) {
|
||||||
|
@ -350,7 +351,7 @@ function updateOverlays() {
|
||||||
|
|
||||||
// get diff between target and eye (a vector pointing to the eye from avatar position)
|
// get diff between target and eye (a vector pointing to the eye from avatar position)
|
||||||
var diff = Vec3.subtract(target, eye);
|
var diff = Vec3.subtract(target, eye);
|
||||||
|
|
||||||
// move a bit in front, towards the camera
|
// move a bit in front, towards the camera
|
||||||
target = Vec3.subtract(target, Vec3.multiply(Vec3.normalize(diff), offset));
|
target = Vec3.subtract(target, Vec3.multiply(Vec3.normalize(diff), offset));
|
||||||
|
|
||||||
|
@ -361,12 +362,12 @@ function updateOverlays() {
|
||||||
overlay.editOverlay({
|
overlay.editOverlay({
|
||||||
color: color(ExtendedOverlay.isSelected(id), overlay.hovering, overlay.audioLevel),
|
color: color(ExtendedOverlay.isSelected(id), overlay.hovering, overlay.audioLevel),
|
||||||
position: target,
|
position: target,
|
||||||
dimensions: 0.032 * distance
|
dimensions: 0.032 * distance
|
||||||
});
|
});
|
||||||
if (overlay.model) {
|
if (overlay.model) {
|
||||||
overlay.model.ping = pingPong;
|
overlay.model.ping = pingPong;
|
||||||
overlay.model.editOverlay({
|
overlay.model.editOverlay({
|
||||||
position: target,
|
position: target,
|
||||||
scale: 0.2 * distance, // constant apparent size
|
scale: 0.2 * distance, // constant apparent size
|
||||||
rotation: Camera.orientation
|
rotation: Camera.orientation
|
||||||
});
|
});
|
||||||
|
@ -433,7 +434,7 @@ function handleMouseMoveEvent(event) { // find out which overlay (if any) is ove
|
||||||
handleMouseMove(pickRay);
|
handleMouseMove(pickRay);
|
||||||
}
|
}
|
||||||
function handleTriggerPressed(hand, value) {
|
function handleTriggerPressed(hand, value) {
|
||||||
// The idea is if you press one trigger, it is the one
|
// The idea is if you press one trigger, it is the one
|
||||||
// we will consider the mouse. Even if the other is pressed,
|
// we will consider the mouse. Even if the other is pressed,
|
||||||
// we ignore it until this one is no longer pressed.
|
// we ignore it until this one is no longer pressed.
|
||||||
isPressed = value > TRIGGER_PRESS_THRESHOLD;
|
isPressed = value > TRIGGER_PRESS_THRESHOLD;
|
||||||
|
@ -441,10 +442,10 @@ function handleTriggerPressed(hand, value) {
|
||||||
currentHandPressed = isPressed ? hand : 0;
|
currentHandPressed = isPressed ? hand : 0;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (currentHandPressed == hand) {
|
if (currentHandPressed == hand) {
|
||||||
currentHandPressed = isPressed ? hand : 0;
|
currentHandPressed = isPressed ? hand : 0;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// otherwise, the other hand is still triggered
|
// otherwise, the other hand is still triggered
|
||||||
// so do nothing.
|
// so do nothing.
|
||||||
}
|
}
|
||||||
|
@ -574,41 +575,48 @@ function receiveMessage(channel, messageString, senderID) {
|
||||||
Messages.subscribe(CHANNEL);
|
Messages.subscribe(CHANNEL);
|
||||||
Messages.messageReceived.connect(receiveMessage);
|
Messages.messageReceived.connect(receiveMessage);
|
||||||
|
|
||||||
|
|
||||||
var AVERAGING_RATIO = 0.05;
|
var AVERAGING_RATIO = 0.05;
|
||||||
|
var LONG_AVERAGING_RATIO = 0.75;
|
||||||
var LOUDNESS_FLOOR = 11.0;
|
var LOUDNESS_FLOOR = 11.0;
|
||||||
var LOUDNESS_SCALE = 2.8 / 5.0;
|
var LOUDNESS_SCALE = 2.8 / 5.0;
|
||||||
var LOG2 = Math.log(2.0);
|
var LOG2 = Math.log(2.0);
|
||||||
var myData = {}; // we're not includied in ExtendedOverlay.get.
|
var myData = {}; // we're not includied in ExtendedOverlay.get.
|
||||||
|
|
||||||
|
function scaleAudio(val) {
|
||||||
|
var audioLevel = 0.0;
|
||||||
|
if (val <= LOUDNESS_FLOOR) {
|
||||||
|
audioLevel = val / LOUDNESS_FLOOR * LOUDNESS_SCALE;
|
||||||
|
} else {
|
||||||
|
audioLevel = (val -(LOUDNESS_FLOOR -1 )) * LOUDNESS_SCALE;
|
||||||
|
}
|
||||||
|
if (audioLevel > 1.0) {
|
||||||
|
audioLevel = 1;
|
||||||
|
}
|
||||||
|
return audioLevel;
|
||||||
|
}
|
||||||
function getAudioLevel(id) {
|
function getAudioLevel(id) {
|
||||||
// the VU meter should work similarly to the one in AvatarInputs: log scale, exponentially averaged
|
// the VU meter should work similarly to the one in AvatarInputs: log scale, exponentially averaged
|
||||||
// But of course it gets the data at a different rate, so we tweak the averaging ratio and frequency
|
// But of course it gets the data at a different rate, so we tweak the averaging ratio and frequency
|
||||||
// of updating (the latter for efficiency too).
|
// of updating (the latter for efficiency too).
|
||||||
var avatar = AvatarList.getAvatar(id);
|
var avatar = AvatarList.getAvatar(id);
|
||||||
var audioLevel = 0.0;
|
var audioLevel = 0.0;
|
||||||
|
var avgAudioLevel = 0.0;
|
||||||
var data = id ? ExtendedOverlay.get(id) : myData;
|
var data = id ? ExtendedOverlay.get(id) : myData;
|
||||||
if (!data) {
|
if (data) {
|
||||||
return audioLevel;
|
|
||||||
}
|
|
||||||
|
|
||||||
// we will do exponential moving average by taking some the last loudness and averaging
|
// we will do exponential moving average by taking some the last loudness and averaging
|
||||||
data.accumulatedLevel = AVERAGING_RATIO * (data.accumulatedLevel || 0) + (1 - AVERAGING_RATIO) * (avatar.audioLoudness);
|
data.accumulatedLevel = AVERAGING_RATIO * (data.accumulatedLevel || 0) + (1 - AVERAGING_RATIO) * (avatar.audioLoudness);
|
||||||
|
data.longAccumulatedLevel = LONG_AVERAGING_RATIO * (data.longAccumulatedLevel || 0) + (1 - LONG_AVERAGING_RATIO) * (avatar.audioLoudness);
|
||||||
|
|
||||||
// add 1 to insure we don't go log() and hit -infinity. Math.log is
|
// add 1 to insure we don't go log() and hit -infinity. Math.log is
|
||||||
// natural log, so to get log base 2, just divide by ln(2).
|
// natural log, so to get log base 2, just divide by ln(2).
|
||||||
var logLevel = Math.log(data.accumulatedLevel + 1) / LOG2;
|
audioLevel = scaleAudio(Math.log(data.accumulatedLevel + 1) / LOG2);
|
||||||
|
avgAudioLevel = scaleAudio(Math.log(data.longAccumulatedLevel + 1) / LOG2);
|
||||||
|
|
||||||
if (logLevel <= LOUDNESS_FLOOR) {
|
data.audioLevel = audioLevel;
|
||||||
audioLevel = logLevel / LOUDNESS_FLOOR * LOUDNESS_SCALE;
|
data.averageAudioLevel = avgAudioLevel;
|
||||||
} else {
|
|
||||||
audioLevel = (logLevel - (LOUDNESS_FLOOR - 1.0)) * LOUDNESS_SCALE;
|
|
||||||
}
|
}
|
||||||
if (audioLevel > 1.0) {
|
return [audioLevel, avgAudioLevel];
|
||||||
audioLevel = 1;
|
|
||||||
}
|
|
||||||
data.audioLevel = audioLevel;
|
|
||||||
return audioLevel;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function createAudioInterval(interval) {
|
function createAudioInterval(interval) {
|
||||||
|
|
Loading…
Reference in a new issue