diff --git a/scripts/system/edit.js b/scripts/system/edit.js index 7c5af27b82..8ca6c5b14c 100644 --- a/scripts/system/edit.js +++ b/scripts/system/edit.js @@ -2522,7 +2522,19 @@ var PropertiesTool = function (opts) { type: 'propertyRangeReply', propertyRanges: propertyRanges, }); - } + } else if (data.type === "materialTargetRequest") { + var properties = Entities.getEntityProperties(data.entityID, ["type", "parentID"]); + var parentModel = properties.parentID !== Uuid.NULL && + Entities.getEntityProperties(properties.parentID, ["type"]).type === "Model"; + var parentModelData; + if (properties.type === "Material" && parentModel) { + parentModelData = Graphics.getModel(properties.parentID); + } + emitScriptEvent({ + type: 'materialTargetReply', + materialTargetData: parentModelData, + }); + } }; HMD.displayModeChanged.connect(function() { diff --git a/scripts/system/html/css/edit-style.css b/scripts/system/html/css/edit-style.css index 926eeaeeb3..d6a281b0c4 100644 --- a/scripts/system/html/css/edit-style.css +++ b/scripts/system/html/css/edit-style.css @@ -861,10 +861,16 @@ span.indented { .multiselect-options input[type=checkbox]:checked + label { background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACwAAAAsCAYAAAAehFoBAAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAC4jAAAuIwF4pT92AAAAB3RJTUUH4goSADMveELP9QAAABl0RVh0Q29tbWVudABDcmVhdGVkIHdpdGggR0lNUFeBDhcAAAIqSURBVFjD7ZmxkqowFIb/7GwJPfT6APZib+9QC33o4QGghz70vIC9sfcBsE966bPNWlxnlQTDRWc4JUT4hpPz5SQSAAofFF/4sJiBx47v+wun04m8E+B6vVbzlJiBZ2CLIYRQQgj1EcBCCEUpRRRF4Jyrtwa+Xq+glEJKia7rkKYpGGPqbYHzPFdSyn+uMcZ6oScBZowpzvmje0jTVHVd9x7ATdMoxtjTMZxzUErV5MDn81mVZak1No7jab+wEEKlaaoNGwQBmQz4pq9H8/IeNo5jMmnRpWmKeyP8FZvN5insfwEuy1JdLpfecb7vI8uy3tb2Szelu91ONU1jtP9jjKmmabRgq6qC4zh2VrpbSsuy1FqNdPUFAK7roqoqeJ6ntXH4Mk1pn9gBoG1bbX1lWaYN2wv8KKWcc+z3+z+7LFv6MgY+Ho9PUyqlBKUUbduqe33pGKFPX0bAQgiV53nvj6WUiKIIt2K0qS/tXTMAEELguq6W6H/nOQ6Hg1V9GX1hz/NIXdckCALtB7Vta1VfxnPYcRwURUEeNSGmYaqvwVqL45gkSfIysKm+Xlo4wjAkdV3D9/1BLxmir5d7ieVySaqqMoYOw3CwEV5ufkyLcbVaIUkSq2d1xt2abjH6vo+iKKwfLA5uL58Vow19jdIPPyrGoiisGGGUBv6+GJMkwWKxGO2M+dvGQ36LEZxztd1uRz0Qt7ZFchwHY8NOelQ1NAjm/+lm4M8G/gH2zx33BSr7jAAAAABJRU5ErkJggg=='); } -.multiselect-options input[type=checkbox]:checked + label:hover { +.multiselect-options input[type=checkbox]:checked + label:hover { background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACwAAAAsCAYAAAAehFoBAAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAC4jAAAuIwF4pT92AAAAB3RJTUUH4goSADMveELP9QAAABl0RVh0Q29tbWVudABDcmVhdGVkIHdpdGggR0lNUFeBDhcAAAIqSURBVFjD7ZmxkqowFIb/7GwJPfT6APZib+9QC33o4QGghz70vIC9sfcBsE966bPNWlxnlQTDRWc4JUT4hpPz5SQSAAofFF/4sJiBx47v+wun04m8E+B6vVbzlJiBZ2CLIYRQQgj1EcBCCEUpRRRF4Jyrtwa+Xq+glEJKia7rkKYpGGPqbYHzPFdSyn+uMcZ6oScBZowpzvmje0jTVHVd9x7ATdMoxtjTMZxzUErV5MDn81mVZak1No7jab+wEEKlaaoNGwQBmQz4pq9H8/IeNo5jMmnRpWmKeyP8FZvN5insfwEuy1JdLpfecb7vI8uy3tb2Szelu91ONU1jtP9jjKmmabRgq6qC4zh2VrpbSsuy1FqNdPUFAK7roqoqeJ6ntXH4Mk1pn9gBoG1bbX1lWaYN2wv8KKWcc+z3+z+7LFv6MgY+Ho9PUyqlBKUUbduqe33pGKFPX0bAQgiV53nvj6WUiKIIt2K0qS/tXTMAEELguq6W6H/nOQ6Hg1V9GX1hz/NIXdckCALtB7Vta1VfxnPYcRwURUEeNSGmYaqvwVqL45gkSfIysKm+Xlo4wjAkdV3D9/1BLxmir5d7ieVySaqqMoYOw3CwEV5ufkyLcbVaIUkSq2d1xt2abjH6vo+iKKwfLA5uL58Vow19jdIPPyrGoiisGGGUBv6+GJMkwWKxGO2M+dvGQ36LEZxztd1uRz0Qt7ZFchwHY8NOelQ1NAjm/+lm4M8G/gH2zx33BSr7jAAAAABJRU5ErkJggg=='); } +.dynamic-multiselect { + position: relative; + top: 6px; + padding-bottom: 6px; +} + div.refresh { box-sizing: border-box; padding-right: 44px; diff --git a/scripts/system/html/js/entityProperties.js b/scripts/system/html/js/entityProperties.js index 8baeb8a1e0..ca04f36dc9 100644 --- a/scripts/system/html/js/entityProperties.js +++ b/scripts/system/html/js/entityProperties.js @@ -701,23 +701,10 @@ const GROUPS = [ propertyID: "materialData", }, { - label: "Select Submesh", - type: "bool", - propertyID: "selectSubmesh", - }, - { - label: "Submesh to Replace", - type: "number-draggable", - min: 0, - step: 1, - propertyID: "submeshToReplace", - indentedLabel: true, - }, - { - label: "Material to Replace", - type: "string", - propertyID: "materialNameToReplace", - indentedLabel: true, + label: "Material Target", + type: "dynamic-multiselect", + propertyUpdate: materialTargetPropertyUpdate, + propertyID: "parentMaterialName", }, { label: "Priority", @@ -1639,6 +1626,8 @@ function getPropertyInputElement(propertyID) { return { red: property.elNumberR.elInput, green: property.elNumberG.elInput, blue: property.elNumberB.elInput }; case 'icon': return property.elLabel; + case 'dynamic-multiselect': + return property.elDivOptions; default: return undefined; } @@ -1761,6 +1750,10 @@ function resetProperties() { property.elInput.imageLoad(property.elInput.value); break; } + case 'dynamic-multiselect': { + resetDynamicMultiselectProperty(property.elDivOptions); + break; + } } let showPropertyRules = properties[propertyID].showPropertyRules; @@ -2437,7 +2430,7 @@ function createTextureProperty(property, elProperty) { return elResult; } -function createButtonsProperty(property, elProperty, elLabel) { +function createButtonsProperty(property, elProperty) { let elementID = property.elementID; let propertyData = property.data; @@ -2450,6 +2443,43 @@ function createButtonsProperty(property, elProperty, elLabel) { return elProperty; } +function createDynamicMultiselectProperty(property, elProperty) { + let elementID = property.elementID; + let propertyData = property.data; + + elProperty.className = "dynamic-multiselect"; + + let elDivOptions = document.createElement('div'); + elDivOptions.setAttribute("id", elementID + "-options"); + elDivOptions.style = "overflow-y:scroll;max-height:160px;" + + let elDivButtons = document.createElement('div'); + elDivButtons.setAttribute("id", elDivOptions.getAttribute("id") + "-buttons"); + + let elLabel = document.createElement('label'); + elLabel.innerText = "No Options"; + elDivOptions.appendChild(elLabel); + + let buttons = [ { id: "selectAll", label: "Select All", className: "black", onClick: selectAllMaterialTarget }, + { id: "clearAll", label: "Clear All", className: "black", onClick: clearAllMaterialTarget } ]; + addButtons(elDivButtons, elementID, buttons, false); + + elProperty.appendChild(elDivOptions); + elProperty.appendChild(elDivButtons); + + return elDivOptions; +} + +function resetDynamicMultiselectProperty(elDivOptions) { + let elInputs = elDivOptions.getElementsByTagName("input"); + while (elInputs.length > 0) { + let elDivOption = elInputs[0].parentNode; + elDivOption.parentNode.removeChild(elDivOption); + } + elDivOptions.firstChild.style.display = "block"; // show "No Options" text + elDivOptions.parentNode.lastChild.style.display = "none"; // hide Select/Clear all buttons +} + function createTupleNumberInput(property, subLabel) { let propertyElementID = property.elementID; let propertyData = property.data; @@ -2573,6 +2603,10 @@ function createProperty(propertyData, propertyElementID, propertyName, propertyI property.elProperty = createButtonsProperty(property, elProperty); break; } + case 'dynamic-multiselect': { + property.elDivOptions = createDynamicMultiselectProperty(property, elProperty); + break; + } case 'placeholder': case 'sub-header': { break; @@ -3109,7 +3143,7 @@ function setDropdownValue(event) { /** - * TEXTAREA / PARENT MATERIAL NAME FUNCTIONS + * TEXTAREA FUNCTIONS */ function setTextareaScrolling(element) { @@ -3117,16 +3151,140 @@ function setTextareaScrolling(element) { element.setAttribute("scrolling", isScrolling ? "true" : "false"); } -function showParentMaterialNameBox(number, elNumber, elString) { - if (number) { - $('#property-submeshToReplace').parent().show(); - $('#property-materialNameToReplace').parent().hide(); - elString.value = ""; - } else { - $('#property-materialNameToReplace').parent().show(); - $('#property-submeshToReplace').parent().hide(); - elNumber.value = 0; + +/** + * MATERIAL TARGET FUNCTIONS + */ + +function setMaterialTargetData(materialTargetData) { + let elDivOptions = getPropertyInputElement("parentMaterialName"); + resetDynamicMultiselectProperty(elDivOptions); + + if (materialTargetData === undefined) { + return; } + + elDivOptions.firstChild.style.display = "none"; // hide "No Options" text + elDivOptions.parentNode.lastChild.style.display = "block"; // show Select/Clear all buttons + + let numMeshes = materialTargetData.numMeshes; + for (let i = 0; i < numMeshes; ++i) { + addMaterialTarget(elDivOptions, i, false); + } + + let materialNames = materialTargetData.materialNames; + let materialNamesAdded = []; + for (let i = 0; i < materialNames.length; ++i) { + let materialName = materialNames[i]; + if (materialNamesAdded.indexOf(materialName) === -1) { + addMaterialTarget(elDivOptions, materialName, true); + materialNamesAdded.push(materialName); + } + } + + materialTargetPropertyUpdate(elDivOptions.propertyValue); +} + +function addMaterialTarget(elDivOptions, targetID, isMaterialName) { + let elementID = elDivOptions.getAttribute("id"); + elementID += isMaterialName ? "-material-" : "-mesh-"; + elementID += targetID; + + let elDiv = document.createElement('div'); + elDiv.className = "materialTargetDiv"; + elDiv.onclick = onToggleMaterialTarget; + elDivOptions.appendChild(elDiv); + + let elInput = document.createElement('input'); + elInput.className = "materialTargetInput"; + elInput.setAttribute("type", "checkbox"); + elInput.setAttribute("id", elementID); + elInput.setAttribute("targetID", targetID); + elInput.setAttribute("isMaterialName", isMaterialName); + elDiv.appendChild(elInput); + + let elLabel = document.createElement('label'); + elLabel.setAttribute("for", elementID); + elLabel.innerText = isMaterialName ? "Material " + targetID : "Mesh Index " + targetID; + elDiv.appendChild(elLabel); + + return elDiv; +} + +function onToggleMaterialTarget(event) { + let elTarget = event.target; + if (elTarget instanceof HTMLInputElement) { + sendMaterialTargetProperty(); + } + event.stopPropagation(); +} + +function setAllMaterialTargetInputs(checked) { + let elDivOptions = getPropertyInputElement("parentMaterialName"); + let elInputs = elDivOptions.getElementsByClassName("materialTargetInput"); + for (let i = 0; i < elInputs.length; ++i) { + elInputs[i].checked = checked; + } +} + +function selectAllMaterialTarget() { + setAllMaterialTargetInputs(true); + sendMaterialTargetProperty(); +} + +function clearAllMaterialTarget() { + setAllMaterialTargetInputs(false); + sendMaterialTargetProperty(); +} + +function sendMaterialTargetProperty() { + let elDivOptions = getPropertyInputElement("parentMaterialName"); + let elInputs = elDivOptions.getElementsByClassName("materialTargetInput"); + + let materialTargetList = ""; + for (let i = 0; i < elInputs.length; ++i) { + let elInput = elInputs[i]; + if (elInput.checked) { + let targetID = elInput.getAttribute("targetID"); + if (elInput.getAttribute("isMaterialName") === "true") { + materialTargetList += "mat::" + targetID + ","; + } else { + materialTargetList += targetID + ","; + } + } + } + + if (materialTargetList !== "") { + materialTargetList = materialTargetList.substring(0, materialTargetList.length - 1); + materialTargetList = "[" + materialTargetList + "]"; + } + + updateProperty("parentMaterialName", materialTargetList, false); +} + +function materialTargetPropertyUpdate(propertyValue) { + let elDivOptions = getPropertyInputElement("parentMaterialName"); + let elInputs = elDivOptions.getElementsByClassName("materialTargetInput"); + + if (propertyValue.charAt(0) === '[') { + propertyValue = propertyValue.substring(1, propertyValue.length); + } + if (propertyValue.charAt(propertyValue.length - 1) === ']') { + propertyValue = propertyValue.substring(0, propertyValue.length - 1); + } + + let materialTargets = propertyValue.split(","); + for (let i = 0; i < elInputs.length; ++i) { + let elInput = elInputs[i]; + let targetID = elInput.getAttribute("targetID"); + let materialTargetName = targetID; + if (elInput.getAttribute("isMaterialName") === "true") { + materialTargetName = "mat::" + targetID; + } + elInput.checked = materialTargets.indexOf(materialTargetName) >= 0; + } + + elDivOptions.propertyValue = propertyValue; } @@ -3555,6 +3713,12 @@ function loaded() { property.elInput.imageLoad(property.elInput.value); break; } + case 'dynamic-multiselect': { + if (property.data.propertyUpdate) { + property.data.propertyUpdate(propertyValue); + } + break; + } } let showPropertyRules = property.showPropertyRules; @@ -3568,22 +3732,6 @@ function loaded() { } updateVisibleSpaceModeProperties(); - - if (selectedEntityProperties.type === "Material") { - let elParentMaterialNameString = getPropertyInputElement("materialNameToReplace"); - let elParentMaterialNameNumber = getPropertyInputElement("submeshToReplace"); - let elParentMaterialNameCheckbox = getPropertyInputElement("selectSubmesh"); - let parentMaterialName = selectedEntityProperties.parentMaterialName; - if (parentMaterialName.startsWith(MATERIAL_PREFIX_STRING)) { - elParentMaterialNameString.value = parentMaterialName.replace(MATERIAL_PREFIX_STRING, ""); - showParentMaterialNameBox(false, elParentMaterialNameNumber, elParentMaterialNameString); - elParentMaterialNameCheckbox.checked = false; - } else { - elParentMaterialNameNumber.value = parseInt(parentMaterialName); - showParentMaterialNameBox(true, elParentMaterialNameNumber, elParentMaterialNameString); - elParentMaterialNameCheckbox.checked = true; - } - } let json = null; try { @@ -3631,6 +3779,10 @@ function loaded() { hideMaterialDataSaved(); } + if (hasSelectedEntityChanged && selectedEntityProperties.type === "Material") { + EventBridge.emitWebEvent(JSON.stringify({ type: 'materialTargetRequest', entityID: selectedEntityProperties.id })); + } + let activeElement = document.activeElement; if (doSelectElement && typeof activeElement.select !== "undefined") { activeElement.select(); @@ -3680,6 +3832,8 @@ function loaded() { } } } + } else if (data.type === 'materialTargetReply') { + setMaterialTargetData(data.materialTargetData); } }); @@ -3749,26 +3903,6 @@ function loaded() { elDiv.insertBefore(elMaterialDataEditor, elMaterialData); elDiv.insertBefore(elMaterialDataEditorStatus, elMaterialData); - // Special Property Callbacks - let elParentMaterialNameString = getPropertyInputElement("materialNameToReplace"); - let elParentMaterialNameNumber = getPropertyInputElement("submeshToReplace"); - let elParentMaterialNameCheckbox = getPropertyInputElement("selectSubmesh"); - elParentMaterialNameString.addEventListener('change', function () { - updateProperty("parentMaterialName", MATERIAL_PREFIX_STRING + this.value, false); - }); - elParentMaterialNameNumber.addEventListener('change', function () { - updateProperty("parentMaterialName", this.value, false); - }); - elParentMaterialNameCheckbox.addEventListener('change', function () { - if (this.checked) { - updateProperty("parentMaterialName", elParentMaterialNameNumber.value, false); - showParentMaterialNameBox(true, elParentMaterialNameNumber, elParentMaterialNameString); - } else { - updateProperty("parentMaterialName", MATERIAL_PREFIX_STRING + elParentMaterialNameString.value, false); - showParentMaterialNameBox(false, elParentMaterialNameNumber, elParentMaterialNameString); - } - }); - // Collapsible sections let elCollapsible = document.getElementsByClassName("collapse-icon");