diff --git a/scripts/developer/utilities/render/deferredLighting.qml b/scripts/developer/utilities/render/deferredLighting.qml index 9737fb7f1a..aef533b7a3 100644 --- a/scripts/developer/utilities/render/deferredLighting.qml +++ b/scripts/developer/utilities/render/deferredLighting.qml @@ -14,7 +14,6 @@ import QtQuick.Layouts 1.3 import stylesUit 1.0 import controlsUit 1.0 as HifiControls import "configSlider" -import "../lib/jet/qml" as Jet Rectangle { HifiConstants { id: hifi;} @@ -249,12 +248,6 @@ Rectangle { checked: render.mainViewTask.getConfig("DrawOverlayHUDOpaqueBounds")["enabled"] onCheckedChanged: { render.mainViewTask.getConfig("DrawOverlayHUDOpaqueBounds")["enabled"] = checked } } - HifiControls.CheckBox { - boxSize: 20 - text: "Transparents in HUD" - checked: render.mainViewTask.getConfig("DrawOverlayHUDTransparentBounds")["enabled"] - onCheckedChanged: { render.mainViewTask.getConfig("DrawOverlayHUDTransparentBounds")["enabled"] = checked } - } } Column { @@ -277,6 +270,12 @@ Rectangle { checked: render.mainViewTask.getConfig("DrawZones")["enabled"] onCheckedChanged: { render.mainViewTask.getConfig("ZoneRenderer")["enabled"] = checked; render.mainViewTask.getConfig("DrawZones")["enabled"] = checked; } } + HifiControls.CheckBox { + boxSize: 20 + text: "Transparents in HUD" + checked: render.mainViewTask.getConfig("DrawOverlayHUDTransparentBounds")["enabled"] + onCheckedChanged: { render.mainViewTask.getConfig("DrawOverlayHUDTransparentBounds")["enabled"] = checked } + } } } Separator {} @@ -303,5 +302,13 @@ Rectangle { } } } + Row { + HifiControls.Button { + text: "Material" + onClicked: { + sendToScript({method: "openMaterialInspectorView"}); + } + } + } } } diff --git a/scripts/developer/utilities/render/luci.js b/scripts/developer/utilities/render/luci.js index cffeb615c9..bae5c4646d 100644 --- a/scripts/developer/utilities/render/luci.js +++ b/scripts/developer/utilities/render/luci.js @@ -12,6 +12,8 @@ (function() { var AppUi = Script.require('appUi'); + + var MaterialInspector = Script.require('./materialInspector.js'); var moveDebugCursor = false; var onMousePressEvent = function (e) { @@ -41,11 +43,12 @@ Render.getConfig("RenderMainView").getConfig("DebugDeferredBuffer").size = { x: nx, y: ny, z: 1.0, w: 1.0 }; } - function Page(title, qmlurl, width, height) { + function Page(title, qmlurl, width, height, handleWindowFunc) { this.title = title; this.qml = qmlurl; this.width = width; this.height = height; + this.handleWindowFunc = handleWindowFunc; this.window; @@ -73,8 +76,10 @@ presentationMode: Desktop.PresentationMode.NATIVE, size: {x: this.width, y: this.height} }); + this.handleWindowFunc(this.window); this.window.closed.connect(function () { that.killView(); + this.handleWindowFunc(undefined); }); } }; @@ -84,8 +89,12 @@ this._pages = {}; }; - Pages.prototype.addPage = function (command, title, qmlurl, width, height) { - this._pages[command] = new Page(title, qmlurl, width, height); + Pages.prototype.addPage = function (command, title, qmlurl, width, height, handleWindowFunc) { + if (handleWindowFunc === undefined) { + // Workaround for bad linter + handleWindowFunc = function(window){}; + } + this._pages[command] = new Page(title, qmlurl, width, height, handleWindowFunc); }; Pages.prototype.open = function (command) { @@ -110,6 +119,7 @@ pages.addPage('openEngineView', 'Render Engine', 'engineInspector.qml', 300, 400); pages.addPage('openEngineLODView', 'Render LOD', 'lod.qml', 300, 400); pages.addPage('openCullInspectorView', 'Cull Inspector', 'culling.qml', 300, 400); + pages.addPage('openMaterialInspectorView', 'Material Inspector', 'materialInspector.qml', 300, 400, MaterialInspector.setWindow); function fromQml(message) { if (pages.open(message.method)) { diff --git a/scripts/developer/utilities/render/materialInspector.js b/scripts/developer/utilities/render/materialInspector.js new file mode 100644 index 0000000000..76e5da5cd0 --- /dev/null +++ b/scripts/developer/utilities/render/materialInspector.js @@ -0,0 +1,165 @@ +// +// materialInspector.js +// +// Created by Sabrina Shanman on 2019-01-17 +// Copyright 2019 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or https://www.apache.org/licenses/LICENSE-2.0.html +// +"use strict"; + +var activeWindow; + +// Adapted from Samuel G's material painting script +function getTopMaterial(multiMaterial) { + // For non-models: multiMaterial[0] will be the top material + // For models, multiMaterial[0] is the base material, and multiMaterial[1] is the highest priority applied material + if (multiMaterial.length > 1) { + if (multiMaterial[1].priority > multiMaterial[0].priority) { + return multiMaterial[1]; + } + } + + return multiMaterial[0]; +} + +function updateMaterial(type, id, meshPart) { + var mesh = Graphics.getModel(id); + var meshPartString = meshPart.toString(); + if (!mesh) { + return; + } + var materials = mesh.materialLayers; + if (!materials[meshPartString] || materials[meshPartString].length <= 0) { + return; + } + + var topMaterial = getTopMaterial(materials[meshPartString]); + var materialJSONText = JSON.stringify({ + materialVersion: 1, + materials: topMaterial.material + }, null, 2); + + toQml({method: "setObjectInfo", params: {id: id, type: type, meshPart: meshPart}}); + toQml({method: "setMaterialJSON", params: {materialJSONText: materialJSONText}}); +} + +// Adapted from Samuel G's material painting script +function getHoveredMaterialLocation(event) { + var pickRay = Camera.computePickRay(event.x, event.y); + var closest; + var id; + var type = "Entity"; + var avatar = AvatarManager.findRayIntersection(pickRay); + var entity = Entities.findRayIntersection(pickRay, true); + var overlay = Overlays.findRayIntersection(pickRay, true); + + closest = entity; + id = entity.entityID; + + if (avatar.intersects && avatar.distance < closest.distance) { + closest = avatar; + id = avatar.avatarID; + type = "Avatar"; + } else if (overlay.intersects && overlay.distance < closest.distance) { + closest = overlay; + id = overlay.overlayID; + type = "Overlay"; + } + + if (closest.intersects) { + return { + type: type, + id: id, + meshPart: (closest.extraInfo.shapeID ? closest.extraInfo.shapeID : 0) + }; + } else { + return undefined; + } +} + +var pressedID; +var pressedMeshPart; + +function mousePressEvent(event) { + if (!event.isLeftButton) { + return; + } + + var result = getHoveredMaterialLocation(event); + + if (result !== undefined) { + pressedID = result.id; + pressedMeshPart = result.meshPart; + } +} + +function mouseReleaseEvent(event) { + if (!event.isLeftButton) { + return; + } + + var result = getHoveredMaterialLocation(event); + + if (result !== undefined && result.id === pressedID && result.meshPart === pressedMeshPart) { + updateMaterial(result.type, result.id, result.meshPart); + setSelectedObject(result.id, result.type); + } +} + +function killWindow() { + setWindow(undefined); +} + +function toQml(message) { + if (activeWindow === undefined) { + return; // Shouldn't happen + } + + activeWindow.sendToQml(message); +} + +function fromQml(message) { + // No cases currently +} + +var SELECT_LIST = "luci_materialInspector_SelectionList"; +Selection.enableListHighlight(SELECT_LIST, { + outlineUnoccludedColor: { red: 255, green: 255, blue: 255 } +}); +function setSelectedObject(id, type) { + Selection.clearSelectedItemsList(SELECT_LIST); + if (id !== undefined && !Uuid.isNull(id)) { + Selection.addToSelectedItemsList(SELECT_LIST, type.toLowerCase(), id); + } +} + +function setWindow(window) { + if (activeWindow !== undefined) { + setSelectedObject(Uuid.NULL, ""); + activeWindow.closed.disconnect(killWindow); + activeWindow.fromQml.disconnect(fromQml); + Controller.mousePressEvent.disconnect(mousePressEvent); + Controller.mouseReleaseEvent.disconnect(mouseReleaseEvent); + activeWindow.close(); + } + if (window !== undefined) { + window.closed.connect(killWindow); + window.fromQml.connect(fromQml); + Controller.mousePressEvent.connect(mousePressEvent); + Controller.mouseReleaseEvent.connect(mouseReleaseEvent); + } + activeWindow = window; +} + +function cleanup() { + setWindow(undefined); + Selection.disableListHighlight(SELECT_LIST); +} + +Script.scriptEnding.connect(cleanup); + +module.exports = { + setWindow: setWindow +}; diff --git a/scripts/developer/utilities/render/materialInspector.qml b/scripts/developer/utilities/render/materialInspector.qml new file mode 100644 index 0000000000..d4dad203cd --- /dev/null +++ b/scripts/developer/utilities/render/materialInspector.qml @@ -0,0 +1,65 @@ +// +// materialInspector.qml +// +// Created by Sabrina Shanman on 2019-01-16 +// Copyright 2019 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or https://www.apache.org/licenses/LICENSE-2.0.html +// +import QtQuick 2.7 +import QtQuick.Controls 2.3 as Original +import QtQuick.Layouts 1.3 + +import stylesUit 1.0 +import controlsUit 1.0 as HifiControls + +Rectangle { + HifiConstants { id: hifi;} + color: Qt.rgba(hifi.colors.baseGray.r, hifi.colors.baseGray.g, hifi.colors.baseGray.b, 0.8); + id: root; + + function fromScript(message) { + switch (message.method) { + case "setObjectInfo": + entityIDInfo.text = "Type: " + message.params.type + "\nID: " + message.params.id + "\nMesh Part: " + message.params.meshPart; + break; + case "setMaterialJSON": + materialJSONText.text = message.params.materialJSONText; + break; + } + } + + Rectangle { + id: entityIDContainer + height: 52 + width: root.width + color: Qt.rgba(root.color.r * 0.7, root.color.g * 0.7, root.color.b * 0.7, 0.8); + TextEdit { + id: entityIDInfo + text: "Type: Unknown\nID: None\nMesh Part: Unknown" + font.pointSize: 9 + color: "#FFFFFF" + readOnly: true + selectByMouse: true + } + } + + Original.ScrollView { + anchors.top: entityIDContainer.bottom + height: root.height - entityIDContainer.height + width: root.width + clip: true + Original.ScrollBar.horizontal.policy: Original.ScrollBar.AlwaysOff + TextEdit { + id: materialJSONText + text: "Click an object to get material JSON" + width: root.width + font.pointSize: 10 + color: "#FFFFFF" + readOnly: true + selectByMouse: true + wrapMode: Text.WordWrap + } + } +}