Merge Alezia's audio zones UI PR
Some checks are pending
Master API-docs CI Build and Deploy / Build and deploy API-docs (push) Waiting to run
Master Doxygen CI Build and Deploy / Build and deploy Doxygen documentation (push) Waiting to run

This commit is contained in:
ksuprynowicz 2025-07-03 21:45:04 +02:00
commit 92dcdf7d25
3 changed files with 264 additions and 14 deletions

View file

@ -223,7 +223,7 @@
"tooltip": "A list of entity IDs representing listener zones with this zone as a source."
},
"audio.listenerAttenuationCoefficients": {
"tooltip": "A list of attenuation coefficients."
"tooltip": "Each coefficient will be applied to sounds coming from this zone and being heard by a listener in the corresponding listenerZone. Factor between 0 and 1: 0 = No attenuation/full sound, 1 = extreme attenuation/no sound. Negative values: interpreted as sound distance limit in meters."
},
"avatarPriority": {
"tooltip": "Alter Avatars' update priorities."

View file

@ -25,6 +25,8 @@ const NO_SELECTION = ",";
const MAX_TAGS_PER_ROWS = 5;
const AUDIO_ATTENUATION_COEFFICIENT_DEFAULT_VALUE = 0.5;
const PROPERTY_SPACE_MODE = Object.freeze({
ALL: 0,
LOCAL: 1,
@ -814,6 +816,13 @@ const GROUPS = [
label: "Listener Zones",
type: "multipleZonesSelection",
propertyID: "audio.listenerZones",
zonesCoefficientID: "audio.listenerAttenuationCoefficients",
},
{
label: "Attenuation Coef.",
type: "zonesCoefficient",
propertyID: "audio.listenerAttenuationCoefficients",
multipleZonesSelectionID: "audio.listenerZones",
}
]
},
@ -2305,6 +2314,8 @@ function getPropertyInputElement(propertyID) {
return property.elInput;
case 'multipleZonesSelection':
return property.elInput;
case 'zonesCoefficient':
return property.elInput;
case 'arrayOfStrings':
return property.elInput;
case 'number-draggable':
@ -2480,6 +2491,12 @@ function resetProperties() {
setArrayOfStringsUi(property.elInput.id, false);
break;
}
case 'zonesCoefficient': {
property.elInput.classList.remove('multi-diff');
property.elInput.value = "[]";
setZonesCoefficientUi(property.elInput.id, false);
break;
}
case 'childList': {
setChildListData(property.elInput, undefined, "");
break;
@ -3730,12 +3747,16 @@ function createProperty(propertyData, propertyElementID, propertyName, propertyI
}
case 'multipleZonesSelection': {
property.elInput = createZonesSelection(property, elProperty);
break;
break;
}
case 'arrayOfStrings': {
property.elInput = createArrayOfStrings(property, elProperty);
break;
}
case 'zonesCoefficient': {
property.elInput = createZonesCoefficient(property, elProperty);
break;
}
case 'childList': {
property.elInput = createChildList(property, elProperty);
break;
@ -4717,16 +4738,23 @@ function addZoneToZonesSelection(propertyId, id) {
if (JSON.stringify(hiddenField.value) === '"undefined"') {
hiddenField.value = "[]";
}
let zonesCoefficientID = document.getElementById(propertyId).getAttribute('zonesCoefficientID');
let selectedZones = JSON.parse(hiddenField.value);
if (id === "ALL") {
for (let i = 0; i < zonesList.length; i++) {
if (!selectedZones.includes(zonesList[i].id)) {
selectedZones.push(zonesList[i].id);
if (zonesCoefficientID !== "") {
addElementToZonesCoefficient("property-" + zonesCoefficientID.replace(".", "-"));
}
}
}
} else {
if (!selectedZones.includes(id)) {
selectedZones.push(id);
if (zonesCoefficientID !== "") {
addElementToZonesCoefficient("property-" + zonesCoefficientID.replace(".", "-"));
}
}
}
hiddenField.value = JSON.stringify(selectedZones);
@ -4738,6 +4766,7 @@ function addZoneToZonesSelection(propertyId, id) {
}
function removeZoneFromZonesSelection(propertyId, zoneId) {
let zonesCoefficientID = document.getElementById(propertyId).getAttribute('zonesCoefficientID');
let hiddenField = document.getElementById(propertyId);
if (JSON.stringify(hiddenField.value) === '"undefined"') {
hiddenField.value = "[]";
@ -4745,7 +4774,10 @@ function removeZoneFromZonesSelection(propertyId, zoneId) {
let selectedZones = JSON.parse(hiddenField.value);
let index = selectedZones.indexOf(zoneId);
if (index > -1) {
selectedZones.splice(index, 1);
selectedZones.splice(index, 1);
if (zonesCoefficientID !== "") {
removeElementFromZonesCoefficient("property-" + zonesCoefficientID.replace(".", "-"), index);
}
}
hiddenField.value = JSON.stringify(selectedZones);
displaySelectedZones(propertyId, true);
@ -4773,17 +4805,7 @@ function displaySelectedZones(propertyId, isEditable) {
}
} else {
for (i = 0; i < selectedZones.length; i++) {
name = "{ERROR: NOT FOUND}";
for (j = 0; j < zonesList.length; j++) {
if (selectedZones[i] === zonesList[j].id) {
if (zonesList[j].name !== "") {
name = zonesList[j].name;
} else {
name = zonesList[j].id;
}
break;
}
}
name = getZoneName(selectedZones[i]);
if (isEditable) {
listedZoneInner += "<tr><td class='zoneItem'>" + name + "</td><td><a href='#' onClick='removeZoneFromZonesSelection(" + '"' + propertyId + '"' + ", " + '"' + selectedZones[i] + '"' + ");' >";
listedZoneInner += "<img src='../../../html/css/img/remove_icon.png'></a></td></tr>";
@ -4802,12 +4824,14 @@ function displaySelectedZones(propertyId, isEditable) {
}
function createZonesSelection(property, elProperty) {
let propertyData = property.data;
let elementID = property.elementID;
requestZoneList();
elProperty.className = "multipleZonesSelection";
let elInput = document.createElement('input');
elInput.setAttribute("id", elementID);
elInput.setAttribute("type", "hidden");
elInput.setAttribute("zonesCoefficientID", propertyData.zonesCoefficientID ?? "");
elInput.className = "hiddenMultiZonesSelection";
let elZonesSelector = document.createElement('div');
@ -4854,6 +4878,176 @@ function setZonesSelectionData(element, isEditable) {
displaySelectedZones(element.id, isEditable);
}
function getZoneName(zoneID) {
let name = "{ERROR: NOT FOUND}";
let j;
for (j = 0; j < zonesList.length; j++) {
if (zoneID === zonesList[j].id) {
if (zonesList[j].name !== "") {
name = zonesList[j].name;
} else {
name = zonesList[j].id;
}
break;
}
}
return name;
}
/**
* ZONES COEFFICIENT
*/
function createZonesCoefficient(property, elProperty) {
let propertyData = property.data;
let elementID = property.elementID;
elProperty.className = "zonesCoefficient";
let elInput = document.createElement('input');
elInput.setAttribute("id", elementID);
elInput.setAttribute("type", "hidden");
elInput.setAttribute("multipleZonesSelectionID", propertyData.multipleZonesSelectionID);
elInput.className = "hiddenZonesCoefficient";
let elZonesCoefficientSelector = document.createElement('div');
elZonesCoefficientSelector.setAttribute("id", "zonesCoefficient-selector-" + elementID);
let elMultiDiff = document.createElement('span');
elMultiDiff.className = "multi-diff";
elProperty.appendChild(elInput);
elProperty.appendChild(elZonesCoefficientSelector);
elProperty.appendChild(elMultiDiff);
return elInput;
}
function setZonesCoefficientUi(propertyId, isEditable) {
let i, listedCoefficientsInner, hiddenData, isMultiple, multipleZonesSelectionID, zoneListData;
hiddenData = document.getElementById(propertyId).value;
multipleZonesSelectionID = document.getElementById(propertyId).getAttribute('multipleZonesSelectionID');
zoneListData = JSON.parse(document.getElementById("property-" + multipleZonesSelectionID.replace(".", "-")).value);
if (JSON.stringify(hiddenData) === '"undefined"') {
isMultiple = true;
hiddenData = "[]";
} else {
isMultiple = false;
}
listedCoefficientsInner = "<div class='zonesCoefficientContainer'>";
let coefficients = JSON.parse(hiddenData);
if (coefficients.length !== zoneListData.length) {
listedCoefficientsInner += "<div class='zonesCoefficientWarning'>";
listedCoefficientsInner += "WARNING: Zones and coefficients are desynchronized.<br>";
listedCoefficientsInner += "<input type='button' value = 'Fix this' onClick='fixUnsynchZonesCoefficients(" + '"' + propertyId + '", ' + zoneListData.length + ");'>";
listedCoefficientsInner += "</div>";
} else {
if (coefficients.length === 0) {
if (isMultiple) {
listedCoefficientsInner += "Not available.";
}
listedCoefficientsInner += "<br>";
} else {
let zoneName, lineColor;
for (i = 0; i < coefficients.length; i++) {
zoneName = getZoneName(zoneListData[i]);
lineColor = "";
if (i % 2 !== 0) {
lineColor = " zonesCoefficientDarkLine";
}
if (isEditable) {
listedCoefficientsInner += "<div class='zonesCoefficientLine" + lineColor + "'>";
listedCoefficientsInner += "<span class='zonesCoefficientCoef'>";
listedCoefficientsInner += "<input class='zonesCoefficient' type='number' step='0.005' max = '1.0' value='" + +coefficients[i].toFixed(4) + "' onChange='setZonesCoefficientValue(" + '"' + propertyId + '", ' + i + ", this.value);'>";
listedCoefficientsInner += "</span>";
listedCoefficientsInner += "<span class='zonesCoefficientZone'>&nbsp;" + zoneName + "</span>";
listedCoefficientsInner += "</div>";
} else {
listedCoefficientsInner += "<div class='zonesCoefficientLine" + lineColor + "'>";
listedCoefficientsInner += "<span class='zonesCoefficientCoef'><div class='zonesCoefficientReadOnly'>" + +coefficients[i].toFixed(4) + "</div></span>";
listedCoefficientsInner += "<span class='zonesCoefficientZone'>&nbsp;&nbsp;" + zoneName + "</span>";
listedCoefficientsInner += "</div>";
}
}
}
}
listedCoefficientsInner += "</div>";
document.getElementById("zonesCoefficient-selector-" + propertyId).innerHTML = listedCoefficientsInner;
}
function fixUnsynchZonesCoefficients(propertyId, expectedLength) {
let hiddenField = document.getElementById(propertyId);
if (JSON.stringify(hiddenField.value) === '"undefined"') {
hiddenField.value = "[]";
}
let originalCoefficients = JSON.parse(hiddenField.value);
let coefficients = [];
let i;
for (i = 0; i < expectedLength; i++) {
if ( i < originalCoefficients.length) {
coefficients[i] = originalCoefficients[i];
} else {
coefficients[i] = AUDIO_ATTENUATION_COEFFICIENT_DEFAULT_VALUE;
}
}
hiddenField.value = JSON.stringify(coefficients);
let propertyName = propertyId.replace("property-", "");
propertyName = propertyName.replace("-", ".");
updateProperty(propertyName, coefficients, false);
}
function setZonesCoefficientValue(propertyId, index, coef) {
function cleanFloat(val) {
let num = parseFloat(val);
if (Number.isNaN(num)) {
return 0;
}
return num;
}
let newCoefValue = cleanFloat(coef);
let hiddenField = document.getElementById(propertyId);
if (JSON.stringify(hiddenField.value) === '"undefined"') {
hiddenField.value = "[]";
}
let coefficients = JSON.parse(hiddenField.value);
coefficients[index] = newCoefValue;
hiddenField.value = JSON.stringify(coefficients);
let propertyName = propertyId.replace("property-", "");
propertyName = propertyName.replace("-", ".");
updateProperty(propertyName, coefficients, false);
}
function removeElementFromZonesCoefficient(propertyId, index) {
let hiddenField = document.getElementById(propertyId);
if (JSON.stringify(hiddenField.value) === '"undefined"') {
hiddenField.value = "[]";
}
let coefficients = JSON.parse(hiddenField.value);
coefficients.splice(index, 1);
hiddenField.value = JSON.stringify(coefficients);
let propertyName = propertyId.replace("property-", "");
propertyName = propertyName.replace("-", ".");
updateProperty(propertyName, coefficients, false);
}
function addElementToZonesCoefficient(propertyId) {
let hiddenField = document.getElementById(propertyId);
if (JSON.stringify(hiddenField.value) === '"undefined"') {
hiddenField.value = "[]";
}
let coefficients = JSON.parse(hiddenField.value);
coefficients.push(AUDIO_ATTENUATION_COEFFICIENT_DEFAULT_VALUE);
hiddenField.value = JSON.stringify(coefficients);
let propertyName = propertyId.replace("property-", "");
propertyName = propertyName.replace("-", ".");
updateProperty(propertyName, coefficients, false);
}
/**
* ARRAY-OF-STRINGS FUNCTIONS
*/
@ -5716,6 +5910,15 @@ function handleEntitySelectionUpdate(selections, isPropertiesToolUpdate) {
}
break;
}
case 'zonesCoefficient': {
property.elInput.value = JSON.stringify(propertyValue);
if (lockedMultiValue.isMultiDiffValue || lockedMultiValue.value) {
setZonesCoefficientUi(property.elInput.id, false);
} else {
setZonesCoefficientUi(property.elInput.id, true);
}
break;
}
case 'childList': {
let parentID = selections[0].properties.parentID;
if (selections.length !== 1 || parentID === UUID_NONE) {

View file

@ -2296,6 +2296,53 @@ font.viewParentIcon {
font-weight: bold;
}
div.zonesCoefficientContainer {
width: 100%;
border: 2px solid #777777;
padding: 1px;
background: #333333;
}
div.zonesCoefficientWarning {
width: 100%;
border: 2px solid #FF0000;
border-radius: 5px;
background: #FFFF00;
color: #000000;
padding: 5px;
text-align: center;
}
div.zonesCoefficientLine {
width: 99%;
padding: 2px;
}
div.zonesCoefficientDarkLine {
background: #222222;
}
span.zonesCoefficientCoef {
width: 20%;
}
span.zonesCoefficientZone {
width: 80%;
padding-left: 4px;
}
div.zonesCoefficientReadOnly {
width: 70px;
display: inline-block;
height: 20px;
padding: 3px 3px 3px 8px;
}
input[type="number"].zonesCoefficient {
width: 80px;
background: #111111;
}
div.arrayOfStringsContainer {
width: 100%;
overflow: hidden;