diff --git a/BUILD_WIN.md b/BUILD_WIN.md index 48530b9351..2d6074cd77 100644 --- a/BUILD_WIN.md +++ b/BUILD_WIN.md @@ -31,7 +31,7 @@ If you do not wish to use the Python installation bundled with Visual Studio, yo ### Step 2. Installing CMake -Download and install the latest version of CMake 3.9. +Download and install the latest version of CMake 3.14. Download the file named win64-x64 Installer from the [CMake Website](https://cmake.org/download/). You can access the installer on this [3.14 Version page](https://cmake.org/files/v3.14/). During installation, make sure to check "Add CMake to system PATH for all users" when prompted. diff --git a/domain-server/src/DomainGatekeeper.cpp b/domain-server/src/DomainGatekeeper.cpp index 6d339852d5..6aaa348f6c 100644 --- a/domain-server/src/DomainGatekeeper.cpp +++ b/domain-server/src/DomainGatekeeper.cpp @@ -136,7 +136,8 @@ void DomainGatekeeper::processConnectRequestPacket(QSharedPointergetSenderSockAddr() << "with hardware address" << nodeConnection.hardwareAddress - << "and machine fingerprint" << nodeConnection.machineFingerprint; + << "and machine fingerprint" << nodeConnection.machineFingerprint + << "sysinfo" << nodeConnection.SystemInfo; } } diff --git a/domain-server/src/NodeConnectionData.cpp b/domain-server/src/NodeConnectionData.cpp index 9703db39c8..5419014622 100644 --- a/domain-server/src/NodeConnectionData.cpp +++ b/domain-server/src/NodeConnectionData.cpp @@ -37,7 +37,11 @@ NodeConnectionData NodeConnectionData::fromDataStream(QDataStream& dataStream, c dataStream >> newHeader.machineFingerprint; // and the operating system type - dataStream >> newHeader.SystemInfo; + QByteArray compressedSystemInfo; + dataStream >> compressedSystemInfo; + if (!compressedSystemInfo.isEmpty()) { + newHeader.SystemInfo = qUncompress(compressedSystemInfo); + } dataStream >> newHeader.connectReason; diff --git a/interface/resources/qml/Stats.qml b/interface/resources/qml/Stats.qml index 2c991aa9dd..23aa256cdc 100644 --- a/interface/resources/qml/Stats.qml +++ b/interface/resources/qml/Stats.qml @@ -340,10 +340,10 @@ Item { text: "GPU: " + root.gpuFrameTime.toFixed(1) + " ms" } StatText { - text: "GPU (Per pixel): " + root.gpuFrameTimePerPixel.toFixed(5) + " ns/pp" + text: "GPU (Per pixel): " + root.gpuFrameTimePerPixel.toFixed(1) + " ns/pp" } StatText { - text: "GPU frame size: " + root.gpuFrameSize.x + " x " + root.gpuFrameSize.y + text: "GPU frame size: " + root.gpuFrameSize.x.toFixed(0) + " x " + root.gpuFrameSize.y.toFixed(0) } StatText { text: "LOD Target: " + root.lodTargetFramerate + " Hz Angle: " + root.lodAngle + " deg" diff --git a/interface/resources/qml/hifi/simplifiedUI/inputDeviceButton/InputDeviceButton.qml b/interface/resources/qml/hifi/simplifiedUI/inputDeviceButton/InputDeviceButton.qml index c546af218b..15f4c42d39 100644 --- a/interface/resources/qml/hifi/simplifiedUI/inputDeviceButton/InputDeviceButton.qml +++ b/interface/resources/qml/hifi/simplifiedUI/inputDeviceButton/InputDeviceButton.qml @@ -33,6 +33,7 @@ Rectangle { readonly property string unmutedIcon: "images/mic-unmute-i.svg" readonly property string mutedIcon: "images/mic-mute-i.svg" readonly property string pushToTalkIcon: "images/mic-ptt-i.svg" + readonly property string pushToTalkMutedIcon: "images/mic-ptt-mute-i.svg" readonly property string clippingIcon: "images/mic-clip-i.svg" readonly property string gatedIcon: "images/mic-gate-i.svg" @@ -48,18 +49,6 @@ Rectangle { } } - opacity: 0.7 - - onLevelChanged: { - var rectOpacity = (muted && (level >= userSpeakingLevel)) ? 1.0 : 0.7; - if (pushToTalk && !pushingToTalk) { - rectOpacity = (mouseArea.containsMouse) ? 1.0 : 0.7; - } else if (mouseArea.containsMouse && rectOpacity != 1.0) { - rectOpacity = 1.0; - } - micBar.opacity = rectOpacity; - } - color: "#00000000" MouseArea { @@ -116,82 +105,84 @@ Rectangle { Item { id: icon anchors.verticalCenter: parent.verticalCenter - anchors.right: parent.horizontalCenter + anchors.horizontalCenter: parent.horizontalCenter anchors.rightMargin: 2 - width: 13 - height: 21 + width: pushToTalk ? 16 : (muted ? 20 : 16) + height: 22 Item { anchors.fill: parent - opacity: mouseArea.containsMouse ? 1.0 : 0.7 Image { id: image + visible: false source: (pushToTalk) ? pushToTalkIcon : muted ? mutedIcon : clipping ? clippingIcon : gated ? gatedIcon : unmutedIcon anchors.fill: parent - fillMode: Image.PreserveAspectFit } ColorOverlay { + opacity: mouseArea.containsMouse ? 1.0 : 0.7 + visible: level === 0 || micBar.muted || micBar.clipping id: imageOverlay anchors { fill: image } source: image - color: pushToTalk ? (pushingToTalk ? colors.unmutedColor : colors.mutedColor) : colors.icon + color: pushToTalk ? (pushingToTalk ? colors.icon : colors.mutedColor) : colors.icon + } + + OpacityMask { + id: bar + visible: level > 0 && !micBar.muted && !micBar.clipping + anchors.fill: meterGradient + source: meterGradient + maskSource: image + } + + LinearGradient { + id: meterGradient + anchors { fill: parent } + visible: false + start: Qt.point(0, 0) + end: Qt.point(0, parent.height) + rotation: 180 + gradient: Gradient { + GradientStop { + position: 1.0 + color: colors.greenStart + } + GradientStop { + position: 0.5 + color: colors.greenEnd + } + GradientStop { + position: 0.0 + color: colors.yellow + } + } } } - } - Item { - id: bar - - anchors.verticalCenter: parent.verticalCenter - anchors.left: parent.horizontalCenter - anchors.leftMargin: 2 - - width: 4 - height: 21 - - Rectangle { // base - id: baseBar - radius: 4 - anchors { fill: parent } - color: colors.gutter - } - - Rectangle { // mask - id: mask - height: micBar.muted ? parent.height : parent.height * level - color: micBar.muted ? colors.mutedColor : "white" + Item { width: parent.width - radius: 5 - anchors { - bottom: parent.bottom - bottomMargin: 0 - left: parent.left - leftMargin: 0 + height: parent.height - parent.height * level + anchors.top: parent.top + anchors.left: parent.left + clip:true + Image { + id: maskImage + visible: false + source: (pushToTalk) ? pushToTalkIcon : muted ? mutedIcon : + clipping ? clippingIcon : gated ? gatedIcon : unmutedIcon + anchors.top: parent.top + anchors.left: parent.left + width: parent.width + height: parent.parent.height } - } - - LinearGradient { - anchors { fill: mask } - visible: mask.visible && !micBar.muted - source: mask - start: Qt.point(0, 0) - end: Qt.point(0, bar.height) - rotation: 180 - gradient: Gradient { - GradientStop { - position: 0.0 - color: colors.greenStart - } - GradientStop { - position: 0.5 - color: colors.greenEnd - } - GradientStop { - position: 1.0 - color: colors.yellow - } + + ColorOverlay { + visible: level > 0 && !micBar.muted && !micBar.clipping + anchors { fill: maskImage } + source: maskImage + color: "#b2b2b2" } } } diff --git a/interface/resources/qml/hifi/simplifiedUI/inputDeviceButton/images/mic-clip-i.svg b/interface/resources/qml/hifi/simplifiedUI/inputDeviceButton/images/mic-clip-i.svg index 8b694c7f3d..f16b9e1a56 100644 --- a/interface/resources/qml/hifi/simplifiedUI/inputDeviceButton/images/mic-clip-i.svg +++ b/interface/resources/qml/hifi/simplifiedUI/inputDeviceButton/images/mic-clip-i.svg @@ -1,20 +1,14 @@ - - - - - - image/svg+xml - - - - - - - - - - - - - + + + + + + + + diff --git a/interface/resources/qml/hifi/simplifiedUI/inputDeviceButton/images/mic-gate-i.svg b/interface/resources/qml/hifi/simplifiedUI/inputDeviceButton/images/mic-gate-i.svg index ac70ce66cb..56e0e1df57 100644 --- a/interface/resources/qml/hifi/simplifiedUI/inputDeviceButton/images/mic-gate-i.svg +++ b/interface/resources/qml/hifi/simplifiedUI/inputDeviceButton/images/mic-gate-i.svg @@ -1,13 +1,11 @@ - - - - - - image/svg+xml - - - - - - + + + + diff --git a/interface/resources/qml/hifi/simplifiedUI/inputDeviceButton/images/mic-mute-a.svg b/interface/resources/qml/hifi/simplifiedUI/inputDeviceButton/images/mic-mute-a.svg deleted file mode 100644 index eb36c2dd55..0000000000 --- a/interface/resources/qml/hifi/simplifiedUI/inputDeviceButton/images/mic-mute-a.svg +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - image/svg+xml - - - - - - - diff --git a/interface/resources/qml/hifi/simplifiedUI/inputDeviceButton/images/mic-mute-i.svg b/interface/resources/qml/hifi/simplifiedUI/inputDeviceButton/images/mic-mute-i.svg index ebca81f370..a00b7f9a9c 100644 --- a/interface/resources/qml/hifi/simplifiedUI/inputDeviceButton/images/mic-mute-i.svg +++ b/interface/resources/qml/hifi/simplifiedUI/inputDeviceButton/images/mic-mute-i.svg @@ -1,13 +1,13 @@ - - - - - - image/svg+xml - - - - - - + + + + diff --git a/interface/resources/qml/hifi/simplifiedUI/inputDeviceButton/images/mic-ptt-a.svg b/interface/resources/qml/hifi/simplifiedUI/inputDeviceButton/images/mic-ptt-a.svg deleted file mode 100644 index 3ce7c0ca51..0000000000 --- a/interface/resources/qml/hifi/simplifiedUI/inputDeviceButton/images/mic-ptt-a.svg +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - image/svg+xml - - mic-ptt-a - - - - - - - mic-ptt-a - - diff --git a/interface/resources/qml/hifi/simplifiedUI/inputDeviceButton/images/mic-ptt-i.svg b/interface/resources/qml/hifi/simplifiedUI/inputDeviceButton/images/mic-ptt-i.svg index 3bf1f1bf9e..7dd65c96a0 100644 --- a/interface/resources/qml/hifi/simplifiedUI/inputDeviceButton/images/mic-ptt-i.svg +++ b/interface/resources/qml/hifi/simplifiedUI/inputDeviceButton/images/mic-ptt-i.svg @@ -1,8 +1,16 @@ - - -image/svg+xml - - + + + + diff --git a/interface/resources/qml/hifi/simplifiedUI/inputDeviceButton/images/mic-ptt-mute-i.svg b/interface/resources/qml/hifi/simplifiedUI/inputDeviceButton/images/mic-ptt-mute-i.svg new file mode 100644 index 0000000000..fef3e533b4 --- /dev/null +++ b/interface/resources/qml/hifi/simplifiedUI/inputDeviceButton/images/mic-ptt-mute-i.svg @@ -0,0 +1,18 @@ + + + + + diff --git a/interface/resources/qml/hifi/simplifiedUI/inputDeviceButton/images/mic-unmute-a.svg b/interface/resources/qml/hifi/simplifiedUI/inputDeviceButton/images/mic-unmute-a.svg deleted file mode 100644 index 0bd0b0c238..0000000000 --- a/interface/resources/qml/hifi/simplifiedUI/inputDeviceButton/images/mic-unmute-a.svg +++ /dev/null @@ -1,57 +0,0 @@ - - - - - - image/svg+xml - - - - - - - - - diff --git a/interface/resources/qml/hifi/simplifiedUI/inputDeviceButton/images/mic-unmute-i.svg b/interface/resources/qml/hifi/simplifiedUI/inputDeviceButton/images/mic-unmute-i.svg index 121873dd1b..dd8bfa087f 100644 --- a/interface/resources/qml/hifi/simplifiedUI/inputDeviceButton/images/mic-unmute-i.svg +++ b/interface/resources/qml/hifi/simplifiedUI/inputDeviceButton/images/mic-unmute-i.svg @@ -1,13 +1,9 @@ - - - - - - image/svg+xml - - - - - - + + + + diff --git a/interface/resources/qml/hifi/simplifiedUI/settingsApp/SettingsApp.qml b/interface/resources/qml/hifi/simplifiedUI/settingsApp/SettingsApp.qml index 1f48d1d753..a9199ff5f1 100644 --- a/interface/resources/qml/hifi/simplifiedUI/settingsApp/SettingsApp.qml +++ b/interface/resources/qml/hifi/simplifiedUI/settingsApp/SettingsApp.qml @@ -91,7 +91,20 @@ Rectangle { Component { id: highlightBar Rectangle { + width: tabListView.currentItem.width + height: tabListView.currentItem.height color: simplifiedUI.colors.darkBackground + x: tabListView.currentItem.x + Behavior on x { + SmoothedAnimation { + duration: 250 + } + } + Behavior on width { + SmoothedAnimation { + duration: 250 + } + } } } @@ -104,6 +117,7 @@ Rectangle { orientation: ListView.Horizontal model: tabListModel highlight: highlightBar + highlightFollowsCurrentItem: false interactive: contentItem.width > width delegate: Item { visible: model.tabTitle !== "Dev" || (model.tabTitle === "Dev" && root.developerModeEnabled) diff --git a/interface/resources/qml/hifi/simplifiedUI/settingsApp/about/About.qml b/interface/resources/qml/hifi/simplifiedUI/settingsApp/about/About.qml index 76ab762a6b..3e3758e7a8 100644 --- a/interface/resources/qml/hifi/simplifiedUI/settingsApp/about/About.qml +++ b/interface/resources/qml/hifi/simplifiedUI/settingsApp/about/About.qml @@ -122,12 +122,22 @@ Flickable { } HifiStylesUit.GraphikRegular { - text: "CPU: " + PlatformInfo.getCPUBrand() + text: "CPU:" Layout.maximumWidth: parent.width height: paintedHeight size: 16 color: simplifiedUI.colors.text.white wrapMode: Text.Wrap + + Component.onCompleted: { + var cpu = JSON.parse(PlatformInfo.getCPU(0)); + var cpuModel = cpu.model; + if (cpuModel.length === 0) { + cpuModel = "Unknown"; + } + + text = "CPU: " + cpuModel; + } } HifiStylesUit.GraphikRegular { @@ -158,12 +168,22 @@ Flickable { } HifiStylesUit.GraphikRegular { - text: "GPU: " + PlatformInfo.getGraphicsCardType() + text: "GPU: " Layout.maximumWidth: parent.width height: paintedHeight size: 16 color: simplifiedUI.colors.text.white wrapMode: Text.Wrap + + Component.onCompleted: { + var gpu = JSON.parse(PlatformInfo.getGPU(0)); + var gpuModel = gpu.model; + if (gpuModel.length === 0) { + gpuModel = "Unknown"; + } + + text = "GPU: " + gpuModel; + } } HifiStylesUit.GraphikRegular { @@ -180,9 +200,11 @@ Flickable { width: 200 height: 32 text: "Copy to Clipboard" + temporaryText: "Copied!" onClicked: { Window.copyToClipboard(root.buildPlatformInfoTextToCopy()); + showTemporaryText(); } } } @@ -206,12 +228,29 @@ Flickable { textToCopy += "Computer Vendor/Model: " + computerVendor + "/" + computerModel + "\n"; textToCopy += "Profiled Platform Tier: " + PlatformInfo.getTierProfiled() + "\n"; textToCopy += "OS Type: " + PlatformInfo.getOperatingSystemType() + "\n"; - textToCopy += "CPU: " + PlatformInfo.getCPUBrand() + "\n"; + + var cpu = JSON.parse(PlatformInfo.getCPU(0)); + var cpuModel = cpu.model; + if (cpuModel.length === 0) { + cpuModel = "Unknown"; + } + + textToCopy += "CPU: " + cpuModel + "\n"; textToCopy += "# CPUs: " + PlatformInfo.getNumCPUs() + "\n"; textToCopy += "# CPU Cores: " + PlatformInfo.getNumLogicalCores() + "\n"; textToCopy += "RAM: " + PlatformInfo.getTotalSystemMemoryMB() + " MB\n"; - textToCopy += "GPU: " + PlatformInfo.getGraphicsCardType() + "\n"; - textToCopy += "VR Hand Controllers: " + (PlatformInfo.hasRiftControllers() ? "Rift" : (PlatformInfo.hasViveControllers() ? "Vive" : "None")); + + var gpu = JSON.parse(PlatformInfo.getGPU(0)); + var gpuModel = gpu.model; + if (gpuModel.length === 0) { + gpuModel = "Unknown"; + } + + textToCopy += "GPU: " + gpuModel + "\n"; + textToCopy += "VR Hand Controllers: " + (PlatformInfo.hasRiftControllers() ? "Rift" : (PlatformInfo.hasViveControllers() ? "Vive" : "None")) + "\n"; + + textToCopy += "\n**All Platform Info**\n"; + textToCopy += JSON.stringify(JSON.parse(PlatformInfo.getPlatform()), null, 4); return textToCopy; } diff --git a/interface/resources/qml/hifi/simplifiedUI/simplifiedControls/Button.qml b/interface/resources/qml/hifi/simplifiedUI/simplifiedControls/Button.qml index 313daab704..1d594a0d6d 100644 --- a/interface/resources/qml/hifi/simplifiedUI/simplifiedControls/Button.qml +++ b/interface/resources/qml/hifi/simplifiedUI/simplifiedControls/Button.qml @@ -16,6 +16,9 @@ import TabletScriptingInterface 1.0 Original.Button { id: root + // The two properties below are used when calling showTemporaryText() + property string originalText: "" + property string temporaryText: "" SimplifiedConstants.SimplifiedConstants { id: simplifiedUI @@ -103,4 +106,31 @@ Original.Button { horizontalAlignment: Text.AlignHCenter text: root.text } + + Timer { + id: showTemporaryTextTimer + interval: 1500 + repeat: false + running: false + + onTriggered: { + buttonText.text = root.originalText; + root.originalText = ""; + } + } + + function showTemporaryText() { + if (root.temporaryText === "") { + return; + } + + if (showTemporaryTextTimer.running) { + showTemporaryTextTimer.restart(); + return; + } + + root.originalText = buttonText.text; + buttonText.text = root.temporaryText; + showTemporaryTextTimer.start(); + } } diff --git a/interface/resources/qml/hifi/simplifiedUI/topBar/SimplifiedTopBar.qml b/interface/resources/qml/hifi/simplifiedUI/topBar/SimplifiedTopBar.qml index 42d53d3f79..61d2fd6634 100644 --- a/interface/resources/qml/hifi/simplifiedUI/topBar/SimplifiedTopBar.qml +++ b/interface/resources/qml/hifi/simplifiedUI/topBar/SimplifiedTopBar.qml @@ -141,7 +141,7 @@ Rectangle { id: avatarButtonContainer anchors.verticalCenter: parent.verticalCenter anchors.left: parent.left - anchors.leftMargin: 16 + anchors.leftMargin: 2 width: 48 height: width @@ -210,7 +210,7 @@ Rectangle { id: inputDeviceButton anchors.verticalCenter: parent.verticalCenter anchors.left: avatarButtonContainer.right - anchors.leftMargin: 6 + anchors.leftMargin: 2 width: 32 height: width } @@ -220,7 +220,7 @@ Rectangle { id: outputDeviceButtonContainer anchors.verticalCenter: parent.verticalCenter anchors.left: inputDeviceButton.right - anchors.leftMargin: 2 + anchors.leftMargin: 7 width: 32 height: width @@ -232,9 +232,8 @@ Rectangle { AudioScriptingInterface.systemInjectorGain === simplifiedUI.numericConstants.mutedValue source: outputDeviceButton.outputMuted ? "./images/outputDeviceMuted.svg" : "./images/outputDeviceLoud.svg" anchors.centerIn: parent - width: 20 - height: 20 - fillMode: Image.PreserveAspectFit + width: outputDeviceButton.outputMuted ? 25 : 26 + height: 22 visible: false } @@ -283,7 +282,7 @@ Rectangle { id: statusButton property string currentStatus anchors.centerIn: parent - width: 15 + width: 22 height: width radius: width/2 visible: false @@ -304,6 +303,21 @@ Rectangle { } } + Image { + id: statusIcon + source: statusButton.currentStatus === "available" ? "images/statusPresent.svg" : "images/statusAway.svg" + anchors.centerIn: parent + width: statusButton.currentStatus === "busy" ? 13 : 14 + height: statusButton.currentStatus === "busy" ? 2 : 10 + } + + ColorOverlay { + anchors.fill: statusIcon + opacity: statusButton.currentStatus ? (statusButtonMouseArea.containsMouse ? 1.0 : 0.7) : 0.7 + source: statusIcon + color: "#ffffff" + } + MouseArea { id: statusButtonMouseArea anchors.fill: parent @@ -329,8 +343,8 @@ Rectangle { id: hmdButtonContainer anchors.verticalCenter: parent.verticalCenter anchors.right: settingsButtonContainer.left - anchors.rightMargin: 14 - width: 32 + anchors.rightMargin: 8 + width: 48 height: width visible: false @@ -338,9 +352,8 @@ Rectangle { id: displayModeImage source: HMD.active ? "./images/desktopMode.svg" : "./images/vrMode.svg" anchors.centerIn: parent - width: 29 - height: 16 - fillMode: Image.PreserveAspectFit + width: HMD.active ? 25 : 43 + height: 22 visible: false } @@ -399,17 +412,16 @@ Rectangle { id: settingsButtonContainer anchors.verticalCenter: parent.verticalCenter anchors.right: parent.right - anchors.rightMargin: 16 - width: 32 + anchors.rightMargin: 3 + width: 36 height: width Image { id: settingsButtonImage source: "./images/settings.svg" anchors.centerIn: parent - width: 20 - height: 20 - fillMode: Image.PreserveAspectFit + width: 22 + height: 22 visible: false } diff --git a/interface/resources/qml/hifi/simplifiedUI/topBar/images/desktopMode.svg b/interface/resources/qml/hifi/simplifiedUI/topBar/images/desktopMode.svg index 8b04caca88..dfb1a1a662 100644 --- a/interface/resources/qml/hifi/simplifiedUI/topBar/images/desktopMode.svg +++ b/interface/resources/qml/hifi/simplifiedUI/topBar/images/desktopMode.svg @@ -1,13 +1,8 @@ - - - - - - image/svg+xml - - - - - - + + + + diff --git a/interface/resources/qml/hifi/simplifiedUI/topBar/images/outputDeviceLoud.svg b/interface/resources/qml/hifi/simplifiedUI/topBar/images/outputDeviceLoud.svg index ebd844c471..798ce62ccd 100644 --- a/interface/resources/qml/hifi/simplifiedUI/topBar/images/outputDeviceLoud.svg +++ b/interface/resources/qml/hifi/simplifiedUI/topBar/images/outputDeviceLoud.svg @@ -1,3 +1,14 @@ - - - + + + + + diff --git a/interface/resources/qml/hifi/simplifiedUI/topBar/images/outputDeviceMuted.svg b/interface/resources/qml/hifi/simplifiedUI/topBar/images/outputDeviceMuted.svg index 4188175c31..9b7e3ad613 100644 --- a/interface/resources/qml/hifi/simplifiedUI/topBar/images/outputDeviceMuted.svg +++ b/interface/resources/qml/hifi/simplifiedUI/topBar/images/outputDeviceMuted.svg @@ -1,14 +1,14 @@ - - - - - - image/svg+xml - - - - - - - + + + + diff --git a/interface/resources/qml/hifi/simplifiedUI/topBar/images/settings.svg b/interface/resources/qml/hifi/simplifiedUI/topBar/images/settings.svg index 04a031d498..910ef353e1 100644 --- a/interface/resources/qml/hifi/simplifiedUI/topBar/images/settings.svg +++ b/interface/resources/qml/hifi/simplifiedUI/topBar/images/settings.svg @@ -1,13 +1,17 @@ - - - - - - image/svg+xml - - - - - - + + + + diff --git a/interface/resources/qml/hifi/simplifiedUI/topBar/images/status.svg b/interface/resources/qml/hifi/simplifiedUI/topBar/images/status.svg deleted file mode 100644 index ebd844c471..0000000000 --- a/interface/resources/qml/hifi/simplifiedUI/topBar/images/status.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/interface/resources/qml/hifi/simplifiedUI/topBar/images/statusAway.svg b/interface/resources/qml/hifi/simplifiedUI/topBar/images/statusAway.svg new file mode 100644 index 0000000000..d5b4798d9e --- /dev/null +++ b/interface/resources/qml/hifi/simplifiedUI/topBar/images/statusAway.svg @@ -0,0 +1,9 @@ + + + + + + diff --git a/interface/resources/qml/hifi/simplifiedUI/topBar/images/statusPresent.svg b/interface/resources/qml/hifi/simplifiedUI/topBar/images/statusPresent.svg new file mode 100644 index 0000000000..af7649ba78 --- /dev/null +++ b/interface/resources/qml/hifi/simplifiedUI/topBar/images/statusPresent.svg @@ -0,0 +1,7 @@ + + + + + diff --git a/interface/resources/qml/hifi/simplifiedUI/topBar/images/vrMode.svg b/interface/resources/qml/hifi/simplifiedUI/topBar/images/vrMode.svg index 57b564813d..f19989c0b1 100644 --- a/interface/resources/qml/hifi/simplifiedUI/topBar/images/vrMode.svg +++ b/interface/resources/qml/hifi/simplifiedUI/topBar/images/vrMode.svg @@ -1,13 +1,10 @@ - - - - - - image/svg+xml - - - - - - + + + + diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index b50ad8f595..1a0030bc12 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -2712,6 +2712,7 @@ void Application::cleanupBeforeQuit() { // Clear any queued processing (I/O, FBX/OBJ/Texture parsing) QThreadPool::globalInstance()->clear(); + QThreadPool::globalInstance()->waitForDone(); DependencyManager::destroy(); @@ -3742,18 +3743,6 @@ void Application::resizeGL() { DependencyManager::get()->setFrameBufferSize(fromGlm(renderSize)); } - auto renderResolutionScale = getRenderResolutionScale(); - if (displayPlugin->getRenderResolutionScale() != renderResolutionScale) { - auto renderConfig = _graphicsEngine.getRenderEngine()->getConfiguration(); - assert(renderConfig); - auto mainView = renderConfig->getConfig("RenderMainView.RenderDeferredTask"); - // mainView can be null if we're rendering in forward mode - if (mainView) { - mainView->setProperty("resolutionScale", renderResolutionScale); - } - displayPlugin->setRenderResolutionScale(renderResolutionScale); - } - // FIXME the aspect ratio for stereo displays is incorrect based on this. float aspectRatio = displayPlugin->getRecommendedAspectRatio(); _myCamera.setProjection(glm::perspective(glm::radians(_fieldOfView.get()), aspectRatio, @@ -8547,23 +8536,7 @@ void Application::shareSnapshot(const QString& path, const QUrl& href) { } float Application::getRenderResolutionScale() const { - auto menu = Menu::getInstance(); - if (!menu) { - return 1.0f; - } - if (menu->isOptionChecked(MenuOption::RenderResolutionOne)) { - return 1.0f; - } else if (menu->isOptionChecked(MenuOption::RenderResolutionTwoThird)) { - return 0.666f; - } else if (menu->isOptionChecked(MenuOption::RenderResolutionHalf)) { - return 0.5f; - } else if (menu->isOptionChecked(MenuOption::RenderResolutionThird)) { - return 0.333f; - } else if (menu->isOptionChecked(MenuOption::RenderResolutionQuarter)) { - return 0.25f; - } else { - return 1.0f; - } + return RenderScriptingInterface::getInstance()->getViewportResolutionScale(); } void Application::notifyPacketVersionMismatch() { diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index bb087c96d5..8bee8de8c3 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -382,28 +382,6 @@ Menu::Menu() { // Developer > Render > OpenVR threaded submit addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::OpenVrThreadedSubmit, 0, true); - // Developer > Render > Resolution - MenuWrapper* resolutionMenu = renderOptionsMenu->addMenu(MenuOption::RenderResolution); - QActionGroup* resolutionGroup = new QActionGroup(resolutionMenu); - resolutionGroup->setExclusive(true); - -#if defined(Q_OS_MAC) - resolutionGroup->addAction(addCheckableActionToQMenuAndActionHash(resolutionMenu, MenuOption::RenderResolutionOne, 0, false)); -#else - resolutionGroup->addAction(addCheckableActionToQMenuAndActionHash(resolutionMenu, MenuOption::RenderResolutionOne, 0, true)); -#endif - - resolutionGroup->addAction(addCheckableActionToQMenuAndActionHash(resolutionMenu, MenuOption::RenderResolutionTwoThird, 0, false)); - - #if defined(Q_OS_MAC) - resolutionGroup->addAction(addCheckableActionToQMenuAndActionHash(resolutionMenu, MenuOption::RenderResolutionHalf, 0, true)); -#else - resolutionGroup->addAction(addCheckableActionToQMenuAndActionHash(resolutionMenu, MenuOption::RenderResolutionHalf, 0, false)); -#endif - - resolutionGroup->addAction(addCheckableActionToQMenuAndActionHash(resolutionMenu, MenuOption::RenderResolutionThird, 0, false)); - resolutionGroup->addAction(addCheckableActionToQMenuAndActionHash(resolutionMenu, MenuOption::RenderResolutionQuarter, 0, false)); - //const QString = "Automatic Texture Memory"; //const QString = "64 MB"; //const QString = "256 MB"; diff --git a/interface/src/Menu.h b/interface/src/Menu.h index 7c462e4e74..70687786a9 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -168,12 +168,6 @@ namespace MenuOption { const QString RenderMaxTexture4096MB = "4096 MB"; const QString RenderMaxTexture6144MB = "6144 MB"; const QString RenderMaxTexture8192MB = "8192 MB"; - const QString RenderResolution = "Scale Resolution"; - const QString RenderResolutionOne = "1"; - const QString RenderResolutionTwoThird = "2/3"; - const QString RenderResolutionHalf = "1/2"; - const QString RenderResolutionThird = "1/3"; - const QString RenderResolutionQuarter = "1/4"; const QString RenderSensorToWorldMatrix = "Show SensorToWorld Matrix"; const QString RenderIKTargets = "Show IK Targets"; const QString RenderIKConstraints = "Show IK Constraints"; diff --git a/interface/src/scripting/RenderScriptingInterface.cpp b/interface/src/scripting/RenderScriptingInterface.cpp index 608f1d30e9..338a5ab883 100644 --- a/interface/src/scripting/RenderScriptingInterface.cpp +++ b/interface/src/scripting/RenderScriptingInterface.cpp @@ -30,14 +30,16 @@ void RenderScriptingInterface::loadSettings() { _shadowsEnabled = (_shadowsEnabledSetting.get()); _ambientOcclusionEnabled = (_ambientOcclusionEnabledSetting.get()); _antialiasingEnabled = (_antialiasingEnabledSetting.get()); + _viewportResolutionScale = (_viewportResolutionScaleSetting.get()); }); forceRenderMethod((RenderMethod)_renderMethod); forceShadowsEnabled(_shadowsEnabled); forceAmbientOcclusionEnabled(_ambientOcclusionEnabled); forceAntialiasingEnabled(_antialiasingEnabled); + forceViewportResolutionScale(_viewportResolutionScale); } -RenderScriptingInterface::RenderMethod RenderScriptingInterface::getRenderMethod() { +RenderScriptingInterface::RenderMethod RenderScriptingInterface::getRenderMethod() const { return (RenderMethod) _renderMethod; } @@ -64,7 +66,7 @@ QStringList RenderScriptingInterface::getRenderMethodNames() const { return refrenderMethodNames; } -bool RenderScriptingInterface::getShadowsEnabled() { +bool RenderScriptingInterface::getShadowsEnabled() const { return _shadowsEnabled; } @@ -88,7 +90,7 @@ void RenderScriptingInterface::forceShadowsEnabled(bool enabled) { }); } -bool RenderScriptingInterface::getAmbientOcclusionEnabled() { +bool RenderScriptingInterface::getAmbientOcclusionEnabled() const { return _ambientOcclusionEnabled; } @@ -112,7 +114,7 @@ void RenderScriptingInterface::forceAmbientOcclusionEnabled(bool enabled) { }); } -bool RenderScriptingInterface::getAntialiasingEnabled() { +bool RenderScriptingInterface::getAntialiasingEnabled() const { return _antialiasingEnabled; } @@ -145,3 +147,37 @@ void RenderScriptingInterface::forceAntialiasingEnabled(bool enabled) { } +float RenderScriptingInterface::getViewportResolutionScale() const { + return _viewportResolutionScale; +} + +void RenderScriptingInterface::setViewportResolutionScale(float scale) { + if (_viewportResolutionScale != scale) { + forceViewportResolutionScale(scale); + emit settingsChanged(); + } +} + +void RenderScriptingInterface::forceViewportResolutionScale(float scale) { + // just not negative values or zero + if (scale <= 0.f) { + return; + } + _renderSettingLock.withWriteLock([&] { + _viewportResolutionScale = (scale); + _viewportResolutionScaleSetting.set(scale); + + auto renderConfig = qApp->getRenderEngine()->getConfiguration(); + assert(renderConfig); + auto deferredView = renderConfig->getConfig("RenderMainView.RenderDeferredTask"); + // mainView can be null if we're rendering in forward mode + if (deferredView) { + deferredView->setProperty("resolutionScale", _viewportResolutionScale); + } + auto forwardView = renderConfig->getConfig("RenderMainView.RenderForwardTask"); + // mainView can be null if we're rendering in forward mode + if (forwardView) { + forwardView->setProperty("resolutionScale", _viewportResolutionScale); + } + }); +} diff --git a/interface/src/scripting/RenderScriptingInterface.h b/interface/src/scripting/RenderScriptingInterface.h index 39a88d4aad..9b96448c9d 100644 --- a/interface/src/scripting/RenderScriptingInterface.h +++ b/interface/src/scripting/RenderScriptingInterface.h @@ -29,6 +29,7 @@ class RenderScriptingInterface : public QObject { Q_PROPERTY(bool shadowsEnabled READ getShadowsEnabled WRITE setShadowsEnabled NOTIFY settingsChanged) Q_PROPERTY(bool ambientOcclusionEnabled READ getAmbientOcclusionEnabled WRITE setAmbientOcclusionEnabled NOTIFY settingsChanged) Q_PROPERTY(bool antialiasingEnabled READ getAntialiasingEnabled WRITE setAntialiasingEnabled NOTIFY settingsChanged) + Q_PROPERTY(float viewportResolutionScale READ getViewportResolutionScale WRITE setViewportResolutionScale NOTIFY settingsChanged) public: RenderScriptingInterface(); @@ -66,7 +67,7 @@ public slots: * @function Render.getRenderMethod * @returns {number} "DEFERRED" or "FORWARD" */ - RenderMethod getRenderMethod(); + RenderMethod getRenderMethod() const; /**jsdoc * Sets the current render method @@ -88,7 +89,7 @@ public slots: * @function Render.getShadowsEnabled * @returns {bool} true if shadows are enabled, otherwise false */ - bool getShadowsEnabled(); + bool getShadowsEnabled() const; /**jsdoc * Enables or disables shadows @@ -102,7 +103,7 @@ public slots: * @function Render.getAmbientOcclusionEnabled * @returns {bool} true if ambient occlusion is enabled, otherwise false */ - bool getAmbientOcclusionEnabled(); + bool getAmbientOcclusionEnabled() const; /**jsdoc * Enables or disables ambient occlusion @@ -116,7 +117,7 @@ public slots: * @function Render.getAntialiasingEnabled * @returns {bool} true if anti-aliasing is enabled, otherwise false */ - bool getAntialiasingEnabled(); + bool getAntialiasingEnabled() const; /**jsdoc * Enables or disables anti-aliasing @@ -130,14 +131,14 @@ public slots: * @function Render.getViewportResolutionScale * @returns {number} */ - // float getViewportResolutionScale(); + float getViewportResolutionScale() const; /**jsdoc * Sets the current viewport resolution scale * @function Render.setViewportResolutionScale * @param {number} resolutionScale - between epsilon and 1.0 */ - // void setViewportResolutionScale(float resolutionScale); + void setViewportResolutionScale(float resolutionScale); signals: void settingsChanged(); @@ -150,19 +151,22 @@ private: int _renderMethod{ RENDER_FORWARD ? render::Args::RenderMethod::FORWARD : render::Args::RenderMethod::DEFERRED }; bool _shadowsEnabled{ true }; bool _ambientOcclusionEnabled{ false }; - bool _antialiasingEnabled { true }; + bool _antialiasingEnabled{ true }; + float _viewportResolutionScale{ 1.0f }; // Actual settings saved on disk Setting::Handle _renderMethodSetting { "renderMethod", RENDER_FORWARD ? render::Args::RenderMethod::FORWARD : render::Args::RenderMethod::DEFERRED }; Setting::Handle _shadowsEnabledSetting { "shadowsEnabled", true }; Setting::Handle _ambientOcclusionEnabledSetting { "ambientOcclusionEnabled", false }; Setting::Handle _antialiasingEnabledSetting { "antialiasingEnabled", true }; + Setting::Handle _viewportResolutionScaleSetting { "viewportResolutionScale", 1.0f }; // Force assign both setting AND runtime value to the parameter value void forceRenderMethod(RenderMethod renderMethod); void forceShadowsEnabled(bool enabled); void forceAmbientOcclusionEnabled(bool enabled); void forceAntialiasingEnabled(bool enabled); + void forceViewportResolutionScale(float scale); static std::once_flag registry_flag; }; diff --git a/interface/src/ui/PreferencesDialog.cpp b/interface/src/ui/PreferencesDialog.cpp index e0505770f5..ce70e91128 100644 --- a/interface/src/ui/PreferencesDialog.cpp +++ b/interface/src/ui/PreferencesDialog.cpp @@ -14,10 +14,9 @@ #include #include #include -#include #include #include - +#include "scripting/RenderScriptingInterface.h" #include "Application.h" #include "DialogsManager.h" #include "LODManager.h" @@ -103,6 +102,22 @@ void setupPreferences() { preference->setItems(refreshRateProfiles); preferences->addPreference(preference); } + { + // Expose the Viewport Resolution Scale + auto getter = []()->float { + return RenderScriptingInterface::getInstance()->getViewportResolutionScale(); + }; + + auto setter = [](float value) { + RenderScriptingInterface::getInstance()->setViewportResolutionScale(value); + }; + + auto scaleSlider = new SliderPreference(GRAPHICS_QUALITY, "Resolution Scale", getter, setter); + scaleSlider->setMin(0.25f); + scaleSlider->setMax(1.0f); + scaleSlider->setStep(0.02f); + preferences->addPreference(scaleSlider); + } // UI static const QString UI_CATEGORY { "User Interface" }; diff --git a/interface/src/ui/Stats.cpp b/interface/src/ui/Stats.cpp index dc9780adf5..8b9b9743f0 100644 --- a/interface/src/ui/Stats.cpp +++ b/interface/src/ui/Stats.cpp @@ -377,7 +377,7 @@ void Stats::updateStats(bool force) { auto displayPlugin = qApp->getActiveDisplayPlugin(); if (displayPlugin) { QVector2D dims(displayPlugin->getRecommendedRenderSize().x, displayPlugin->getRecommendedRenderSize().y); - dims *= displayPlugin->getRenderResolutionScale(); + dims *= qApp->getRenderResolutionScale(); STAT_UPDATE(gpuFrameSize, dims); STAT_UPDATE(gpuFrameTimePerPixel, (float)(gpuContext->getFrameTimerGPUAverage()*1000000.0 / double(dims.x()*dims.y()))); } diff --git a/launchers/darwin/CMakeLists.txt b/launchers/darwin/CMakeLists.txt index 4da675fcc9..a3fc0dc7c1 100644 --- a/launchers/darwin/CMakeLists.txt +++ b/launchers/darwin/CMakeLists.txt @@ -1,4 +1,5 @@ cmake_minimum_required(VERSION 3.0) +set(ENV{MACOSX_DEPLOYMENT_TARGET} 10.9) project(HQLauncher) set (CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules/") set(src_files @@ -26,6 +27,8 @@ set(src_files src/LatestBuildRequest.m src/OrganizationRequest.m src/OrganizationRequest.h + src/Interface.h + src/Interface.m src/ErrorViewController.h src/ErrorViewController.m src/Settings.h @@ -67,7 +70,7 @@ add_executable(${PROJECT_NAME} MACOSX_BUNDLE ${src_files}) set_target_properties(${PROJECT_NAME} PROPERTIES OUTPUT_NAME ${APP_NAME} MACOSX_BUNDLE_BUNDLE_NAME ${APP_NAME}) set_from_env(LAUNCHER_HMAC_SECRET LAUNCHER_HMAC_SECRET "") -if (LAUNCHER_HMAC_SECRET STREQUAL "") +if ("${LAUNCHER_HMAC_SECRET}" STREQUAL "") message(FATAL_ERROR "LAUNCHER_HMAC_SECRET is not set") endif() diff --git a/launchers/darwin/cmake/modules/MacOSXBundleInfo.plist.in b/launchers/darwin/cmake/modules/MacOSXBundleInfo.plist.in index 4c87bff3cf..3fe8e80f7a 100644 --- a/launchers/darwin/cmake/modules/MacOSXBundleInfo.plist.in +++ b/launchers/darwin/cmake/modules/MacOSXBundleInfo.plist.in @@ -32,6 +32,6 @@ CFBundleName ${MACOSX_BUNDLE_BUNDLE_NAME} CFBundleDisplayName - CFBundleName + ${MACOSX_BUNDLE_BUNDLE_NAME} diff --git a/launchers/darwin/nib/DisplayNameScreen.xib b/launchers/darwin/nib/DisplayNameScreen.xib index e56f2bf66b..3ba3deba53 100644 --- a/launchers/darwin/nib/DisplayNameScreen.xib +++ b/launchers/darwin/nib/DisplayNameScreen.xib @@ -69,8 +69,17 @@ + + + + + + + + + + + + + + + + + + + + + @@ -91,5 +112,14 @@ + + + + + + + + + diff --git a/launchers/darwin/src/DisplayNameScreen.m b/launchers/darwin/src/DisplayNameScreen.m index 7a402d792b..581eabc4ee 100644 --- a/launchers/darwin/src/DisplayNameScreen.m +++ b/launchers/darwin/src/DisplayNameScreen.m @@ -31,4 +31,9 @@ { [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:@"https://www.highfidelity.com/hq-support"]]; } + +- (IBAction)termsOfService:(id)sender +{ + [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:@"https://www.highfidelity.com/termsofservice"]]; +} @end diff --git a/launchers/darwin/src/Interface.h b/launchers/darwin/src/Interface.h new file mode 100644 index 0000000000..c142aeecf5 --- /dev/null +++ b/launchers/darwin/src/Interface.h @@ -0,0 +1,8 @@ +#import + +@interface Interface : NSObject + +-(id _Nonnull) initWith:(NSString * _Nonnull) aPathToInterface; +-(NSInteger) getVersion:(out NSError * _Nullable * _Nonnull) anError; + +@end diff --git a/launchers/darwin/src/Interface.m b/launchers/darwin/src/Interface.m new file mode 100644 index 0000000000..d02b66b581 --- /dev/null +++ b/launchers/darwin/src/Interface.m @@ -0,0 +1,90 @@ +#import "Interface.h" + +@implementation Interface +{ + NSString *pathTo; +} + +-(id) initWith:(NSString*)aPathToInterface +{ + [self init]; + self->pathTo = [NSString stringWithFormat:@"%@/Contents/MacOS/interface", aPathToInterface]; + return self; +} + +-(NSInteger) getVersion:(out NSError * _Nullable *) outError +{ + NSTask * interface = [[NSTask alloc] init]; + NSPipe * standardOut = [NSPipe pipe]; + + interface.launchPath = self->pathTo; + interface.arguments = @[ @"--version" ]; + interface.standardOutput = standardOut; + + NSLog(@"calling interface at %@", self->pathTo); + + NSError *error = nil; + [interface launch]; + [interface waitUntilExit]; + if (0 != [interface terminationStatus]) { + *outError = [NSError errorWithDomain:@"interface" + code:-1 + userInfo:@{NSUnderlyingErrorKey: error}]; + return 0; + } + + NSFileHandle * fh = [standardOut fileHandleForReading]; + NSData * data = [fh readDataToEndOfFile]; + NSString * output = [NSString stringWithUTF8String:[data bytes]]; + if (output == nil) { + NSDictionary * userInfo = @{ + NSLocalizedDescriptionKey: NSLocalizedString(@"Couldn't start interface", nil) + }; + *outError = [NSError errorWithDomain:@"interface" + code:-1 + userInfo:userInfo]; + return 0; + } + + // Interface returns the build version as a string like this: + // "Interface 33333-DEADBEEF". This code grabs the substring + // between "Interface " and the hyphon ("-") + NSRange start = [output rangeOfString:@"Interface "]; + if (start.length == 0) { + NSDictionary * userInfo = @{ + NSLocalizedDescriptionKey: NSLocalizedString(@"Couldn't read interface's version", nil) + }; + *outError = [NSError errorWithDomain:@"interface" + code:-2 + userInfo:userInfo]; + return 0; + } + NSRange end = [output rangeOfString:@"-"]; + if (end.length == 0) { + NSDictionary * userInfo = @{ + NSLocalizedDescriptionKey: NSLocalizedString(@"Couldn't read interface's version", nil) + }; + *outError = [NSError errorWithDomain:@"interface" + code:-2 + userInfo:userInfo]; + return 0; + } + NSRange subRange = {start.length, end.location - start.length}; + NSString * versionStr; + @try { + versionStr = [output substringWithRange:subRange]; + } + @catch (NSException *) { + NSDictionary * userInfo = @{ + NSLocalizedDescriptionKey: NSLocalizedString(@"Couldn't read interface's version", nil) + }; + *outError = [NSError errorWithDomain:@"interface" + code:-2 + userInfo:userInfo]; + return 0; + } + + return versionStr.integerValue; +} + +@end diff --git a/launchers/darwin/src/LatestBuildRequest.m b/launchers/darwin/src/LatestBuildRequest.m index 5119efa8f6..0c136dce3c 100644 --- a/launchers/darwin/src/LatestBuildRequest.m +++ b/launchers/darwin/src/LatestBuildRequest.m @@ -1,17 +1,36 @@ #import "LatestBuildRequest.h" #import "Launcher.h" #import "Settings.h" +#import "Interface.h" @implementation LatestBuildRequest +- (NSInteger) getCurrentVersion { + NSInteger currentVersion; + @try { + NSString* interfaceAppPath = [[Launcher.sharedLauncher getAppPath] stringByAppendingString:@"interface.app"]; + NSError * error = nil; + Interface * interface = [[Interface alloc] initWith:interfaceAppPath]; + currentVersion = [interface getVersion:&error]; + if (currentVersion == 0 && error != nil) { + NSLog(@"can't get version from interface, falling back to settings: %@", error); + currentVersion = [Settings.sharedSettings latestBuildVersion]; + } + } @catch (NSException *exception) { + NSLog(@"an exception was thrown: %@", exception); + currentVersion = [Settings.sharedSettings latestBuildVersion]; + } + return currentVersion; +} + - (void) requestLatestBuildInfo { NSMutableURLRequest *request = [NSMutableURLRequest new]; [request setURL:[NSURL URLWithString:@"https://thunder.highfidelity.com/builds/api/tags/latest?format=json"]]; [request setHTTPMethod:@"GET"]; [request setValue:@"application/json" forHTTPHeaderField:@"Content-Type"]; - - NSURLSession* session = [NSURLSession sharedSession]; + // We're using an ephermeral session here to ensure the tags api response is never cached. + NSURLSession * session = [NSURLSession sessionWithConfiguration:NSURLSessionConfiguration.ephemeralSessionConfiguration]; NSURLSessionDataTask* dataTask = [session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) { @@ -45,8 +64,8 @@ BOOL appDirectoryExist = [fileManager fileExistsAtPath:[[sharedLauncher getAppPath] stringByAppendingString:@"interface.app"]]; dispatch_async(dispatch_get_main_queue(), ^{ - Settings* settings = [Settings sharedSettings]; - NSInteger currentVersion = [settings latestBuildVersion]; + + NSInteger currentVersion = [self getCurrentVersion]; NSLog(@"Latest Build Request -> does build directory exist: %@", appDirectoryExist ? @"TRUE" : @"FALSE"); NSLog(@"Latest Build Request -> current version: %ld", currentVersion); NSLog(@"Latest Build Request -> latest version: %ld", buildNumber.integerValue); @@ -105,11 +124,10 @@ NSDictionary* macInstallerObject = [installers objectForKey:@"mac"]; NSString* macInstallerUrl = [macInstallerObject valueForKey:@"zip_url"]; - BOOL appDirectoryExist = [fileManager fileExistsAtPath:[[sharedLauncher getAppPath] stringByAppendingString:@"interface.app"]]; - - Settings* settings = [Settings sharedSettings]; - NSInteger currentVersion = [settings latestBuildVersion]; - BOOL latestVersionAvailable = (currentVersion != buildNumber.integerValue); + NSString* interfaceAppPath = [[sharedLauncher getAppPath] stringByAppendingString:@"interface.app"]; + BOOL appDirectoryExist = [fileManager fileExistsAtPath:interfaceAppPath]; + + BOOL latestVersionAvailable = ([self getCurrentVersion] != buildNumber.integerValue); [[Settings sharedSettings] buildVersion:buildNumber.integerValue]; BOOL shouldDownloadInterface = (latestVersionAvailable || !appDirectoryExist); diff --git a/launchers/darwin/src/main.mm b/launchers/darwin/src/main.mm index 7feab64d86..b6555aad87 100644 --- a/launchers/darwin/src/main.mm +++ b/launchers/darwin/src/main.mm @@ -5,13 +5,13 @@ void redirectLogToDocuments() { NSString* filePath = [[NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, YES) objectAtIndex:0] stringByAppendingString:@"/Launcher/"]; - + if (![[NSFileManager defaultManager] fileExistsAtPath:filePath]) { NSError * error = nil; [[NSFileManager defaultManager] createDirectoryAtPath:filePath withIntermediateDirectories:TRUE attributes:nil error:&error]; } NSString *pathForLog = [filePath stringByAppendingPathComponent:@"log.txt"]; - + freopen([pathForLog cStringUsingEncoding:NSASCIIStringEncoding],"a+",stderr); } @@ -23,12 +23,12 @@ int main(int argc, const char* argv[]) { NSLog(@"launcher is already running"); return 0; } - + [NSApplication sharedApplication]; Launcher* sharedLauncher = [Launcher sharedLauncher]; [Settings sharedSettings]; [NSApp setDelegate: sharedLauncher]; - + // Referenced from https://stackoverflow.com/questions/9155015/handle-cmd-q-in-cocoa-application-and-menu-item-quit-application-programmatic id menubar = [[NSMenu new] autorelease]; id appMenuItem = [[NSMenuItem new] autorelease]; @@ -40,7 +40,7 @@ int main(int argc, const char* argv[]) { id quitMenuItem = [[[NSMenuItem alloc] initWithTitle:quitTitle action:@selector(terminate:) keyEquivalent:@"q"] autorelease]; [appMenu addItem:quitMenuItem]; [appMenuItem setSubmenu:appMenu]; - + [[NSApplication sharedApplication] activateIgnoringOtherApps:TRUE]; return NSApplicationMain(argc, argv); } diff --git a/launchers/win32/Launcher.rc b/launchers/win32/Launcher.rc index 49f3ca53c5..4d9e0ffcd6 100644 --- a/launchers/win32/Launcher.rc +++ b/launchers/win32/Launcher.rc @@ -103,7 +103,7 @@ BEGIN CTEXT "",IDC_MESSAGE2_LABEL,35,172,239,15,NOT WS_VISIBLE CTEXT "",IDC_ACTION2_LABEL,15,147,278,25,NOT WS_VISIBLE RTEXT "",IDC_TERMS,15,172,180,15,NOT WS_VISIBLE - LTEXT "",IDC_TERMS2,197,172,80,15,NOT WS_VISIBLE + CONTROL "",IDC_TERMS_LINK,"Button", BS_OWNERDRAW | BS_FLAT | NOT WS_VISIBLE | WS_TABSTOP,197,172,80,15 CTEXT "",IDC_TROUBLE,65,203,174,15,NOT WS_VISIBLE CONTROL "NEXT",IDC_BUTTON_NEXT,"Button",BS_OWNERDRAW | BS_FLAT | NOT WS_VISIBLE | WS_TABSTOP,107,158,94,16 CONTROL "Having Trouble?",IDC_TROUBLE_LINK,"Button",BS_OWNERDRAW | BS_FLAT | NOT WS_VISIBLE | WS_TABSTOP,126,203,56,11 diff --git a/launchers/win32/LauncherDlg.cpp b/launchers/win32/LauncherDlg.cpp index 98c61794a0..a4fe9e494b 100644 --- a/launchers/win32/LauncherDlg.cpp +++ b/launchers/win32/LauncherDlg.cpp @@ -37,6 +37,7 @@ static CString GRAPHIK_REGULAR = _T("Graphik-Regular"); static CString GRAPHIK_SEMIBOLD = _T("Graphik-Semibold"); static CString TROUBLE_URL = _T("https://www.highfidelity.com/hq-support"); +static CString TERMS_URL = _T("https://www.highfidelity.com/termsofservice"); CLauncherDlg::CLauncherDlg(CWnd* pParent) @@ -54,6 +55,7 @@ void CLauncherDlg::DoDataExchange(CDataExchange* pDX) { DDX_Control(pDX, IDC_BUTTON_NEXT, m_btnNext); DDX_Control(pDX, IDC_TROUBLE_LINK, m_trouble_link); + DDX_Control(pDX, IDC_TERMS_LINK, m_terms_link); DDX_Control(pDX, IDC_ORGNAME, m_orgname); DDX_Control(pDX, IDC_USERNAME, m_username); DDX_Control(pDX, IDC_PASSWORD, m_password); @@ -69,6 +71,7 @@ BEGIN_MESSAGE_MAP(CLauncherDlg, CDialog) ON_EN_SETFOCUS(IDC_PASSWORD, &CLauncherDlg::OnPassEditChangeFocus) ON_BN_CLICKED(IDC_BUTTON_NEXT, &CLauncherDlg::OnNextClicked) ON_BN_CLICKED(IDC_TROUBLE_LINK, &CLauncherDlg::OnTroubleClicked) + ON_BN_CLICKED(IDC_TERMS_LINK, &CLauncherDlg::OnTermsClicked) ON_WM_CTLCOLOR() ON_WM_DRAWITEM() ON_WM_SETCURSOR() @@ -103,7 +106,6 @@ BOOL CLauncherDlg::OnInitDialog() { m_password_banner = (CStatic *)GetDlgItem(IDC_PASSWORD_BANNER); m_terms = (CStatic *)GetDlgItem(IDC_TERMS); - m_terms2 = (CStatic *)GetDlgItem(IDC_TERMS2); m_trouble = (CStatic *)GetDlgItem(IDC_TROUBLE); m_voxel = (CStatic *)GetDlgItem(IDC_VOXEL); @@ -223,6 +225,10 @@ afx_msg void CLauncherDlg::OnTroubleClicked() { LauncherUtils::executeOnForeground(TROUBLE_URL, _T("")); } +afx_msg void CLauncherDlg::OnTermsClicked() { + LauncherUtils::executeOnForeground(TERMS_URL, _T("")); +} + afx_msg void CLauncherDlg::OnNextClicked() { if (_drawStep != DrawStep::DrawChoose) { CString token; @@ -275,7 +281,7 @@ void CLauncherDlg::drawLogo(CHwndRenderTarget* pRenderTarget) { CD2DBitmap m_pBitmamLogo(pRenderTarget, IDB_PNG2, _T("PNG")); auto size = pRenderTarget->GetSize(); int logoWidth = 231; - int logoHeight = 181; + int logoHeight = 173; float logoPosX = 0.5f * (size.width - logoWidth); float logoPosY = 0.95f * (size.height - logoHeight); CD2DRectF logoRec(logoPosX, logoPosY, logoPosX + logoWidth, logoPosY + logoHeight); @@ -362,9 +368,9 @@ void CLauncherDlg::prepareChoose() { m_action_label->SetWindowTextW(_T("Choose a display name")); m_message_label->SetWindowTextW(_T("This is the name that your teammates will see.")); m_terms->ShowWindow(SW_SHOW); - m_terms2->ShowWindow(SW_SHOW); + m_terms_link.ShowWindow(SW_SHOW); m_terms->SetWindowTextW(_T("By signing in, you agree to the High Fidelity")); - m_terms2->SetWindowTextW(_T("Terms of Service")); + m_terms_link.SetWindowTextW(_T("Terms of Service")); CRect rec; m_btnNext.GetWindowRect(&rec); ScreenToClient(&rec); @@ -380,7 +386,7 @@ void CLauncherDlg::prepareProcess(DrawStep step) { m_trouble->ShowWindow(SW_HIDE); m_trouble_link.ShowWindow(SW_HIDE); m_terms->ShowWindow(SW_HIDE); - m_terms2->ShowWindow(SW_HIDE); + m_terms_link.ShowWindow(SW_HIDE); m_orgname_banner->ShowWindow(SW_HIDE); m_username_banner->ShowWindow(SW_HIDE); m_password_banner->ShowWindow(SW_HIDE); @@ -465,7 +471,7 @@ BOOL CLauncherDlg::getTextFormat(int resID, TextFormat& formatOut) { case IDC_TERMS: formatOut.size = TERMS_FONT_SIZE; break; - case IDC_TERMS2: + case IDC_TERMS_LINK: formatOut.size = TERMS_FONT_SIZE; formatOut.isBold = true; break; @@ -542,6 +548,14 @@ void CLauncherDlg::OnDrawItem(int nIDCtl, LPDRAWITEMSTRUCT lpDrawItemStruct) dc.SelectObject(buttonFont); } dc.DrawText(_T("Having Trouble"), CRect(rect.left, rect.top, rect.right, rect.bottom), DT_CENTER | DT_VCENTER | DT_SINGLELINE); + } else if (nIDCtl == IDC_TERMS_LINK) { + dc.FillSolidRect(rect, COLOR_BLACK); + dc.SetTextColor(COLOR_LIGHT_GREY); + CFont buttonFont; + if (LauncherUtils::getFont(GRAPHIK_SEMIBOLD, TERMS_FONT_SIZE, true, buttonFont)) { + dc.SelectObject(buttonFont); + } + dc.DrawText(_T("Terms of Service"), CRect(rect.left, rect.top, rect.right, rect.bottom), DT_LEFT | DT_TOP | DT_SINGLELINE); } } diff --git a/launchers/win32/LauncherDlg.h b/launchers/win32/LauncherDlg.h index beaaedcdb6..6357c2a5b0 100644 --- a/launchers/win32/LauncherDlg.h +++ b/launchers/win32/LauncherDlg.h @@ -72,13 +72,13 @@ protected: HICON m_hIcon; CButton m_btnNext; CButton m_trouble_link; + CButton m_terms_link; CStatic* m_message_label; CStatic* m_action_label; CStatic* m_message2_label; CStatic* m_action2_label; CStatic* m_terms; - CStatic* m_terms2; CStatic* m_trouble; CStatic* m_voxel; @@ -111,6 +111,7 @@ protected: afx_msg HCURSOR OnQueryDragIcon(); afx_msg void OnNextClicked(); afx_msg void OnTroubleClicked(); + afx_msg void OnTermsClicked(); afx_msg void OnOrgEditChangeFocus(); afx_msg void OnUserEditChangeFocus(); afx_msg void OnPassEditChangeFocus(); diff --git a/launchers/win32/resource.h b/launchers/win32/resource.h index 51e6e7bd71..f5a1e3ef07 100644 --- a/launchers/win32/resource.h +++ b/launchers/win32/resource.h @@ -23,7 +23,7 @@ #define IDC_USERNAME_BANNER 1019 #define IDC_PASSWORD_BANNER 1020 #define IDC_TERMS 1021 -#define IDC_TERMS2 1022 +#define IDC_TERMS_LINK 1022 #define IDC_TROUBLE 1023 #define IDC_VOXEL 1024 #define IDC_TROUBLE_LINK 1027 diff --git a/libraries/display-plugins/src/display-plugins/Basic2DWindowOpenGLDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/Basic2DWindowOpenGLDisplayPlugin.cpp index 4258d4db75..b8912d95b6 100644 --- a/libraries/display-plugins/src/display-plugins/Basic2DWindowOpenGLDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/Basic2DWindowOpenGLDisplayPlugin.cpp @@ -109,23 +109,20 @@ bool Basic2DWindowOpenGLDisplayPlugin::internalActivate() { return Parent::internalActivate(); } -gpu::PipelinePointer Basic2DWindowOpenGLDisplayPlugin::getCompositeScenePipeline() { +gpu::PipelinePointer Basic2DWindowOpenGLDisplayPlugin::getRenderTexturePipeline() { #if defined(Q_OS_ANDROID) - return _linearToSRGBPipeline; + return _linearToSRGBPipeline; #else - return _SRGBToLinearPipeline; + +#ifndef USE_GLES + return _SRGBToLinearPipeline; +#else + return _drawTexturePipeline; +#endif + #endif } -gpu::Element Basic2DWindowOpenGLDisplayPlugin::getCompositeFBColorSpace() { -#if defined(Q_OS_ANDROID) - return gpu::Element::COLOR_SRGBA_32; -#else - return gpu::Element::COLOR_RGBA_32; -#endif -} - - void Basic2DWindowOpenGLDisplayPlugin::compositeExtra() { #if defined(Q_OS_ANDROID) auto& virtualPadManager = VirtualPad::Manager::instance(); diff --git a/libraries/display-plugins/src/display-plugins/Basic2DWindowOpenGLDisplayPlugin.h b/libraries/display-plugins/src/display-plugins/Basic2DWindowOpenGLDisplayPlugin.h index 7f654915e1..68301ff8b4 100644 --- a/libraries/display-plugins/src/display-plugins/Basic2DWindowOpenGLDisplayPlugin.h +++ b/libraries/display-plugins/src/display-plugins/Basic2DWindowOpenGLDisplayPlugin.h @@ -37,8 +37,7 @@ public: virtual void pluginUpdate() override {}; - virtual gpu::PipelinePointer getCompositeScenePipeline() override; - virtual gpu::Element getCompositeFBColorSpace() override; + virtual gpu::PipelinePointer getRenderTexturePipeline() override; protected: mutable bool _isThrottled = false; diff --git a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp index 73bc1d0aad..75cdf5ebef 100644 --- a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp @@ -62,7 +62,7 @@ public: PresentThread() { connect(qApp, &QCoreApplication::aboutToQuit, [this] { - shutdown(); + shutdown(); }); setObjectName("Present"); @@ -82,12 +82,11 @@ public: Lock lock(_mutex); _shutdown = true; - _condition.wait(lock, [&] { return !_shutdown; }); + _condition.wait(lock, [&] { return !_shutdown; }); qCDebug(displayPlugins) << "Present thread shutdown"; } } - void setNewDisplayPlugin(OpenGLDisplayPlugin* plugin) { Lock lock(_mutex); if (isRunning()) { @@ -104,7 +103,6 @@ public: _context->moveToThread(this); } - virtual void run() override { PROFILE_SET_THREAD_NAME("Present Thread"); @@ -131,7 +129,6 @@ public: _condition.notify_one(); } - { // Main thread does it's thing while we wait on the lock to release Lock lock(_mutex); @@ -201,7 +198,7 @@ public: #if defined(Q_OS_MAC) _context->doneCurrent(); #endif - + _refreshRateController->sleepThreadIfNeeded(this, currentPlugin->isHmd()); } @@ -235,7 +232,6 @@ public: _condition.notify_one(); } - private: void makeCurrent(); void doneCurrent(); @@ -245,7 +241,6 @@ private: // Used to allow the main thread to perform context operations Condition _condition; - QThread* _targetOperationThread { nullptr }; bool _pendingOtherThreadOperation { false }; bool _finishedOtherThreadOperation { false }; @@ -302,7 +297,6 @@ bool OpenGLDisplayPlugin::activate() { return false; } - // This should not return until the new context has been customized // and the old context (if any) has been uncustomized presentThread->setNewDisplayPlugin(this); @@ -334,7 +328,7 @@ void OpenGLDisplayPlugin::deactivate() { _container->showDisplayPluginsTools(false); if (!_container->currentDisplayActions().isEmpty()) { - foreach(auto itemInfo, _container->currentDisplayActions()) { + foreach (auto itemInfo, _container->currentDisplayActions()) { _container->removeMenuItem(itemInfo.first, itemInfo.second); } _container->currentDisplayActions().clear(); @@ -368,7 +362,6 @@ void OpenGLDisplayPlugin::customizeContext() { image = image.convertToFormat(QImage::Format_ARGB32); } if ((image.width() > 0) && (image.height() > 0)) { - cursorData.texture = gpu::Texture::createStrict( gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA), image.width(), image.height(), @@ -384,7 +377,7 @@ void OpenGLDisplayPlugin::customizeContext() { } } - if (!_drawTexturePipeline) { + if (!_linearToSRGBPipeline) { gpu::StatePointer blendState = gpu::StatePointer(new gpu::State()); blendState->setDepthTest(gpu::State::DepthTest(false)); blendState->setBlendFunction(true, @@ -397,17 +390,17 @@ void OpenGLDisplayPlugin::customizeContext() { scissorState->setDepthTest(gpu::State::DepthTest(false)); scissorState->setScissorEnable(true); - _drawTexturePipeline = gpu::Pipeline::create(gpu::Shader::createProgram(DrawTexture), scissorState); + _drawTexturePipeline = gpu::Pipeline::create(gpu::Shader::createProgram(DrawTexture), scissorState); - _linearToSRGBPipeline = gpu::Pipeline::create(gpu::Shader::createProgram(DrawTextureGammaLinearToSRGB), scissorState); + _linearToSRGBPipeline = gpu::Pipeline::create(gpu::Shader::createProgram(DrawTextureGammaLinearToSRGB), scissorState); - _SRGBToLinearPipeline = gpu::Pipeline::create(gpu::Shader::createProgram(DrawTextureGammaSRGBToLinear), scissorState); + _SRGBToLinearPipeline = gpu::Pipeline::create(gpu::Shader::createProgram(DrawTextureGammaSRGBToLinear), scissorState); - _hudPipeline = gpu::Pipeline::create(gpu::Shader::createProgram(DrawTexture), blendState); + _hudPipeline = gpu::Pipeline::create(gpu::Shader::createProgram(DrawTexture), blendState); - _mirrorHUDPipeline = gpu::Pipeline::create(gpu::Shader::createProgram(DrawTextureMirroredX), blendState); + _mirrorHUDPipeline = gpu::Pipeline::create(gpu::Shader::createProgram(DrawTextureMirroredX), blendState); - _cursorPipeline = gpu::Pipeline::create(gpu::Shader::createProgram(DrawTransformedTexture), blendState); + _cursorPipeline = gpu::Pipeline::create(gpu::Shader::createProgram(DrawTransformedTexture), blendState); } updateCompositeFramebuffer(); @@ -418,9 +411,9 @@ void OpenGLDisplayPlugin::uncustomizeContext() { _drawTexturePipeline.reset(); _linearToSRGBPipeline.reset(); _SRGBToLinearPipeline.reset(); + _cursorPipeline.reset(); _hudPipeline.reset(); _mirrorHUDPipeline.reset(); - _cursorPipeline.reset(); _compositeFramebuffer.reset(); withPresentThreadLock([&] { @@ -433,7 +426,6 @@ void OpenGLDisplayPlugin::uncustomizeContext() { }); } - // Pressing Alt (and Meta) key alone activates the menubar because its style inherits the // SHMenuBarAltKeyNavigation from QWindowsStyle. This makes it impossible for a scripts to // receive keyPress events for the Alt (and Meta) key in a reliable manner. @@ -514,11 +506,18 @@ void OpenGLDisplayPlugin::captureFrame(const std::string& filename) const { }); } -void OpenGLDisplayPlugin::renderFromTexture(gpu::Batch& batch, const gpu::TexturePointer& texture, const glm::ivec4& viewport, const glm::ivec4& scissor) { +void OpenGLDisplayPlugin::renderFromTexture(gpu::Batch& batch, + const gpu::TexturePointer& texture, + const glm::ivec4& viewport, + const glm::ivec4& scissor) { renderFromTexture(batch, texture, viewport, scissor, nullptr); } -void OpenGLDisplayPlugin::renderFromTexture(gpu::Batch& batch, const gpu::TexturePointer& texture, const glm::ivec4& viewport, const glm::ivec4& scissor, const gpu::FramebufferPointer& copyFbo /*=gpu::FramebufferPointer()*/) { +void OpenGLDisplayPlugin::renderFromTexture(gpu::Batch& batch, + const gpu::TexturePointer& texture, + const glm::ivec4& viewport, + const glm::ivec4& scissor, + const gpu::FramebufferPointer& copyFbo /*=gpu::FramebufferPointer()*/) { auto fbo = gpu::FramebufferPointer(); batch.enableStereo(false); batch.resetViewTransform(); @@ -528,13 +527,13 @@ void OpenGLDisplayPlugin::renderFromTexture(gpu::Batch& batch, const gpu::Textur batch.setViewportTransform(viewport); batch.setResourceTexture(0, texture); - batch.setPipeline(_drawTexturePipeline); - + batch.setPipeline(getRenderTexturePipeline()); + batch.draw(gpu::TRIANGLE_STRIP, 4); if (copyFbo) { gpu::Vec4i copyFboRect(0, 0, copyFbo->getWidth(), copyFbo->getHeight()); gpu::Vec4i sourceRect(scissor.x, scissor.y, scissor.x + scissor.z, scissor.y + scissor.w); - float aspectRatio = (float)scissor.w / (float) scissor.z; // height/width + float aspectRatio = (float)scissor.w / (float)scissor.z; // height/width // scale width first int xOffset = 0; int yOffset = 0; @@ -635,11 +634,6 @@ void OpenGLDisplayPlugin::compositePointer() { }); } -// Overridden by Basic2DWindowDisplayPlugin and OculusDisplayPlugin -gpu::PipelinePointer OpenGLDisplayPlugin::getCompositeScenePipeline() { - return _drawTexturePipeline; -} - void OpenGLDisplayPlugin::compositeScene() { render([&](gpu::Batch& batch) { batch.enableStereo(false); @@ -682,7 +676,7 @@ void OpenGLDisplayPlugin::internalPresent() { render([&](gpu::Batch& batch) { // Note: _displayTexture must currently be the same size as the display. uvec2 dims = _displayTexture ? uvec2(_displayTexture->getDimensions()) : getSurfacePixels(); - auto viewport = ivec4(uvec2(0), dims); + auto viewport = ivec4(uvec2(0), dims); renderFromTexture(batch, _displayTexture ? _displayTexture : _compositeFramebuffer->getRenderBuffer(0), viewport, viewport); }); swapBuffers(); @@ -765,7 +759,7 @@ float OpenGLDisplayPlugin::presentRate() const { return _presentRate.rate(); } -std::function OpenGLDisplayPlugin::getRefreshRateOperator() { +std::function OpenGLDisplayPlugin::getRefreshRateOperator() { return [](int targetRefreshRate) { auto refreshRateController = DependencyManager::get()->getRefreshRateController(); refreshRateController->setRefreshRateLimitPeriod(targetRefreshRate); @@ -781,7 +775,6 @@ float OpenGLDisplayPlugin::renderRate() const { return _renderRate.rate(); } - void OpenGLDisplayPlugin::swapBuffers() { static auto context = _container->getPrimaryWidget()->context(); context->swapBuffers(); @@ -909,17 +902,10 @@ void OpenGLDisplayPlugin::render(std::function f) { OpenGLDisplayPlugin::~OpenGLDisplayPlugin() { } -// Added this to allow desktop composite framebuffer to be RGBA while mobile is SRGBA -// Overridden by Basic2DWindowDisplayPlugin -// FIXME: Eventually it would be ideal to have both framebuffers be of the same type -gpu::Element OpenGLDisplayPlugin::getCompositeFBColorSpace() { - return gpu::Element::COLOR_RGBA_32; -} - void OpenGLDisplayPlugin::updateCompositeFramebuffer() { auto renderSize = glm::uvec2(getRecommendedRenderSize()); if (!_compositeFramebuffer || _compositeFramebuffer->getSize() != renderSize) { - _compositeFramebuffer = gpu::FramebufferPointer(gpu::Framebuffer::create("OpenGLDisplayPlugin::composite", getCompositeFBColorSpace(), renderSize.x, renderSize.y)); + _compositeFramebuffer = gpu::FramebufferPointer(gpu::Framebuffer::create("OpenGLDisplayPlugin::composite", gpu::Element::COLOR_RGBA_32, renderSize.x, renderSize.y)); } } @@ -950,14 +936,13 @@ void OpenGLDisplayPlugin::copyTextureToQuickFramebuffer(NetworkTexturePointer ne glClearColor(0.0f, 0.0f, 0.0f, 0.0f); glClear(GL_COLOR_BUFFER_BIT); - // maintain aspect ratio, filling the width first if possible. If that makes the height too // much, fill height instead. TODO: only do this when texture changes GLint newX = 0; GLint newY = 0; float aspectRatio = (float)texHeight / (float)texWidth; GLint newWidth = target->width(); - GLint newHeight = std::round(aspectRatio * (float) target->width()); + GLint newHeight = std::round(aspectRatio * (float)target->width()); if (newHeight > target->height()) { newHeight = target->height(); newWidth = std::round((float)target->height() / aspectRatio); @@ -966,7 +951,7 @@ void OpenGLDisplayPlugin::copyTextureToQuickFramebuffer(NetworkTexturePointer ne newY = (target->height() - newHeight) / 2; } - glBlitNamedFramebuffer(fbo[0], fbo[1], 0, 0, texWidth, texHeight, newX, newY, newX + newWidth, newY + newHeight, GL_DEPTH_BUFFER_BIT|GL_COLOR_BUFFER_BIT, GL_NEAREST); + glBlitNamedFramebuffer(fbo[0], fbo[1], 0, 0, texWidth, texHeight, newX, newY, newX + newWidth, newY + newHeight, GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT, GL_NEAREST); // don't delete the textures! glDeleteFramebuffers(2, fbo); @@ -975,3 +960,11 @@ void OpenGLDisplayPlugin::copyTextureToQuickFramebuffer(NetworkTexturePointer ne #endif } +gpu::PipelinePointer OpenGLDisplayPlugin::getRenderTexturePipeline() { + return _drawTexturePipeline; +} + +gpu::PipelinePointer OpenGLDisplayPlugin::getCompositeScenePipeline() { + return _drawTexturePipeline; +} + diff --git a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.h b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.h index eae9f86710..777c74822a 100644 --- a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.h +++ b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.h @@ -23,11 +23,9 @@ #include -namespace gpu { - namespace gl { - class GLBackend; - } -} +namespace gpu { namespace gl { +class GLBackend; +}} // namespace gpu::gl class RefreshRateController; @@ -35,10 +33,12 @@ class OpenGLDisplayPlugin : public DisplayPlugin { Q_OBJECT Q_PROPERTY(float hudAlpha MEMBER _hudAlpha) using Parent = DisplayPlugin; + protected: using Mutex = std::mutex; using Lock = std::unique_lock; using Condition = std::condition_variable; + public: ~OpenGLDisplayPlugin(); // These must be final to ensure proper ordering of operations @@ -55,13 +55,9 @@ public: void captureFrame(const std::string& outputName) const override; void submitFrame(const gpu::FramePointer& newFrame) override; - glm::uvec2 getRecommendedRenderSize() const override { - return getSurfacePixels(); - } + glm::uvec2 getRecommendedRenderSize() const override { return getSurfacePixels(); } - glm::uvec2 getRecommendedUiSize() const override { - return getSurfaceSize(); - } + glm::uvec2 getRecommendedUiSize() const override { return getSurfaceSize(); } virtual bool setDisplayTexture(const QString& name) override; virtual bool onDisplayTextureReset() { return false; }; @@ -84,9 +80,10 @@ public: // Three threads, one for rendering, one for texture transfers, one reserved for the GL driver int getRequiredThreadCount() const override { return 3; } - void copyTextureToQuickFramebuffer(NetworkTexturePointer source, QOpenGLFramebufferObject* target, GLsync* fenceSync) override; - virtual std::function getHUDOperator() override; + void copyTextureToQuickFramebuffer(NetworkTexturePointer source, + QOpenGLFramebufferObject* target, + GLsync* fenceSync) override; protected: friend class PresentThread; @@ -105,10 +102,7 @@ protected: virtual void compositeLayers(); virtual void compositeScene(); virtual void compositePointer(); - virtual void compositeExtra() {}; - - virtual gpu::PipelinePointer getCompositeScenePipeline(); - virtual gpu::Element getCompositeFBColorSpace(); + virtual void compositeExtra(){}; // These functions must only be called on the presentation thread virtual void customizeContext(); @@ -125,8 +119,15 @@ protected: // Plugin specific functionality to send the composed scene to the output window or device virtual void internalPresent(); - void renderFromTexture(gpu::Batch& batch, const gpu::TexturePointer& texture, const glm::ivec4& viewport, const glm::ivec4& scissor, const gpu::FramebufferPointer& fbo); - void renderFromTexture(gpu::Batch& batch, const gpu::TexturePointer& texture, const glm::ivec4& viewport, const glm::ivec4& scissor); + void renderFromTexture(gpu::Batch& batch, + const gpu::TexturePointer& texture, + const glm::ivec4& viewport, + const glm::ivec4& scissor, + const gpu::FramebufferPointer& fbo); + void renderFromTexture(gpu::Batch& batch, + const gpu::TexturePointer& texture, + const glm::ivec4& viewport, + const glm::ivec4& scissor); virtual void updateFrameData(); virtual glm::mat4 getViewCorrection() { return glm::mat4(); } @@ -138,7 +139,7 @@ protected: void render(std::function f); - bool _vsyncEnabled { true }; + bool _vsyncEnabled{ true }; QThread* _presentThread{ nullptr }; std::queue _newFrameQueue; RateCounter<200> _droppedFrameRate; @@ -147,7 +148,7 @@ protected: RateCounter<200> _renderRate; gpu::FramePointer _currentFrame; - gpu::Frame* _lastFrame { nullptr }; + gpu::Frame* _lastFrame{ nullptr }; mat4 _prevRenderView; gpu::FramebufferPointer _compositeFramebuffer; gpu::PipelinePointer _hudPipeline; @@ -157,9 +158,11 @@ protected: gpu::PipelinePointer _linearToSRGBPipeline; gpu::PipelinePointer _SRGBToLinearPipeline; gpu::PipelinePointer _cursorPipeline; - gpu::TexturePointer _displayTexture{}; - float _compositeHUDAlpha { 1.0f }; + float _compositeHUDAlpha{ 1.0f }; + + virtual gpu::PipelinePointer getRenderTexturePipeline(); + virtual gpu::PipelinePointer getCompositeScenePipeline(); struct CursorData { QImage image; @@ -169,19 +172,19 @@ protected: }; std::map _cursorsData; - bool _lockCurrentTexture { false }; + bool _lockCurrentTexture{ false }; void assertNotPresentThread() const; void assertIsPresentThread() const; - template + template void withPresentThreadLock(F f) const { assertIsPresentThread(); Lock lock(_presentMutex); f(); } - template + template void withNonPresentThreadLock(F f) const { assertNotPresentThread(); Lock lock(_presentMutex); @@ -198,4 +201,3 @@ protected: QImage getScreenshot(float aspectRatio); QImage getSecondaryCameraScreenshot(); }; - diff --git a/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.cpp index 3952c2c90e..0c7d83cdd8 100644 --- a/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.cpp @@ -174,6 +174,10 @@ float HmdDisplayPlugin::getLeftCenterPixel() const { return leftCenterPixel; } +gpu::PipelinePointer HmdDisplayPlugin::getRenderTexturePipeline() { + return _SRGBToLinearPipeline; +} + void HmdDisplayPlugin::internalPresent() { PROFILE_RANGE_EX(render, __FUNCTION__, 0xff00ff00, (uint64_t)presentCount()) diff --git a/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.h b/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.h index 9942222f48..1f34e1b52a 100644 --- a/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.h +++ b/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.h @@ -51,6 +51,8 @@ public: std::function getHUDOperator() override; virtual StencilMaskMode getStencilMaskMode() const override { return StencilMaskMode::PAINT; } + virtual gpu::PipelinePointer getRenderTexturePipeline() override; + signals: void hmdMountedChanged(); void hmdVisibleChanged(bool visible); diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.cpp b/libraries/entities-renderer/src/EntityTreeRenderer.cpp index 5ac6e4f642..5cb7b89de5 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.cpp +++ b/libraries/entities-renderer/src/EntityTreeRenderer.cpp @@ -207,7 +207,7 @@ void EntityTreeRenderer::stopDomainAndNonOwnedEntities() { if (entityItem && !entityItem->getScript().isEmpty()) { if (!(entityItem->isLocalEntity() || (entityItem->isAvatarEntity() && entityItem->getOwningAvatarID() == getTree()->getMyAvatarSessionUUID()))) { - if (entityItem->contains(_avatarPosition)) { + if (_currentEntitiesInside.contains(entityID)) { _entitiesScriptEngine->callEntityScriptMethod(entityID, "leaveEntity"); } _entitiesScriptEngine->unloadEntityScript(entityID, true); @@ -222,6 +222,7 @@ void EntityTreeRenderer::clearDomainAndNonOwnedEntities() { auto sessionUUID = getTree()->getMyAvatarSessionUUID(); std::unordered_map savedEntities; + std::unordered_set savedRenderables; // remove all entities from the scene auto scene = _viewState->getMain3DScene(); if (scene) { @@ -232,11 +233,12 @@ void EntityTreeRenderer::clearDomainAndNonOwnedEntities() { fadeOutRenderable(renderer); } else { savedEntities[entry.first] = entry.second; + savedRenderables.insert(entry.second); } } } - _renderablesToUpdate = savedEntities; + _renderablesToUpdate = savedRenderables; _entitiesInScene = savedEntities; if (_layeredZones.clearDomainAndNonOwnedZones(sessionUUID)) { @@ -389,13 +391,7 @@ void EntityTreeRenderer::updateChangedEntities(const render::ScenePointer& scene PerformanceTimer pt("change"); std::unordered_set changedEntities; _changedEntitiesGuard.withWriteLock([&] { -#if 0 - // FIXME Weird build failure in latest VC update that fails to compile when using std::swap changedEntities.swap(_changedEntities); -#else - changedEntities.insert(_changedEntities.begin(), _changedEntities.end()); - _changedEntities.clear(); -#endif }); { @@ -404,7 +400,7 @@ void EntityTreeRenderer::updateChangedEntities(const render::ScenePointer& scene auto renderable = renderableForEntityId(entityId); if (renderable) { // only add valid renderables _renderablesToUpdate - _renderablesToUpdate.insert({ entityId, renderable }); + _renderablesToUpdate.insert(renderable); } } } @@ -414,8 +410,7 @@ void EntityTreeRenderer::updateChangedEntities(const render::ScenePointer& scene // we expect to update all renderables within available time budget PROFILE_RANGE_EX(simulation_physics, "UpdateRenderables", 0xffff00ff, (uint64_t)_renderablesToUpdate.size()); uint64_t updateStart = usecTimestampNow(); - for (const auto& entry : _renderablesToUpdate) { - const auto& renderable = entry.second; + for (const auto& renderable : _renderablesToUpdate) { assert(renderable); // only valid renderables are added to _renderablesToUpdate renderable->updateInScene(scene, transaction); } @@ -424,8 +419,8 @@ void EntityTreeRenderer::updateChangedEntities(const render::ScenePointer& scene // compute average per-renderable update cost float cost = (float)(usecTimestampNow() - updateStart) / (float)(numRenderables); - const float blend = 0.1f; - _avgRenderableUpdateCost = (1.0f - blend) * _avgRenderableUpdateCost + blend * cost; + const float BLEND = 0.1f; + _avgRenderableUpdateCost = (1.0f - BLEND) * _avgRenderableUpdateCost + BLEND * cost; } else { // we expect the cost to updating all renderables to exceed available time budget // so we first sort by priority and update in order until out of time @@ -450,43 +445,40 @@ void EntityTreeRenderer::updateChangedEntities(const render::ScenePointer& scene PrioritySortUtil::PriorityQueue sortedRenderables(views); sortedRenderables.reserve(_renderablesToUpdate.size()); { - PROFILE_RANGE_EX(simulation_physics, "SortRenderables", 0xffff00ff, (uint64_t)_renderablesToUpdate.size()); - std::unordered_map::iterator itr = _renderablesToUpdate.begin(); - while (itr != _renderablesToUpdate.end()) { - assert(itr->second); // only valid renderables are added to _renderablesToUpdate - sortedRenderables.push(SortableRenderer(itr->second)); - ++itr; + PROFILE_RANGE_EX(simulation_physics, "BuildSortedRenderables", 0xffff00ff, (uint64_t)_renderablesToUpdate.size()); + for (const auto& renderable : _renderablesToUpdate) { + assert(renderable); // only valid renderables are added to _renderablesToUpdate + sortedRenderables.push(SortableRenderer(renderable)); } } { - PROFILE_RANGE_EX(simulation_physics, "UpdateRenderables", 0xffff00ff, sortedRenderables.size()); + PROFILE_RANGE_EX(simulation_physics, "SortAndUpdateRenderables", 0xffff00ff, sortedRenderables.size()); // compute remaining time budget + const auto& sortedRenderablesVector = sortedRenderables.getSortedVector(); uint64_t updateStart = usecTimestampNow(); - uint64_t timeBudget = MIN_SORTED_UPDATE_RENDERABLES_TIME_BUDGET; uint64_t sortCost = updateStart - sortStart; + uint64_t timeBudget = MIN_SORTED_UPDATE_RENDERABLES_TIME_BUDGET; if (sortCost < MAX_UPDATE_RENDERABLES_TIME_BUDGET - MIN_SORTED_UPDATE_RENDERABLES_TIME_BUDGET) { timeBudget = MAX_UPDATE_RENDERABLES_TIME_BUDGET - sortCost; } uint64_t expiry = updateStart + timeBudget; // process the sorted renderables - size_t numSorted = sortedRenderables.size(); - const auto& sortedRenderablesVector = sortedRenderables.getSortedVector(); for (const auto& sortedRenderable : sortedRenderablesVector) { if (usecTimestampNow() > expiry) { break; } const auto& renderable = sortedRenderable.getRenderer(); renderable->updateInScene(scene, transaction); - _renderablesToUpdate.erase(renderable->getEntity()->getID()); + _renderablesToUpdate.erase(renderable); } // compute average per-renderable update cost - size_t numUpdated = numSorted - sortedRenderables.size() + 1; // add one to avoid divide by zero + size_t numUpdated = sortedRenderables.size() - _renderablesToUpdate.size() + 1; // add one to avoid divide by zero float cost = (float)(usecTimestampNow() - updateStart) / (float)(numUpdated); - const float blend = 0.1f; - _avgRenderableUpdateCost = (1.0f - blend) * _avgRenderableUpdateCost + blend * cost; + const float BLEND = 0.1f; + _avgRenderableUpdateCost = (1.0f - BLEND) * _avgRenderableUpdateCost + BLEND * cost; } } } @@ -990,7 +982,6 @@ void EntityTreeRenderer::mouseMoveEvent(QMouseEvent* event) { void EntityTreeRenderer::deletingEntity(const EntityItemID& entityID) { // If it's in a pending queue, remove it - _renderablesToUpdate.erase(entityID); _entitiesToAdd.erase(entityID); auto itr = _entitiesInScene.find(entityID); @@ -1000,7 +991,7 @@ void EntityTreeRenderer::deletingEntity(const EntityItemID& entityID) { } if (_tree && !_shuttingDown && _entitiesScriptEngine && !itr->second->getEntity()->getScript().isEmpty()) { - if (itr->second->getEntity()->contains(_avatarPosition)) { + if (_currentEntitiesInside.contains(entityID)) { _entitiesScriptEngine->callEntityScriptMethod(entityID, "leaveEntity"); } _entitiesScriptEngine->unloadEntityScript(entityID, true); @@ -1013,6 +1004,7 @@ void EntityTreeRenderer::deletingEntity(const EntityItemID& entityID) { } auto renderable = itr->second; + _renderablesToUpdate.erase(renderable); _entitiesInScene.erase(itr); if (!renderable) { @@ -1047,7 +1039,7 @@ void EntityTreeRenderer::checkAndCallPreload(const EntityItemID& entityID, bool QString scriptUrl = entity->getScript(); if ((shouldLoad && unloadFirst) || scriptUrl.isEmpty()) { if (_entitiesScriptEngine) { - if (entity->contains(_avatarPosition)) { + if (_currentEntitiesInside.contains(entityID)) { _entitiesScriptEngine->callEntityScriptMethod(entityID, "leaveEntity"); } _entitiesScriptEngine->unloadEntityScript(entityID); diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.h b/libraries/entities-renderer/src/EntityTreeRenderer.h index f794d947ed..bea04f106b 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.h +++ b/libraries/entities-renderer/src/EntityTreeRenderer.h @@ -246,7 +246,7 @@ private: ReadWriteLockable _changedEntitiesGuard; std::unordered_set _changedEntities; - std::unordered_map _renderablesToUpdate; + std::unordered_set _renderablesToUpdate; std::unordered_map _entitiesInScene; std::unordered_map _entitiesToAdd; diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index dbc347631d..dca3ac595a 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -2121,9 +2121,10 @@ void EntityTree::fixupNeedsParentFixups() { _needsParentFixup.clear(); } + std::unordered_set seenEntityIds; QMutableVectorIterator iter(entitiesToFixup); while (iter.hasNext()) { - EntityItemWeakPointer entityWP = iter.next(); + const auto& entityWP = iter.next(); EntityItemPointer entity = entityWP.lock(); if (!entity) { // entity was deleted before we found its parent @@ -2131,6 +2132,17 @@ void EntityTree::fixupNeedsParentFixups() { continue; } + const auto id = entity->getID(); + // BUGZ-771 some entities seem to never be removed by the below logic and further seem to accumulate dupes within the _needsParentFixup list + // This block ensures that duplicates are removed from entitiesToFixup before it's re-appended to _needsParentFixup + if (0 != seenEntityIds.count(id)) { + // Entity was duplicated inside entitiesToFixup + iter.remove(); + continue; + } + + seenEntityIds.insert(id); + entity->requiresRecalcBoxes(); bool queryAACubeSuccess { false }; bool maxAACubeSuccess { false }; diff --git a/libraries/networking/CMakeLists.txt b/libraries/networking/CMakeLists.txt index c3592c5da2..9f63f2cb00 100644 --- a/libraries/networking/CMakeLists.txt +++ b/libraries/networking/CMakeLists.txt @@ -1,6 +1,6 @@ set(TARGET_NAME networking) setup_hifi_library(Network) -link_hifi_libraries(shared) +link_hifi_libraries(shared platform) target_openssl() target_tbb() diff --git a/libraries/networking/src/AccountManager.cpp b/libraries/networking/src/AccountManager.cpp index 3a7d3e0a67..e2e9d33eb6 100644 --- a/libraries/networking/src/AccountManager.cpp +++ b/libraries/networking/src/AccountManager.cpp @@ -956,7 +956,7 @@ void AccountManager::saveLoginStatus(bool isLoggedIn) { QProcess launcher; launcher.setProgram(launcherPath); launcher.startDetached(); - qApp->quit(); + QMetaObject::invokeMethod(qApp, "quit", Qt::QueuedConnection); } } } \ No newline at end of file diff --git a/libraries/networking/src/NodeList.cpp b/libraries/networking/src/NodeList.cpp index 96b713c583..9dd7716823 100644 --- a/libraries/networking/src/NodeList.cpp +++ b/libraries/networking/src/NodeList.cpp @@ -26,6 +26,8 @@ #include #include #include +#include +#include #include "AccountManager.h" #include "AddressManager.h" @@ -42,6 +44,7 @@ using namespace std::chrono; const int KEEPALIVE_PING_INTERVAL_MS = 1000; +const int MAX_SYSTEM_INFO_SIZE = 1000; NodeList::NodeList(char newOwnerType, int socketListenPort, int dtlsListenPort) : LimitedNodeList(socketListenPort, dtlsListenPort), @@ -418,19 +421,20 @@ void NodeList::sendDomainServerCheckIn() { auto accountManager = DependencyManager::get(); packetStream << FingerprintUtils::getMachineFingerprint(); - QString systemInfo; -#if defined Q_OS_WIN - systemInfo = "OS:Windows"; -#elif defined Q_OS_OSX - systemInfo = "OS:OSX"; -#elif defined Q_OS_LINUX - systemInfo = "OS:Linux"; -#elif defined Q_OS_ANDROID - systemInfo = "OS:Android"; -#else - systemInfo = "OS:Unknown"; -#endif - packetStream << systemInfo; + auto desc = platform::getAll(); + + QByteArray systemInfo(desc.dump().c_str()); + QByteArray compressedSystemInfo = qCompress(systemInfo); + + if (compressedSystemInfo.size() > MAX_SYSTEM_INFO_SIZE) { + // Highly unlikely, as not even unreasonable machines will + // overflow the max size, but prevent MTU overflow anyway. + // We could do something sophisticated like clearing specific + // values if they're too big, but we'll save that for later. + compressedSystemInfo.clear(); + } + + packetStream << compressedSystemInfo; packetStream << _connectReason; diff --git a/libraries/networking/src/ThreadedAssignment.cpp b/libraries/networking/src/ThreadedAssignment.cpp index 2b415073f2..b8a8f65080 100644 --- a/libraries/networking/src/ThreadedAssignment.cpp +++ b/libraries/networking/src/ThreadedAssignment.cpp @@ -20,6 +20,7 @@ #include #include +#include #include "NetworkLogging.h" ThreadedAssignment::ThreadedAssignment(ReceivedMessage& message) : @@ -38,6 +39,16 @@ ThreadedAssignment::ThreadedAssignment(ReceivedMessage& message) : // if the NL tells us we got a DS response, clear our member variable of queued check-ins auto nodeList = DependencyManager::get(); connect(nodeList.data(), &NodeList::receivedDomainServerList, this, &ThreadedAssignment::clearQueuedCheckIns); + + platform::create(); + if (!platform::enumeratePlatform()) { + qCDebug(networking) << "Failed to enumerate platform."; + } +} + +ThreadedAssignment::~ThreadedAssignment() { + stop(); + platform::destroy(); } void ThreadedAssignment::setFinished(bool isFinished) { diff --git a/libraries/networking/src/ThreadedAssignment.h b/libraries/networking/src/ThreadedAssignment.h index e76533b2a1..12096cf23f 100644 --- a/libraries/networking/src/ThreadedAssignment.h +++ b/libraries/networking/src/ThreadedAssignment.h @@ -22,7 +22,7 @@ class ThreadedAssignment : public Assignment { Q_OBJECT public: ThreadedAssignment(ReceivedMessage& message); - ~ThreadedAssignment() { stop(); } + ~ThreadedAssignment(); virtual void aboutToFinish() { }; void addPacketStatsAndSendStatsPacket(QJsonObject statsObject); diff --git a/libraries/networking/src/udt/PacketHeaders.cpp b/libraries/networking/src/udt/PacketHeaders.cpp index e434867c9a..ed68fe89dc 100644 --- a/libraries/networking/src/udt/PacketHeaders.cpp +++ b/libraries/networking/src/udt/PacketHeaders.cpp @@ -72,7 +72,7 @@ PacketVersion versionForPacketType(PacketType packetType) { return static_cast(DomainConnectionDeniedVersion::IncludesExtraInfo); case PacketType::DomainConnectRequest: - return static_cast(DomainConnectRequestVersion::HasSystemInfo); + return static_cast(DomainConnectRequestVersion::HasCompressedSystemInfo); case PacketType::DomainServerAddedNode: return static_cast(DomainServerAddedNodeVersion::PermissionsGrid); diff --git a/libraries/networking/src/udt/PacketHeaders.h b/libraries/networking/src/udt/PacketHeaders.h index a244399c5a..6230b8b11e 100644 --- a/libraries/networking/src/udt/PacketHeaders.h +++ b/libraries/networking/src/udt/PacketHeaders.h @@ -347,7 +347,8 @@ enum class DomainConnectRequestVersion : PacketVersion { AlwaysHasMachineFingerprint, HasTimestamp, HasReason, - HasSystemInfo + HasSystemInfo, + HasCompressedSystemInfo }; enum class DomainConnectionDeniedVersion : PacketVersion { diff --git a/libraries/platform/CMakeLists.txt b/libraries/platform/CMakeLists.txt index 70f3157e1e..55203d45a4 100644 --- a/libraries/platform/CMakeLists.txt +++ b/libraries/platform/CMakeLists.txt @@ -5,3 +5,11 @@ link_hifi_libraries(shared) GroupSources("src") target_json() + +if (APPLE) + # link in required OS X frameworks and include the right GL headers + find_library(OpenGL OpenGL) + find_library(AppKit AppKit) + + target_link_libraries(${TARGET_NAME} ${OpenGL} ${AppKit}) +endif () diff --git a/libraries/plugins/src/plugins/DisplayPlugin.h b/libraries/plugins/src/plugins/DisplayPlugin.h index 1cad9b1e11..ca4e3bc392 100644 --- a/libraries/plugins/src/plugins/DisplayPlugin.h +++ b/libraries/plugins/src/plugins/DisplayPlugin.h @@ -147,14 +147,6 @@ public: virtual void submitFrame(const gpu::FramePointer& newFrame) = 0; virtual void captureFrame(const std::string& outputName) const { } - virtual float getRenderResolutionScale() const { - return _renderResolutionScale; - } - - void setRenderResolutionScale(float renderResolutionScale) { - _renderResolutionScale = renderResolutionScale; - } - // The size of the rendering target (may be larger than the device size due to distortion) virtual glm::uvec2 getRecommendedRenderSize() const = 0; @@ -235,8 +227,6 @@ protected: MovingAverage _movingAveragePresent; - float _renderResolutionScale { 1.0f }; - private: QMutex _presentMutex; QWaitCondition _presentCondition; diff --git a/libraries/render-utils/src/DeferredLightingEffect.cpp b/libraries/render-utils/src/DeferredLightingEffect.cpp index 51729fc5cf..3ab9340906 100644 --- a/libraries/render-utils/src/DeferredLightingEffect.cpp +++ b/libraries/render-utils/src/DeferredLightingEffect.cpp @@ -273,44 +273,6 @@ graphics::MeshPointer DeferredLightingEffect::getSpotLightMesh() { return _spotLightMesh; } -gpu::FramebufferPointer PreparePrimaryFramebuffer::createFramebuffer(const char* name, const glm::uvec2& frameSize) { - gpu::FramebufferPointer framebuffer = gpu::FramebufferPointer(gpu::Framebuffer::create(name)); - auto colorFormat = gpu::Element::COLOR_SRGBA_32; - - auto defaultSampler = gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_LINEAR); - auto primaryColorTexture = gpu::Texture::createRenderBuffer(colorFormat, frameSize.x, frameSize.y, gpu::Texture::SINGLE_MIP, defaultSampler); - - framebuffer->setRenderBuffer(0, primaryColorTexture); - - auto depthFormat = gpu::Element(gpu::SCALAR, gpu::UINT32, gpu::DEPTH_STENCIL); // Depth24_Stencil8 texel format - auto primaryDepthTexture = gpu::Texture::createRenderBuffer(depthFormat, frameSize.x, frameSize.y, gpu::Texture::SINGLE_MIP, defaultSampler); - - framebuffer->setDepthStencilBuffer(primaryDepthTexture, depthFormat); - - return framebuffer; -} - -void PreparePrimaryFramebuffer::configure(const Config& config) { - _resolutionScale = config.resolutionScale; -} - -void PreparePrimaryFramebuffer::run(const RenderContextPointer& renderContext, Output& primaryFramebuffer) { - glm::uvec2 frameSize(renderContext->args->_viewport.z, renderContext->args->_viewport.w); - glm::uvec2 scaledFrameSize(glm::vec2(frameSize) * _resolutionScale); - - // Resizing framebuffers instead of re-building them seems to cause issues with threaded - // rendering - if (!_primaryFramebuffer || _primaryFramebuffer->getSize() != scaledFrameSize) { - _primaryFramebuffer = createFramebuffer("deferredPrimary", scaledFrameSize); - } - - primaryFramebuffer = _primaryFramebuffer; - - // Set viewport for the rest of the scaled passes - renderContext->args->_viewport.z = scaledFrameSize.x; - renderContext->args->_viewport.w = scaledFrameSize.y; -} - void PrepareDeferred::run(const RenderContextPointer& renderContext, const Inputs& inputs, Outputs& outputs) { auto args = renderContext->args; diff --git a/libraries/render-utils/src/DeferredLightingEffect.h b/libraries/render-utils/src/DeferredLightingEffect.h index 84b3127443..4779376410 100644 --- a/libraries/render-utils/src/DeferredLightingEffect.h +++ b/libraries/render-utils/src/DeferredLightingEffect.h @@ -74,36 +74,6 @@ private: friend class RenderDeferredCleanup; }; -class PreparePrimaryFramebufferConfig : public render::Job::Config { - Q_OBJECT - Q_PROPERTY(float resolutionScale MEMBER resolutionScale NOTIFY dirty) -public: - - float resolutionScale{ 1.0f }; - -signals: - void dirty(); -}; - -class PreparePrimaryFramebuffer { -public: - - using Output = gpu::FramebufferPointer; - using Config = PreparePrimaryFramebufferConfig; - using JobModel = render::Job::ModelO; - - PreparePrimaryFramebuffer(float resolutionScale = 1.0f) : _resolutionScale{resolutionScale} {} - void configure(const Config& config); - void run(const render::RenderContextPointer& renderContext, Output& primaryFramebuffer); - - gpu::FramebufferPointer _primaryFramebuffer; - float _resolutionScale{ 1.0f }; - -private: - - static gpu::FramebufferPointer createFramebuffer(const char* name, const glm::uvec2& size); -}; - class PrepareDeferred { public: // Inputs: primaryFramebuffer and lightingModel diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index 64a46f3c1e..74cf1ffa39 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -1691,10 +1691,7 @@ public: } }; - -using packBlendshapeOffsetTo = void(glm::uvec4& packed, const BlendshapeOffsetUnpacked& unpacked); - -void packBlendshapeOffsetTo_Pos_F32_3xSN10_Nor_3xSN10_Tan_3xSN10(glm::uvec4& packed, const BlendshapeOffsetUnpacked& unpacked) { +static void packBlendshapeOffsetTo_Pos_F32_3xSN10_Nor_3xSN10_Tan_3xSN10(glm::uvec4& packed, const BlendshapeOffsetUnpacked& unpacked) { float len = glm::compMax(glm::abs(unpacked.positionOffset)); glm::vec3 normalizedPos(unpacked.positionOffset); if (len > 0.0f) { @@ -1711,6 +1708,37 @@ void packBlendshapeOffsetTo_Pos_F32_3xSN10_Nor_3xSN10_Tan_3xSN10(glm::uvec4& pac ); } +static void packBlendshapeOffsets_ref(BlendshapeOffsetUnpacked* unpacked, BlendshapeOffsetPacked* packed, int size) { + for (int i = 0; i < size; ++i) { + packBlendshapeOffsetTo_Pos_F32_3xSN10_Nor_3xSN10_Tan_3xSN10((*packed).packedPosNorTan, (*unpacked)); + ++unpacked; + ++packed; + } +} + +#if defined(_M_IX86) || defined(_M_X64) || defined(__i386__) || defined(__x86_64__) +// +// Runtime CPU dispatch +// +#include + +void packBlendshapeOffsets_AVX2(float (*unpacked)[9], uint32_t (*packed)[4], int size); + +static void packBlendshapeOffsets(BlendshapeOffsetUnpacked* unpacked, BlendshapeOffsetPacked* packed, int size) { + static bool _cpuSupportsAVX2 = cpuSupportsAVX2(); + if (_cpuSupportsAVX2) { + static_assert(sizeof(BlendshapeOffsetUnpacked) == 9 * sizeof(float), "struct BlendshapeOffsetUnpacked size doesn't match."); + static_assert(sizeof(BlendshapeOffsetPacked) == 4 * sizeof(uint32_t), "struct BlendshapeOffsetPacked size doesn't match."); + packBlendshapeOffsets_AVX2((float(*)[9])unpacked, (uint32_t(*)[4])packed, size); + } else { + packBlendshapeOffsets_ref(unpacked, packed, size); + } +} + +#else // portable reference code +static auto& packBlendshapeOffsets = packBlendshapeOffsets_ref; +#endif + class Blender : public QRunnable { public: @@ -1735,21 +1763,28 @@ Blender::Blender(ModelPointer model, HFMModel::ConstPointer hfmModel, int blendN void Blender::run() { DETAILED_PROFILE_RANGE_EX(simulation_animation, __FUNCTION__, 0xFFFF0000, 0, { { "url", _model->getURL().toString() } }); int numBlendshapeOffsets = 0; // number of offsets required for all meshes. + int maxBlendshapeOffsets = 0; // number of offsets in the largest mesh. int numMeshes = 0; // number of meshes in this model. for (auto meshIter = _hfmModel->meshes.cbegin(); meshIter != _hfmModel->meshes.cend(); ++meshIter) { numMeshes++; + if (meshIter->blendshapes.isEmpty()) { + continue; + } int numVertsInMesh = meshIter->vertices.size(); numBlendshapeOffsets += numVertsInMesh; + maxBlendshapeOffsets = std::max(maxBlendshapeOffsets, numVertsInMesh); } - // all elements are default constructed to zero offsets. - QVector packedBlendshapeOffsets(numBlendshapeOffsets); - QVector unpackedBlendshapeOffsets(numBlendshapeOffsets); - - // allocate the required size + // allocate the required sizes QVector blendedMeshSizes; blendedMeshSizes.reserve(numMeshes); + QVector packedBlendshapeOffsets; + packedBlendshapeOffsets.resize(numBlendshapeOffsets); + + QVector unpackedBlendshapeOffsets; + unpackedBlendshapeOffsets.resize(maxBlendshapeOffsets); // reuse for all meshes + int offset = 0; for (auto meshIter = _hfmModel->meshes.cbegin(); meshIter != _hfmModel->meshes.cend(); ++meshIter) { if (meshIter->blendshapes.isEmpty()) { @@ -1759,6 +1794,9 @@ void Blender::run() { int numVertsInMesh = meshIter->vertices.size(); blendedMeshSizes.push_back(numVertsInMesh); + // initialize offsets to zero + memset(unpackedBlendshapeOffsets.data(), 0, numVertsInMesh * sizeof(BlendshapeOffsetUnpacked)); + // for each blendshape in this mesh, accumulate the offsets into unpackedBlendshapeOffsets. const float NORMAL_COEFFICIENT_SCALE = 0.01f; for (int i = 0, n = qMin(_blendshapeCoefficients.size(), meshIter->blendshapes.size()); i < n; i++) { @@ -1773,7 +1811,7 @@ void Blender::run() { for (int j = 0; j < blendshape.indices.size(); ++j) { int index = blendshape.indices.at(j); - auto& currentBlendshapeOffset = unpackedBlendshapeOffsets[offset + index]; + auto& currentBlendshapeOffset = unpackedBlendshapeOffsets[index]; currentBlendshapeOffset.positionOffset += blendshape.vertices.at(j) * vertexCoefficient; currentBlendshapeOffset.normalOffset += blendshape.normals.at(j) * normalCoefficient; if (j < blendshape.tangents.size()) { @@ -1781,20 +1819,15 @@ void Blender::run() { } } } + + // convert unpackedBlendshapeOffsets into packedBlendshapeOffsets for the gpu. + auto unpacked = unpackedBlendshapeOffsets.data(); + auto packed = packedBlendshapeOffsets.data() + offset; + packBlendshapeOffsets(unpacked, packed, numVertsInMesh); + offset += numVertsInMesh; } - - // convert unpackedBlendshapeOffsets into packedBlendshapeOffsets for the gpu. - // FIXME it feels like we could be more effectively using SIMD here - { - auto unpacked = unpackedBlendshapeOffsets.data(); - auto packed = packedBlendshapeOffsets.data(); - for (int i = 0; i < unpackedBlendshapeOffsets.size(); ++i) { - packBlendshapeOffsetTo_Pos_F32_3xSN10_Nor_3xSN10_Tan_3xSN10((*packed).packedPosNorTan, (*unpacked)); - ++unpacked; - ++packed; - } - } + Q_ASSERT(offset == numBlendshapeOffsets); // post the result to the ModelBlender, which will dispatch to the model if still alive QMetaObject::invokeMethod(DependencyManager::get().data(), "setBlendedVertices", diff --git a/libraries/render-utils/src/RenderCommonTask.cpp b/libraries/render-utils/src/RenderCommonTask.cpp index ae53539770..e5de6ccd27 100644 --- a/libraries/render-utils/src/RenderCommonTask.cpp +++ b/libraries/render-utils/src/RenderCommonTask.cpp @@ -106,34 +106,6 @@ void DrawLayered3D::run(const RenderContextPointer& renderContext, const Inputs& } } -void CompositeHUD::run(const RenderContextPointer& renderContext, const gpu::FramebufferPointer& inputs) { - assert(renderContext->args); - assert(renderContext->args->_context); - - // We do not want to render HUD elements in secondary camera - if (nsightActive() || renderContext->args->_renderMode == RenderArgs::RenderMode::SECONDARY_CAMERA_RENDER_MODE) { - return; - } - - // Grab the HUD texture -#if !defined(DISABLE_QML) - gpu::doInBatch("CompositeHUD", renderContext->args->_context, [&](gpu::Batch& batch) { - glm::mat4 projMat; - Transform viewMat; - renderContext->args->getViewFrustum().evalProjectionMatrix(projMat); - renderContext->args->getViewFrustum().evalViewTransform(viewMat); - batch.setProjectionTransform(projMat); - batch.setViewTransform(viewMat, true); - if (inputs) { - batch.setFramebuffer(inputs); - } - if (renderContext->args->_hudOperator) { - renderContext->args->_hudOperator(batch, renderContext->args->_hudTexture, renderContext->args->_renderMode == RenderArgs::RenderMode::MIRROR_RENDER_MODE); - } - }); -#endif -} - void Blit::run(const RenderContextPointer& renderContext, const gpu::FramebufferPointer& srcFramebuffer) { assert(renderContext->args); assert(renderContext->args->_context); @@ -205,6 +177,34 @@ void Blit::run(const RenderContextPointer& renderContext, const gpu::Framebuffer }); } +void NewOrDefaultFramebuffer::run(const render::RenderContextPointer& renderContext, const Input& input, Output& output) { + RenderArgs* args = renderContext->args; + // auto frameSize = input; + glm::uvec2 frameSize(args->_viewport.z, args->_viewport.w); + output.reset(); + + // First if the default Framebuffer is the correct size then use it + auto destBlitFbo = args->_blitFramebuffer; + if (destBlitFbo && destBlitFbo->getSize() == frameSize) { + output = destBlitFbo; + return; + } + + // Else use the lodal Framebuffer + if (_outputFramebuffer && _outputFramebuffer->getSize() != frameSize) { + _outputFramebuffer.reset(); + } + + if (!_outputFramebuffer) { + _outputFramebuffer = gpu::FramebufferPointer(gpu::Framebuffer::create("newFramebuffer.out")); + auto colorFormat = gpu::Element::COLOR_SRGBA_32; + auto defaultSampler = gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_LINEAR); + auto colorTexture = gpu::Texture::createRenderBuffer(colorFormat, frameSize.x, frameSize.y, gpu::Texture::SINGLE_MIP, defaultSampler); + _outputFramebuffer->setRenderBuffer(0, colorTexture); + } + + output = _outputFramebuffer; +} void ResolveFramebuffer::run(const render::RenderContextPointer& renderContext, const Inputs& inputs, Outputs& outputs) { RenderArgs* args = renderContext->args; @@ -235,42 +235,6 @@ void ResolveFramebuffer::run(const render::RenderContextPointer& renderContext, }); } -void ResolveNewFramebuffer::run(const render::RenderContextPointer& renderContext, const Inputs& inputs, Outputs& outputs) { - RenderArgs* args = renderContext->args; - auto srcFbo = inputs; - outputs.reset(); - - // Check valid src - if (!srcFbo) { - return; - } - - // Check valid size for sr and dest - auto frameSize(srcFbo->getSize()); - - // Resizing framebuffers instead of re-building them seems to cause issues with threaded rendering - if (_outputFramebuffer && _outputFramebuffer->getSize() != frameSize) { - _outputFramebuffer.reset(); - } - - if (!_outputFramebuffer) { - _outputFramebuffer = gpu::FramebufferPointer(gpu::Framebuffer::create("resolvedNew.out")); - auto colorFormat = gpu::Element::COLOR_SRGBA_32; - auto defaultSampler = gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_LINEAR); - auto colorTexture = gpu::Texture::createRenderBuffer(colorFormat, frameSize.x, frameSize.y, gpu::Texture::SINGLE_MIP, defaultSampler); - _outputFramebuffer->setRenderBuffer(0, colorTexture); - } - - gpu::Vec4i rectSrc; - rectSrc.z = frameSize.x; - rectSrc.w = frameSize.y; - gpu::doInBatch("ResolveNew", args->_context, [&](gpu::Batch& batch) { batch.blit(srcFbo, rectSrc, _outputFramebuffer, rectSrc); }); - - outputs = _outputFramebuffer; -} - - - void ExtractFrustums::run(const render::RenderContextPointer& renderContext, const Inputs& inputs, Outputs& output) { assert(renderContext->args); assert(renderContext->args->_context); diff --git a/libraries/render-utils/src/RenderCommonTask.h b/libraries/render-utils/src/RenderCommonTask.h index 4f72600d34..756445a30f 100644 --- a/libraries/render-utils/src/RenderCommonTask.h +++ b/libraries/render-utils/src/RenderCommonTask.h @@ -13,6 +13,8 @@ #include "LightStage.h" #include "LightingModel.h" + + class BeginGPURangeTimer { public: using JobModel = render::Job::ModelO; @@ -75,16 +77,6 @@ protected: bool _opaquePass { true }; }; -class CompositeHUD { -public: - // IF specified the input Framebuffer is actively set by the batch of this job before calling the HUDOperator. - // If not, the current Framebuffer is left unchanged. - //using Inputs = gpu::FramebufferPointer; - using JobModel = render::Job::ModelI; - - void run(const render::RenderContextPointer& renderContext, const gpu::FramebufferPointer& inputs); -}; - class Blit { public: using JobModel = render::Job::ModelI; @@ -92,6 +84,16 @@ public: void run(const render::RenderContextPointer& renderContext, const gpu::FramebufferPointer& srcFramebuffer); }; +class NewOrDefaultFramebuffer { +public: + using Input = glm::uvec2; + using Output = gpu::FramebufferPointer; + using JobModel = render::Job::ModelIO; + + void run(const render::RenderContextPointer& renderContext, const Input& input, Output& output); +private: + gpu::FramebufferPointer _outputFramebuffer; +}; class ResolveFramebuffer { public: @@ -102,18 +104,6 @@ public: void run(const render::RenderContextPointer& renderContext, const Inputs& source, Outputs& dest); }; -class ResolveNewFramebuffer { -public: - using Inputs = gpu::FramebufferPointer; - using Outputs = gpu::FramebufferPointer; - using JobModel = render::Job::ModelIO; - - void run(const render::RenderContextPointer& renderContext, const Inputs& source, Outputs& dest); -private: - gpu::FramebufferPointer _outputFramebuffer; -}; - - class ExtractFrustums { public: diff --git a/libraries/render-utils/src/RenderDeferredTask.cpp b/libraries/render-utils/src/RenderDeferredTask.cpp index 624869bbf5..e513fb7282 100644 --- a/libraries/render-utils/src/RenderDeferredTask.cpp +++ b/libraries/render-utils/src/RenderDeferredTask.cpp @@ -47,6 +47,7 @@ #include "FadeEffect.h" #include "BloomStage.h" #include "RenderUtilsLogging.h" +#include "RenderHUDLayerTask.h" #include "AmbientOcclusionEffect.h" #include "AntialiasingEffect.h" @@ -96,11 +97,8 @@ RenderDeferredTask::RenderDeferredTask() void RenderDeferredTask::configure(const Config& config) { // Propagate resolution scale to sub jobs who need it auto preparePrimaryBufferConfig = config.getConfig("PreparePrimaryBuffer"); - auto upsamplePrimaryBufferConfig = config.getConfig("PrimaryBufferUpscale"); assert(preparePrimaryBufferConfig); - assert(upsamplePrimaryBufferConfig); - preparePrimaryBufferConfig->setProperty("resolutionScale", config.resolutionScale); - upsamplePrimaryBufferConfig->setProperty("factor", 1.0f / config.resolutionScale); + preparePrimaryBufferConfig->setResolutionScale(config.resolutionScale); } void RenderDeferredTask::build(JobModel& task, const render::Varying& input, render::Varying& output) { @@ -232,8 +230,6 @@ void RenderDeferredTask::build(JobModel& task, const render::Varying& input, ren task.addJob("DrawInFrontOpaque", inFrontOpaquesInputs, true); task.addJob("DrawInFrontTransparent", inFrontTransparentsInputs, false); - const auto toneAndPostRangeTimer = task.addJob("BeginToneAndPostRangeTimer", "PostToneLayeredAntialiasing"); - // AA job before bloom to limit flickering const auto antialiasingInputs = Antialiasing::Inputs(deferredFrameTransform, lightingFramebuffer, linearDepthTarget, velocityBuffer).asVarying(); task.addJob("Antialiasing", antialiasingInputs); @@ -243,8 +239,8 @@ void RenderDeferredTask::build(JobModel& task, const render::Varying& input, ren task.addJob("Bloom", bloomInputs); // Lighting Buffer ready for tone mapping - const auto toneMappingInputs = ToneMappingDeferred::Inputs(lightingFramebuffer, scaledPrimaryFramebuffer).asVarying(); - task.addJob("ToneMapping", toneMappingInputs); + const auto toneMappingInputs = ToneMappingDeferred::Input(lightingFramebuffer, scaledPrimaryFramebuffer).asVarying(); + const auto toneMappedBuffer = task.addJob("ToneMapping", toneMappingInputs); // Debugging task is happening in the "over" layer after tone mapping and just before HUD { // Debug the bounds of the rendered items, still look at the zbuffer @@ -255,21 +251,11 @@ void RenderDeferredTask::build(JobModel& task, const render::Varying& input, ren } // Upscale to finale resolution - const auto primaryFramebuffer = task.addJob("PrimaryBufferUpscale", scaledPrimaryFramebuffer); + const auto primaryFramebuffer = task.addJob("PrimaryBufferUpscale", toneMappedBuffer); - // Composite the HUD and HUD overlays - task.addJob("HUD", primaryFramebuffer); - - const auto nullJitter = Varying(glm::vec2(0.0f, 0.0f)); - const auto hudOpaquesInputs = DrawLayered3D::Inputs(hudOpaque, lightingModel, nullJitter).asVarying(); - const auto hudTransparentsInputs = DrawLayered3D::Inputs(hudTransparent, lightingModel, nullJitter).asVarying(); - task.addJob("DrawHUDOpaque", hudOpaquesInputs, true); - task.addJob("DrawHUDTransparent", hudTransparentsInputs, false); - - task.addJob("ToneAndPostRangeTimer", toneAndPostRangeTimer); - - // Blit! - task.addJob("Blit", primaryFramebuffer); + // HUD Layer + const auto renderHUDLayerInputs = RenderHUDLayerTask::Input(primaryFramebuffer, lightingModel, hudOpaque, hudTransparent).asVarying(); + task.addJob("RenderHUDLayer", renderHUDLayerInputs); } RenderDeferredTaskDebug::RenderDeferredTaskDebug() { @@ -435,6 +421,44 @@ void RenderDeferredTaskDebug::build(JobModel& task, const render::Varying& input } +gpu::FramebufferPointer PreparePrimaryFramebuffer::createFramebuffer(const char* name, const glm::uvec2& frameSize) { + gpu::FramebufferPointer framebuffer = gpu::FramebufferPointer(gpu::Framebuffer::create(name)); + auto colorFormat = gpu::Element::COLOR_SRGBA_32; + + auto defaultSampler = gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_LINEAR); + auto primaryColorTexture = gpu::Texture::createRenderBuffer(colorFormat, frameSize.x, frameSize.y, gpu::Texture::SINGLE_MIP, defaultSampler); + + framebuffer->setRenderBuffer(0, primaryColorTexture); + + auto depthFormat = gpu::Element(gpu::SCALAR, gpu::UINT32, gpu::DEPTH_STENCIL); // Depth24_Stencil8 texel format + auto primaryDepthTexture = gpu::Texture::createRenderBuffer(depthFormat, frameSize.x, frameSize.y, gpu::Texture::SINGLE_MIP, defaultSampler); + + framebuffer->setDepthStencilBuffer(primaryDepthTexture, depthFormat); + + return framebuffer; +} + +void PreparePrimaryFramebuffer::configure(const Config& config) { + _resolutionScale = config.getResolutionScale(); +} + +void PreparePrimaryFramebuffer::run(const RenderContextPointer& renderContext, Output& primaryFramebuffer) { + glm::uvec2 frameSize(renderContext->args->_viewport.z, renderContext->args->_viewport.w); + glm::uvec2 scaledFrameSize(glm::vec2(frameSize) * _resolutionScale); + + // Resizing framebuffers instead of re-building them seems to cause issues with threaded + // rendering + if (!_primaryFramebuffer || _primaryFramebuffer->getSize() != scaledFrameSize) { + _primaryFramebuffer = createFramebuffer("deferredPrimary", scaledFrameSize); + } + + primaryFramebuffer = _primaryFramebuffer; + + // Set viewport for the rest of the scaled passes + renderContext->args->_viewport.z = scaledFrameSize.x; + renderContext->args->_viewport.w = scaledFrameSize.y; +} + void RenderTransparentDeferred::run(const RenderContextPointer& renderContext, const Inputs& inputs) { assert(renderContext->args); diff --git a/libraries/render-utils/src/RenderDeferredTask.h b/libraries/render-utils/src/RenderDeferredTask.h index 3eb1153928..969094488e 100644 --- a/libraries/render-utils/src/RenderDeferredTask.h +++ b/libraries/render-utils/src/RenderDeferredTask.h @@ -149,4 +149,42 @@ public: private: }; + +class PreparePrimaryFramebufferConfig : public render::Job::Config { + Q_OBJECT + Q_PROPERTY(float resolutionScale WRITE setResolutionScale READ getResolutionScale) +public: + float getResolutionScale() const { return resolutionScale; } + void setResolutionScale(float scale) { + const float SCALE_RANGE_MIN = 0.1f; + const float SCALE_RANGE_MAX = 2.0f; + resolutionScale = std::max(SCALE_RANGE_MIN, std::min(SCALE_RANGE_MAX, scale)); + } + +signals: + void dirty(); + +protected: + float resolutionScale{ 1.0f }; +}; + +class PreparePrimaryFramebuffer { +public: + + using Output = gpu::FramebufferPointer; + using Config = PreparePrimaryFramebufferConfig; + using JobModel = render::Job::ModelO; + + PreparePrimaryFramebuffer(float resolutionScale = 1.0f) : _resolutionScale{ resolutionScale } {} + void configure(const Config& config); + void run(const render::RenderContextPointer& renderContext, Output& primaryFramebuffer); + + gpu::FramebufferPointer _primaryFramebuffer; + float _resolutionScale{ 1.0f }; + +private: + + static gpu::FramebufferPointer createFramebuffer(const char* name, const glm::uvec2& size); +}; + #endif // hifi_RenderDeferredTask_h diff --git a/libraries/render-utils/src/RenderForwardTask.cpp b/libraries/render-utils/src/RenderForwardTask.cpp index de55f3f4ff..d65ad18aa1 100755 --- a/libraries/render-utils/src/RenderForwardTask.cpp +++ b/libraries/render-utils/src/RenderForwardTask.cpp @@ -19,6 +19,7 @@ #include #include #include +#include #include @@ -32,6 +33,7 @@ #include "FramebufferCache.h" #include "TextureCache.h" #include "RenderCommonTask.h" +#include "RenderHUDLayerTask.h" namespace ru { using render_utils::slot::texture::Texture; @@ -47,6 +49,13 @@ using namespace render; extern void initForwardPipelines(ShapePlumber& plumber); +void RenderForwardTask::configure(const Config& config) { + // Propagate resolution scale to sub jobs who need it + auto preparePrimaryBufferConfig = config.getConfig("PreparePrimaryBuffer"); + assert(preparePrimaryBufferConfig); + preparePrimaryBufferConfig->setResolutionScale(config.resolutionScale); +} + void RenderForwardTask::build(JobModel& task, const render::Varying& input, render::Varying& output) { task.addJob("SetRenderMethodTask", render::Args::FORWARD); @@ -87,16 +96,19 @@ void RenderForwardTask::build(JobModel& task, const render::Varying& input, rend // First job, alter faded fadeEffect->build(task, opaques); - // Prepare objects shared by several jobs - const auto deferredFrameTransform = task.addJob("DeferredFrameTransform"); // GPU jobs: Start preparing the main framebuffer - const auto framebuffer = task.addJob("PrepareFramebuffer"); + const auto scaledPrimaryFramebuffer = task.addJob("PreparePrimaryBuffer"); - task.addJob("PrepareForward", lightFrame); + // Prepare deferred, generate the shared Deferred Frame Transform. Only valid with the scaled frame buffer + const auto deferredFrameTransform = task.addJob("DeferredFrameTransform"); + + // Prepare Forward Framebuffer pass + const auto prepareForwardInputs = PrepareForward::Inputs(scaledPrimaryFramebuffer, lightFrame).asVarying(); + task.addJob("PrepareForward", prepareForwardInputs); // draw a stencil mask in hidden regions of the framebuffer. - task.addJob("PrepareStencil", framebuffer); + task.addJob("PrepareStencil", scaledPrimaryFramebuffer); // Draw opaques forward const auto opaqueInputs = DrawForward::Inputs(opaques, lightingModel).asVarying(); @@ -128,94 +140,103 @@ void RenderForwardTask::build(JobModel& task, const render::Varying& input, rend task.addJob("DrawZoneStack", debugZoneInputs); } - // Just resolve the msaa - const auto resolveInputs = - ResolveFramebuffer::Inputs(framebuffer, static_cast(nullptr)).asVarying(); - const auto resolvedFramebuffer = task.addJob("Resolve", resolveInputs); - //auto resolvedFramebuffer = task.addJob("Resolve", framebuffer); - #if defined(Q_OS_ANDROID) + + // Just resolve the msaa + const auto resolveInputs = ResolveFramebuffer::Inputs(scaledPrimaryFramebuffer, static_cast(nullptr)).asVarying(); + const auto resolvedFramebuffer = task.addJob("Resolve", resolveInputs); + + const auto toneMappedBuffer = resolvedFramebuffer; #else + const auto newResolvedFramebuffer = task.addJob("MakeResolvingFramebuffer"); + + + // Just resolve the msaa + const auto resolveInputs = ResolveFramebuffer::Inputs(scaledPrimaryFramebuffer, newResolvedFramebuffer).asVarying(); + const auto resolvedFramebuffer = task.addJob("Resolve", resolveInputs); + // Lighting Buffer ready for tone mapping // Forward rendering on GLES doesn't support tonemapping to and from the same FBO, so we specify // the output FBO as null, which causes the tonemapping to target the blit framebuffer - const auto toneMappingInputs = ToneMappingDeferred::Inputs(resolvedFramebuffer, static_cast(nullptr)).asVarying(); - task.addJob("ToneMapping", toneMappingInputs); + const auto toneMappingInputs = ToneMappingDeferred::Input(resolvedFramebuffer, resolvedFramebuffer).asVarying(); + const auto toneMappedBuffer = task.addJob("ToneMapping", toneMappingInputs); + #endif - // Layered Overlays - // Composite the HUD and HUD overlays - task.addJob("HUD", resolvedFramebuffer); + // Upscale to finale resolution + const auto primaryFramebuffer = task.addJob("PrimaryBufferUpscale", toneMappedBuffer); - const auto hudOpaquesInputs = DrawLayered3D::Inputs(hudOpaque, lightingModel, nullJitter).asVarying(); - const auto hudTransparentsInputs = DrawLayered3D::Inputs(hudTransparent, lightingModel, nullJitter).asVarying(); - task.addJob("DrawHUDOpaque", hudOpaquesInputs, true); - task.addJob("DrawHUDTransparent", hudTransparentsInputs, false); - - // Disable blit because we do tonemapping and compositing directly to the blit FBO - // Blit! - // task.addJob("Blit", framebuffer); + // HUD Layer + const auto renderHUDLayerInputs = RenderHUDLayerTask::Input(primaryFramebuffer, lightingModel, hudOpaque, hudTransparent).asVarying(); + task.addJob("RenderHUDLayer", renderHUDLayerInputs); } -void PrepareFramebuffer::configure(const Config& config) { +gpu::FramebufferPointer PreparePrimaryFramebufferMSAA::createFramebuffer(const char* name, const glm::uvec2& frameSize, int numSamples) { + gpu::FramebufferPointer framebuffer = gpu::FramebufferPointer(gpu::Framebuffer::create(name)); + + auto defaultSampler = gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_LINEAR); + + auto colorFormat = gpu::Element::COLOR_SRGBA_32; + auto colorTexture = + gpu::Texture::createRenderBufferMultisample(colorFormat, frameSize.x, frameSize.y, numSamples, defaultSampler); + framebuffer->setRenderBuffer(0, colorTexture); + + auto depthFormat = gpu::Element(gpu::SCALAR, gpu::UINT32, gpu::DEPTH_STENCIL); // Depth24_Stencil8 texel format + auto depthTexture = + gpu::Texture::createRenderBufferMultisample(depthFormat, frameSize.x, frameSize.y, numSamples, defaultSampler); + framebuffer->setDepthStencilBuffer(depthTexture, depthFormat); + + return framebuffer; +} + +void PreparePrimaryFramebufferMSAA::configure(const Config& config) { + _resolutionScale = config.getResolutionScale(); _numSamples = config.getNumSamples(); } -void PrepareFramebuffer::run(const RenderContextPointer& renderContext, gpu::FramebufferPointer& framebuffer) { +void PreparePrimaryFramebufferMSAA::run(const RenderContextPointer& renderContext, gpu::FramebufferPointer& framebuffer) { glm::uvec2 frameSize(renderContext->args->_viewport.z, renderContext->args->_viewport.w); + glm::uvec2 scaledFrameSize(glm::vec2(frameSize) * _resolutionScale); // Resizing framebuffers instead of re-building them seems to cause issues with threaded rendering - if (_framebuffer && (_framebuffer->getSize() != frameSize || _framebuffer->getNumSamples() != _numSamples)) { - _framebuffer.reset(); + if (!_framebuffer || (_framebuffer->getSize() != scaledFrameSize) || (_framebuffer->getNumSamples() != _numSamples)) { + _framebuffer = createFramebuffer("forward", scaledFrameSize, _numSamples); } - if (!_framebuffer) { - _framebuffer = gpu::FramebufferPointer(gpu::Framebuffer::create("forward")); - - int numSamples = _numSamples; - - auto colorFormat = gpu::Element::COLOR_SRGBA_32; - auto defaultSampler = gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_LINEAR); - auto colorTexture = - gpu::Texture::createRenderBufferMultisample(colorFormat, frameSize.x, frameSize.y, numSamples, defaultSampler); - _framebuffer->setRenderBuffer(0, colorTexture); - - auto depthFormat = gpu::Element(gpu::SCALAR, gpu::UINT32, gpu::DEPTH_STENCIL); // Depth24_Stencil8 texel format - auto depthTexture = - gpu::Texture::createRenderBufferMultisample(depthFormat, frameSize.x, frameSize.y, numSamples, defaultSampler); - _framebuffer->setDepthStencilBuffer(depthTexture, depthFormat); - } - - auto args = renderContext->args; - gpu::doInBatch("PrepareFramebuffer::run", args->_context, [&](gpu::Batch& batch) { - batch.enableStereo(false); - batch.setViewportTransform(args->_viewport); - batch.setStateScissorRect(args->_viewport); - - batch.setFramebuffer(_framebuffer); - batch.clearFramebuffer(gpu::Framebuffer::BUFFER_COLOR0 | gpu::Framebuffer::BUFFER_DEPTH | - gpu::Framebuffer::BUFFER_STENCIL, - vec4(vec3(0), 0), 1.0, 0, true); - }); - framebuffer = _framebuffer; + + // Set viewport for the rest of the scaled passes + renderContext->args->_viewport.z = scaledFrameSize.x; + renderContext->args->_viewport.w = scaledFrameSize.y; } void PrepareForward::run(const RenderContextPointer& renderContext, const Inputs& inputs) { RenderArgs* args = renderContext->args; + auto primaryFramebuffer = inputs.get0(); + auto lightStageFrame = inputs.get1(); + gpu::doInBatch("RenderForward::Draw::run", args->_context, [&](gpu::Batch& batch) { args->_batch = &batch; + batch.enableStereo(false); + batch.setViewportTransform(args->_viewport); + batch.setStateScissorRect(args->_viewport); + + batch.setFramebuffer(primaryFramebuffer); + batch.clearFramebuffer(gpu::Framebuffer::BUFFER_COLOR0 | gpu::Framebuffer::BUFFER_DEPTH | + gpu::Framebuffer::BUFFER_STENCIL, + vec4(vec3(0), 0), 1.0, 0, true); + graphics::LightPointer keySunLight; auto lightStage = args->_scene->getStage(); if (lightStage) { - keySunLight = lightStage->getCurrentKeyLight(*inputs); + keySunLight = lightStage->getCurrentKeyLight(*lightStageFrame); } graphics::LightPointer keyAmbiLight; if (lightStage) { - keyAmbiLight = lightStage->getCurrentAmbientLight(*inputs); + keyAmbiLight = lightStage->getCurrentAmbientLight(*lightStageFrame); } if (keySunLight) { diff --git a/libraries/render-utils/src/RenderForwardTask.h b/libraries/render-utils/src/RenderForwardTask.h index 40d004ddb2..baf7f66c6c 100755 --- a/libraries/render-utils/src/RenderForwardTask.h +++ b/libraries/render-utils/src/RenderForwardTask.h @@ -17,39 +17,59 @@ #include "AssembleLightingStageTask.h" #include "LightingModel.h" +class RenderForwardTaskConfig : public render::Task::Config { + Q_OBJECT + Q_PROPERTY(float resolutionScale MEMBER resolutionScale NOTIFY dirty) +public: + float resolutionScale{ 1.f }; + +signals: + void dirty(); +}; + class RenderForwardTask { public: using Input = render::VaryingSet3; - using JobModel = render::Task::ModelI; + using Config = RenderForwardTaskConfig; + using JobModel = render::Task::ModelI; RenderForwardTask() {} + void configure(const Config& config); void build(JobModel& task, const render::Varying& input, render::Varying& output); }; -class PrepareFramebufferConfig : public render::Job::Config { +class PreparePrimaryFramebufferMSAAConfig : public render::Job::Config { Q_OBJECT - Q_PROPERTY(int numSamples WRITE setNumSamples READ getNumSamples NOTIFY dirty) + Q_PROPERTY(float resolutionScale WRITE setResolutionScale READ getResolutionScale) + Q_PROPERTY(int numSamples WRITE setNumSamples READ getNumSamples) public: + float getResolutionScale() const { return resolutionScale; } + void setResolutionScale(float scale) { + const float SCALE_RANGE_MIN = 0.1f; + const float SCALE_RANGE_MAX = 2.0f; + resolutionScale = std::max(SCALE_RANGE_MIN, std::min(SCALE_RANGE_MAX, scale)); + } + int getNumSamples() const { return numSamples; } void setNumSamples(int num) { numSamples = std::max(1, std::min(32, num)); - emit dirty(); } signals: void dirty(); protected: + float resolutionScale{ 1.0f }; int numSamples{ 4 }; }; -class PrepareFramebuffer { +class PreparePrimaryFramebufferMSAA { public: - using Inputs = gpu::FramebufferPointer; - using Config = PrepareFramebufferConfig; - using JobModel = render::Job::ModelO; + using Output = gpu::FramebufferPointer; + using Config = PreparePrimaryFramebufferMSAAConfig; + using JobModel = render::Job::ModelO; void configure(const Config& config); void run(const render::RenderContextPointer& renderContext, @@ -57,12 +77,15 @@ public: private: gpu::FramebufferPointer _framebuffer; + float _resolutionScale{ 1.0f }; int _numSamples; + + static gpu::FramebufferPointer createFramebuffer(const char* name, const glm::uvec2& frameSize, int numSamples); }; class PrepareForward { public: - using Inputs = LightStage::FramePointer; + using Inputs = render::VaryingSet2 ; using JobModel = render::Job::ModelI; void run(const render::RenderContextPointer& renderContext, diff --git a/libraries/render-utils/src/RenderHUDLayerTask.cpp b/libraries/render-utils/src/RenderHUDLayerTask.cpp new file mode 100644 index 0000000000..ac7a867366 --- /dev/null +++ b/libraries/render-utils/src/RenderHUDLayerTask.cpp @@ -0,0 +1,60 @@ +// +// Created by Sam Gateau on 2019/06/14 +// Copyright 2013-2019 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// +#include "RenderHUDLayerTask.h" + +#include +#include "RenderCommonTask.h" + +using namespace render; + +void CompositeHUD::run(const RenderContextPointer& renderContext, const gpu::FramebufferPointer& inputs) { + assert(renderContext->args); + assert(renderContext->args->_context); + + // We do not want to render HUD elements in secondary camera + if (nsightActive() || renderContext->args->_renderMode == RenderArgs::RenderMode::SECONDARY_CAMERA_RENDER_MODE) { + return; + } + + // Grab the HUD texture +#if !defined(DISABLE_QML) + gpu::doInBatch("CompositeHUD", renderContext->args->_context, [&](gpu::Batch& batch) { + glm::mat4 projMat; + Transform viewMat; + renderContext->args->getViewFrustum().evalProjectionMatrix(projMat); + renderContext->args->getViewFrustum().evalViewTransform(viewMat); + batch.setProjectionTransform(projMat); + batch.setViewTransform(viewMat, true); + if (inputs) { + batch.setFramebuffer(inputs); + } + if (renderContext->args->_hudOperator) { + renderContext->args->_hudOperator(batch, renderContext->args->_hudTexture, renderContext->args->_renderMode == RenderArgs::RenderMode::MIRROR_RENDER_MODE); + } + }); +#endif +} + +void RenderHUDLayerTask::build(JobModel& task, const render::Varying& input, render::Varying& output) { + const auto& inputs = input.get(); + + const auto& primaryFramebuffer = inputs[0]; + const auto& lightingModel = inputs[1]; + const auto& hudOpaque = inputs[2]; + const auto& hudTransparent = inputs[3]; + + // Composite the HUD and HUD overlays + task.addJob("HUD", primaryFramebuffer); + + // And HUD Layer objects + const auto nullJitter = Varying(glm::vec2(0.0f, 0.0f)); + const auto hudOpaquesInputs = DrawLayered3D::Inputs(hudOpaque, lightingModel, nullJitter).asVarying(); + const auto hudTransparentsInputs = DrawLayered3D::Inputs(hudTransparent, lightingModel, nullJitter).asVarying(); + task.addJob("DrawHUDOpaque", hudOpaquesInputs, true); + task.addJob("DrawHUDTransparent", hudTransparentsInputs, false); +} diff --git a/libraries/render-utils/src/RenderHUDLayerTask.h b/libraries/render-utils/src/RenderHUDLayerTask.h new file mode 100644 index 0000000000..78cd009636 --- /dev/null +++ b/libraries/render-utils/src/RenderHUDLayerTask.h @@ -0,0 +1,34 @@ +// +// Created by Sam Gateau on 2019/06/14 +// Copyright 2013-2019 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_RenderHUDLayerTask_h +#define hifi_RenderHUDLayerTask_h + +#include "LightingModel.h" + + +class CompositeHUD { +public: + // IF specified the input Framebuffer is actively set by the batch of this job before calling the HUDOperator. + // If not, the current Framebuffer is left unchanged. + //using Inputs = gpu::FramebufferPointer; + using JobModel = render::Job::ModelI; + + void run(const render::RenderContextPointer& renderContext, const gpu::FramebufferPointer& inputs); +}; + +class RenderHUDLayerTask { +public: + // Framebuffer where to draw, lighting model, opaque items, transparent items + using Input = render::VaryingSet4; + using JobModel = render::Task::ModelI; + + void build(JobModel& task, const render::Varying& input, render::Varying& output); +}; + +#endif // hifi_RenderHUDLayerTask_h \ No newline at end of file diff --git a/libraries/render-utils/src/ToneMappingEffect.cpp b/libraries/render-utils/src/ToneMappingEffect.cpp index 64a2adb5d4..b7cc5d3d80 100644 --- a/libraries/render-utils/src/ToneMappingEffect.cpp +++ b/libraries/render-utils/src/ToneMappingEffect.cpp @@ -47,14 +47,13 @@ void ToneMappingEffect::setToneCurve(ToneCurve curve) { } } -void ToneMappingEffect::render(RenderArgs* args, const gpu::TexturePointer& lightingBuffer, const gpu::FramebufferPointer& requestedDestinationFramebuffer) { +void ToneMappingEffect::render(RenderArgs* args, const gpu::TexturePointer& lightingBuffer, const gpu::FramebufferPointer& destinationFramebuffer) { if (!_blitLightBuffer) { init(args); } - - auto destinationFramebuffer = requestedDestinationFramebuffer; - if (!destinationFramebuffer) { - destinationFramebuffer = args->_blitFramebuffer; + + if (!lightingBuffer || !destinationFramebuffer) { + return; } auto framebufferSize = glm::ivec2(lightingBuffer->getDimensions()); @@ -83,9 +82,15 @@ void ToneMappingDeferred::configure(const Config& config) { _toneMappingEffect.setToneCurve((ToneMappingEffect::ToneCurve)config.curve); } -void ToneMappingDeferred::run(const render::RenderContextPointer& renderContext, const Inputs& inputs) { +void ToneMappingDeferred::run(const render::RenderContextPointer& renderContext, const Input& input, Output& output) { + + auto lightingBuffer = input.get0()->getRenderBuffer(0); + auto destFbo = input.get1(); + + if (!destFbo) { + destFbo = renderContext->args->_blitFramebuffer; + } - auto lightingBuffer = inputs.get0()->getRenderBuffer(0); - auto destFbo = inputs.get1(); _toneMappingEffect.render(renderContext->args, lightingBuffer, destFbo); + output = destFbo; } diff --git a/libraries/render-utils/src/ToneMappingEffect.h b/libraries/render-utils/src/ToneMappingEffect.h index 69694b13f5..faf6e514e9 100644 --- a/libraries/render-utils/src/ToneMappingEffect.h +++ b/libraries/render-utils/src/ToneMappingEffect.h @@ -82,12 +82,13 @@ signals: class ToneMappingDeferred { public: // Inputs: lightingFramebuffer, destinationFramebuffer - using Inputs = render::VaryingSet2; + using Input = render::VaryingSet2; + using Output = gpu::FramebufferPointer; using Config = ToneMappingConfig; - using JobModel = render::Job::ModelI; + using JobModel = render::Job::ModelIO; void configure(const Config& config); - void run(const render::RenderContextPointer& renderContext, const Inputs& inputs); + void run(const render::RenderContextPointer& renderContext, const Input& input, Output& output); ToneMappingEffect _toneMappingEffect; }; diff --git a/libraries/render/src/render/ResampleTask.cpp b/libraries/render/src/render/ResampleTask.cpp index ed4d0ddfd0..3e9bfec8da 100644 --- a/libraries/render/src/render/ResampleTask.cpp +++ b/libraries/render/src/render/ResampleTask.cpp @@ -135,3 +135,43 @@ void Upsample::run(const RenderContextPointer& renderContext, const gpu::Framebu args->_viewport = viewport; } } + +gpu::PipelinePointer UpsampleToBlitFramebuffer::_pipeline; + +void UpsampleToBlitFramebuffer::run(const RenderContextPointer& renderContext, const Input& input, gpu::FramebufferPointer& resampledFrameBuffer) { + assert(renderContext->args); + assert(renderContext->args->hasViewFrustum()); + RenderArgs* args = renderContext->args; + auto sourceFramebuffer = input; + + resampledFrameBuffer = args->_blitFramebuffer; + + if (resampledFrameBuffer != sourceFramebuffer) { + if (!_pipeline) { + gpu::ShaderPointer program = gpu::Shader::createProgram(shader::gpu::program::drawTransformUnitQuadTextureOpaque); + gpu::StatePointer state = gpu::StatePointer(new gpu::State()); + state->setDepthTest(gpu::State::DepthTest(false, false)); + _pipeline = gpu::Pipeline::create(program, state); + } + const auto bufferSize = resampledFrameBuffer->getSize(); + glm::ivec4 viewport{ 0, 0, bufferSize.x, bufferSize.y }; + + gpu::doInBatch("Upsample::run", args->_context, [&](gpu::Batch& batch) { + batch.enableStereo(false); + + batch.setFramebuffer(resampledFrameBuffer); + + batch.setViewportTransform(viewport); + batch.setProjectionTransform(glm::mat4()); + batch.resetViewTransform(); + batch.setPipeline(_pipeline); + + batch.setModelTransform(gpu::Framebuffer::evalSubregionTexcoordTransform(bufferSize, viewport)); + batch.setResourceTexture(0, sourceFramebuffer->getRenderBuffer(0)); + batch.draw(gpu::TRIANGLE_STRIP, 4); + }); + + // Set full final viewport + args->_viewport = viewport; + } +} diff --git a/libraries/render/src/render/ResampleTask.h b/libraries/render/src/render/ResampleTask.h index 25f9c6a3e9..e62b76e6d0 100644 --- a/libraries/render/src/render/ResampleTask.h +++ b/libraries/render/src/render/ResampleTask.h @@ -67,6 +67,20 @@ namespace render { gpu::FramebufferPointer getResampledFrameBuffer(const gpu::FramebufferPointer& sourceFramebuffer); }; + + class UpsampleToBlitFramebuffer { + public: + using Input = gpu::FramebufferPointer; + using JobModel = Job::ModelIO; + + UpsampleToBlitFramebuffer() {} + + void run(const RenderContextPointer& renderContext, const Input& input, gpu::FramebufferPointer& resampledFrameBuffer); + + protected: + + static gpu::PipelinePointer _pipeline; + }; } #endif // hifi_render_ResampleTask_h diff --git a/libraries/shared/src/PrioritySortUtil.h b/libraries/shared/src/PrioritySortUtil.h index d6a740231c..e4a258a065 100644 --- a/libraries/shared/src/PrioritySortUtil.h +++ b/libraries/shared/src/PrioritySortUtil.h @@ -45,7 +45,7 @@ namespace PrioritySortUtil { class PriorityQueue { public: PriorityQueue() = delete; - PriorityQueue(const ConicalViewFrustums& views) : _views(views) { } + PriorityQueue(const ConicalViewFrustums& views) : _views(views), _usecCurrentTime(usecTimestampNow()) { } PriorityQueue(const ConicalViewFrustums& views, float angularWeight, float centerWeight, float ageWeight) : _views(views), _angularWeight(angularWeight), _centerWeight(centerWeight), _ageWeight(ageWeight) , _usecCurrentTime(usecTimestampNow()) { diff --git a/libraries/shared/src/avx2/BlendshapePacking_avx2.cpp b/libraries/shared/src/avx2/BlendshapePacking_avx2.cpp new file mode 100644 index 0000000000..1fea60a315 --- /dev/null +++ b/libraries/shared/src/avx2/BlendshapePacking_avx2.cpp @@ -0,0 +1,285 @@ +// +// BlendshapePacking_avx2.cpp +// +// Created by Ken Cooke on 6/22/19. +// Copyright 2019 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifdef __AVX2__ + +#include +#include + +void packBlendshapeOffsets_AVX2(float (*unpacked)[9], uint32_t (*packed)[4], int size) { + + int i = 0; + for (; i < size - 7; i += 8) { // blocks of 8 + + // + // deinterleave (8x9 to 9x8 matrix transpose) + // + __m256 s0 = _mm256_insertf128_ps(_mm256_castps128_ps256(_mm_loadu_ps(&unpacked[i+0][0])), _mm_loadu_ps(&unpacked[i+4][0]), 1); + __m256 s1 = _mm256_insertf128_ps(_mm256_castps128_ps256(_mm_loadu_ps(&unpacked[i+1][0])), _mm_loadu_ps(&unpacked[i+5][0]), 1); + __m256 s2 = _mm256_insertf128_ps(_mm256_castps128_ps256(_mm_loadu_ps(&unpacked[i+2][0])), _mm_loadu_ps(&unpacked[i+6][0]), 1); + __m256 s3 = _mm256_insertf128_ps(_mm256_castps128_ps256(_mm_loadu_ps(&unpacked[i+3][0])), _mm_loadu_ps(&unpacked[i+7][0]), 1); + __m256 s4 = _mm256_insertf128_ps(_mm256_castps128_ps256(_mm_loadu_ps(&unpacked[i+0][4])), _mm_loadu_ps(&unpacked[i+4][4]), 1); + __m256 s5 = _mm256_insertf128_ps(_mm256_castps128_ps256(_mm_loadu_ps(&unpacked[i+1][4])), _mm_loadu_ps(&unpacked[i+5][4]), 1); + __m256 s6 = _mm256_insertf128_ps(_mm256_castps128_ps256(_mm_loadu_ps(&unpacked[i+2][4])), _mm_loadu_ps(&unpacked[i+6][4]), 1); + __m256 s7 = _mm256_insertf128_ps(_mm256_castps128_ps256(_mm_loadu_ps(&unpacked[i+3][4])), _mm_loadu_ps(&unpacked[i+7][4]), 1); + + __m256 t0 = _mm256_unpacklo_ps(s0, s1); + __m256 t1 = _mm256_unpackhi_ps(s0, s1); + __m256 t2 = _mm256_unpacklo_ps(s2, s3); + __m256 t3 = _mm256_unpackhi_ps(s2, s3); + __m256 t4 = _mm256_unpacklo_ps(s4, s5); + __m256 t5 = _mm256_unpackhi_ps(s4, s5); + __m256 t6 = _mm256_unpacklo_ps(s6, s7); + __m256 t7 = _mm256_unpackhi_ps(s6, s7); + + __m256 px = _mm256_shuffle_ps(t0, t2, _MM_SHUFFLE(1,0,1,0)); + __m256 py = _mm256_shuffle_ps(t0, t2, _MM_SHUFFLE(3,2,3,2)); + __m256 pz = _mm256_shuffle_ps(t1, t3, _MM_SHUFFLE(1,0,1,0)); + __m256 nx = _mm256_shuffle_ps(t1, t3, _MM_SHUFFLE(3,2,3,2)); + __m256 ny = _mm256_shuffle_ps(t4, t6, _MM_SHUFFLE(1,0,1,0)); + __m256 nz = _mm256_shuffle_ps(t4, t6, _MM_SHUFFLE(3,2,3,2)); + __m256 tx = _mm256_shuffle_ps(t5, t7, _MM_SHUFFLE(1,0,1,0)); + __m256 ty = _mm256_shuffle_ps(t5, t7, _MM_SHUFFLE(3,2,3,2)); + + __m256 tz = _mm256_i32gather_ps(unpacked[i+0], _mm256_setr_epi32(8,17,26,35,44,53,62,71), sizeof(float)); + + // abs(pos) + __m256 apx = _mm256_andnot_ps(_mm256_set1_ps(-0.0f), px); + __m256 apy = _mm256_andnot_ps(_mm256_set1_ps(-0.0f), py); + __m256 apz = _mm256_andnot_ps(_mm256_set1_ps(-0.0f), pz); + + // len = compMax(abs(pos)) + __m256 len = _mm256_max_ps(_mm256_max_ps(apx, apy), apz); + + // detect zeros + __m256 mask = _mm256_cmp_ps(len, _mm256_setzero_ps(), _CMP_EQ_OQ); + + // rcp = 1.0f / len + __m256 rcp = _mm256_div_ps(_mm256_set1_ps(1.0f), len); + + // replace +inf with 1.0f + rcp = _mm256_blendv_ps(rcp, _mm256_set1_ps(1.0f), mask); + len = _mm256_blendv_ps(len, _mm256_set1_ps(1.0f), mask); + + // pos *= 1.0f / len + px = _mm256_mul_ps(px, rcp); + py = _mm256_mul_ps(py, rcp); + pz = _mm256_mul_ps(pz, rcp); + + // clamp(vec, -1.0f, 1.0f) + px = _mm256_min_ps(_mm256_max_ps(px, _mm256_set1_ps(-1.0f)), _mm256_set1_ps(1.0f)); + py = _mm256_min_ps(_mm256_max_ps(py, _mm256_set1_ps(-1.0f)), _mm256_set1_ps(1.0f)); + pz = _mm256_min_ps(_mm256_max_ps(pz, _mm256_set1_ps(-1.0f)), _mm256_set1_ps(1.0f)); + nx = _mm256_min_ps(_mm256_max_ps(nx, _mm256_set1_ps(-1.0f)), _mm256_set1_ps(1.0f)); + ny = _mm256_min_ps(_mm256_max_ps(ny, _mm256_set1_ps(-1.0f)), _mm256_set1_ps(1.0f)); + nz = _mm256_min_ps(_mm256_max_ps(nz, _mm256_set1_ps(-1.0f)), _mm256_set1_ps(1.0f)); + tx = _mm256_min_ps(_mm256_max_ps(tx, _mm256_set1_ps(-1.0f)), _mm256_set1_ps(1.0f)); + ty = _mm256_min_ps(_mm256_max_ps(ty, _mm256_set1_ps(-1.0f)), _mm256_set1_ps(1.0f)); + tz = _mm256_min_ps(_mm256_max_ps(tz, _mm256_set1_ps(-1.0f)), _mm256_set1_ps(1.0f)); + + // vec *= 511.0f + px = _mm256_mul_ps(px, _mm256_set1_ps(511.0f)); + py = _mm256_mul_ps(py, _mm256_set1_ps(511.0f)); + pz = _mm256_mul_ps(pz, _mm256_set1_ps(511.0f)); + nx = _mm256_mul_ps(nx, _mm256_set1_ps(511.0f)); + ny = _mm256_mul_ps(ny, _mm256_set1_ps(511.0f)); + nz = _mm256_mul_ps(nz, _mm256_set1_ps(511.0f)); + tx = _mm256_mul_ps(tx, _mm256_set1_ps(511.0f)); + ty = _mm256_mul_ps(ty, _mm256_set1_ps(511.0f)); + tz = _mm256_mul_ps(tz, _mm256_set1_ps(511.0f)); + + // veci = lrint(vec) & 03ff + __m256i pxi = _mm256_and_si256(_mm256_cvtps_epi32(px), _mm256_set1_epi32(0x3ff)); + __m256i pyi = _mm256_and_si256(_mm256_cvtps_epi32(py), _mm256_set1_epi32(0x3ff)); + __m256i pzi = _mm256_and_si256(_mm256_cvtps_epi32(pz), _mm256_set1_epi32(0x3ff)); + __m256i nxi = _mm256_and_si256(_mm256_cvtps_epi32(nx), _mm256_set1_epi32(0x3ff)); + __m256i nyi = _mm256_and_si256(_mm256_cvtps_epi32(ny), _mm256_set1_epi32(0x3ff)); + __m256i nzi = _mm256_and_si256(_mm256_cvtps_epi32(nz), _mm256_set1_epi32(0x3ff)); + __m256i txi = _mm256_and_si256(_mm256_cvtps_epi32(tx), _mm256_set1_epi32(0x3ff)); + __m256i tyi = _mm256_and_si256(_mm256_cvtps_epi32(ty), _mm256_set1_epi32(0x3ff)); + __m256i tzi = _mm256_and_si256(_mm256_cvtps_epi32(tz), _mm256_set1_epi32(0x3ff)); + + // pack = (xi << 0) | (yi << 10) | (zi << 20); + __m256i li = _mm256_castps_si256(len); // length + __m256i pi = _mm256_or_si256(_mm256_or_si256(pxi, _mm256_slli_epi32(pyi, 10)), _mm256_slli_epi32(pzi, 20)); // position + __m256i ni = _mm256_or_si256(_mm256_or_si256(nxi, _mm256_slli_epi32(nyi, 10)), _mm256_slli_epi32(nzi, 20)); // normal + __m256i ti = _mm256_or_si256(_mm256_or_si256(txi, _mm256_slli_epi32(tyi, 10)), _mm256_slli_epi32(tzi, 20)); // tangent + + // + // interleave (4x4 matrix transpose) + // + __m256i u0 = _mm256_unpacklo_epi32(li, pi); + __m256i u1 = _mm256_unpackhi_epi32(li, pi); + __m256i u2 = _mm256_unpacklo_epi32(ni, ti); + __m256i u3 = _mm256_unpackhi_epi32(ni, ti); + + __m256i v0 = _mm256_unpacklo_epi64(u0, u2); + __m256i v1 = _mm256_unpackhi_epi64(u0, u2); + __m256i v2 = _mm256_unpacklo_epi64(u1, u3); + __m256i v3 = _mm256_unpackhi_epi64(u1, u3); + + __m256i w0 = _mm256_permute2f128_si256(v0, v1, 0x20); + __m256i w1 = _mm256_permute2f128_si256(v2, v3, 0x20); + __m256i w2 = _mm256_permute2f128_si256(v0, v1, 0x31); + __m256i w3 = _mm256_permute2f128_si256(v2, v3, 0x31); + + // store pack x 8 + _mm256_storeu_si256((__m256i*)packed[i+0], w0); + _mm256_storeu_si256((__m256i*)packed[i+2], w1); + _mm256_storeu_si256((__m256i*)packed[i+4], w2); + _mm256_storeu_si256((__m256i*)packed[i+6], w3); + } + + if (i < size) { // remainder + int rem = size - i; + + // + // deinterleave (8x9 to 9x8 matrix transpose) + // + __m256 s0 = _mm256_setzero_ps(); + __m256 s1 = _mm256_setzero_ps(); + __m256 s2 = _mm256_setzero_ps(); + __m256 s3 = _mm256_setzero_ps(); + __m256 s4 = _mm256_setzero_ps(); + __m256 s5 = _mm256_setzero_ps(); + __m256 s6 = _mm256_setzero_ps(); + __m256 s7 = _mm256_setzero_ps(); + + switch (rem) { + case 7: s6 = _mm256_loadu_ps(unpacked[i+6]); + case 6: s5 = _mm256_loadu_ps(unpacked[i+5]); + case 5: s4 = _mm256_loadu_ps(unpacked[i+4]); + case 4: s3 = _mm256_loadu_ps(unpacked[i+3]); + case 3: s2 = _mm256_loadu_ps(unpacked[i+2]); + case 2: s1 = _mm256_loadu_ps(unpacked[i+1]); + case 1: s0 = _mm256_loadu_ps(unpacked[i+0]); + } + + __m256 t0 = _mm256_unpacklo_ps(s0, s1); + __m256 t1 = _mm256_unpackhi_ps(s0, s1); + __m256 t2 = _mm256_unpacklo_ps(s2, s3); + __m256 t3 = _mm256_unpackhi_ps(s2, s3); + __m256 t4 = _mm256_unpacklo_ps(s4, s5); + __m256 t5 = _mm256_unpackhi_ps(s4, s5); + __m256 t6 = _mm256_unpacklo_ps(s6, s7); + __m256 t7 = _mm256_unpackhi_ps(s6, s7); + + s0 = _mm256_shuffle_ps(t0, t2, _MM_SHUFFLE(1,0,1,0)); + s1 = _mm256_shuffle_ps(t0, t2, _MM_SHUFFLE(3,2,3,2)); + s2 = _mm256_shuffle_ps(t1, t3, _MM_SHUFFLE(1,0,1,0)); + s3 = _mm256_shuffle_ps(t1, t3, _MM_SHUFFLE(3,2,3,2)); + s4 = _mm256_shuffle_ps(t4, t6, _MM_SHUFFLE(1,0,1,0)); + s5 = _mm256_shuffle_ps(t4, t6, _MM_SHUFFLE(3,2,3,2)); + s6 = _mm256_shuffle_ps(t5, t7, _MM_SHUFFLE(1,0,1,0)); + s7 = _mm256_shuffle_ps(t5, t7, _MM_SHUFFLE(3,2,3,2)); + + __m256 px = _mm256_permute2f128_ps(s0, s4, 0x20); + __m256 py = _mm256_permute2f128_ps(s1, s5, 0x20); + __m256 pz = _mm256_permute2f128_ps(s2, s6, 0x20); + __m256 nx = _mm256_permute2f128_ps(s3, s7, 0x20); + __m256 ny = _mm256_permute2f128_ps(s0, s4, 0x31); + __m256 nz = _mm256_permute2f128_ps(s1, s5, 0x31); + __m256 tx = _mm256_permute2f128_ps(s2, s6, 0x31); + __m256 ty = _mm256_permute2f128_ps(s3, s7, 0x31); + + __m256i loadmask = _mm256_cvtepi8_epi32(_mm_cvtsi64_si128(0xffffffffffffffffULL >> (64 - 8 * rem))); + __m256 tz = _mm256_mask_i32gather_ps(_mm256_setzero_ps(), unpacked[i+0], _mm256_setr_epi32(8,17,26,35,44,53,62,71), + _mm256_castsi256_ps(loadmask), sizeof(float)); + // abs(pos) + __m256 apx = _mm256_andnot_ps(_mm256_set1_ps(-0.0f), px); + __m256 apy = _mm256_andnot_ps(_mm256_set1_ps(-0.0f), py); + __m256 apz = _mm256_andnot_ps(_mm256_set1_ps(-0.0f), pz); + + // len = compMax(abs(pos)) + __m256 len = _mm256_max_ps(_mm256_max_ps(apx, apy), apz); + + // detect zeros + __m256 mask = _mm256_cmp_ps(len, _mm256_setzero_ps(), _CMP_EQ_OQ); + + // rcp = 1.0f / len + __m256 rcp = _mm256_div_ps(_mm256_set1_ps(1.0f), len); + + // replace +inf with 1.0f + rcp = _mm256_blendv_ps(rcp, _mm256_set1_ps(1.0f), mask); + len = _mm256_blendv_ps(len, _mm256_set1_ps(1.0f), mask); + + // pos *= 1.0f / len + px = _mm256_mul_ps(px, rcp); + py = _mm256_mul_ps(py, rcp); + pz = _mm256_mul_ps(pz, rcp); + + // clamp(vec, -1.0f, 1.0f) + px = _mm256_min_ps(_mm256_max_ps(px, _mm256_set1_ps(-1.0f)), _mm256_set1_ps(1.0f)); + py = _mm256_min_ps(_mm256_max_ps(py, _mm256_set1_ps(-1.0f)), _mm256_set1_ps(1.0f)); + pz = _mm256_min_ps(_mm256_max_ps(pz, _mm256_set1_ps(-1.0f)), _mm256_set1_ps(1.0f)); + nx = _mm256_min_ps(_mm256_max_ps(nx, _mm256_set1_ps(-1.0f)), _mm256_set1_ps(1.0f)); + ny = _mm256_min_ps(_mm256_max_ps(ny, _mm256_set1_ps(-1.0f)), _mm256_set1_ps(1.0f)); + nz = _mm256_min_ps(_mm256_max_ps(nz, _mm256_set1_ps(-1.0f)), _mm256_set1_ps(1.0f)); + tx = _mm256_min_ps(_mm256_max_ps(tx, _mm256_set1_ps(-1.0f)), _mm256_set1_ps(1.0f)); + ty = _mm256_min_ps(_mm256_max_ps(ty, _mm256_set1_ps(-1.0f)), _mm256_set1_ps(1.0f)); + tz = _mm256_min_ps(_mm256_max_ps(tz, _mm256_set1_ps(-1.0f)), _mm256_set1_ps(1.0f)); + + // vec *= 511.0f + px = _mm256_mul_ps(px, _mm256_set1_ps(511.0f)); + py = _mm256_mul_ps(py, _mm256_set1_ps(511.0f)); + pz = _mm256_mul_ps(pz, _mm256_set1_ps(511.0f)); + nx = _mm256_mul_ps(nx, _mm256_set1_ps(511.0f)); + ny = _mm256_mul_ps(ny, _mm256_set1_ps(511.0f)); + nz = _mm256_mul_ps(nz, _mm256_set1_ps(511.0f)); + tx = _mm256_mul_ps(tx, _mm256_set1_ps(511.0f)); + ty = _mm256_mul_ps(ty, _mm256_set1_ps(511.0f)); + tz = _mm256_mul_ps(tz, _mm256_set1_ps(511.0f)); + + // veci = lrint(vec) & 03ff + __m256i pxi = _mm256_and_si256(_mm256_cvtps_epi32(px), _mm256_set1_epi32(0x3ff)); + __m256i pyi = _mm256_and_si256(_mm256_cvtps_epi32(py), _mm256_set1_epi32(0x3ff)); + __m256i pzi = _mm256_and_si256(_mm256_cvtps_epi32(pz), _mm256_set1_epi32(0x3ff)); + __m256i nxi = _mm256_and_si256(_mm256_cvtps_epi32(nx), _mm256_set1_epi32(0x3ff)); + __m256i nyi = _mm256_and_si256(_mm256_cvtps_epi32(ny), _mm256_set1_epi32(0x3ff)); + __m256i nzi = _mm256_and_si256(_mm256_cvtps_epi32(nz), _mm256_set1_epi32(0x3ff)); + __m256i txi = _mm256_and_si256(_mm256_cvtps_epi32(tx), _mm256_set1_epi32(0x3ff)); + __m256i tyi = _mm256_and_si256(_mm256_cvtps_epi32(ty), _mm256_set1_epi32(0x3ff)); + __m256i tzi = _mm256_and_si256(_mm256_cvtps_epi32(tz), _mm256_set1_epi32(0x3ff)); + + // pack = (xi << 0) | (yi << 10) | (zi << 20); + __m256i li = _mm256_castps_si256(len); // length + __m256i pi = _mm256_or_si256(_mm256_or_si256(pxi, _mm256_slli_epi32(pyi, 10)), _mm256_slli_epi32(pzi, 20)); // position + __m256i ni = _mm256_or_si256(_mm256_or_si256(nxi, _mm256_slli_epi32(nyi, 10)), _mm256_slli_epi32(nzi, 20)); // normal + __m256i ti = _mm256_or_si256(_mm256_or_si256(txi, _mm256_slli_epi32(tyi, 10)), _mm256_slli_epi32(tzi, 20)); // tangent + + // + // interleave (4x4 matrix transpose) + // + __m256i u0 = _mm256_unpacklo_epi32(li, pi); + __m256i u1 = _mm256_unpackhi_epi32(li, pi); + __m256i u2 = _mm256_unpacklo_epi32(ni, ti); + __m256i u3 = _mm256_unpackhi_epi32(ni, ti); + + __m256i v0 = _mm256_unpacklo_epi64(u0, u2); + __m256i v1 = _mm256_unpackhi_epi64(u0, u2); + __m256i v2 = _mm256_unpacklo_epi64(u1, u3); + __m256i v3 = _mm256_unpackhi_epi64(u1, u3); + + // store pack x 8 + switch (rem) { + case 7: _mm_storeu_si128((__m128i*)packed[i+6], _mm256_extractf128_si256(v2, 1)); + case 6: _mm_storeu_si128((__m128i*)packed[i+5], _mm256_extractf128_si256(v1, 1)); + case 5: _mm_storeu_si128((__m128i*)packed[i+4], _mm256_extractf128_si256(v0, 1)); + case 4: _mm_storeu_si128((__m128i*)packed[i+3], _mm256_castsi256_si128(v3)); + case 3: _mm_storeu_si128((__m128i*)packed[i+2], _mm256_castsi256_si128(v2)); + case 2: _mm_storeu_si128((__m128i*)packed[i+1], _mm256_castsi256_si128(v1)); + case 1: _mm_storeu_si128((__m128i*)packed[i+0], _mm256_castsi256_si128(v0)); + } + } + + _mm256_zeroupper(); +} + +#endif diff --git a/plugins/oculus/src/OculusDisplayPlugin.cpp b/plugins/oculus/src/OculusDisplayPlugin.cpp index c493588992..161266c4af 100644 --- a/plugins/oculus/src/OculusDisplayPlugin.cpp +++ b/plugins/oculus/src/OculusDisplayPlugin.cpp @@ -124,10 +124,6 @@ void OculusDisplayPlugin::uncustomizeContext() { Parent::uncustomizeContext(); } -gpu::PipelinePointer OculusDisplayPlugin::getCompositeScenePipeline() { - return _SRGBToLinearPipeline; -} - static const uint64_t FRAME_BUDGET = (11 * USECS_PER_MSEC); static const uint64_t FRAME_OVER_BUDGET = (15 * USECS_PER_MSEC); @@ -167,7 +163,7 @@ void OculusDisplayPlugin::hmdPresent() { batch.setStateScissorRect(ivec4(uvec2(), _outputFramebuffer->getSize())); batch.resetViewTransform(); batch.setProjectionTransform(mat4()); - batch.setPipeline(_drawTexturePipeline); + batch.setPipeline(_SRGBToLinearPipeline); batch.setResourceTexture(0, _compositeFramebuffer->getRenderBuffer(0)); batch.draw(gpu::TRIANGLE_STRIP, 4); }); diff --git a/plugins/oculus/src/OculusDisplayPlugin.h b/plugins/oculus/src/OculusDisplayPlugin.h index 8eda599fa9..9209fd373e 100644 --- a/plugins/oculus/src/OculusDisplayPlugin.h +++ b/plugins/oculus/src/OculusDisplayPlugin.h @@ -34,8 +34,6 @@ protected: void uncustomizeContext() override; void cycleDebugOutput() override; - virtual gpu::PipelinePointer getCompositeScenePipeline() override; - private: static const char* NAME; ovrTextureSwapChain _textureSwapChain; diff --git a/script-archive/light_modifier/README.md b/script-archive/light_modifier/README.md index f23bd25dda..d78fe3ef41 100644 --- a/script-archive/light_modifier/README.md +++ b/script-archive/light_modifier/README.md @@ -18,7 +18,7 @@ When you run the lightLoader.js script, several scripts will be loaded: - visiblePanel.js (the transparent panel) - closeButton.js (for closing the ui) - ../libraries/lightOverlayManager.js (shows 2d overlays for lights in the world) -- ../libraries/entitySelectionTool.js (visualizes volume of the lights) +- ../../scripts/system/create/entitySelectionTool/entitySelectionTool.js (visualizes volume of the lights) Current sliders are (top to bottom): red diff --git a/script-archive/light_modifier/lightModifier.js b/script-archive/light_modifier/lightModifier.js index 0c691e2b2e..35cdfb95ce 100644 --- a/script-archive/light_modifier/lightModifier.js +++ b/script-archive/light_modifier/lightModifier.js @@ -33,8 +33,8 @@ var PARENT_SCRIPT_URL = Script.resolvePath('lightParent.js?' + Math.random(0 - 1 if (SHOW_OVERLAYS === true) { - Script.include('../libraries/gridTool.js'); - Script.include('../libraries/entitySelectionTool.js?' + Math.random(0 - 100)); + Script.include('../../scripts/system/libraries/gridTool.js'); + Script.include('../../scripts/system/create/entitySelectionTool/entitySelectionTool.js?' + Math.random(0 - 100)); Script.include('../libraries/lightOverlayManager.js'); var grid = Grid(); diff --git a/scripts/defaultScripts.js b/scripts/defaultScripts.js index e392680df9..607ee3165b 100644 --- a/scripts/defaultScripts.js +++ b/scripts/defaultScripts.js @@ -27,7 +27,7 @@ var DEFAULT_SCRIPTS_COMBINED = [ "system/marketplaces/marketplaces.js", "system/notifications.js", "system/commerce/wallet.js", - "system/edit.js", + "system/create/edit.js", "system/dialTone.js", "system/firstPersonHMD.js", "system/tablet-ui/tabletUI.js", @@ -36,7 +36,7 @@ var DEFAULT_SCRIPTS_COMBINED = [ "system/audioMuteOverlay.js" ]; var DEFAULT_SCRIPTS_SEPARATE = [ - "system/controllers/controllerScripts.js", + "system/controllers/controllerScripts.js" //"system/chat.js" ]; diff --git a/scripts/developer/utilities/lib/prop/PropGroup.qml b/scripts/developer/utilities/lib/prop/PropGroup.qml index 22facb71c6..848ff8b288 100644 --- a/scripts/developer/utilities/lib/prop/PropGroup.qml +++ b/scripts/developer/utilities/lib/prop/PropGroup.qml @@ -17,6 +17,8 @@ PropFolderPanel { Global { id: global } id: root + property var rootObject: {} + property alias propItemsPanel: root.panelFrameContent // Prop Group is designed to author an array of ProItems, they are defined with an array of the tuplets describing each individual item: @@ -79,12 +81,24 @@ PropFolderPanel { }) } break; case 'object': { - var component = Qt.createComponent("PropItem.qml"); + console.log('Item is an object, create PropGroup: ' + JSON.stringify(proItem.object[proItem.property])); + var itemRootObject = proItem.object[proItem.property]; + var itemLabel = proItem.property; + var itemDepth = root.indentDepth + 1; + if (Array.isArray(itemRootObject)) { + if (objectItem.length > 1) { + itemLabel = itemLabel + " " + objectItem.length + } else { + itemLabel = itemLabel + " " + objectItem.length + itemRootObject = itemRootObject[0]; + } + } + var component = Qt.createComponent("PropGroup.qml"); component.createObject(propItemsContainer, { - "label": proItem.property, - "object": proItem.object, - "property": proItem.property, - }) + "label": itemLabel, + "rootObject":itemRootObject, + "indentDepth": itemDepth, + }) } break; case 'printLabel': { var component = Qt.createComponent("PropItem.qml"); @@ -110,19 +124,46 @@ PropFolderPanel { function populateFromObjectProps(object) { var propsModel = [] - var props = Object.keys(object); + if (Array.isArray(object)) { + if (object.length <= 1) { + object = object[0]; + } + } + + var props = Object.keys(object); for (var p in props) { var o = {}; o["object"] = object o["property"] = props[p]; // o["readOnly"] = true; - o["type"] = "string"; - propsModel.push(o) + + var thePropThing = object[props[p]]; + if ((thePropThing !== undefined) && (thePropThing !== null)) { + var theType = typeof(thePropThing) + switch(theType) { + case 'object': { + o["type"] = "object"; + propsModel.push(o) + } break; + default: { + o["type"] = "string"; + propsModel.push(o) + } break; + } + + } else { + o["type"] = "string"; + propsModel.push(o) + } } + root.updatePropItems(root.propItemsPanel, propsModel); } Component.onCompleted: { + if (root.rootObject !== null) { + populateFromObjectProps(root.rootObject) + } } } diff --git a/scripts/developer/utilities/lib/prop/PropScalar.qml b/scripts/developer/utilities/lib/prop/PropScalar.qml index 33e1bfc958..ae86c6ef81 100644 --- a/scripts/developer/utilities/lib/prop/PropScalar.qml +++ b/scripts/developer/utilities/lib/prop/PropScalar.qml @@ -20,17 +20,22 @@ PropItem { property bool integral: false property var numDigits: 2 - property alias valueVar : sliderControl.value + property alias min: sliderControl.minimumValue property alias max: sliderControl.maximumValue property bool showValue: true - signal valueChanged(real value) - Component.onCompleted: { - valueVar = root.valueVarGetter(); - } + } + + property var sourceValueVar: root.valueVarGetter() + + function applyValueVarFromWidgets(value) { + if (!root.readOnly) { + root.valueVarSetter(value) + } + } PropLabel { id: valueLabel @@ -42,7 +47,7 @@ PropItem { horizontalAlignment: global.valueTextAlign height: global.slimHeight - text: root.valueVarGetter().toFixed(root.integral ? 0 : root.numDigits) + text: root.sourceValueVar.toFixed(root.integral ? 0 : root.numDigits) background: Rectangle { color: global.color @@ -59,8 +64,8 @@ PropItem { anchors.left: valueLabel.right anchors.right: root.right anchors.verticalCenter: root.verticalCenter - - onValueChanged: { if (!root.readOnly) { root.valueVarSetter(value)} } + value: root.sourceValueVar + onValueChanged: { applyValueVarFromWidgets(value) } } diff --git a/scripts/developer/utilities/render/luci/Platform.qml b/scripts/developer/utilities/render/luci/Platform.qml index 9c5ffda070..eaa4766b32 100644 --- a/scripts/developer/utilities/render/luci/Platform.qml +++ b/scripts/developer/utilities/render/luci/Platform.qml @@ -23,46 +23,31 @@ Column { id: computer label: "Computer" isUnfold: true - - Component.onCompleted: { - computer.populateFromObjectProps(JSON.parse(PlatformInfo.getComputer())) - } + rootObject:JSON.parse(PlatformInfo.getComputer()) } Prop.PropGroup { id: cpu label: "CPU" isUnfold: true - - Component.onCompleted: { - cpu.populateFromObjectProps(JSON.parse(PlatformInfo.getCPU(0))) - } + rootObject:JSON.parse(PlatformInfo.getPlatform()).cpus } Prop.PropGroup { id: memory label: "Memory" isUnfold: true - - Component.onCompleted: { - memory.populateFromObjectProps(JSON.parse(PlatformInfo.getMemory())) - } + rootObject:JSON.parse(PlatformInfo.getMemory()) } Prop.PropGroup { id: gpu label: "GPU" isUnfold: true - - Component.onCompleted: { - gpu.populateFromObjectProps(JSON.parse(PlatformInfo.getGPU(0))) - } + rootObject:JSON.parse(PlatformInfo.getPlatform()).gpus } Prop.PropGroup { id: display label: "Display" isUnfold: true - - Component.onCompleted: { - display.populateFromObjectProps(JSON.parse(PlatformInfo.getDisplay(0))) - } + rootObject:JSON.parse(PlatformInfo.getPlatform()).displays } } diff --git a/scripts/developer/utilities/render/luci/RenderSettings.qml b/scripts/developer/utilities/render/luci/RenderSettings.qml index 906c117b3a..bd76964070 100644 --- a/scripts/developer/utilities/render/luci/RenderSettings.qml +++ b/scripts/developer/utilities/render/luci/RenderSettings.qml @@ -30,5 +30,12 @@ Column { object: Render property: "shadowsEnabled" } + Prop.PropScalar { + label: "Viewport Resolution Scale" + object: Render + property: "viewportResolutionScale" + min: 0.25 + max: 1.5 + } } diff --git a/scripts/simplifiedUI/system/html/entityProperties.html b/scripts/simplifiedUI/system/html/entityProperties.html deleted file mode 100644 index 67f03a33a2..0000000000 --- a/scripts/simplifiedUI/system/html/entityProperties.html +++ /dev/null @@ -1,51 +0,0 @@ - - - - - Properties - - - - - - - - - - - - - - - - - - - -
-
- -
-
-
-
-
-
-
-
-
-
-
- -
- - - diff --git a/scripts/simplifiedUI/system/libraries/EditEntityList.qml b/scripts/simplifiedUI/system/libraries/EditEntityList.qml deleted file mode 100644 index 4fc5ff19ef..0000000000 --- a/scripts/simplifiedUI/system/libraries/EditEntityList.qml +++ /dev/null @@ -1,12 +0,0 @@ -import QtQuick 2.7 -import QtQuick.Controls 2.2 -import QtWebChannel 1.0 -import QtGraphicalEffects 1.0 -import "qrc:///qml/controls" as HifiControls - -HifiControls.WebView { - id: entityListToolWebView - url: Qt.resolvedUrl("../html/entityList.html") - enabled: true - blurOnCtrlShift: false -} diff --git a/scripts/simplifiedUI/ui/simplifiedNametag/resources/modules/nameTagListManager.js b/scripts/simplifiedUI/ui/simplifiedNametag/resources/modules/nameTagListManager.js index 2ba9904845..de783f8661 100644 --- a/scripts/simplifiedUI/ui/simplifiedNametag/resources/modules/nameTagListManager.js +++ b/scripts/simplifiedUI/ui/simplifiedNametag/resources/modules/nameTagListManager.js @@ -19,7 +19,8 @@ var HALF = 0.5; var CLEAR_ENTITY_EDIT_PROPS = true; var MILISECONDS_IN_SECOND = 1000; var SECONDS_IN_MINUTE = 60; -var ALWAYS_ON_MAX_LIFETIME_IN_SECONDS = 20 * SECONDS_IN_MINUTE; // Delete after 20 minutes in case a nametag is hanging around in on mode +// Delete after 5 minutes in case a nametag is hanging around in on mode +var ALWAYS_ON_MAX_LIFETIME_IN_SECONDS = 5 * SECONDS_IN_MINUTE; // ************************************* // START UTILTY @@ -377,6 +378,11 @@ function maybeAddOrRemoveIntervalCheck() { add(avatar); return; } + if (avatar in _this.avatars) { + var entity = _this.avatars[avatar].nametag; + var age = entity.get("age"); + entity.edit('lifetime', age + ALWAYS_ON_MAX_LIFETIME_IN_SECONDS); + } if (avatarDistance > MAX_RADIUS_IGNORE_METERS) { maybeRemove(avatar); } diff --git a/scripts/system/assets/images/icon-particles.svg b/scripts/system/assets/images/icon-particles.svg deleted file mode 100644 index 5e0105d7cd..0000000000 --- a/scripts/system/assets/images/icon-particles.svg +++ /dev/null @@ -1,29 +0,0 @@ - - - - - - - - - - - - - - - - diff --git a/scripts/system/assets/images/icon-point-light.svg b/scripts/system/assets/images/icon-point-light.svg deleted file mode 100644 index 896c35b63b..0000000000 --- a/scripts/system/assets/images/icon-point-light.svg +++ /dev/null @@ -1,57 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/scripts/system/assets/images/icon-spot-light.svg b/scripts/system/assets/images/icon-spot-light.svg deleted file mode 100644 index ac2f87bb27..0000000000 --- a/scripts/system/assets/images/icon-spot-light.svg +++ /dev/null @@ -1,37 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/scripts/system/assets/images/icon-zone.svg b/scripts/system/assets/images/icon-zone.svg deleted file mode 100644 index 41aeac4951..0000000000 --- a/scripts/system/assets/images/icon-zone.svg +++ /dev/null @@ -1,73 +0,0 @@ - - - -image/svg+xml \ No newline at end of file diff --git a/scripts/system/assets/data/createAppTooltips.json b/scripts/system/create/assets/data/createAppTooltips.json similarity index 100% rename from scripts/system/assets/data/createAppTooltips.json rename to scripts/system/create/assets/data/createAppTooltips.json diff --git a/scripts/simplifiedUI/system/assets/images/hourglass.svg b/scripts/system/create/assets/images/hourglass.svg similarity index 100% rename from scripts/simplifiedUI/system/assets/images/hourglass.svg rename to scripts/system/create/assets/images/hourglass.svg diff --git a/scripts/simplifiedUI/system/assets/images/icon-particles.svg b/scripts/system/create/assets/images/icon-particles.svg similarity index 100% rename from scripts/simplifiedUI/system/assets/images/icon-particles.svg rename to scripts/system/create/assets/images/icon-particles.svg diff --git a/scripts/simplifiedUI/system/assets/images/icon-point-light.svg b/scripts/system/create/assets/images/icon-point-light.svg similarity index 100% rename from scripts/simplifiedUI/system/assets/images/icon-point-light.svg rename to scripts/system/create/assets/images/icon-point-light.svg diff --git a/scripts/simplifiedUI/system/assets/images/icon-spot-light.svg b/scripts/system/create/assets/images/icon-spot-light.svg similarity index 100% rename from scripts/simplifiedUI/system/assets/images/icon-spot-light.svg rename to scripts/system/create/assets/images/icon-spot-light.svg diff --git a/scripts/simplifiedUI/system/assets/images/icon-zone.svg b/scripts/system/create/assets/images/icon-zone.svg similarity index 100% rename from scripts/simplifiedUI/system/assets/images/icon-zone.svg rename to scripts/system/create/assets/images/icon-zone.svg diff --git a/scripts/simplifiedUI/system/edit.js b/scripts/system/create/edit.js similarity index 99% rename from scripts/simplifiedUI/system/edit.js rename to scripts/system/create/edit.js index cf99f3a618..f50bc547e5 100644 --- a/scripts/simplifiedUI/system/edit.js +++ b/scripts/system/create/edit.js @@ -24,16 +24,16 @@ var EDIT_TOGGLE_BUTTON = "com.highfidelity.interface.system.editButton"; var CONTROLLER_MAPPING_NAME = "com.highfidelity.editMode"; Script.include([ - "libraries/stringHelpers.js", - "libraries/dataViewHelpers.js", - "libraries/progressDialog.js", - "libraries/entitySelectionTool.js", - "libraries/ToolTip.js", - "libraries/entityCameraTool.js", - "libraries/gridTool.js", - "libraries/entityList.js", - "libraries/utils.js", - "libraries/entityIconOverlayManager.js" + "../libraries/stringHelpers.js", + "../libraries/dataViewHelpers.js", + "../libraries/progressDialog.js", + "../libraries/ToolTip.js", + "../libraries/entityCameraTool.js", + "../libraries/utils.js", + "../libraries/entityIconOverlayManager.js", + "../libraries/gridTool.js", + "entityList/entityList.js", + "entitySelectionTool/entitySelectionTool.js" ]); var CreateWindow = Script.require('./modules/createWindow.js'); @@ -45,7 +45,7 @@ var MAX_DEFAULT_ENTITY_LIST_HEIGHT = 942; var DEFAULT_IMAGE = "https://hifi-content.s3.amazonaws.com/DomainContent/production/no-image.jpg"; var createToolsWindow = new CreateWindow( - Script.resolvePath("create/EditTools.qml"), + Script.resolvePath("qml/EditTools.qml"), 'Create Tools', 'com.highfidelity.create.createToolsWindow', function () { @@ -174,7 +174,7 @@ var IMPORTING_SVO_OVERLAY_HEIGHT = 30; var IMPORTING_SVO_OVERLAY_MARGIN = 5; var IMPORTING_SVO_OVERLAY_LEFT_MARGIN = 34; var importingSVOImageOverlay = Overlays.addOverlay("image", { - imageURL: Script.resolvePath("assets") + "/images/hourglass.svg", + imageURL: Script.resolvePath("assets/images/hourglass.svg"), width: 20, height: 20, alpha: 1.0, @@ -812,7 +812,7 @@ var toolBar = (function () { tablet.screenChanged.connect(function (type, url) { var isGoingToHomescreenOnDesktop = (!shouldUseEditTabletApp() && (url === 'hifi/tablet/TabletHome.qml' || url === '')); - if (isActive && (type !== "QML" || url !== Script.resolvePath("create/Edit.qml")) && !isGoingToHomescreenOnDesktop) { + if (isActive && (type !== "QML" || url !== Script.resolvePath("qml/Edit.qml")) && !isGoingToHomescreenOnDesktop) { that.setActive(false); } }); @@ -841,10 +841,10 @@ var toolBar = (function () { if (shouldUseEditTabletApp()) { // tablet version of new-model dialog var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system"); - tablet.pushOntoStack(Script.resolvePath("create/New" + entityType + "Dialog.qml")); + tablet.pushOntoStack(Script.resolvePath("qml/New" + entityType + "Dialog.qml")); } else { closeExistingDialogWindow(); - var qmlPath = Script.resolvePath("create/New" + entityType + "Window.qml"); + var qmlPath = Script.resolvePath("qml/New" + entityType + "Window.qml"); var DIALOG_WINDOW_SIZE = { x: 500, y: 300 }; dialogWindow = Desktop.createWindow(qmlPath, { title: "New " + entityType + " Entity", @@ -965,7 +965,7 @@ var toolBar = (function () { Controller.disableMapping(CONTROLLER_MAPPING_NAME); } else { if (shouldUseEditTabletApp()) { - tablet.loadQMLSource(Script.resolvePath("create/Edit.qml"), true); + tablet.loadQMLSource(Script.resolvePath("qml/Edit.qml"), true); } else { // make other apps inactive while in desktop mode tablet.gotoHomeScreen(); diff --git a/scripts/simplifiedUI/system/libraries/entityList.js b/scripts/system/create/entityList/entityList.js similarity index 99% rename from scripts/simplifiedUI/system/libraries/entityList.js rename to scripts/system/create/entityList/entityList.js index 585e4e06a5..06e100f457 100644 --- a/scripts/simplifiedUI/system/libraries/entityList.js +++ b/scripts/system/create/entityList/entityList.js @@ -37,7 +37,7 @@ EntityListTool = function(shouldUseEditTabletApp) { var ENTITY_LIST_WIDTH = 495; var MAX_DEFAULT_CREATE_TOOLS_HEIGHT = 778; var entityListWindow = new CreateWindow( - Script.resolvePath("../create/EditEntityList.qml"), + Script.resolvePath("./qml/EditEntityList.qml"), 'Entity List', 'com.highfidelity.create.entityListWindow', function () { diff --git a/scripts/simplifiedUI/system/html/entityList.html b/scripts/system/create/entityList/html/entityList.html similarity index 89% rename from scripts/simplifiedUI/system/html/entityList.html rename to scripts/system/create/entityList/html/entityList.html index 986e5c09b0..3e17a66df5 100644 --- a/scripts/simplifiedUI/system/html/entityList.html +++ b/scripts/system/create/entityList/html/entityList.html @@ -11,15 +11,15 @@ Entity List - + - - - + + + + + - - diff --git a/scripts/system/html/js/entityList.js b/scripts/system/create/entityList/html/js/entityList.js similarity index 100% rename from scripts/system/html/js/entityList.js rename to scripts/system/create/entityList/html/js/entityList.js diff --git a/scripts/system/html/js/entityListContextMenu.js b/scripts/system/create/entityList/html/js/entityListContextMenu.js similarity index 100% rename from scripts/system/html/js/entityListContextMenu.js rename to scripts/system/create/entityList/html/js/entityListContextMenu.js diff --git a/scripts/system/html/js/listView.js b/scripts/system/create/entityList/html/js/listView.js similarity index 100% rename from scripts/system/html/js/listView.js rename to scripts/system/create/entityList/html/js/listView.js diff --git a/scripts/system/create/EditEntityList.qml b/scripts/system/create/entityList/qml/EditEntityList.qml similarity index 82% rename from scripts/system/create/EditEntityList.qml rename to scripts/system/create/entityList/qml/EditEntityList.qml index 94935c7bb5..1d5beb9914 100644 --- a/scripts/system/create/EditEntityList.qml +++ b/scripts/system/create/entityList/qml/EditEntityList.qml @@ -10,7 +10,7 @@ import stylesUit 1.0 WebView { id: entityListToolWebView - url: Paths.defaultScripts + "/system/html/entityList.html" + url: Qt.resolvedUrl("../html/entityList.html") enabled: true blurOnCtrlShift: false } diff --git a/scripts/system/create/EntityList.qml b/scripts/system/create/entityList/qml/EntityList.qml similarity index 58% rename from scripts/system/create/EntityList.qml rename to scripts/system/create/entityList/qml/EntityList.qml index 2f8a8863be..a70fc6d15d 100644 --- a/scripts/system/create/EntityList.qml +++ b/scripts/system/create/entityList/qml/EntityList.qml @@ -1,6 +1,6 @@ WebView { id: entityListToolWebView - url: Paths.defaultScripts + "/system/html/entityList.html" + url: QT.resolvedURL("../html/entityList.html") enabled: true blurOnCtrlShift: false } diff --git a/scripts/system/html/entityProperties.html b/scripts/system/create/entityProperties/html/entityProperties.html similarity index 65% rename from scripts/system/html/entityProperties.html rename to scripts/system/create/entityProperties/html/entityProperties.html index 67f03a33a2..876e75ec35 100644 --- a/scripts/system/html/entityProperties.html +++ b/scripts/system/create/entityProperties/html/entityProperties.html @@ -12,21 +12,22 @@ Properties - - - - - + + + - - + + + + + + + - - - + diff --git a/scripts/simplifiedUI/system/html/js/createAppTooltip.js b/scripts/system/create/entityProperties/html/js/createAppTooltip.js similarity index 100% rename from scripts/simplifiedUI/system/html/js/createAppTooltip.js rename to scripts/system/create/entityProperties/html/js/createAppTooltip.js diff --git a/scripts/system/html/js/draggableNumber.js b/scripts/system/create/entityProperties/html/js/draggableNumber.js similarity index 100% rename from scripts/system/html/js/draggableNumber.js rename to scripts/system/create/entityProperties/html/js/draggableNumber.js diff --git a/scripts/system/html/js/entityProperties.js b/scripts/system/create/entityProperties/html/js/entityProperties.js similarity index 100% rename from scripts/system/html/js/entityProperties.js rename to scripts/system/create/entityProperties/html/js/entityProperties.js diff --git a/scripts/system/html/js/underscore-min.js b/scripts/system/create/entityProperties/html/js/underscore-min.js similarity index 100% rename from scripts/system/html/js/underscore-min.js rename to scripts/system/create/entityProperties/html/js/underscore-min.js diff --git a/scripts/simplifiedUI/system/libraries/entitySelectionTool.js b/scripts/system/create/entitySelectionTool/entitySelectionTool.js similarity index 99% rename from scripts/simplifiedUI/system/libraries/entitySelectionTool.js rename to scripts/system/create/entitySelectionTool/entitySelectionTool.js index 3fdc1d6652..9c993d6d73 100644 --- a/scripts/simplifiedUI/system/libraries/entitySelectionTool.js +++ b/scripts/system/create/entitySelectionTool/entitySelectionTool.js @@ -21,9 +21,9 @@ const SPACE_WORLD = "world"; const HIGHLIGHT_LIST_NAME = "editHandleHighlightList"; Script.include([ - "./controllers.js", - "./controllerDispatcherUtils.js", - "./utils.js" + "../../libraries/controllers.js", + "../../libraries/controllerDispatcherUtils.js", + "../../libraries/utils.js" ]); diff --git a/scripts/system/modules/createWindow.js b/scripts/system/create/modules/createWindow.js similarity index 100% rename from scripts/system/modules/createWindow.js rename to scripts/system/create/modules/createWindow.js diff --git a/scripts/system/modules/entityShapeVisualizer.js b/scripts/system/create/modules/entityShapeVisualizer.js similarity index 98% rename from scripts/system/modules/entityShapeVisualizer.js rename to scripts/system/create/modules/entityShapeVisualizer.js index da28369cdd..dbf09a1cb7 100644 --- a/scripts/system/modules/entityShapeVisualizer.js +++ b/scripts/system/create/modules/entityShapeVisualizer.js @@ -146,8 +146,8 @@ EntityShape.prototype = { parentID: this.entity, priority: 1, materialMappingMode: PROJECTED_MATERIALS ? "projected" : "uv", - materialURL: Script.resolvePath("../assets/images/materials/GridPattern.json"), - ignorePickIntersection: true, + materialURL: Script.resolvePath("../../assets/images/materials/GridPattern.json"), + ignorePickIntersection: true }, "local"); }, update: function() { diff --git a/scripts/system/create/Edit.qml b/scripts/system/create/qml/Edit.qml similarity index 100% rename from scripts/system/create/Edit.qml rename to scripts/system/create/qml/Edit.qml diff --git a/scripts/system/create/EditTabButton.qml b/scripts/system/create/qml/EditTabButton.qml similarity index 100% rename from scripts/system/create/EditTabButton.qml rename to scripts/system/create/qml/EditTabButton.qml diff --git a/scripts/system/create/EditTabView.qml b/scripts/system/create/qml/EditTabView.qml similarity index 93% rename from scripts/system/create/EditTabView.qml rename to scripts/system/create/qml/EditTabView.qml index 7e8789487c..f90a962f7a 100644 --- a/scripts/system/create/EditTabView.qml +++ b/scripts/system/create/qml/EditTabView.qml @@ -72,7 +72,7 @@ TabBar { NewEntityButton { - icon: "create-icons/94-model-01.svg" + icon: "icons/94-model-01.svg" text: "MODEL" onClicked: { editRoot.sendToScript({ @@ -84,7 +84,7 @@ TabBar { } NewEntityButton { - icon: "create-icons/21-cube-01.svg" + icon: "icons/21-cube-01.svg" text: "SHAPE" onClicked: { editRoot.sendToScript({ @@ -96,7 +96,7 @@ TabBar { } NewEntityButton { - icon: "create-icons/24-light-01.svg" + icon: "icons/24-light-01.svg" text: "LIGHT" onClicked: { editRoot.sendToScript({ @@ -108,7 +108,7 @@ TabBar { } NewEntityButton { - icon: "create-icons/20-text-01.svg" + icon: "icons/20-text-01.svg" text: "TEXT" onClicked: { editRoot.sendToScript({ @@ -120,7 +120,7 @@ TabBar { } NewEntityButton { - icon: "create-icons/image.svg" + icon: "icons/image.svg" text: "IMAGE" onClicked: { editRoot.sendToScript({ @@ -132,7 +132,7 @@ TabBar { } NewEntityButton { - icon: "create-icons/25-web-1-01.svg" + icon: "icons/25-web-1-01.svg" text: "WEB" onClicked: { editRoot.sendToScript({ @@ -144,7 +144,7 @@ TabBar { } NewEntityButton { - icon: "create-icons/23-zone-01.svg" + icon: "icons/23-zone-01.svg" text: "ZONE" onClicked: { editRoot.sendToScript({ @@ -156,7 +156,7 @@ TabBar { } NewEntityButton { - icon: "create-icons/90-particles-01.svg" + icon: "icons/90-particles-01.svg" text: "PARTICLE" onClicked: { editRoot.sendToScript({ @@ -168,7 +168,7 @@ TabBar { } NewEntityButton { - icon: "create-icons/126-material-01.svg" + icon: "icons/126-material-01.svg" text: "MATERIAL" onClicked: { editRoot.sendToScript({ @@ -231,7 +231,7 @@ TabBar { property Component visualItem: Component { WebView { id: entityListToolWebView - url: Paths.defaultScripts + "/system/html/entityList.html" + url: Qt.resolvedUrl("../entityList/html/entityList.html") enabled: true blurOnCtrlShift: false } @@ -247,7 +247,7 @@ TabBar { property Component visualItem: Component { WebView { id: entityPropertiesWebView - url: Paths.defaultScripts + "/system/html/entityProperties.html" + url: Qt.resolvedURL("../entityProperties/html/entityProperties.html") enabled: true blurOnCtrlShift: false } @@ -263,7 +263,7 @@ TabBar { property Component visualItem: Component { WebView { id: gridControlsWebView - url: Paths.defaultScripts + "/system/html/gridControls.html" + url: Qt.resolvedURL("../../html/gridControls.html") enabled: true blurOnCtrlShift: false } diff --git a/scripts/system/create/EditTools.qml b/scripts/system/create/qml/EditTools.qml similarity index 100% rename from scripts/system/create/EditTools.qml rename to scripts/system/create/qml/EditTools.qml diff --git a/scripts/system/create/EditToolsTabView.qml b/scripts/system/create/qml/EditToolsTabView.qml similarity index 93% rename from scripts/system/create/EditToolsTabView.qml rename to scripts/system/create/qml/EditToolsTabView.qml index a333acc586..0ce8d8e8d4 100644 --- a/scripts/system/create/EditToolsTabView.qml +++ b/scripts/system/create/qml/EditToolsTabView.qml @@ -78,7 +78,7 @@ TabBar { NewEntityButton { - icon: "create-icons/94-model-01.svg" + icon: "icons/94-model-01.svg" text: "MODEL" onClicked: { editRoot.sendToScript({ @@ -90,7 +90,7 @@ TabBar { } NewEntityButton { - icon: "create-icons/21-cube-01.svg" + icon: "icons/21-cube-01.svg" text: "SHAPE" onClicked: { editRoot.sendToScript({ @@ -102,7 +102,7 @@ TabBar { } NewEntityButton { - icon: "create-icons/24-light-01.svg" + icon: "icons/24-light-01.svg" text: "LIGHT" onClicked: { editRoot.sendToScript({ @@ -114,7 +114,7 @@ TabBar { } NewEntityButton { - icon: "create-icons/20-text-01.svg" + icon: "icons/20-text-01.svg" text: "TEXT" onClicked: { editRoot.sendToScript({ @@ -126,7 +126,7 @@ TabBar { } NewEntityButton { - icon: "create-icons/image.svg" + icon: "icons/image.svg" text: "IMAGE" onClicked: { editRoot.sendToScript({ @@ -138,7 +138,7 @@ TabBar { } NewEntityButton { - icon: "create-icons/25-web-1-01.svg" + icon: "icons/25-web-1-01.svg" text: "WEB" onClicked: { editRoot.sendToScript({ @@ -150,7 +150,7 @@ TabBar { } NewEntityButton { - icon: "create-icons/23-zone-01.svg" + icon: "icons/23-zone-01.svg" text: "ZONE" onClicked: { editRoot.sendToScript({ @@ -162,7 +162,7 @@ TabBar { } NewEntityButton { - icon: "create-icons/90-particles-01.svg" + icon: "icons/90-particles-01.svg" text: "PARTICLE" onClicked: { editRoot.sendToScript({ @@ -174,7 +174,7 @@ TabBar { } NewEntityButton { - icon: "create-icons/126-material-01.svg" + icon: "icons/126-material-01.svg" text: "MATERIAL" onClicked: { editRoot.sendToScript({ @@ -237,7 +237,7 @@ TabBar { property Component visualItem: Component { WebView { id: entityPropertiesWebView - url: Paths.defaultScripts + "/system/html/entityProperties.html" + url: Qt.resolvedUrl("../entityProperties/html/entityProperties.html") enabled: true blurOnCtrlShift: false } @@ -253,7 +253,7 @@ TabBar { property Component visualItem: Component { WebView { id: gridControlsWebView - url: Paths.defaultScripts + "/system/html/gridControls.html" + url: Qt.resolvedUrl("../../html/gridControls.html") enabled: true blurOnCtrlShift: false } diff --git a/scripts/system/create/NewEntityButton.qml b/scripts/system/create/qml/NewEntityButton.qml similarity index 100% rename from scripts/system/create/NewEntityButton.qml rename to scripts/system/create/qml/NewEntityButton.qml diff --git a/scripts/system/create/NewMaterialDialog.qml b/scripts/system/create/qml/NewMaterialDialog.qml similarity index 100% rename from scripts/system/create/NewMaterialDialog.qml rename to scripts/system/create/qml/NewMaterialDialog.qml diff --git a/scripts/system/create/NewMaterialWindow.qml b/scripts/system/create/qml/NewMaterialWindow.qml similarity index 100% rename from scripts/system/create/NewMaterialWindow.qml rename to scripts/system/create/qml/NewMaterialWindow.qml diff --git a/scripts/system/create/NewModelDialog.qml b/scripts/system/create/qml/NewModelDialog.qml similarity index 100% rename from scripts/system/create/NewModelDialog.qml rename to scripts/system/create/qml/NewModelDialog.qml diff --git a/scripts/system/create/NewModelWindow.qml b/scripts/system/create/qml/NewModelWindow.qml similarity index 100% rename from scripts/system/create/NewModelWindow.qml rename to scripts/system/create/qml/NewModelWindow.qml diff --git a/scripts/system/create/create-icons/126-material-01.svg b/scripts/system/create/qml/icons/126-material-01.svg similarity index 100% rename from scripts/system/create/create-icons/126-material-01.svg rename to scripts/system/create/qml/icons/126-material-01.svg diff --git a/scripts/system/create/create-icons/20-text-01.svg b/scripts/system/create/qml/icons/20-text-01.svg similarity index 100% rename from scripts/system/create/create-icons/20-text-01.svg rename to scripts/system/create/qml/icons/20-text-01.svg diff --git a/scripts/system/create/create-icons/21-cube-01.svg b/scripts/system/create/qml/icons/21-cube-01.svg similarity index 100% rename from scripts/system/create/create-icons/21-cube-01.svg rename to scripts/system/create/qml/icons/21-cube-01.svg diff --git a/scripts/system/create/create-icons/22-sphere-01.svg b/scripts/system/create/qml/icons/22-sphere-01.svg similarity index 100% rename from scripts/system/create/create-icons/22-sphere-01.svg rename to scripts/system/create/qml/icons/22-sphere-01.svg diff --git a/scripts/system/create/create-icons/23-zone-01.svg b/scripts/system/create/qml/icons/23-zone-01.svg similarity index 100% rename from scripts/system/create/create-icons/23-zone-01.svg rename to scripts/system/create/qml/icons/23-zone-01.svg diff --git a/scripts/system/create/create-icons/24-light-01.svg b/scripts/system/create/qml/icons/24-light-01.svg similarity index 100% rename from scripts/system/create/create-icons/24-light-01.svg rename to scripts/system/create/qml/icons/24-light-01.svg diff --git a/scripts/system/create/create-icons/25-web-1-01.svg b/scripts/system/create/qml/icons/25-web-1-01.svg similarity index 100% rename from scripts/system/create/create-icons/25-web-1-01.svg rename to scripts/system/create/qml/icons/25-web-1-01.svg diff --git a/scripts/system/create/create-icons/90-particles-01.svg b/scripts/system/create/qml/icons/90-particles-01.svg similarity index 100% rename from scripts/system/create/create-icons/90-particles-01.svg rename to scripts/system/create/qml/icons/90-particles-01.svg diff --git a/scripts/system/create/create-icons/94-model-01.svg b/scripts/system/create/qml/icons/94-model-01.svg similarity index 100% rename from scripts/system/create/create-icons/94-model-01.svg rename to scripts/system/create/qml/icons/94-model-01.svg diff --git a/scripts/system/create/create-icons/image.svg b/scripts/system/create/qml/icons/image.svg similarity index 100% rename from scripts/system/create/create-icons/image.svg rename to scripts/system/create/qml/icons/image.svg diff --git a/scripts/system/edit.js b/scripts/system/edit.js deleted file mode 100644 index cf99f3a618..0000000000 --- a/scripts/system/edit.js +++ /dev/null @@ -1,2858 +0,0 @@ -// edit.js -// -// Created by Brad Hefta-Gaub on 10/2/14. -// Persist toolbar by HRS 6/11/15. -// Copyright 2014 High Fidelity, Inc. -// -// This script allows you to edit entities with a new UI/UX for mouse and trackpad based editing -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -/* global Script, SelectionDisplay, LightOverlayManager, CameraManager, Grid, GridTool, EntityListTool, Vec3, SelectionManager, - Overlays, OverlayWebWindow, UserActivityLogger, Settings, Entities, Tablet, Toolbars, Messages, Menu, Camera, - progressDialog, tooltip, MyAvatar, Quat, Controller, Clipboard, HMD, UndoStack, OverlaySystemWindow, - keyUpEventFromUIWindow:true */ - -(function() { // BEGIN LOCAL_SCOPE - -"use strict"; - -var EDIT_TOGGLE_BUTTON = "com.highfidelity.interface.system.editButton"; - -var CONTROLLER_MAPPING_NAME = "com.highfidelity.editMode"; - -Script.include([ - "libraries/stringHelpers.js", - "libraries/dataViewHelpers.js", - "libraries/progressDialog.js", - "libraries/entitySelectionTool.js", - "libraries/ToolTip.js", - "libraries/entityCameraTool.js", - "libraries/gridTool.js", - "libraries/entityList.js", - "libraries/utils.js", - "libraries/entityIconOverlayManager.js" -]); - -var CreateWindow = Script.require('./modules/createWindow.js'); - -var TITLE_OFFSET = 60; -var CREATE_TOOLS_WIDTH = 490; -var MAX_DEFAULT_ENTITY_LIST_HEIGHT = 942; - -var DEFAULT_IMAGE = "https://hifi-content.s3.amazonaws.com/DomainContent/production/no-image.jpg"; - -var createToolsWindow = new CreateWindow( - Script.resolvePath("create/EditTools.qml"), - 'Create Tools', - 'com.highfidelity.create.createToolsWindow', - function () { - var windowHeight = Window.innerHeight - TITLE_OFFSET; - if (windowHeight > MAX_DEFAULT_ENTITY_LIST_HEIGHT) { - windowHeight = MAX_DEFAULT_ENTITY_LIST_HEIGHT; - } - return { - size: { - x: CREATE_TOOLS_WIDTH, - y: windowHeight - }, - position: { - x: Window.x + Window.innerWidth - CREATE_TOOLS_WIDTH, - y: Window.y + TITLE_OFFSET - } - } - }, - false -); - -/** - * @description Returns true in case we should use the tablet version of the CreateApp - * @returns boolean - */ -var shouldUseEditTabletApp = function() { - return HMD.active || (!HMD.active && !Settings.getValue("desktopTabletBecomesToolbar", true)); -}; - - -var selectionDisplay = SelectionDisplay; -var selectionManager = SelectionManager; - -var PARTICLE_SYSTEM_URL = Script.resolvePath("assets/images/icon-particles.svg"); -var POINT_LIGHT_URL = Script.resolvePath("assets/images/icon-point-light.svg"); -var SPOT_LIGHT_URL = Script.resolvePath("assets/images/icon-spot-light.svg"); -var ZONE_URL = Script.resolvePath("assets/images/icon-zone.svg"); - -var entityIconOverlayManager = new EntityIconOverlayManager(['Light', 'ParticleEffect', 'Zone'], function(entityID) { - var properties = Entities.getEntityProperties(entityID, ['type', 'isSpotlight']); - if (properties.type === 'Light') { - return { - url: properties.isSpotlight ? SPOT_LIGHT_URL : POINT_LIGHT_URL, - }; - } else if (properties.type === 'Zone') { - return { - url: ZONE_URL, - }; - } else { - return { - url: PARTICLE_SYSTEM_URL, - }; - } -}); - -var cameraManager = new CameraManager(); - -var grid = new Grid(); -var gridTool = new GridTool({ - horizontalGrid: grid, - createToolsWindow: createToolsWindow, - shouldUseEditTabletApp: shouldUseEditTabletApp -}); -gridTool.setVisible(false); - -var EntityShapeVisualizer = Script.require('./modules/entityShapeVisualizer.js'); -var entityShapeVisualizer = new EntityShapeVisualizer(["Zone"]); - -var entityListTool = new EntityListTool(shouldUseEditTabletApp); - -selectionManager.addEventListener(function () { - selectionDisplay.updateHandles(); - entityIconOverlayManager.updatePositions(); - entityShapeVisualizer.setEntities(selectionManager.selections); -}); - -var DEGREES_TO_RADIANS = Math.PI / 180.0; -var RADIANS_TO_DEGREES = 180.0 / Math.PI; - -var MIN_ANGULAR_SIZE = 2; -var MAX_ANGULAR_SIZE = 45; -var allowLargeModels = true; -var allowSmallModels = true; - -var DEFAULT_DIMENSION = 0.20; - -var DEFAULT_DIMENSIONS = { - x: DEFAULT_DIMENSION, - y: DEFAULT_DIMENSION, - z: DEFAULT_DIMENSION -}; - -var DEFAULT_LIGHT_DIMENSIONS = Vec3.multiply(20, DEFAULT_DIMENSIONS); - -var MENU_AUTO_FOCUS_ON_SELECT = "Auto Focus on Select"; -var MENU_EASE_ON_FOCUS = "Ease Orientation on Focus"; -var MENU_SHOW_LIGHTS_AND_PARTICLES_IN_EDIT_MODE = "Show Lights and Particle Systems in Create Mode"; -var MENU_SHOW_ZONES_IN_EDIT_MODE = "Show Zones in Create Mode"; - -var MENU_CREATE_ENTITIES_GRABBABLE = "Create Entities As Grabbable (except Zones, Particles, and Lights)"; -var MENU_ALLOW_SELECTION_LARGE = "Allow Selecting of Large Models"; -var MENU_ALLOW_SELECTION_SMALL = "Allow Selecting of Small Models"; -var MENU_ALLOW_SELECTION_LIGHTS = "Allow Selecting of Lights"; - -var SETTING_AUTO_FOCUS_ON_SELECT = "autoFocusOnSelect"; -var SETTING_EASE_ON_FOCUS = "cameraEaseOnFocus"; -var SETTING_SHOW_LIGHTS_AND_PARTICLES_IN_EDIT_MODE = "showLightsAndParticlesInEditMode"; -var SETTING_SHOW_ZONES_IN_EDIT_MODE = "showZonesInEditMode"; - -var SETTING_EDIT_PREFIX = "Edit/"; - - -var CREATE_ENABLED_ICON = "icons/tablet-icons/edit-i.svg"; -var CREATE_DISABLED_ICON = "icons/tablet-icons/edit-disabled.svg"; - -// marketplace info, etc. not quite ready yet. -var SHOULD_SHOW_PROPERTY_MENU = false; -var INSUFFICIENT_PERMISSIONS_ERROR_MSG = "You do not have the necessary permissions to edit on this domain."; -var INSUFFICIENT_PERMISSIONS_IMPORT_ERROR_MSG = "You do not have the necessary permissions to place items on this domain."; - -var isActive = false; -var createButton = null; - -var IMPORTING_SVO_OVERLAY_WIDTH = 144; -var IMPORTING_SVO_OVERLAY_HEIGHT = 30; -var IMPORTING_SVO_OVERLAY_MARGIN = 5; -var IMPORTING_SVO_OVERLAY_LEFT_MARGIN = 34; -var importingSVOImageOverlay = Overlays.addOverlay("image", { - imageURL: Script.resolvePath("assets") + "/images/hourglass.svg", - width: 20, - height: 20, - alpha: 1.0, - x: Window.innerWidth - IMPORTING_SVO_OVERLAY_WIDTH, - y: Window.innerHeight - IMPORTING_SVO_OVERLAY_HEIGHT, - visible: false -}); -var importingSVOTextOverlay = Overlays.addOverlay("text", { - font: { - size: 14 - }, - text: "Importing SVO...", - leftMargin: IMPORTING_SVO_OVERLAY_LEFT_MARGIN, - x: Window.innerWidth - IMPORTING_SVO_OVERLAY_WIDTH - IMPORTING_SVO_OVERLAY_MARGIN, - y: Window.innerHeight - IMPORTING_SVO_OVERLAY_HEIGHT - IMPORTING_SVO_OVERLAY_MARGIN, - width: IMPORTING_SVO_OVERLAY_WIDTH, - height: IMPORTING_SVO_OVERLAY_HEIGHT, - backgroundColor: { - red: 80, - green: 80, - blue: 80 - }, - backgroundAlpha: 0.7, - visible: false -}); - -var MARKETPLACE_URL = Account.metaverseServerURL + "/marketplace"; -var marketplaceWindow = new OverlayWebWindow({ - title: 'Marketplace', - source: "about:blank", - width: 900, - height: 700, - visible: false -}); - -function showMarketplace(marketplaceID) { - var url = MARKETPLACE_URL; - if (marketplaceID) { - url = url + "/items/" + marketplaceID; - } - marketplaceWindow.setURL(url); - marketplaceWindow.setVisible(true); - marketplaceWindow.raise(); - - UserActivityLogger.logAction("opened_marketplace"); -} - -function hideMarketplace() { - marketplaceWindow.setVisible(false); - marketplaceWindow.setURL("about:blank"); -} - -// function toggleMarketplace() { -// if (marketplaceWindow.visible) { -// hideMarketplace(); -// } else { -// showMarketplace(); -// } -// } - -function adjustPositionPerBoundingBox(position, direction, registration, dimensions, orientation) { - // Adjust the position such that the bounding box (registration, dimensions and orientation) lies behind the original - // position in the given direction. - var CORNERS = [ - { x: 0, y: 0, z: 0 }, - { x: 0, y: 0, z: 1 }, - { x: 0, y: 1, z: 0 }, - { x: 0, y: 1, z: 1 }, - { x: 1, y: 0, z: 0 }, - { x: 1, y: 0, z: 1 }, - { x: 1, y: 1, z: 0 }, - { x: 1, y: 1, z: 1 }, - ]; - - // Go through all corners and find least (most negative) distance in front of position. - var distance = 0; - for (var i = 0, length = CORNERS.length; i < length; i++) { - var cornerVector = - Vec3.multiplyQbyV(orientation, Vec3.multiplyVbyV(Vec3.subtract(CORNERS[i], registration), dimensions)); - var cornerDistance = Vec3.dot(cornerVector, direction); - distance = Math.min(cornerDistance, distance); - } - position = Vec3.sum(Vec3.multiply(distance, direction), position); - return position; -} - -var GRABBABLE_ENTITIES_MENU_CATEGORY = "Edit"; - -// Handles any edit mode updates required when domains have switched -function checkEditPermissionsAndUpdate() { - if ((createButton === null) || (createButton === undefined)) { - //--EARLY EXIT--( nothing to safely update ) - return; - } - - var hasRezPermissions = (Entities.canRez() || Entities.canRezTmp() || Entities.canRezCertified() || Entities.canRezTmpCertified()); - createButton.editProperties({ - icon: (hasRezPermissions ? CREATE_ENABLED_ICON : CREATE_DISABLED_ICON), - captionColor: (hasRezPermissions ? "#ffffff" : "#888888"), - }); - - if (!hasRezPermissions && isActive) { - that.setActive(false); - tablet.gotoHomeScreen(); - } -} - -// Copies the properties in `b` into `a`. `a` will be modified. -function copyProperties(a, b) { - for (var key in b) { - a[key] = b[key]; - } - return a; -} - -const DEFAULT_DYNAMIC_PROPERTIES = { - dynamic: true, - damping: 0.39347, - angularDamping: 0.39347, - gravity: { x: 0, y: -9.8, z: 0 }, -}; - -const DEFAULT_NON_DYNAMIC_PROPERTIES = { - dynamic: false, - damping: 0, - angularDamping: 0, - gravity: { x: 0, y: 0, z: 0 }, -}; - -const DEFAULT_ENTITY_PROPERTIES = { - All: { - description: "", - rotation: { x: 0, y: 0, z: 0, w: 1 }, - collidesWith: "static,dynamic,kinematic,otherAvatar,myAvatar", - collisionSoundURL: "", - cloneable: false, - ignoreIK: true, - canCastShadow: true, - href: "", - script: "", - serverScripts:"", - velocity: { - x: 0, - y: 0, - z: 0 - }, - angularVelocity: { - x: 0, - y: 0, - z: 0 - }, - restitution: 0.5, - friction: 0.5, - density: 1000, - dynamic: false, - }, - Shape: { - shape: "Box", - dimensions: { x: 0.2, y: 0.2, z: 0.2 }, - color: { red: 0, green: 180, blue: 239 }, - }, - Text: { - text: "Text", - dimensions: { - x: 0.65, - y: 0.3, - z: 0.01 - }, - textColor: { red: 255, green: 255, blue: 255 }, - backgroundColor: { red: 0, green: 0, blue: 0 }, - lineHeight: 0.06, - faceCamera: false, - }, - Zone: { - dimensions: { - x: 10, - y: 10, - z: 10 - }, - flyingAllowed: true, - ghostingAllowed: true, - filter: "", - keyLightMode: "inherit", - keyLightColor: { red: 255, green: 255, blue: 255 }, - keyLight: { - intensity: 1.0, - direction: { - x: 0.0, - y: -0.707106769084930, // 45 degrees - z: 0.7071067690849304 - }, - castShadows: true - }, - ambientLightMode: "inherit", - ambientLight: { - ambientIntensity: 0.5, - ambientURL: "" - }, - hazeMode: "inherit", - haze: { - hazeRange: 1000, - hazeAltitudeEffect: false, - hazeBaseRef: 0, - hazeColor: { - red: 128, - green: 154, - blue: 179 - }, - hazeBackgroundBlend: 0, - hazeEnableGlare: false, - hazeGlareColor: { - red: 255, - green: 229, - blue: 179 - }, - }, - shapeType: "box", - bloomMode: "inherit", - avatarPriority: "inherit" - }, - Model: { - collisionShape: "none", - compoundShapeURL: "", - animation: { - url: "", - running: false, - allowTranslation: false, - loop: true, - hold: false, - currentFrame: 0, - firstFrame: 0, - lastFrame: 100000, - fps: 30.0, - } - }, - Image: { - dimensions: { - x: 0.5385, - y: 0.2819, - z: 0.0092 - }, - shapeType: "box", - collisionless: true, - keepAspectRatio: false, - imageURL: DEFAULT_IMAGE - }, - Web: { - dimensions: { - x: 1.6, - y: 0.9, - z: 0.01 - }, - sourceUrl: "https://highfidelity.com/", - dpi: 30, - }, - ParticleEffect: { - lifespan: 1.5, - maxParticles: 10, - textures: "https://content.highfidelity.com/DomainContent/production/Particles/wispy-smoke.png", - emitRate: 5.5, - emitSpeed: 0, - speedSpread: 0, - emitDimensions: { x: 0, y: 0, z: 0 }, - emitOrientation: { x: 0, y: 0, z: 0, w: 1 }, - emitterShouldTrail: true, - particleRadius: 0.25, - radiusStart: 0, - radiusSpread: 0, - particleColor: { - red: 255, - green: 255, - blue: 255 - }, - colorSpread: { - red: 0, - green: 0, - blue: 0 - }, - alpha: 0, - alphaStart: 1, - alphaSpread: 0, - emitAcceleration: { - x: 0, - y: 2.5, - z: 0 - }, - accelerationSpread: { - x: 0, - y: 0, - z: 0 - }, - particleSpin: 0, - spinSpread: 0, - rotateWithEntity: false, - polarStart: 0, - polarFinish: Math.PI, - azimuthStart: -Math.PI, - azimuthFinish: Math.PI - }, - Light: { - color: { red: 255, green: 255, blue: 255 }, - intensity: 5.0, - dimensions: DEFAULT_LIGHT_DIMENSIONS, - falloffRadius: 1.0, - isSpotlight: false, - exponent: 1.0, - cutoff: 75.0, - }, -}; - -var toolBar = (function () { - var EDIT_SETTING = "io.highfidelity.isEditing"; // for communication with other scripts - var that = {}, - toolBar, - activeButton = null, - systemToolbar = null, - dialogWindow = null, - tablet = null; - - function createNewEntity(requestedProperties) { - var dimensions = requestedProperties.dimensions ? requestedProperties.dimensions : DEFAULT_DIMENSIONS; - var position = getPositionToCreateEntity(); - var entityID = null; - - var properties = {}; - - copyProperties(properties, DEFAULT_ENTITY_PROPERTIES.All); - - var type = requestedProperties.type; - if (type === "Box" || type === "Sphere") { - copyProperties(properties, DEFAULT_ENTITY_PROPERTIES.Shape); - } else { - copyProperties(properties, DEFAULT_ENTITY_PROPERTIES[type]); - } - - // We apply the requested properties first so that they take priority over any default properties. - copyProperties(properties, requestedProperties); - - if (properties.dynamic) { - copyProperties(properties, DEFAULT_DYNAMIC_PROPERTIES); - } else { - copyProperties(properties, DEFAULT_NON_DYNAMIC_PROPERTIES); - } - - - if (position !== null && position !== undefined) { - var direction; - if (Camera.mode === "entity" || Camera.mode === "independent") { - direction = Camera.orientation; - } else { - direction = MyAvatar.orientation; - } - direction = Vec3.multiplyQbyV(direction, Vec3.UNIT_Z); - - var PRE_ADJUST_ENTITY_TYPES = ["Box", "Sphere", "Shape", "Text", "Image", "Web", "Material"]; - if (PRE_ADJUST_ENTITY_TYPES.indexOf(properties.type) !== -1) { - - // Adjust position of entity per bounding box prior to creating it. - var registration = properties.registration; - if (registration === undefined) { - var DEFAULT_REGISTRATION = { x: 0.5, y: 0.5, z: 0.5 }; - registration = DEFAULT_REGISTRATION; - } - - var orientation = properties.orientation; - if (orientation === undefined) { - properties.orientation = MyAvatar.orientation; - var DEFAULT_ORIENTATION = properties.orientation; - orientation = DEFAULT_ORIENTATION; - } else { - // If the orientation is already defined, we perform the corresponding rotation assuming that - // our start referential is the avatar referential. - properties.orientation = Quat.multiply(MyAvatar.orientation, properties.orientation); - var DEFAULT_ORIENTATION = properties.orientation; - orientation = DEFAULT_ORIENTATION; - } - - position = adjustPositionPerBoundingBox(position, direction, registration, dimensions, orientation); - } - - position = grid.snapToSurface(grid.snapToGrid(position, false, dimensions), dimensions); - properties.position = position; - - if (!properties.grab) { - properties.grab = {}; - if (Menu.isOptionChecked(MENU_CREATE_ENTITIES_GRABBABLE) && - !(properties.type === "Zone" || properties.type === "Light" || properties.type === "ParticleEffect")) { - properties.grab.grabbable = true; - } else { - properties.grab.grabbable = false; - } - } - - entityID = Entities.addEntity(properties); - SelectionManager.addEntity(entityID, false, this); - SelectionManager.saveProperties(); - pushCommandForSelections([{ - entityID: entityID, - properties: properties - }], [], true); - - var POST_ADJUST_ENTITY_TYPES = ["Model"]; - if (POST_ADJUST_ENTITY_TYPES.indexOf(properties.type) !== -1) { - // Adjust position of entity per bounding box after it has been created and auto-resized. - var initialDimensions = Entities.getEntityProperties(entityID, ["dimensions"]).dimensions; - var DIMENSIONS_CHECK_INTERVAL = 200; - var MAX_DIMENSIONS_CHECKS = 10; - var dimensionsCheckCount = 0; - var dimensionsCheckFunction = function () { - dimensionsCheckCount++; - var properties = Entities.getEntityProperties(entityID, ["dimensions", "registrationPoint", "rotation"]); - if (!Vec3.equal(properties.dimensions, initialDimensions)) { - position = adjustPositionPerBoundingBox(position, direction, properties.registrationPoint, - properties.dimensions, properties.rotation); - position = grid.snapToSurface(grid.snapToGrid(position, false, properties.dimensions), - properties.dimensions); - Entities.editEntity(entityID, { - position: position - }); - selectionManager._update(false, this); - } else if (dimensionsCheckCount < MAX_DIMENSIONS_CHECKS) { - Script.setTimeout(dimensionsCheckFunction, DIMENSIONS_CHECK_INTERVAL); - } - }; - Script.setTimeout(dimensionsCheckFunction, DIMENSIONS_CHECK_INTERVAL); - } - } else { - Window.notifyEditError("Can't create " + properties.type + ": " + - properties.type + " would be out of bounds."); - } - - selectionManager.clearSelections(this); - entityListTool.sendUpdate(); - selectionManager.setSelections([entityID], this); - - Window.setFocus(); - - return entityID; - } - - function closeExistingDialogWindow() { - if (dialogWindow) { - dialogWindow.close(); - dialogWindow = null; - } - } - - function cleanup() { - that.setActive(false); - if (tablet) { - tablet.removeButton(activeButton); - } - if (systemToolbar) { - systemToolbar.removeButton(EDIT_TOGGLE_BUTTON); - } - } - - var buttonHandlers = {}; // only used to tablet mode - - function addButton(name, handler) { - buttonHandlers[name] = handler; - } - - var SHAPE_TYPE_NONE = 0; - var SHAPE_TYPE_SIMPLE_HULL = 1; - var SHAPE_TYPE_SIMPLE_COMPOUND = 2; - var SHAPE_TYPE_STATIC_MESH = 3; - var SHAPE_TYPE_BOX = 4; - var SHAPE_TYPE_SPHERE = 5; - var DYNAMIC_DEFAULT = false; - - var MATERIAL_MODE_UV = 0; - var MATERIAL_MODE_PROJECTED = 1; - - function handleNewModelDialogResult(result) { - if (result) { - var url = result.url; - var shapeType; - switch (result.collisionShapeIndex) { - case SHAPE_TYPE_SIMPLE_HULL: - shapeType = "simple-hull"; - break; - case SHAPE_TYPE_SIMPLE_COMPOUND: - shapeType = "simple-compound"; - break; - case SHAPE_TYPE_STATIC_MESH: - shapeType = "static-mesh"; - break; - case SHAPE_TYPE_BOX: - shapeType = "box"; - break; - case SHAPE_TYPE_SPHERE: - shapeType = "sphere"; - break; - default: - shapeType = "none"; - } - - var dynamic = result.dynamic !== null ? result.dynamic : DYNAMIC_DEFAULT; - if (shapeType === "static-mesh" && dynamic) { - // The prompt should prevent this case - print("Error: model cannot be both static mesh and dynamic. This should never happen."); - } else if (url) { - createNewEntity({ - type: "Model", - modelURL: url, - shapeType: shapeType, - grab: { - grabbable: result.grabbable - }, - dynamic: dynamic, - }); - } - } - } - - function handleNewMaterialDialogResult(result) { - if (result) { - var materialURL = result.textInput; - //var materialMappingMode; - //switch (result.comboBox) { - // case MATERIAL_MODE_PROJECTED: - // materialMappingMode = "projected"; - // break; - // default: - // shapeType = "uv"; - //} - var materialData = ""; - if (materialURL.startsWith("materialData")) { - materialData = JSON.stringify({ - "materials": {} - }); - } - - var DEFAULT_LAYERED_MATERIAL_PRIORITY = 1; - if (materialURL) { - createNewEntity({ - type: "Material", - materialURL: materialURL, - //materialMappingMode: materialMappingMode, - priority: DEFAULT_LAYERED_MATERIAL_PRIORITY, - materialData: materialData - }); - } - } - } - - function fromQml(message) { // messages are {method, params}, like json-rpc. See also sendToQml. - var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system"); - tablet.popFromStack(); - switch (message.method) { - case "newModelDialogAdd": - handleNewModelDialogResult(message.params); - closeExistingDialogWindow(); - break; - case "newModelDialogCancel": - closeExistingDialogWindow(); - break; - case "newEntityButtonClicked": - buttonHandlers[message.params.buttonName](); - break; - case "newMaterialDialogAdd": - handleNewMaterialDialogResult(message.params); - closeExistingDialogWindow(); - break; - case "newMaterialDialogCancel": - closeExistingDialogWindow(); - break; - } - } - - var entitiesToDelete = []; - var deletedEntityTimer = null; - var DELETE_ENTITY_TIMER_TIMEOUT = 100; - - function checkDeletedEntityAndUpdate(entityID) { - // Allow for multiple entity deletes before updating the entities selected. - entitiesToDelete.push(entityID); - if (deletedEntityTimer !== null) { - Script.clearTimeout(deletedEntityTimer); - } - deletedEntityTimer = Script.setTimeout(function () { - if (entitiesToDelete.length > 0) { - selectionManager.removeEntities(entitiesToDelete, this); - } - entityListTool.removeEntities(entitiesToDelete, selectionManager.selections); - entitiesToDelete = []; - deletedEntityTimer = null; - }, DELETE_ENTITY_TIMER_TIMEOUT); - } - - function initialize() { - Script.scriptEnding.connect(cleanup); - Window.domainChanged.connect(function () { - if (isActive) { - tablet.gotoHomeScreen(); - } - that.setActive(false); - that.clearEntityList(); - checkEditPermissionsAndUpdate(); - }); - - HMD.displayModeChanged.connect(function() { - if (isActive) { - tablet.gotoHomeScreen(); - } - that.setActive(false); - }); - - Entities.canAdjustLocksChanged.connect(function (canAdjustLocks) { - if (isActive && !canAdjustLocks) { - that.setActive(false); - } - checkEditPermissionsAndUpdate(); - }); - - Entities.canRezChanged.connect(checkEditPermissionsAndUpdate); - Entities.canRezTmpChanged.connect(checkEditPermissionsAndUpdate); - Entities.canRezCertifiedChanged.connect(checkEditPermissionsAndUpdate); - Entities.canRezTmpCertifiedChanged.connect(checkEditPermissionsAndUpdate); - var hasRezPermissions = (Entities.canRez() || Entities.canRezTmp() || Entities.canRezCertified() || Entities.canRezTmpCertified()); - - Entities.deletingEntity.connect(checkDeletedEntityAndUpdate); - - var createButtonIconRsrc = (hasRezPermissions ? CREATE_ENABLED_ICON : CREATE_DISABLED_ICON); - tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system"); - activeButton = tablet.addButton({ - captionColor: hasRezPermissions ? "#ffffff" : "#888888", - icon: createButtonIconRsrc, - activeIcon: "icons/tablet-icons/edit-a.svg", - text: "CREATE", - sortOrder: 10 - }); - createButton = activeButton; - tablet.screenChanged.connect(function (type, url) { - var isGoingToHomescreenOnDesktop = (!shouldUseEditTabletApp() && - (url === 'hifi/tablet/TabletHome.qml' || url === '')); - if (isActive && (type !== "QML" || url !== Script.resolvePath("create/Edit.qml")) && !isGoingToHomescreenOnDesktop) { - that.setActive(false); - } - }); - tablet.fromQml.connect(fromQml); - createToolsWindow.fromQml.addListener(fromQml); - - createButton.clicked.connect(function() { - if ( ! (Entities.canRez() || Entities.canRezTmp() || Entities.canRezCertified() || Entities.canRezTmpCertified()) ) { - Window.notifyEditError(INSUFFICIENT_PERMISSIONS_ERROR_MSG); - return; - } - - that.toggle(); - }); - - addButton("importEntitiesButton", function() { - Window.browseChanged.connect(onFileOpenChanged); - Window.browseAsync("Select Model to Import", "", "*.json"); - }); - - addButton("openAssetBrowserButton", function() { - Window.showAssetServer(); - }); - function createNewEntityDialogButtonCallback(entityType) { - return function() { - if (shouldUseEditTabletApp()) { - // tablet version of new-model dialog - var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system"); - tablet.pushOntoStack(Script.resolvePath("create/New" + entityType + "Dialog.qml")); - } else { - closeExistingDialogWindow(); - var qmlPath = Script.resolvePath("create/New" + entityType + "Window.qml"); - var DIALOG_WINDOW_SIZE = { x: 500, y: 300 }; - dialogWindow = Desktop.createWindow(qmlPath, { - title: "New " + entityType + " Entity", - flags: Desktop.ALWAYS_ON_TOP | Desktop.CLOSE_BUTTON_HIDES, - presentationMode: Desktop.PresentationMode.NATIVE, - size: DIALOG_WINDOW_SIZE, - visible: true - }); - dialogWindow.fromQml.connect(fromQml); - } - }; - } - - addButton("newModelButton", createNewEntityDialogButtonCallback("Model")); - - addButton("newShapeButton", function () { - createNewEntity({ - type: "Shape", - shape: "Cube", - }); - }); - - addButton("newLightButton", function () { - createNewEntity({ - type: "Light", - }); - }); - - addButton("newTextButton", function () { - createNewEntity({ - type: "Text", - }); - }); - - addButton("newImageButton", function () { - createNewEntity({ - type: "Image", - }); - }); - - addButton("newWebButton", function () { - createNewEntity({ - type: "Web", - }); - }); - - addButton("newZoneButton", function () { - createNewEntity({ - type: "Zone", - }); - }); - - addButton("newParticleButton", function () { - createNewEntity({ - type: "ParticleEffect", - }); - }); - - addButton("newMaterialButton", createNewEntityDialogButtonCallback("Material")); - - var deactivateCreateIfDesktopWindowsHidden = function() { - if (!shouldUseEditTabletApp() && !entityListTool.isVisible() && !createToolsWindow.isVisible()) { - that.setActive(false); - } - }; - entityListTool.interactiveWindowHidden.addListener(this, deactivateCreateIfDesktopWindowsHidden); - createToolsWindow.interactiveWindowHidden.addListener(this, deactivateCreateIfDesktopWindowsHidden); - - that.setActive(false); - } - - that.clearEntityList = function () { - entityListTool.clearEntityList(); - }; - - that.toggle = function () { - that.setActive(!isActive); - if (!isActive) { - tablet.gotoHomeScreen(); - } - }; - - that.setActive = function (active) { - ContextOverlay.enabled = !active; - Settings.setValue(EDIT_SETTING, active); - if (active) { - Controller.captureEntityClickEvents(); - } else { - Controller.releaseEntityClickEvents(); - - closeExistingDialogWindow(); - } - if (active === isActive) { - return; - } - if (active && !Entities.canRez() && !Entities.canRezTmp() && !Entities.canRezCertified() && !Entities.canRezTmpCertified()) { - Window.notifyEditError(INSUFFICIENT_PERMISSIONS_ERROR_MSG); - return; - } - Messages.sendLocalMessage("edit-events", JSON.stringify({ - enabled: active - })); - isActive = active; - activeButton.editProperties({isActive: isActive}); - undoHistory.setEnabled(isActive); - - var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system"); - - if (!isActive) { - entityListTool.setVisible(false); - gridTool.setVisible(false); - grid.setEnabled(false); - propertiesTool.setVisible(false); - selectionManager.clearSelections(this); - cameraManager.disable(); - selectionDisplay.disableTriggerMapping(); - tablet.landscape = false; - Controller.disableMapping(CONTROLLER_MAPPING_NAME); - } else { - if (shouldUseEditTabletApp()) { - tablet.loadQMLSource(Script.resolvePath("create/Edit.qml"), true); - } else { - // make other apps inactive while in desktop mode - tablet.gotoHomeScreen(); - } - UserActivityLogger.enabledEdit(); - entityListTool.setVisible(true); - entityListTool.sendUpdate(); - gridTool.setVisible(true); - grid.setEnabled(true); - propertiesTool.setVisible(true); - selectionDisplay.enableTriggerMapping(); - print("starting tablet in landscape mode"); - tablet.landscape = true; - Controller.enableMapping(CONTROLLER_MAPPING_NAME); - // Not sure what the following was meant to accomplish, but it currently causes - // everybody else to think that Interface has lost focus overall. fogbugzid:558 - // Window.setFocus(); - } - entityIconOverlayManager.setVisible(isActive && Menu.isOptionChecked(MENU_SHOW_LIGHTS_AND_PARTICLES_IN_EDIT_MODE)); - Entities.setDrawZoneBoundaries(isActive && Menu.isOptionChecked(MENU_SHOW_ZONES_IN_EDIT_MODE)); - }; - - initialize(); - return that; -})(); - -var selectedEntityID; -var orientation; -var intersection; - - -function rayPlaneIntersection(pickRay, point, normal) { // - // - // This version of the test returns the intersection of a line with a plane - // - var collides = Vec3.dot(pickRay.direction, normal); - - var d = -Vec3.dot(point, normal); - var t = -(Vec3.dot(pickRay.origin, normal) + d) / collides; - - return Vec3.sum(pickRay.origin, Vec3.multiply(pickRay.direction, t)); -} - -function rayPlaneIntersection2(pickRay, point, normal) { - // - // This version of the test returns false if the ray is directed away from the plane - // - var collides = Vec3.dot(pickRay.direction, normal); - var d = -Vec3.dot(point, normal); - var t = -(Vec3.dot(pickRay.origin, normal) + d) / collides; - if (t < 0.0) { - return false; - } else { - return Vec3.sum(pickRay.origin, Vec3.multiply(pickRay.direction, t)); - } -} - -function findClickedEntity(event) { - var pickZones = event.isControl; - - if (pickZones) { - Entities.setZonesArePickable(true); - } - - var pickRay = Camera.computePickRay(event.x, event.y); - var tabletIDs = getMainTabletIDs(); - if (tabletIDs.length > 0) { - var overlayResult = Overlays.findRayIntersection(pickRay, true, tabletIDs); - if (overlayResult.intersects) { - return null; - } - } - - var entityResult = Entities.findRayIntersection(pickRay, true); // want precision picking - var iconResult = entityIconOverlayManager.findRayIntersection(pickRay); - iconResult.accurate = true; - - if (pickZones) { - Entities.setZonesArePickable(false); - } - - var result; - - if (iconResult.intersects) { - result = iconResult; - } else if (entityResult.intersects) { - result = entityResult; - } else { - return null; - } - - if (!result.accurate) { - return null; - } - - var foundEntity = result.entityID; - return { - pickRay: pickRay, - entityID: foundEntity, - intersection: result.intersection - }; -} - -// Handles selections on overlays while in edit mode by querying entities from -// entityIconOverlayManager. -function handleOverlaySelectionToolUpdates(channel, message, sender) { - var wantDebug = false; - if (sender !== MyAvatar.sessionUUID || channel !== 'entityToolUpdates') - return; - - var data = JSON.parse(message); - - if (data.method === "selectOverlay") { - if (!selectionDisplay.triggered() || selectionDisplay.triggeredHand === data.hand) { - if (wantDebug) { - print("setting selection to overlay " + data.overlayID); - } - var entity = entityIconOverlayManager.findEntity(data.overlayID); - - if (entity !== null) { - selectionManager.setSelections([entity], this); - } - } - } -} - -function handleMessagesReceived(channel, message, sender) { - switch( channel ){ - case 'entityToolUpdates': { - handleOverlaySelectionToolUpdates( channel, message, sender ); - break; - } - default: { - return; - } - } -} - -Messages.subscribe("entityToolUpdates"); -Messages.messageReceived.connect(handleMessagesReceived); - -var mouseHasMovedSincePress = false; -var mousePressStartTime = 0; -var mousePressStartPosition = { - x: 0, - y: 0 -}; -var mouseDown = false; - -function mousePressEvent(event) { - mouseDown = true; - mousePressStartPosition = { - x: event.x, - y: event.y - }; - mousePressStartTime = Date.now(); - mouseHasMovedSincePress = false; - mouseCapturedByTool = false; - - if (propertyMenu.mousePressEvent(event) || progressDialog.mousePressEvent(event)) { - mouseCapturedByTool = true; - return; - } - if (isActive) { - if (cameraManager.mousePressEvent(event) || selectionDisplay.mousePressEvent(event)) { - // Event handled; do nothing. - return; - } - } -} - -var mouseCapturedByTool = false; -var lastMousePosition = null; -var CLICK_TIME_THRESHOLD = 500 * 1000; // 500 ms -var CLICK_MOVE_DISTANCE_THRESHOLD = 20; -var IDLE_MOUSE_TIMEOUT = 200; - -var lastMouseMoveEvent = null; - -function mouseMoveEventBuffered(event) { - lastMouseMoveEvent = event; -} - -function mouseMove(event) { - if (mouseDown && !mouseHasMovedSincePress) { - var timeSincePressMicro = Date.now() - mousePressStartTime; - - var dX = mousePressStartPosition.x - event.x; - var dY = mousePressStartPosition.y - event.y; - var sqDist = (dX * dX) + (dY * dY); - - // If less than CLICK_TIME_THRESHOLD has passed since the mouse click AND the mouse has moved - // less than CLICK_MOVE_DISTANCE_THRESHOLD distance, then don't register this as a mouse move - // yet. The goal is to provide mouse clicks that are more lenient to small movements. - if (timeSincePressMicro < CLICK_TIME_THRESHOLD && sqDist < CLICK_MOVE_DISTANCE_THRESHOLD) { - return; - } - mouseHasMovedSincePress = true; - } - - if (!isActive) { - return; - } - - // allow the selectionDisplay and cameraManager to handle the event first, if it doesn't handle it, then do our own thing - if (selectionDisplay.mouseMoveEvent(event) || propertyMenu.mouseMoveEvent(event) || cameraManager.mouseMoveEvent(event)) { - return; - } - - lastMousePosition = { - x: event.x, - y: event.y - }; -} - -function mouseReleaseEvent(event) { - mouseDown = false; - - if (lastMouseMoveEvent) { - mouseMove(lastMouseMoveEvent); - lastMouseMoveEvent = null; - } - if (propertyMenu.mouseReleaseEvent(event)) { - return true; - } - if (isActive && selectionManager.hasSelection()) { - tooltip.show(false); - } - if (mouseCapturedByTool) { - - return; - } - - cameraManager.mouseReleaseEvent(event); - - if (!mouseHasMovedSincePress) { - mouseClickEvent(event); - } -} - -function wasTabletOrEditHandleClicked(event) { - var rayPick = Camera.computePickRay(event.x, event.y); - var result = Overlays.findRayIntersection(rayPick, true); - if (result.intersects) { - var overlayID = result.overlayID; - var tabletIDs = getMainTabletIDs(); - if (tabletIDs.indexOf(overlayID) >= 0) { - return true; - } else if (selectionDisplay.isEditHandle(overlayID)) { - return true; - } - } - return false; -} - -function mouseClickEvent(event) { - var wantDebug = false; - var result, properties, tabletClicked; - if (isActive && event.isLeftButton) { - result = findClickedEntity(event); - var tabletOrEditHandleClicked = wasTabletOrEditHandleClicked(event); - if (tabletOrEditHandleClicked) { - return; - } - - if (result === null || result === undefined) { - if (!event.isShifted) { - selectionManager.clearSelections(this); - } - return; - } - toolBar.setActive(true); - var pickRay = result.pickRay; - var foundEntity = result.entityID; - if (HMD.tabletID && foundEntity === HMD.tabletID) { - return; - } - properties = Entities.getEntityProperties(foundEntity); - var halfDiagonal = Vec3.length(properties.dimensions) / 2.0; - - if (wantDebug) { - print("Checking properties: " + properties.id + " " + " - Half Diagonal:" + halfDiagonal); - } - // P P - Model - // /| A - Palm - // / | d B - unit vector toward tip - // / | X - base of the perpendicular line - // A---X----->B d - distance fom axis - // x x - distance from A - // - // |X-A| = (P-A).B - // X === A + ((P-A).B)B - // d = |P-X| - - var A = pickRay.origin; - var B = Vec3.normalize(pickRay.direction); - var P = properties.position; - - var x = Vec3.dot(Vec3.subtract(P, A), B); - - var angularSize = 2 * Math.atan(halfDiagonal / Vec3.distance(Camera.getPosition(), properties.position)) * - 180 / Math.PI; - - var sizeOK = (allowLargeModels || angularSize < MAX_ANGULAR_SIZE) && - (allowSmallModels || angularSize > MIN_ANGULAR_SIZE); - - if (0 < x && sizeOK) { - selectedEntityID = foundEntity; - orientation = MyAvatar.orientation; - intersection = rayPlaneIntersection(pickRay, P, Quat.getForward(orientation)); - - if (!event.isShifted) { - selectionManager.setSelections([foundEntity], this); - } else { - selectionManager.addEntity(foundEntity, true, this); - } - selectionManager.saveProperties(); - - if (wantDebug) { - print("Model selected: " + foundEntity); - } - selectionDisplay.select(selectedEntityID, event); - - if (Menu.isOptionChecked(MENU_AUTO_FOCUS_ON_SELECT)) { - cameraManager.enable(); - cameraManager.focus(selectionManager.worldPosition, - selectionManager.worldDimensions, - Menu.isOptionChecked(MENU_EASE_ON_FOCUS)); - } - } - } else if (event.isRightButton) { - result = findClickedEntity(event); - if (result) { - if (SHOULD_SHOW_PROPERTY_MENU !== true) { - return; - } - properties = Entities.getEntityProperties(result.entityID); - if (properties.marketplaceID) { - propertyMenu.marketplaceID = properties.marketplaceID; - propertyMenu.updateMenuItemText(showMenuItem, "Show in Marketplace"); - } else { - propertyMenu.marketplaceID = null; - propertyMenu.updateMenuItemText(showMenuItem, "No marketplace info"); - } - propertyMenu.setPosition(event.x, event.y); - propertyMenu.show(); - } else { - propertyMenu.hide(); - } - } -} - -Controller.mousePressEvent.connect(mousePressEvent); -Controller.mouseMoveEvent.connect(mouseMoveEventBuffered); -Controller.mouseReleaseEvent.connect(mouseReleaseEvent); - - -// In order for editVoxels and editModels to play nice together, they each check to see if a "delete" menu item already -// exists. If it doesn't they add it. If it does they don't. They also only delete the menu item if they were the one that -// added it. -var modelMenuAddedDelete = false; -var originalLightsArePickable = Entities.getLightsArePickable(); - -function setupModelMenus() { - // adj our menuitems - Menu.addMenuItem({ - menuName: "Edit", - menuItemName: "Undo", - shortcutKey: 'Ctrl+Z', - position: 0, - }); - Menu.addMenuItem({ - menuName: "Edit", - menuItemName: "Redo", - shortcutKey: 'Ctrl+Y', - position: 1, - }); - - Menu.addMenuItem({ - menuName: "Edit", - menuItemName: "Entities", - isSeparator: true - }); - if (!Menu.menuItemExists("Edit", "Delete")) { - Menu.addMenuItem({ - menuName: "Edit", - menuItemName: "Delete", - shortcutKeyEvent: { - text: "delete" - }, - afterItem: "Entities", - }); - modelMenuAddedDelete = true; - } - - Menu.addMenuItem({ - menuName: "Edit", - menuItemName: "Parent Entity to Last", - afterItem: "Entities" - }); - - Menu.addMenuItem({ - menuName: "Edit", - menuItemName: "Unparent Entity", - afterItem: "Parent Entity to Last" - }); - - Menu.addMenuItem({ - menuName: GRABBABLE_ENTITIES_MENU_CATEGORY, - menuItemName: MENU_CREATE_ENTITIES_GRABBABLE, - afterItem: "Unparent Entity", - isCheckable: true, - isChecked: Settings.getValue(SETTING_EDIT_PREFIX + MENU_CREATE_ENTITIES_GRABBABLE, true) - }); - - Menu.addMenuItem({ - menuName: "Edit", - menuItemName: MENU_ALLOW_SELECTION_LARGE, - afterItem: MENU_CREATE_ENTITIES_GRABBABLE, - isCheckable: true, - isChecked: Settings.getValue(SETTING_EDIT_PREFIX + MENU_ALLOW_SELECTION_LARGE, true) - }); - Menu.addMenuItem({ - menuName: "Edit", - menuItemName: MENU_ALLOW_SELECTION_SMALL, - afterItem: MENU_ALLOW_SELECTION_LARGE, - isCheckable: true, - isChecked: Settings.getValue(SETTING_EDIT_PREFIX + MENU_ALLOW_SELECTION_SMALL, true) - }); - Menu.addMenuItem({ - menuName: "Edit", - menuItemName: MENU_ALLOW_SELECTION_LIGHTS, - afterItem: MENU_ALLOW_SELECTION_SMALL, - isCheckable: true, - isChecked: Settings.getValue(SETTING_EDIT_PREFIX + MENU_ALLOW_SELECTION_LIGHTS, false) - }); - Menu.addMenuItem({ - menuName: "Edit", - menuItemName: "Select All Entities In Box", - afterItem: "Allow Selecting of Lights" - }); - Menu.addMenuItem({ - menuName: "Edit", - menuItemName: "Select All Entities Touching Box", - afterItem: "Select All Entities In Box" - }); - - Menu.addMenuItem({ - menuName: "Edit", - menuItemName: "Export Entities", - afterItem: "Entities" - }); - Menu.addMenuItem({ - menuName: "Edit", - menuItemName: "Import Entities", - afterItem: "Export Entities" - }); - Menu.addMenuItem({ - menuName: "Edit", - menuItemName: "Import Entities from URL", - afterItem: "Import Entities" - }); - - Menu.addMenuItem({ - menuName: "Edit", - menuItemName: MENU_AUTO_FOCUS_ON_SELECT, - isCheckable: true, - isChecked: Settings.getValue(SETTING_AUTO_FOCUS_ON_SELECT) === "true" - }); - Menu.addMenuItem({ - menuName: "Edit", - menuItemName: MENU_EASE_ON_FOCUS, - afterItem: MENU_AUTO_FOCUS_ON_SELECT, - isCheckable: true, - isChecked: Settings.getValue(SETTING_EASE_ON_FOCUS) === "true" - }); - Menu.addMenuItem({ - menuName: "Edit", - menuItemName: MENU_SHOW_LIGHTS_AND_PARTICLES_IN_EDIT_MODE, - afterItem: MENU_EASE_ON_FOCUS, - isCheckable: true, - isChecked: Settings.getValue(SETTING_SHOW_LIGHTS_AND_PARTICLES_IN_EDIT_MODE) !== "false" - }); - Menu.addMenuItem({ - menuName: "Edit", - menuItemName: MENU_SHOW_ZONES_IN_EDIT_MODE, - afterItem: MENU_SHOW_LIGHTS_AND_PARTICLES_IN_EDIT_MODE, - isCheckable: true, - isChecked: Settings.getValue(SETTING_SHOW_ZONES_IN_EDIT_MODE) !== "false" - }); - - Entities.setLightsArePickable(false); -} - -setupModelMenus(); // do this when first running our script. - -function cleanupModelMenus() { - Menu.removeMenuItem("Edit", "Undo"); - Menu.removeMenuItem("Edit", "Redo"); - - Menu.removeSeparator("Edit", "Entities"); - if (modelMenuAddedDelete) { - // delete our menuitems - Menu.removeMenuItem("Edit", "Delete"); - } - - Menu.removeMenuItem("Edit", "Parent Entity to Last"); - Menu.removeMenuItem("Edit", "Unparent Entity"); - Menu.removeMenuItem("Edit", "Allow Selecting of Large Models"); - Menu.removeMenuItem("Edit", "Allow Selecting of Small Models"); - Menu.removeMenuItem("Edit", "Allow Selecting of Lights"); - Menu.removeMenuItem("Edit", "Select All Entities In Box"); - Menu.removeMenuItem("Edit", "Select All Entities Touching Box"); - - Menu.removeMenuItem("Edit", "Export Entities"); - Menu.removeMenuItem("Edit", "Import Entities"); - Menu.removeMenuItem("Edit", "Import Entities from URL"); - - Menu.removeMenuItem("Edit", MENU_AUTO_FOCUS_ON_SELECT); - Menu.removeMenuItem("Edit", MENU_EASE_ON_FOCUS); - Menu.removeMenuItem("Edit", MENU_SHOW_LIGHTS_AND_PARTICLES_IN_EDIT_MODE); - Menu.removeMenuItem("Edit", MENU_SHOW_ZONES_IN_EDIT_MODE); - Menu.removeMenuItem("Edit", MENU_CREATE_ENTITIES_GRABBABLE); -} - -Script.scriptEnding.connect(function () { - toolBar.setActive(false); - Settings.setValue(SETTING_AUTO_FOCUS_ON_SELECT, Menu.isOptionChecked(MENU_AUTO_FOCUS_ON_SELECT)); - Settings.setValue(SETTING_EASE_ON_FOCUS, Menu.isOptionChecked(MENU_EASE_ON_FOCUS)); - Settings.setValue(SETTING_SHOW_LIGHTS_AND_PARTICLES_IN_EDIT_MODE, Menu.isOptionChecked(MENU_SHOW_LIGHTS_AND_PARTICLES_IN_EDIT_MODE)); - Settings.setValue(SETTING_SHOW_ZONES_IN_EDIT_MODE, Menu.isOptionChecked(MENU_SHOW_ZONES_IN_EDIT_MODE)); - - Settings.setValue(SETTING_EDIT_PREFIX + MENU_ALLOW_SELECTION_LARGE, Menu.isOptionChecked(MENU_ALLOW_SELECTION_LARGE)); - Settings.setValue(SETTING_EDIT_PREFIX + MENU_ALLOW_SELECTION_SMALL, Menu.isOptionChecked(MENU_ALLOW_SELECTION_SMALL)); - Settings.setValue(SETTING_EDIT_PREFIX + MENU_ALLOW_SELECTION_LIGHTS, Menu.isOptionChecked(MENU_ALLOW_SELECTION_LIGHTS)); - - - progressDialog.cleanup(); - cleanupModelMenus(); - tooltip.cleanup(); - selectionDisplay.cleanup(); - entityShapeVisualizer.cleanup(); - Entities.setLightsArePickable(originalLightsArePickable); - - Overlays.deleteOverlay(importingSVOImageOverlay); - Overlays.deleteOverlay(importingSVOTextOverlay); - - Controller.keyReleaseEvent.disconnect(keyReleaseEvent); - Controller.keyPressEvent.disconnect(keyPressEvent); - - Controller.mousePressEvent.disconnect(mousePressEvent); - Controller.mouseMoveEvent.disconnect(mouseMoveEventBuffered); - Controller.mouseReleaseEvent.disconnect(mouseReleaseEvent); - - Messages.messageReceived.disconnect(handleMessagesReceived); - Messages.unsubscribe("entityToolUpdates"); - createButton = null; -}); - -var lastOrientation = null; -var lastPosition = null; - -// Do some stuff regularly, like check for placement of various overlays -Script.update.connect(function (deltaTime) { - progressDialog.move(); - selectionDisplay.checkControllerMove(); - var dOrientation = Math.abs(Quat.dot(Camera.orientation, lastOrientation) - 1); - var dPosition = Vec3.distance(Camera.position, lastPosition); - if (dOrientation > 0.001 || dPosition > 0.001) { - propertyMenu.hide(); - lastOrientation = Camera.orientation; - lastPosition = Camera.position; - } - if (lastMouseMoveEvent) { - mouseMove(lastMouseMoveEvent); - lastMouseMoveEvent = null; - } -}); - -function insideBox(center, dimensions, point) { - return (Math.abs(point.x - center.x) <= (dimensions.x / 2.0)) && - (Math.abs(point.y - center.y) <= (dimensions.y / 2.0)) && - (Math.abs(point.z - center.z) <= (dimensions.z / 2.0)); -} - -function selectAllEntitiesInCurrentSelectionBox(keepIfTouching) { - if (selectionManager.hasSelection()) { - // Get all entities touching the bounding box of the current selection - var boundingBoxCorner = Vec3.subtract(selectionManager.worldPosition, - Vec3.multiply(selectionManager.worldDimensions, 0.5)); - var entities = Entities.findEntitiesInBox(boundingBoxCorner, selectionManager.worldDimensions); - - if (!keepIfTouching) { - var isValid; - if (selectionManager.localPosition === null || selectionManager.localPosition === undefined) { - isValid = function (position) { - return insideBox(selectionManager.worldPosition, selectionManager.worldDimensions, position); - }; - } else { - isValid = function (position) { - var localPosition = Vec3.multiplyQbyV(Quat.inverse(selectionManager.localRotation), - Vec3.subtract(position, - selectionManager.localPosition)); - return insideBox(Vec3.ZERO, selectionManager.localDimensions, localPosition); - }; - } - for (var i = 0; i < entities.length; ++i) { - var properties = Entities.getEntityProperties(entities[i]); - if (!isValid(properties.position)) { - entities.splice(i, 1); - --i; - } - } - } - selectionManager.setSelections(entities, this); - } -} - -function sortSelectedEntities(selected) { - var sortedEntities = selected.slice(); - var begin = 0; - while (begin < sortedEntities.length) { - var elementRemoved = false; - var next = begin + 1; - while (next < sortedEntities.length) { - var beginID = sortedEntities[begin]; - var nextID = sortedEntities[next]; - - if (Entities.isChildOfParent(beginID, nextID)) { - sortedEntities[begin] = nextID; - sortedEntities[next] = beginID; - sortedEntities.splice(next, 1); - elementRemoved = true; - break; - } else if (Entities.isChildOfParent(nextID, beginID)) { - sortedEntities.splice(next, 1); - elementRemoved = true; - break; - } - next++; - } - if (!elementRemoved) { - begin++; - } - } - return sortedEntities; -} - -function recursiveDelete(entities, childrenList, deletedIDs, entityHostType) { - var wantDebug = false; - var entitiesLength = entities.length; - var initialPropertySets = Entities.getMultipleEntityProperties(entities); - var entityHostTypes = Entities.getMultipleEntityProperties(entities, 'entityHostType'); - for (var i = 0; i < entitiesLength; ++i) { - var entityID = entities[i]; - - if (entityHostTypes[i].entityHostType !== entityHostType) { - if (wantDebug) { - console.log("Skipping deletion of entity " + entityID + " with conflicting entityHostType: " + - entityHostTypes[i].entityHostType + ", expected: " + entityHostType); - } - continue; - } - - var children = Entities.getChildrenIDs(entityID); - var grandchildrenList = []; - recursiveDelete(children, grandchildrenList, deletedIDs, entityHostType); - childrenList.push({ - entityID: entityID, - properties: initialPropertySets[i], - children: grandchildrenList - }); - deletedIDs.push(entityID); - Entities.deleteEntity(entityID); - } -} - -function unparentSelectedEntities() { - if (SelectionManager.hasSelection()) { - var selectedEntities = selectionManager.selections; - var parentCheck = false; - - if (selectedEntities.length < 1) { - Window.notifyEditError("You must have an entity selected in order to unparent it."); - return; - } - selectedEntities.forEach(function (id, index) { - var parentId = Entities.getEntityProperties(id, ["parentID"]).parentID; - if (parentId !== null && parentId.length > 0 && parentId !== Uuid.NULL) { - parentCheck = true; - } - Entities.editEntity(id, {parentID: null}); - return true; - }); - if (parentCheck) { - if (selectedEntities.length > 1) { - Window.notify("Entities unparented"); - } else { - Window.notify("Entity unparented"); - } - } else { - if (selectedEntities.length > 1) { - Window.notify("Selected Entities have no parents"); - } else { - Window.notify("Selected Entity does not have a parent"); - } - } - } else { - Window.notifyEditError("You have nothing selected to unparent"); - } -} -function parentSelectedEntities() { - if (SelectionManager.hasSelection()) { - var selectedEntities = selectionManager.selections; - if (selectedEntities.length <= 1) { - Window.notifyEditError("You must have multiple entities selected in order to parent them"); - return; - } - var parentCheck = false; - var lastEntityId = selectedEntities[selectedEntities.length - 1]; - selectedEntities.forEach(function (id, index) { - if (lastEntityId !== id) { - var parentId = Entities.getEntityProperties(id, ["parentID"]).parentID; - if (parentId !== lastEntityId) { - parentCheck = true; - } - Entities.editEntity(id, {parentID: lastEntityId}); - } - }); - - if (parentCheck) { - Window.notify("Entities parented"); - } else { - Window.notify("Entities are already parented to last"); - } - } else { - Window.notifyEditError("You have nothing selected to parent"); - } -} -function deleteSelectedEntities() { - if (SelectionManager.hasSelection()) { - var deletedIDs = []; - - SelectionManager.saveProperties(); - var savedProperties = []; - var newSortedSelection = sortSelectedEntities(selectionManager.selections); - var entityHostTypes = Entities.getMultipleEntityProperties(newSortedSelection, 'entityHostType'); - for (var i = 0; i < newSortedSelection.length; ++i) { - var entityID = newSortedSelection[i]; - var initialProperties = SelectionManager.savedProperties[entityID]; - if (initialProperties.locked || - (initialProperties.avatarEntity && initialProperties.owningAvatarID !== MyAvatar.sessionUUID)) { - continue; - } - var children = Entities.getChildrenIDs(entityID); - var childList = []; - recursiveDelete(children, childList, deletedIDs, entityHostTypes[i].entityHostType); - savedProperties.push({ - entityID: entityID, - properties: initialProperties, - children: childList - }); - deletedIDs.push(entityID); - Entities.deleteEntity(entityID); - } - - if (savedProperties.length > 0) { - SelectionManager.clearSelections(this); - pushCommandForSelections([], savedProperties); - entityListTool.deleteEntities(deletedIDs); - } - } -} - -function toggleSelectedEntitiesLocked() { - if (SelectionManager.hasSelection()) { - var locked = !Entities.getEntityProperties(SelectionManager.selections[0], ["locked"]).locked; - for (var i = 0; i < selectionManager.selections.length; i++) { - var entityID = SelectionManager.selections[i]; - Entities.editEntity(entityID, { - locked: locked - }); - } - entityListTool.sendUpdate(); - selectionManager._update(false, this); - } -} - -function toggleSelectedEntitiesVisible() { - if (SelectionManager.hasSelection()) { - var visible = !Entities.getEntityProperties(SelectionManager.selections[0], ["visible"]).visible; - for (var i = 0; i < selectionManager.selections.length; i++) { - var entityID = SelectionManager.selections[i]; - Entities.editEntity(entityID, { - visible: visible - }); - } - entityListTool.sendUpdate(); - selectionManager._update(false, this); - } -} - -function onFileSaveChanged(filename) { - Window.saveFileChanged.disconnect(onFileSaveChanged); - if (filename !== "") { - var success = Clipboard.exportEntities(filename, selectionManager.selections); - if (!success) { - Window.notifyEditError("Export failed."); - } - } -} - -function onFileOpenChanged(filename) { - // disconnect the event, otherwise the requests will stack up - try { - // Not all calls to onFileOpenChanged() connect an event. - Window.browseChanged.disconnect(onFileOpenChanged); - } catch (e) { - // Ignore. - } - - var importURL = null; - if (filename !== "") { - importURL = filename; - if (!/^(http|https):\/\//.test(filename)) { - importURL = "file:///" + importURL; - } - } - if (importURL) { - if (!isActive && (Entities.canRez() && Entities.canRezTmp() && Entities.canRezCertified() && Entities.canRezTmpCertified())) { - toolBar.toggle(); - } - importSVO(importURL); - } -} - -function onPromptTextChanged(prompt) { - Window.promptTextChanged.disconnect(onPromptTextChanged); - if (prompt !== "") { - if (!isActive && (Entities.canRez() && Entities.canRezTmp() && Entities.canRezCertified() && Entities.canRezTmpCertified())) { - toolBar.toggle(); - } - importSVO(prompt); - } -} - -function handleMenuEvent(menuItem) { - if (menuItem === "Allow Selecting of Small Models") { - allowSmallModels = Menu.isOptionChecked("Allow Selecting of Small Models"); - } else if (menuItem === "Allow Selecting of Large Models") { - allowLargeModels = Menu.isOptionChecked("Allow Selecting of Large Models"); - } else if (menuItem === "Allow Selecting of Lights") { - Entities.setLightsArePickable(Menu.isOptionChecked("Allow Selecting of Lights")); - } else if (menuItem === "Delete") { - deleteSelectedEntities(); - } else if (menuItem === "Undo") { - undoHistory.undo(); - } else if (menuItem === "Redo") { - undoHistory.redo(); - } else if (menuItem === "Parent Entity to Last") { - parentSelectedEntities(); - } else if (menuItem === "Unparent Entity") { - unparentSelectedEntities(); - } else if (menuItem === "Export Entities") { - if (!selectionManager.hasSelection()) { - Window.notifyEditError("No entities have been selected."); - } else { - Window.saveFileChanged.connect(onFileSaveChanged); - Window.saveAsync("Select Where to Save", "", "*.json"); - } - } else if (menuItem === "Import Entities" || menuItem === "Import Entities from URL") { - if (menuItem === "Import Entities") { - Window.browseChanged.connect(onFileOpenChanged); - Window.browseAsync("Select Model to Import", "", "*.json"); - } else { - Window.promptTextChanged.connect(onPromptTextChanged); - Window.promptAsync("URL of SVO to import", ""); - } - } else if (menuItem === "Select All Entities In Box") { - selectAllEntitiesInCurrentSelectionBox(false); - } else if (menuItem === "Select All Entities Touching Box") { - selectAllEntitiesInCurrentSelectionBox(true); - } else if (menuItem === MENU_SHOW_LIGHTS_AND_PARTICLES_IN_EDIT_MODE) { - entityIconOverlayManager.setVisible(isActive && Menu.isOptionChecked(MENU_SHOW_LIGHTS_AND_PARTICLES_IN_EDIT_MODE)); - } else if (menuItem === MENU_SHOW_ZONES_IN_EDIT_MODE) { - Entities.setDrawZoneBoundaries(isActive && Menu.isOptionChecked(MENU_SHOW_ZONES_IN_EDIT_MODE)); - } else if (menuItem === MENU_CREATE_ENTITIES_GRABBABLE) { - Settings.setValue(SETTING_EDIT_PREFIX + menuItem, Menu.isOptionChecked(menuItem)); - } - tooltip.show(false); -} - -var HALF_TREE_SCALE = 16384; - -function getPositionToCreateEntity(extra) { - var CREATE_DISTANCE = 2; - var position; - var delta = extra !== undefined ? extra : 0; - if (Camera.mode === "entity" || Camera.mode === "independent") { - position = Vec3.sum(Camera.position, Vec3.multiply(Quat.getForward(Camera.orientation), CREATE_DISTANCE + delta)); - } else { - position = Vec3.sum(MyAvatar.position, Vec3.multiply(Quat.getForward(MyAvatar.orientation), CREATE_DISTANCE + delta)); - } - - if (position.x > HALF_TREE_SCALE || position.y > HALF_TREE_SCALE || position.z > HALF_TREE_SCALE) { - return null; - } - return position; -} - -function importSVO(importURL) { - if (!Entities.canRez() && !Entities.canRezTmp() && - !Entities.canRezCertified() && !Entities.canRezTmpCertified()) { - Window.notifyEditError(INSUFFICIENT_PERMISSIONS_IMPORT_ERROR_MSG); - return; - } - - Overlays.editOverlay(importingSVOTextOverlay, { - visible: true - }); - Overlays.editOverlay(importingSVOImageOverlay, { - visible: true - }); - - var success = Clipboard.importEntities(importURL); - - if (success) { - var VERY_LARGE = 10000; - var isLargeImport = Clipboard.getClipboardContentsLargestDimension() >= VERY_LARGE; - var position = Vec3.ZERO; - if (!isLargeImport) { - position = getPositionToCreateEntity(Clipboard.getClipboardContentsLargestDimension() / 2); - } - if (position !== null && position !== undefined) { - var pastedEntityIDs = Clipboard.pasteEntities(position); - if (!isLargeImport) { - // The first entity in Clipboard gets the specified position with the rest being relative to it. Therefore, move - // entities after they're imported so that they're all the correct distance in front of and with geometric mean - // centered on the avatar/camera direction. - var deltaPosition = Vec3.ZERO; - var entityPositions = []; - var entityParentIDs = []; - - var propType = Entities.getEntityProperties(pastedEntityIDs[0], ["type"]).type; - var NO_ADJUST_ENTITY_TYPES = ["Zone", "Light", "ParticleEffect"]; - if (NO_ADJUST_ENTITY_TYPES.indexOf(propType) === -1) { - var targetDirection; - if (Camera.mode === "entity" || Camera.mode === "independent") { - targetDirection = Camera.orientation; - } else { - targetDirection = MyAvatar.orientation; - } - targetDirection = Vec3.multiplyQbyV(targetDirection, Vec3.UNIT_Z); - - var targetPosition = getPositionToCreateEntity(); - var deltaParallel = HALF_TREE_SCALE; // Distance to move entities parallel to targetDirection. - var deltaPerpendicular = Vec3.ZERO; // Distance to move entities perpendicular to targetDirection. - for (var i = 0, length = pastedEntityIDs.length; i < length; i++) { - var curLoopEntityProps = Entities.getEntityProperties(pastedEntityIDs[i], ["position", "dimensions", - "registrationPoint", "rotation", "parentID"]); - var adjustedPosition = adjustPositionPerBoundingBox(targetPosition, targetDirection, - curLoopEntityProps.registrationPoint, curLoopEntityProps.dimensions, curLoopEntityProps.rotation); - var delta = Vec3.subtract(adjustedPosition, curLoopEntityProps.position); - var distance = Vec3.dot(delta, targetDirection); - deltaParallel = Math.min(distance, deltaParallel); - deltaPerpendicular = Vec3.sum(Vec3.subtract(delta, Vec3.multiply(distance, targetDirection)), - deltaPerpendicular); - entityPositions[i] = curLoopEntityProps.position; - entityParentIDs[i] = curLoopEntityProps.parentID; - } - deltaPerpendicular = Vec3.multiply(1 / pastedEntityIDs.length, deltaPerpendicular); - deltaPosition = Vec3.sum(Vec3.multiply(deltaParallel, targetDirection), deltaPerpendicular); - } - - if (grid.getSnapToGrid()) { - var firstEntityProps = Entities.getEntityProperties(pastedEntityIDs[0], ["position", "dimensions", - "registrationPoint"]); - var positionPreSnap = Vec3.sum(deltaPosition, firstEntityProps.position); - position = grid.snapToSurface(grid.snapToGrid(positionPreSnap, false, firstEntityProps.dimensions, - firstEntityProps.registrationPoint), firstEntityProps.dimensions, firstEntityProps.registrationPoint); - deltaPosition = Vec3.subtract(position, firstEntityProps.position); - } - - if (!Vec3.equal(deltaPosition, Vec3.ZERO)) { - for (var editEntityIndex = 0, numEntities = pastedEntityIDs.length; editEntityIndex < numEntities; editEntityIndex++) { - if (Uuid.isNull(entityParentIDs[editEntityIndex])) { - Entities.editEntity(pastedEntityIDs[editEntityIndex], { - position: Vec3.sum(deltaPosition, entityPositions[editEntityIndex]) - }); - } - } - } - } - - if (isActive) { - selectionManager.setSelections(pastedEntityIDs, this); - } - } else { - Window.notifyEditError("Can't import entities: entities would be out of bounds."); - } - } else { - Window.notifyEditError("There was an error importing the entity file."); - } - - Overlays.editOverlay(importingSVOTextOverlay, { - visible: false - }); - Overlays.editOverlay(importingSVOImageOverlay, { - visible: false - }); -} -Window.svoImportRequested.connect(importSVO); - -Menu.menuItemEvent.connect(handleMenuEvent); - -var keyPressEvent = function (event) { - if (isActive) { - cameraManager.keyPressEvent(event); - } -}; -var keyReleaseEvent = function (event) { - if (isActive) { - cameraManager.keyReleaseEvent(event); - } -}; -Controller.keyReleaseEvent.connect(keyReleaseEvent); -Controller.keyPressEvent.connect(keyPressEvent); - -function deleteKey(value) { - if (value === 0) { // on release - deleteSelectedEntities(); - } -} -function deselectKey(value) { - if (value === 0) { // on release - selectionManager.clearSelections(this); - } -} -function toggleKey(value) { - if (value === 0) { // on release - selectionDisplay.toggleSpaceMode(); - } -} -function focusKey(value) { - if (value === 0) { // on release - cameraManager.enable(); - if (selectionManager.hasSelection()) { - cameraManager.focus(selectionManager.worldPosition, selectionManager.worldDimensions, - Menu.isOptionChecked(MENU_EASE_ON_FOCUS)); - } - } -} -function gridKey(value) { - if (value === 0) { // on release - if (selectionManager.hasSelection()) { - grid.moveToSelection(); - } - } -} -function recursiveAdd(newParentID, parentData) { - if (parentData.children !== undefined) { - var children = parentData.children; - for (var i = 0; i < children.length; i++) { - var childProperties = children[i].properties; - childProperties.parentID = newParentID; - var newChildID = Entities.addEntity(childProperties); - recursiveAdd(newChildID, children[i]); - } - } -} - -var UndoHistory = function(onUpdate) { - this.history = []; - // The current position is the index of the last executed action in the history array. - // - // -1 0 1 2 3 <- position - // A B C D <- actions in history - // - // If our lastExecutedIndex is 1, the last executed action is B. - // If we undo, we undo B (index 1). If we redo, we redo C (index 2). - this.lastExecutedIndex = -1; - this.enabled = true; - this.onUpdate = onUpdate; -}; - -UndoHistory.prototype.pushCommand = function(undoFn, undoArgs, redoFn, redoArgs) { - if (!this.enabled) { - return; - } - // Delete any history following the last executed action. - this.history.splice(this.lastExecutedIndex + 1); - this.history.push({ - undoFn: undoFn, - undoArgs: undoArgs, - redoFn: redoFn, - redoArgs: redoArgs - }); - this.lastExecutedIndex++; - - if (this.onUpdate) { - this.onUpdate(); - } -}; -UndoHistory.prototype.setEnabled = function(enabled) { - this.enabled = enabled; - if (this.onUpdate) { - this.onUpdate(); - } -}; -UndoHistory.prototype.canUndo = function() { - return this.enabled && this.lastExecutedIndex >= 0; -}; -UndoHistory.prototype.canRedo = function() { - return this.enabled && this.lastExecutedIndex < this.history.length - 1; -}; -UndoHistory.prototype.undo = function() { - if (!this.canUndo()) { - console.warn("Cannot undo action"); - return; - } - - var command = this.history[this.lastExecutedIndex]; - command.undoFn(command.undoArgs); - this.lastExecutedIndex--; - - if (this.onUpdate) { - this.onUpdate(); - } -}; -UndoHistory.prototype.redo = function() { - if (!this.canRedo()) { - console.warn("Cannot redo action"); - return; - } - - var command = this.history[this.lastExecutedIndex + 1]; - command.redoFn(command.redoArgs); - this.lastExecutedIndex++; - - if (this.onUpdate) { - this.onUpdate(); - } -}; - -function updateUndoRedoMenuItems() { - Menu.setMenuEnabled("Edit > Undo", undoHistory.canUndo()); - Menu.setMenuEnabled("Edit > Redo", undoHistory.canRedo()); -} -var undoHistory = new UndoHistory(updateUndoRedoMenuItems); -updateUndoRedoMenuItems(); - -// When an entity has been deleted we need a way to "undo" this deletion. Because it's not currently -// possible to create an entity with a specific id, earlier undo commands to the deleted entity -// will fail if there isn't a way to find the new entity id. -var DELETED_ENTITY_MAP = {}; - -function applyEntityProperties(data) { - var editEntities = data.editEntities; - var createEntities = data.createEntities; - var deleteEntities = data.deleteEntities; - var selectedEntityIDs = []; - var selectEdits = createEntities.length === 0 || !data.selectCreated; - var i, entityID, entityProperties; - for (i = 0; i < createEntities.length; i++) { - entityID = createEntities[i].entityID; - entityProperties = createEntities[i].properties; - var newEntityID = Entities.addEntity(entityProperties); - recursiveAdd(newEntityID, createEntities[i]); - DELETED_ENTITY_MAP[entityID] = newEntityID; - if (data.selectCreated) { - selectedEntityIDs.push(newEntityID); - } - } - for (i = 0; i < deleteEntities.length; i++) { - entityID = deleteEntities[i].entityID; - if (DELETED_ENTITY_MAP[entityID] !== undefined) { - entityID = DELETED_ENTITY_MAP[entityID]; - } - Entities.deleteEntity(entityID); - var index = selectedEntityIDs.indexOf(entityID); - if (index >= 0) { - selectedEntityIDs.splice(index, 1); - } - } - for (i = 0; i < editEntities.length; i++) { - entityID = editEntities[i].entityID; - if (DELETED_ENTITY_MAP[entityID] !== undefined) { - entityID = DELETED_ENTITY_MAP[entityID]; - } - entityProperties = editEntities[i].properties; - if (entityProperties !== null) { - Entities.editEntity(entityID, entityProperties); - } - if (selectEdits) { - selectedEntityIDs.push(entityID); - } - } - - // We might be getting an undo while edit.js is disabled. If that is the case, don't set - // our selections, causing the edit widgets to display. - if (isActive) { - selectionManager.setSelections(selectedEntityIDs, this); - selectionManager.saveProperties(); - } -} - -// For currently selected entities, push a command to the UndoStack that uses the current entity properties for the -// redo command, and the saved properties for the undo command. Also, include create and delete entity data. -function pushCommandForSelections(createdEntityData, deletedEntityData, doNotSaveEditProperties) { - doNotSaveEditProperties = false; - var undoData = { - editEntities: [], - createEntities: deletedEntityData || [], - deleteEntities: createdEntityData || [], - selectCreated: true - }; - var redoData = { - editEntities: [], - createEntities: createdEntityData || [], - deleteEntities: deletedEntityData || [], - selectCreated: true - }; - for (var i = 0; i < SelectionManager.selections.length; i++) { - var entityID = SelectionManager.selections[i]; - var initialProperties = SelectionManager.savedProperties[entityID]; - var currentProperties = null; - if (!initialProperties) { - continue; - } - - if (doNotSaveEditProperties) { - initialProperties = null; - } else { - currentProperties = Entities.getEntityProperties(entityID); - } - - undoData.editEntities.push({ - entityID: entityID, - properties: initialProperties - }); - redoData.editEntities.push({ - entityID: entityID, - properties: currentProperties - }); - } - undoHistory.pushCommand(applyEntityProperties, undoData, applyEntityProperties, redoData); -} - -var ServerScriptStatusMonitor = function(entityID, statusCallback) { - var self = this; - - self.entityID = entityID; - self.active = true; - self.sendRequestTimerID = null; - - var onStatusReceived = function(success, isRunning, status, errorInfo) { - if (self.active) { - statusCallback({ - statusRetrieved: success, - isRunning: isRunning, - status: status, - errorInfo: errorInfo - }); - self.sendRequestTimerID = Script.setTimeout(function() { - if (self.active) { - Entities.getServerScriptStatus(entityID, onStatusReceived); - } - }, 1000); - } - }; - self.stop = function() { - self.active = false; - }; - - Entities.getServerScriptStatus(entityID, onStatusReceived); -}; - -var PropertiesTool = function (opts) { - var that = {}; - - var webView = null; - webView = Tablet.getTablet("com.highfidelity.interface.tablet.system"); - webView.setVisible = function(value) {}; - - var visible = false; - - // This keeps track of the last entity ID that was selected. If multiple entities - // are selected or if no entity is selected this will be `null`. - var currentSelectedEntityID = null; - var statusMonitor = null; - var blockPropertyUpdates = false; - - that.setVisible = function (newVisible) { - visible = newVisible; - webView.setVisible(shouldUseEditTabletApp() && visible); - createToolsWindow.setVisible(!shouldUseEditTabletApp() && visible); - }; - - that.setVisible(false); - - function emitScriptEvent(data) { - var dataString = JSON.stringify(data); - webView.emitScriptEvent(dataString); - createToolsWindow.emitScriptEvent(dataString); - } - - function updateScriptStatus(info) { - info.type = "server_script_status"; - emitScriptEvent(info); - } - - function resetScriptStatus() { - updateScriptStatus({ - statusRetrieved: undefined, - isRunning: undefined, - status: "", - errorInfo: "" - }); - } - - that.setSpaceMode = function(spaceMode) { - emitScriptEvent({ - type: 'setSpaceMode', - spaceMode: spaceMode - }) - }; - - function updateSelections(selectionUpdated, caller) { - if (blockPropertyUpdates) { - return; - } - - var data = { - type: 'update', - spaceMode: selectionDisplay.getSpaceMode(), - isPropertiesToolUpdate: caller === this, - }; - - if (selectionUpdated) { - resetScriptStatus(); - - if (selectionManager.selections.length !== 1) { - if (statusMonitor !== null) { - statusMonitor.stop(); - statusMonitor = null; - } - currentSelectedEntityID = null; - } else if (currentSelectedEntityID !== selectionManager.selections[0]) { - if (statusMonitor !== null) { - statusMonitor.stop(); - } - var entityID = selectionManager.selections[0]; - currentSelectedEntityID = entityID; - statusMonitor = new ServerScriptStatusMonitor(entityID, updateScriptStatus); - } - } - - var selections = []; - for (var i = 0; i < selectionManager.selections.length; i++) { - var entity = {}; - entity.id = selectionManager.selections[i]; - entity.properties = Entities.getEntityProperties(selectionManager.selections[i]); - if (entity.properties.rotation !== undefined) { - entity.properties.rotation = Quat.safeEulerAngles(entity.properties.rotation); - } - if (entity.properties.localRotation !== undefined) { - entity.properties.localRotation = Quat.safeEulerAngles(entity.properties.localRotation); - } - if (entity.properties.emitOrientation !== undefined) { - entity.properties.emitOrientation = Quat.safeEulerAngles(entity.properties.emitOrientation); - } - if (entity.properties.keyLight !== undefined && entity.properties.keyLight.direction !== undefined) { - entity.properties.keyLight.direction = Vec3.toPolar(entity.properties.keyLight.direction); - entity.properties.keyLight.direction.z = 0.0; - } - selections.push(entity); - } - data.selections = selections; - - emitScriptEvent(data); - } - selectionManager.addEventListener(updateSelections, this); - - - var onWebEventReceived = function(data) { - try { - data = JSON.parse(data); - } catch(e) { - return; - } - var i, properties, dY, diff, newPosition; - if (data.type === "update") { - - if (data.properties || data.propertiesMap) { - var propertiesMap = data.propertiesMap; - if (propertiesMap === undefined) { - propertiesMap = [{ - entityIDs: data.ids, - properties: data.properties, - }]; - } - - var sendListUpdate = false; - propertiesMap.forEach(function(propertiesObject) { - var properties = propertiesObject.properties; - var updateEntityIDs = propertiesObject.entityIDs; - if (properties.dynamic === false) { - // this object is leaving dynamic, so we zero its velocities - properties.localVelocity = Vec3.ZERO; - properties.localAngularVelocity = Vec3.ZERO; - } - if (properties.rotation !== undefined) { - properties.rotation = Quat.fromVec3Degrees(properties.rotation); - } - if (properties.localRotation !== undefined) { - properties.localRotation = Quat.fromVec3Degrees(properties.localRotation); - } - if (properties.emitOrientation !== undefined) { - properties.emitOrientation = Quat.fromVec3Degrees(properties.emitOrientation); - } - if (properties.keyLight !== undefined && properties.keyLight.direction !== undefined) { - var currentKeyLightDirection = Vec3.toPolar(Entities.getEntityProperties(selectionManager.selections[0], ['keyLight.direction']).keyLight.direction); - if (properties.keyLight.direction.x === undefined) { - properties.keyLight.direction.x = currentKeyLightDirection.x; - } - if (properties.keyLight.direction.y === undefined) { - properties.keyLight.direction.y = currentKeyLightDirection.y; - } - properties.keyLight.direction = Vec3.fromPolar(properties.keyLight.direction.x, properties.keyLight.direction.y); - } - - updateEntityIDs.forEach(function (entityID) { - Entities.editEntity(entityID, properties); - }); - - if (properties.name !== undefined || properties.modelURL !== undefined || properties.materialURL !== undefined || - properties.visible !== undefined || properties.locked !== undefined) { - - sendListUpdate = true; - } - - }); - if (sendListUpdate) { - entityListTool.sendUpdate(); - } - } - - - if (data.onlyUpdateEntities) { - blockPropertyUpdates = true; - } else { - pushCommandForSelections(); - SelectionManager.saveProperties(); - } - selectionManager._update(false, this); - blockPropertyUpdates = false; - } else if (data.type === 'saveUserData' || data.type === 'saveMaterialData') { - data.ids.forEach(function(entityID) { - Entities.editEntity(entityID, data.properties); - }); - } else if (data.type === "showMarketplace") { - showMarketplace(); - } else if (data.type === "action") { - if (data.action === "moveSelectionToGrid") { - if (selectionManager.hasSelection()) { - selectionManager.saveProperties(); - dY = grid.getOrigin().y - (selectionManager.worldPosition.y - selectionManager.worldDimensions.y / 2); - diff = { - x: 0, - y: dY, - z: 0 - }; - for (i = 0; i < selectionManager.selections.length; i++) { - properties = selectionManager.savedProperties[selectionManager.selections[i]]; - newPosition = Vec3.sum(properties.position, diff); - Entities.editEntity(selectionManager.selections[i], { - position: newPosition - }); - } - pushCommandForSelections(); - selectionManager._update(false, this); - } - } else if (data.action === "moveAllToGrid") { - if (selectionManager.hasSelection()) { - selectionManager.saveProperties(); - for (i = 0; i < selectionManager.selections.length; i++) { - properties = selectionManager.savedProperties[selectionManager.selections[i]]; - var bottomY = properties.boundingBox.center.y - properties.boundingBox.dimensions.y / 2; - dY = grid.getOrigin().y - bottomY; - diff = { - x: 0, - y: dY, - z: 0 - }; - newPosition = Vec3.sum(properties.position, diff); - Entities.editEntity(selectionManager.selections[i], { - position: newPosition - }); - } - pushCommandForSelections(); - selectionManager._update(false, this); - } - } else if (data.action === "resetToNaturalDimensions") { - if (selectionManager.hasSelection()) { - selectionManager.saveProperties(); - for (i = 0; i < selectionManager.selections.length; i++) { - properties = selectionManager.savedProperties[selectionManager.selections[i]]; - var naturalDimensions = properties.naturalDimensions; - - // If any of the natural dimensions are not 0, resize - if (properties.type === "Model" && naturalDimensions.x === 0 && naturalDimensions.y === 0 && - naturalDimensions.z === 0) { - Window.notifyEditError("Cannot reset entity to its natural dimensions: Model URL" + - " is invalid or the model has not yet been loaded."); - } else { - Entities.editEntity(selectionManager.selections[i], { - dimensions: properties.naturalDimensions - }); - } - } - pushCommandForSelections(); - selectionManager._update(false, this); - } - } else if (data.action === "previewCamera") { - if (selectionManager.hasSelection()) { - Camera.mode = "entity"; - Camera.cameraEntity = selectionManager.selections[0]; - } - } else if (data.action === "rescaleDimensions") { - var multiplier = data.percentage / 100.0; - if (selectionManager.hasSelection()) { - selectionManager.saveProperties(); - for (i = 0; i < selectionManager.selections.length; i++) { - properties = selectionManager.savedProperties[selectionManager.selections[i]]; - Entities.editEntity(selectionManager.selections[i], { - dimensions: Vec3.multiply(multiplier, properties.dimensions) - }); - } - pushCommandForSelections(); - selectionManager._update(false, this); - } - } else if (data.action === "reloadClientScripts") { - if (selectionManager.hasSelection()) { - var timestamp = Date.now(); - for (i = 0; i < selectionManager.selections.length; i++) { - Entities.editEntity(selectionManager.selections[i], { - scriptTimestamp: timestamp - }); - } - } - } else if (data.action === "reloadServerScripts") { - if (selectionManager.hasSelection()) { - for (i = 0; i < selectionManager.selections.length; i++) { - Entities.reloadServerScripts(selectionManager.selections[i]); - } - } - } - } else if (data.type === "propertiesPageReady") { - updateSelections(true); - } else if (data.type === "tooltipsRequest") { - emitScriptEvent({ - type: 'tooltipsReply', - tooltips: Script.require('./assets/data/createAppTooltips.json'), - hmdActive: HMD.active, - }); - } else if (data.type === "propertyRangeRequest") { - var propertyRanges = {}; - data.properties.forEach(function (property) { - propertyRanges[property] = Entities.getPropertyInfo(property); - }); - emitScriptEvent({ - type: 'propertyRangeReply', - propertyRanges: propertyRanges, - }); - } else if (data.type === "materialTargetRequest") { - var parentModelData; - var properties = Entities.getEntityProperties(data.entityID, ["type", "parentID"]); - if (properties.type === "Material" && properties.parentID !== Uuid.NULL) { - var parentType = Entities.getEntityProperties(properties.parentID, ["type"]).type; - if (parentType === "Model" || Entities.getNestableType(properties.parentID) === "avatar") { - parentModelData = Graphics.getModel(properties.parentID); - } else if (parentType === "Shape" || parentType === "Box" || parentType === "Sphere") { - parentModelData = {}; - parentModelData.numMeshes = 1; - parentModelData.materialNames = []; - } - } - emitScriptEvent({ - type: 'materialTargetReply', - entityID: data.entityID, - materialTargetData: parentModelData, - }); - } - }; - - HMD.displayModeChanged.connect(function() { - emitScriptEvent({ - type: 'hmdActiveChanged', - hmdActive: HMD.active, - }); - }); - - createToolsWindow.webEventReceived.addListener(this, onWebEventReceived); - - webView.webEventReceived.connect(this, onWebEventReceived); - - return that; -}; - - -var PopupMenu = function () { - var self = this; - - var MENU_ITEM_HEIGHT = 21; - var MENU_ITEM_SPACING = 1; - var TEXT_MARGIN = 7; - - var overlays = []; - var overlayInfo = {}; - - var visible = false; - - var upColor = { - red: 0, - green: 0, - blue: 0 - }; - var downColor = { - red: 192, - green: 192, - blue: 192 - }; - var overColor = { - red: 128, - green: 128, - blue: 128 - }; - - self.onSelectMenuItem = function () {}; - - self.addMenuItem = function (name) { - var id = Overlays.addOverlay("text", { - text: name, - backgroundAlpha: 1.0, - backgroundColor: upColor, - topMargin: TEXT_MARGIN, - leftMargin: TEXT_MARGIN, - width: 210, - height: MENU_ITEM_HEIGHT, - font: { - size: 12 - }, - visible: false - }); - overlays.push(id); - overlayInfo[id] = { - name: name - }; - return id; - }; - - self.updateMenuItemText = function (id, newText) { - Overlays.editOverlay(id, { - text: newText - }); - }; - - self.setPosition = function (x, y) { - for (var key in overlayInfo) { - Overlays.editOverlay(key, { - x: x, - y: y - }); - y += MENU_ITEM_HEIGHT + MENU_ITEM_SPACING; - } - }; - - self.onSelected = function () {}; - - var pressingOverlay = null; - var hoveringOverlay = null; - - self.mousePressEvent = function (event) { - if (event.isLeftButton) { - var overlay = Overlays.getOverlayAtPoint({ - x: event.x, - y: event.y - }); - if (overlay in overlayInfo) { - pressingOverlay = overlay; - Overlays.editOverlay(pressingOverlay, { - backgroundColor: downColor - }); - } else { - self.hide(); - } - return false; - } - }; - self.mouseMoveEvent = function (event) { - if (visible) { - var overlay = Overlays.getOverlayAtPoint({ - x: event.x, - y: event.y - }); - if (!pressingOverlay) { - if (hoveringOverlay !== null && overlay !== hoveringOverlay) { - Overlays.editOverlay(hoveringOverlay, { - backgroundColor: upColor - }); - hoveringOverlay = null; - } - if (overlay !== hoveringOverlay && overlay in overlayInfo) { - Overlays.editOverlay(overlay, { - backgroundColor: overColor - }); - hoveringOverlay = overlay; - } - } - } - return false; - }; - self.mouseReleaseEvent = function (event) { - var overlay = Overlays.getOverlayAtPoint({ - x: event.x, - y: event.y - }); - if (pressingOverlay !== null && pressingOverlay !== undefined) { - if (overlay === pressingOverlay) { - self.onSelectMenuItem(overlayInfo[overlay].name); - } - Overlays.editOverlay(pressingOverlay, { - backgroundColor: upColor - }); - pressingOverlay = null; - self.hide(); - } - }; - - self.setVisible = function (newVisible) { - if (newVisible !== visible) { - visible = newVisible; - for (var key in overlayInfo) { - Overlays.editOverlay(key, { - visible: newVisible - }); - } - } - }; - self.show = function () { - self.setVisible(true); - }; - self.hide = function () { - self.setVisible(false); - }; - - function cleanup() { - ContextOverlay.enabled = true; - for (var i = 0; i < overlays.length; i++) { - Overlays.deleteOverlay(overlays[i]); - } - Controller.mousePressEvent.disconnect(self.mousePressEvent); - Controller.mouseMoveEvent.disconnect(self.mouseMoveEvent); - Controller.mouseReleaseEvent.disconnect(self.mouseReleaseEvent); - - Entities.canRezChanged.disconnect(checkEditPermissionsAndUpdate); - Entities.canRezTmpChanged.disconnect(checkEditPermissionsAndUpdate); - Entities.canRezCertifiedChanged.disconnect(checkEditPermissionsAndUpdate); - Entities.canRezTmpCertifiedChanged.disconnect(checkEditPermissionsAndUpdate); - } - - Controller.mousePressEvent.connect(self.mousePressEvent); - Controller.mouseMoveEvent.connect(self.mouseMoveEvent); - Controller.mouseReleaseEvent.connect(self.mouseReleaseEvent); - Script.scriptEnding.connect(cleanup); - - return this; -}; - -function whenPressed(fn) { - return function(value) { - if (value > 0) { - fn(); - } - }; -} - -function whenReleased(fn) { - return function(value) { - if (value === 0) { - fn(); - } - }; -} - -var isOnMacPlatform = Controller.getValue(Controller.Hardware.Application.PlatformMac); - -var mapping = Controller.newMapping(CONTROLLER_MAPPING_NAME); -if (isOnMacPlatform) { - mapping.from([Controller.Hardware.Keyboard.Backspace]).to(deleteKey); -} else { - mapping.from([Controller.Hardware.Keyboard.Delete]).to(deleteKey); -} -mapping.from([Controller.Hardware.Keyboard.T]).to(toggleKey); -mapping.from([Controller.Hardware.Keyboard.F]).to(focusKey); -mapping.from([Controller.Hardware.Keyboard.G]).to(gridKey); -mapping.from([Controller.Hardware.Keyboard.X]) - .when([Controller.Hardware.Keyboard.Control]) - .to(whenReleased(function() { selectionManager.cutSelectedEntities() })); -mapping.from([Controller.Hardware.Keyboard.C]) - .when([Controller.Hardware.Keyboard.Control]) - .to(whenReleased(function() { selectionManager.copySelectedEntities() })); -mapping.from([Controller.Hardware.Keyboard.V]) - .when([Controller.Hardware.Keyboard.Control]) - .to(whenReleased(function() { selectionManager.pasteEntities() })); -mapping.from([Controller.Hardware.Keyboard.D]) - .when([Controller.Hardware.Keyboard.Control]) - .to(whenReleased(function() { selectionManager.duplicateSelection() })); - -// Bind undo to ctrl-shift-z to maintain backwards-compatibility -mapping.from([Controller.Hardware.Keyboard.Z]) - .when([Controller.Hardware.Keyboard.Control, Controller.Hardware.Keyboard.Shift]) - .to(whenPressed(function() { undoHistory.redo() })); - - -mapping.from([Controller.Hardware.Keyboard.P]) - .when([Controller.Hardware.Keyboard.Control, Controller.Hardware.Keyboard.Shift]) - .to(whenReleased(function() { unparentSelectedEntities(); })); - -mapping.from([Controller.Hardware.Keyboard.P]) - .when([Controller.Hardware.Keyboard.Control, !Controller.Hardware.Keyboard.Shift]) - .to(whenReleased(function() { parentSelectedEntities(); })); - -keyUpEventFromUIWindow = function(keyUpEvent) { - var WANT_DEBUG_MISSING_SHORTCUTS = false; - - var pressedValue = 0.0; - - if ((!isOnMacPlatform && keyUpEvent.keyCodeString === "Delete") - || (isOnMacPlatform && keyUpEvent.keyCodeString === "Backspace")) { - - deleteKey(pressedValue); - } else if (keyUpEvent.keyCodeString === "T") { - toggleKey(pressedValue); - } else if (keyUpEvent.keyCodeString === "F") { - focusKey(pressedValue); - } else if (keyUpEvent.keyCodeString === "G") { - gridKey(pressedValue); - } else if (keyUpEvent.controlKey && keyUpEvent.keyCodeString === "X") { - selectionManager.cutSelectedEntities(); - } else if (keyUpEvent.controlKey && keyUpEvent.keyCodeString === "C") { - selectionManager.copySelectedEntities(); - } else if (keyUpEvent.controlKey && keyUpEvent.keyCodeString === "V") { - selectionManager.pasteEntities(); - } else if (keyUpEvent.controlKey && keyUpEvent.keyCodeString === "D") { - selectionManager.duplicateSelection(); - } else if (!isOnMacPlatform && keyUpEvent.controlKey && !keyUpEvent.shiftKey && keyUpEvent.keyCodeString === "Z") { - undoHistory.undo(); // undo is only handled via handleMenuItem on Mac - } else if (keyUpEvent.controlKey && !keyUpEvent.shiftKey && keyUpEvent.keyCodeString === "P") { - parentSelectedEntities(); - } else if (keyUpEvent.controlKey && keyUpEvent.shiftKey && keyUpEvent.keyCodeString === "P") { - unparentSelectedEntities(); - } else if (!isOnMacPlatform && - ((keyUpEvent.controlKey && keyUpEvent.shiftKey && keyUpEvent.keyCodeString === "Z") || - (keyUpEvent.controlKey && keyUpEvent.keyCodeString === "Y"))) { - undoHistory.redo(); // redo is only handled via handleMenuItem on Mac - } else if (WANT_DEBUG_MISSING_SHORTCUTS) { - console.warn("unhandled key event: " + JSON.stringify(keyUpEvent)) - } -}; - -var propertyMenu = new PopupMenu(); - -propertyMenu.onSelectMenuItem = function (name) { - - if (propertyMenu.marketplaceID) { - showMarketplace(propertyMenu.marketplaceID); - } -}; - -var showMenuItem = propertyMenu.addMenuItem("Show in Marketplace"); - -var propertiesTool = new PropertiesTool(); - -selectionDisplay.onSpaceModeChange = function(spaceMode) { - entityListTool.setSpaceMode(spaceMode); - propertiesTool.setSpaceMode(spaceMode); -}; - -}()); // END LOCAL_SCOPE diff --git a/scripts/system/html/entityList.html b/scripts/system/html/entityList.html deleted file mode 100644 index 986e5c09b0..0000000000 --- a/scripts/system/html/entityList.html +++ /dev/null @@ -1,93 +0,0 @@ - - - - Entity List - - - - - - - - - - - - - -
- -
- - -
- - - -
-
-
-
-
- -
-
-
- -
- - -
-
-
-
- Y -
- -
- - -
-
-
- - - - -
-
-
- -
-
-
- -
-
-
- There are no entities to display. Please check your filters or create an entity to begin. -
-
-
- - - diff --git a/scripts/system/html/js/createAppTooltip.js b/scripts/system/html/js/createAppTooltip.js deleted file mode 100644 index 3eb206d8a3..0000000000 --- a/scripts/system/html/js/createAppTooltip.js +++ /dev/null @@ -1,116 +0,0 @@ -// createAppTooltip.js -// -// Created by Thijs Wenker on 17 Oct 2018 -// Copyright 2018 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html - -const CREATE_APP_TOOLTIP_OFFSET = 20; -const TOOLTIP_DELAY = 500; // ms -const TOOLTIP_DEBUG = false; - -function CreateAppTooltip() { - this._tooltipData = null; - this._tooltipDiv = null; - this._delayTimeout = null; - this._isEnabled = false; -} - -CreateAppTooltip.prototype = { - _tooltipData: null, - _tooltipDiv: null, - _delayTimeout: null, - _isEnabled: null, - - _removeTooltipIfExists: function() { - if (this._delayTimeout !== null) { - window.clearTimeout(this._delayTimeout); - this._delayTimeout = null; - } - - if (this._tooltipDiv !== null) { - this._tooltipDiv.remove(); - this._tooltipDiv = null; - } - }, - - setIsEnabled: function(isEnabled) { - this._isEnabled = isEnabled; - }, - - setTooltipData: function(tooltipData) { - this._tooltipData = tooltipData; - }, - - registerTooltipElement: function(element, tooltipID, jsPropertyName) { - element.addEventListener("mouseover", function() { - if (!this._isEnabled) { - return; - } - - this._removeTooltipIfExists(); - - this._delayTimeout = window.setTimeout(function() { - let tooltipData = this._tooltipData[tooltipID]; - - if (!tooltipData || tooltipData.tooltip === "") { - if (!TOOLTIP_DEBUG) { - return; - } - tooltipData = { tooltip: 'PLEASE SET THIS TOOLTIP' }; - } - - let elementRect = element.getBoundingClientRect(); - let elTip = document.createElement("div"); - elTip.className = "create-app-tooltip"; - - let elTipDescription = document.createElement("div"); - elTipDescription.className = "create-app-tooltip-description"; - elTipDescription.innerText = tooltipData.tooltip; - elTip.appendChild(elTipDescription); - - let jsAttribute = jsPropertyName; - if (tooltipData.jsPropertyName) { - jsAttribute = tooltipData.jsPropertyName; - } - - if (!tooltipData.skipJSProperty) { - let elTipJSAttribute = document.createElement("div"); - elTipJSAttribute.className = "create-app-tooltip-js-attribute"; - elTipJSAttribute.innerText = `JS Attribute: ${jsAttribute}`; - elTip.appendChild(elTipJSAttribute); - } - - document.body.appendChild(elTip); - - let elementTop = window.pageYOffset + elementRect.top; - - let desiredTooltipTop = elementTop + element.clientHeight + CREATE_APP_TOOLTIP_OFFSET; - let desiredTooltipLeft = window.pageXOffset + elementRect.left; - - if ((window.innerHeight + window.pageYOffset) < (desiredTooltipTop + elTip.clientHeight)) { - // show above when otherwise out of bounds - elTip.style.top = elementTop - CREATE_APP_TOOLTIP_OFFSET - elTip.clientHeight; - } else { - // show tooltip below by default - elTip.style.top = desiredTooltipTop; - } - if ((window.innerWidth + window.pageXOffset) < (desiredTooltipLeft + elTip.clientWidth)) { - elTip.style.left = document.body.clientWidth + window.pageXOffset - elTip.offsetWidth; - } else { - elTip.style.left = desiredTooltipLeft; - } - - this._tooltipDiv = elTip; - }.bind(this), TOOLTIP_DELAY); - }.bind(this), false); - element.addEventListener("mouseout", function() { - if (!this._isEnabled) { - return; - } - - this._removeTooltipIfExists(); - }.bind(this), false); - } -}; diff --git a/scripts/system/libraries/EditEntityList.qml b/scripts/system/libraries/EditEntityList.qml deleted file mode 100644 index 4fc5ff19ef..0000000000 --- a/scripts/system/libraries/EditEntityList.qml +++ /dev/null @@ -1,12 +0,0 @@ -import QtQuick 2.7 -import QtQuick.Controls 2.2 -import QtWebChannel 1.0 -import QtGraphicalEffects 1.0 -import "qrc:///qml/controls" as HifiControls - -HifiControls.WebView { - id: entityListToolWebView - url: Qt.resolvedUrl("../html/entityList.html") - enabled: true - blurOnCtrlShift: false -} diff --git a/scripts/system/libraries/entityList.js b/scripts/system/libraries/entityList.js deleted file mode 100644 index 585e4e06a5..0000000000 --- a/scripts/system/libraries/entityList.js +++ /dev/null @@ -1,330 +0,0 @@ -"use strict"; - -// entityList.js -// -// Copyright 2014 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -/* global EntityListTool, Tablet, selectionManager, Entities, Camera, MyAvatar, Vec3, Menu, Messages, - cameraManager, MENU_EASE_ON_FOCUS, deleteSelectedEntities, toggleSelectedEntitiesLocked, toggleSelectedEntitiesVisible, - keyUpEventFromUIWindow, Script, SelectionDisplay, SelectionManager, Clipboard */ - -var PROFILING_ENABLED = false; -var profileIndent = ''; -const PROFILE_NOOP = function(_name, fn, args) { - fn.apply(this, args); -}; -const PROFILE = !PROFILING_ENABLED ? PROFILE_NOOP : function(name, fn, args) { - console.log("PROFILE-Script " + profileIndent + "(" + name + ") Begin"); - var previousIndent = profileIndent; - profileIndent += ' '; - var before = Date.now(); - fn.apply(this, args); - var delta = Date.now() - before; - profileIndent = previousIndent; - console.log("PROFILE-Script " + profileIndent + "(" + name + ") End " + delta + "ms"); -}; - -EntityListTool = function(shouldUseEditTabletApp) { - var that = {}; - - var CreateWindow = Script.require('../modules/createWindow.js'); - - var TITLE_OFFSET = 60; - var ENTITY_LIST_WIDTH = 495; - var MAX_DEFAULT_CREATE_TOOLS_HEIGHT = 778; - var entityListWindow = new CreateWindow( - Script.resolvePath("../create/EditEntityList.qml"), - 'Entity List', - 'com.highfidelity.create.entityListWindow', - function () { - var windowHeight = Window.innerHeight - TITLE_OFFSET; - if (windowHeight > MAX_DEFAULT_CREATE_TOOLS_HEIGHT) { - windowHeight = MAX_DEFAULT_CREATE_TOOLS_HEIGHT; - } - return { - size: { - x: ENTITY_LIST_WIDTH, - y: windowHeight - }, - position: { - x: Window.x, - y: Window.y + TITLE_OFFSET - } - }; - }, - false - ); - - var webView = null; - webView = Tablet.getTablet("com.highfidelity.interface.tablet.system"); - webView.setVisible = function(value){ }; - - var filterInView = false; - var searchRadius = 100; - - var visible = false; - - that.webView = webView; - - that.setVisible = function(newVisible) { - visible = newVisible; - webView.setVisible(shouldUseEditTabletApp() && visible); - entityListWindow.setVisible(!shouldUseEditTabletApp() && visible); - }; - - that.isVisible = function() { - return entityListWindow.isVisible(); - }; - - that.setVisible(false); - - function emitJSONScriptEvent(data) { - var dataString; - PROFILE("Script-JSON.stringify", function() { - dataString = JSON.stringify(data); - }); - PROFILE("Script-emitScriptEvent", function() { - webView.emitScriptEvent(dataString); - if (entityListWindow.window) { - entityListWindow.window.emitScriptEvent(dataString); - } - }); - } - - that.toggleVisible = function() { - that.setVisible(!visible); - }; - - selectionManager.addEventListener(function(isSelectionUpdate, caller) { - if (caller === that) { - // ignore events that we emitted from the entity list itself - return; - } - var selectedIDs = []; - - for (var i = 0; i < selectionManager.selections.length; i++) { - selectedIDs.push(selectionManager.selections[i]); - } - - emitJSONScriptEvent({ - type: 'selectionUpdate', - selectedIDs: selectedIDs - }); - }); - - that.setSpaceMode = function(spaceMode) { - emitJSONScriptEvent({ - type: 'setSpaceMode', - spaceMode: spaceMode - }); - }; - - that.clearEntityList = function() { - emitJSONScriptEvent({ - type: 'clearEntityList' - }); - }; - - that.removeEntities = function (deletedIDs, selectedIDs) { - emitJSONScriptEvent({ - type: 'removeEntities', - deletedIDs: deletedIDs, - selectedIDs: selectedIDs - }); - }; - - that.deleteEntities = function (deletedIDs) { - emitJSONScriptEvent({ - type: "deleted", - ids: deletedIDs - }); - }; - - function valueIfDefined(value) { - return value !== undefined ? value : ""; - } - - function entityIsBaked(properties) { - if (properties.type === "Model") { - var lowerModelURL = properties.modelURL.toLowerCase(); - return lowerModelURL.endsWith(".baked.fbx") || lowerModelURL.endsWith(".baked.fst"); - } else if (properties.type === "Zone") { - var lowerSkyboxURL = properties.skybox ? properties.skybox.url.toLowerCase() : ""; - var lowerAmbientURL = properties.ambientLight ? properties.ambientLight.ambientURL.toLowerCase() : ""; - return (lowerSkyboxURL === "" || lowerSkyboxURL.endsWith(".texmeta.json")) && - (lowerAmbientURL === "" || lowerAmbientURL.endsWith(".texmeta.json")); - } else { - return false; - } - } - - that.sendUpdate = function() { - PROFILE('Script-sendUpdate', function() { - var entities = []; - - var ids; - PROFILE("findEntities", function() { - if (filterInView) { - ids = Entities.findEntitiesInFrustum(Camera.frustum); - } else { - ids = Entities.findEntities(MyAvatar.position, searchRadius); - } - }); - - var cameraPosition = Camera.position; - PROFILE("getMultipleProperties", function () { - var multipleProperties = Entities.getMultipleEntityProperties(ids, ['name', 'type', 'locked', - 'visible', 'renderInfo', 'modelURL', 'materialURL', 'imageURL', 'script', 'certificateID', - 'skybox.url', 'ambientLight.url']); - for (var i = 0; i < multipleProperties.length; i++) { - var properties = multipleProperties[i]; - - if (!filterInView || Vec3.distance(properties.position, cameraPosition) <= searchRadius) { - var url = ""; - if (properties.type === "Model") { - url = properties.modelURL; - } else if (properties.type === "Material") { - url = properties.materialURL; - } else if (properties.type === "Image") { - url = properties.imageURL; - } - entities.push({ - id: ids[i], - name: properties.name, - type: properties.type, - url: url, - locked: properties.locked, - visible: properties.visible, - certificateID: properties.certificateID, - verticesCount: (properties.renderInfo !== undefined ? - valueIfDefined(properties.renderInfo.verticesCount) : ""), - texturesCount: (properties.renderInfo !== undefined ? - valueIfDefined(properties.renderInfo.texturesCount) : ""), - texturesSize: (properties.renderInfo !== undefined ? - valueIfDefined(properties.renderInfo.texturesSize) : ""), - hasTransparent: (properties.renderInfo !== undefined ? - valueIfDefined(properties.renderInfo.hasTransparent) : ""), - isBaked: entityIsBaked(properties), - drawCalls: (properties.renderInfo !== undefined ? - valueIfDefined(properties.renderInfo.drawCalls) : ""), - hasScript: properties.script !== "" - }); - } - } - }); - - var selectedIDs = []; - for (var j = 0; j < selectionManager.selections.length; j++) { - selectedIDs.push(selectionManager.selections[j]); - } - - emitJSONScriptEvent({ - type: "update", - entities: entities, - selectedIDs: selectedIDs, - spaceMode: SelectionDisplay.getSpaceMode(), - }); - }); - }; - - function onFileSaveChanged(filename) { - Window.saveFileChanged.disconnect(onFileSaveChanged); - if (filename !== "") { - var success = Clipboard.exportEntities(filename, selectionManager.selections); - if (!success) { - Window.notifyEditError("Export failed."); - } - } - } - - var onWebEventReceived = function(data) { - try { - data = JSON.parse(data); - } catch(e) { - print("entityList.js: Error parsing JSON"); - return; - } - - if (data.type === "selectionUpdate") { - var ids = data.entityIds; - var entityIDs = []; - for (var i = 0; i < ids.length; i++) { - entityIDs.push(ids[i]); - } - selectionManager.setSelections(entityIDs, that); - if (data.focus) { - cameraManager.enable(); - cameraManager.focus(selectionManager.worldPosition, - selectionManager.worldDimensions, - Menu.isOptionChecked(MENU_EASE_ON_FOCUS)); - } - } else if (data.type === "refresh") { - that.sendUpdate(); - } else if (data.type === "teleport") { - if (selectionManager.hasSelection()) { - MyAvatar.position = selectionManager.worldPosition; - } - } else if (data.type === "export") { - if (!selectionManager.hasSelection()) { - Window.notifyEditError("No entities have been selected."); - } else { - Window.saveFileChanged.connect(onFileSaveChanged); - Window.saveAsync("Select Where to Save", "", "*.json"); - } - } else if (data.type === "pal") { - var sessionIds = {}; // Collect the sessionsIds of all selected entities, w/o duplicates. - selectionManager.selections.forEach(function (id) { - var lastEditedBy = Entities.getEntityProperties(id, 'lastEditedBy').lastEditedBy; - if (lastEditedBy) { - sessionIds[lastEditedBy] = true; - } - }); - var dedupped = Object.keys(sessionIds); - if (!selectionManager.selections.length) { - Window.alert('No objects selected.'); - } else if (!dedupped.length) { - Window.alert('There were no recent users of the ' + selectionManager.selections.length + ' selected objects.'); - } else { - // No need to subscribe if we're just sending. - Messages.sendMessage('com.highfidelity.pal', JSON.stringify({method: 'select', params: [dedupped, true, false]}), 'local'); - } - } else if (data.type === "delete") { - deleteSelectedEntities(); - } else if (data.type === "toggleLocked") { - toggleSelectedEntitiesLocked(); - } else if (data.type === "toggleVisible") { - toggleSelectedEntitiesVisible(); - } else if (data.type === "filterInView") { - filterInView = data.filterInView === true; - } else if (data.type === "radius") { - searchRadius = data.radius; - } else if (data.type === "cut") { - SelectionManager.cutSelectedEntities(); - } else if (data.type === "copy") { - SelectionManager.copySelectedEntities(); - } else if (data.type === "paste") { - SelectionManager.pasteEntities(); - } else if (data.type === "duplicate") { - SelectionManager.duplicateSelection(); - that.sendUpdate(); - } else if (data.type === "rename") { - Entities.editEntity(data.entityID, {name: data.name}); - // make sure that the name also gets updated in the properties window - SelectionManager._update(); - } else if (data.type === "toggleSpaceMode") { - SelectionDisplay.toggleSpaceMode(); - } else if (data.type === 'keyUpEvent') { - keyUpEventFromUIWindow(data.keyUpEvent); - } - }; - - webView.webEventReceived.connect(onWebEventReceived); - entityListWindow.webEventReceived.addListener(onWebEventReceived); - that.interactiveWindowHidden = entityListWindow.interactiveWindowHidden; - - return that; -}; diff --git a/scripts/system/libraries/entitySelectionTool.js b/scripts/system/libraries/entitySelectionTool.js deleted file mode 100644 index 3fdc1d6652..0000000000 --- a/scripts/system/libraries/entitySelectionTool.js +++ /dev/null @@ -1,2925 +0,0 @@ -// -// entitySelectionTool.js -// examples -// -// Created by Brad hefta-Gaub on 10/1/14. -// Modified by Daniela Fontes * @DanielaFifo and Tiago Andrade @TagoWill on 4/7/2017 -// Modified by David Back on 1/9/2018 -// Copyright 2014 High Fidelity, Inc. -// -// This script implements a class useful for building tools for editing entities. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -/* global SelectionManager, SelectionDisplay, grid, rayPlaneIntersection, rayPlaneIntersection2, pushCommandForSelections, - getMainTabletIDs, getControllerWorldLocation, TRIGGER_ON_VALUE */ - -const SPACE_LOCAL = "local"; -const SPACE_WORLD = "world"; -const HIGHLIGHT_LIST_NAME = "editHandleHighlightList"; - -Script.include([ - "./controllers.js", - "./controllerDispatcherUtils.js", - "./utils.js" -]); - - -function deepCopy(v) { - return JSON.parse(JSON.stringify(v)); -} - -SelectionManager = (function() { - var that = {}; - - // FUNCTION: SUBSCRIBE TO UPDATE MESSAGES - function subscribeToUpdateMessages() { - Messages.subscribe("entityToolUpdates"); - Messages.messageReceived.connect(handleEntitySelectionToolUpdates); - } - - // FUNCTION: HANDLE ENTITY SELECTION TOOL UPDATES - function handleEntitySelectionToolUpdates(channel, message, sender) { - if (channel !== 'entityToolUpdates') { - return; - } - if (sender !== MyAvatar.sessionUUID) { - return; - } - - var wantDebug = false; - var messageParsed; - try { - messageParsed = JSON.parse(message); - } catch (err) { - print("ERROR: entitySelectionTool.handleEntitySelectionToolUpdates - got malformed message"); - return; - } - - if (messageParsed.method === "selectEntity") { - if (!SelectionDisplay.triggered() || SelectionDisplay.triggeredHand === messageParsed.hand) { - if (wantDebug) { - print("setting selection to " + messageParsed.entityID); - } - that.setSelections([messageParsed.entityID], that); - } - } else if (messageParsed.method === "clearSelection") { - if (!SelectionDisplay.triggered() || SelectionDisplay.triggeredHand === messageParsed.hand) { - that.clearSelections(); - } - } else if (messageParsed.method === "pointingAt") { - if (messageParsed.hand === Controller.Standard.RightHand) { - that.pointingAtDesktopWindowRight = messageParsed.desktopWindow; - that.pointingAtTabletRight = messageParsed.tablet; - } else { - that.pointingAtDesktopWindowLeft = messageParsed.desktopWindow; - that.pointingAtTabletLeft = messageParsed.tablet; - } - } - } - - subscribeToUpdateMessages(); - - // disabling this for now as it is causing rendering issues with the other handle overlays - /* - var COLOR_ORANGE_HIGHLIGHT = { red: 255, green: 99, blue: 9 }; - var editHandleOutlineStyle = { - outlineUnoccludedColor: COLOR_ORANGE_HIGHLIGHT, - outlineOccludedColor: COLOR_ORANGE_HIGHLIGHT, - fillUnoccludedColor: COLOR_ORANGE_HIGHLIGHT, - fillOccludedColor: COLOR_ORANGE_HIGHLIGHT, - outlineUnoccludedAlpha: 1, - outlineOccludedAlpha: 0, - fillUnoccludedAlpha: 0, - fillOccludedAlpha: 0, - outlineWidth: 3, - isOutlineSmooth: true - }; - Selection.enableListHighlight(HIGHLIGHT_LIST_NAME, editHandleOutlineStyle); - */ - - that.savedProperties = {}; - that.selections = []; - var listeners = []; - - that.localRotation = Quat.IDENTITY; - that.localPosition = Vec3.ZERO; - that.localDimensions = Vec3.ZERO; - that.localRegistrationPoint = Vec3.HALF; - - that.worldRotation = Quat.IDENTITY; - that.worldPosition = Vec3.ZERO; - that.worldDimensions = Vec3.ZERO; - that.worldRegistrationPoint = Vec3.HALF; - that.centerPosition = Vec3.ZERO; - - that.pointingAtDesktopWindowLeft = false; - that.pointingAtDesktopWindowRight = false; - that.pointingAtTabletLeft = false; - that.pointingAtTabletRight = false; - - that.saveProperties = function() { - that.savedProperties = {}; - for (var i = 0; i < that.selections.length; i++) { - var entityID = that.selections[i]; - that.savedProperties[entityID] = Entities.getEntityProperties(entityID); - } - }; - - that.addEventListener = function(func, thisContext) { - listeners.push({ - callback: func, - thisContext: thisContext - }); - }; - - that.hasSelection = function() { - return that.selections.length > 0; - }; - - that.setSelections = function(entityIDs, caller) { - that.selections = []; - for (var i = 0; i < entityIDs.length; i++) { - var entityID = entityIDs[i]; - that.selections.push(entityID); - Selection.addToSelectedItemsList(HIGHLIGHT_LIST_NAME, "entity", entityID); - } - - that._update(true, caller); - }; - - that.addEntity = function(entityID, toggleSelection, caller) { - if (entityID) { - var idx = -1; - for (var i = 0; i < that.selections.length; i++) { - if (entityID === that.selections[i]) { - idx = i; - break; - } - } - if (idx === -1) { - that.selections.push(entityID); - Selection.addToSelectedItemsList(HIGHLIGHT_LIST_NAME, "entity", entityID); - } else if (toggleSelection) { - that.selections.splice(idx, 1); - Selection.removeFromSelectedItemsList(HIGHLIGHT_LIST_NAME, "entity", entityID); - } - } - - that._update(true, caller); - }; - - function removeEntityByID(entityID) { - var idx = that.selections.indexOf(entityID); - if (idx >= 0) { - that.selections.splice(idx, 1); - Selection.removeFromSelectedItemsList(HIGHLIGHT_LIST_NAME, "entity", entityID); - } - } - - that.removeEntity = function (entityID, caller) { - removeEntityByID(entityID); - that._update(true, caller); - }; - - that.removeEntities = function(entityIDs, caller) { - for (var i = 0, length = entityIDs.length; i < length; i++) { - removeEntityByID(entityIDs[i]); - } - that._update(true, caller); - }; - - that.clearSelections = function(caller) { - that.selections = []; - that._update(true, caller); - }; - - that.addChildrenEntities = function(parentEntityID, entityList, entityHostType) { - var wantDebug = false; - var children = Entities.getChildrenIDs(parentEntityID); - var entityHostTypes = Entities.getMultipleEntityProperties(children, 'entityHostType'); - for (var i = 0; i < children.length; i++) { - var childID = children[i]; - - if (entityHostTypes[i].entityHostType !== entityHostType) { - if (wantDebug) { - console.log("Skipping addition of entity " + childID + " with conflicting entityHostType: " + - entityHostTypes[i].entityHostType + ", expected: " + entityHostType); - } - continue; - } - - if (entityList.indexOf(childID) < 0) { - entityList.push(childID); - } - that.addChildrenEntities(childID, entityList, entityHostType); - } - }; - - // Determine if an entity is being grabbed. - // This is mostly a heuristic - there is no perfect way to know if an entity is being - // grabbed. - // - // @return {boolean} true if the given entity with `properties` is being grabbed by an avatar - function nonDynamicEntityIsBeingGrabbedByAvatar(properties) { - if (properties.dynamic || Uuid.isNull(properties.parentID)) { - return false; - } - - var avatar = AvatarList.getAvatar(properties.parentID); - if (Uuid.isNull(avatar.sessionUUID)) { - return false; - } - - var grabJointNames = [ - 'RightHand', 'LeftHand', - '_CONTROLLER_RIGHTHAND', '_CONTROLLER_LEFTHAND', - '_CAMERA_RELATIVE_CONTROLLER_RIGHTHAND', '_CAMERA_RELATIVE_CONTROLLER_LEFTHAND', - '_FARGRAB_RIGHTHAND', '_FARGRAB_LEFTHAND', '_FARGRAB_MOUSE' - ]; - - for (var i = 0; i < grabJointNames.length; ++i) { - if (avatar.getJointIndex(grabJointNames[i]) === properties.parentJointIndex) { - return true; - } - } - - return false; - } - - var entityClipboard = { - entities: {}, // Map of id -> properties for copied entities - position: { x: 0, y: 0, z: 0 }, - dimensions: { x: 0, y: 0, z: 0 }, - }; - - that.duplicateSelection = function() { - var entitiesToDuplicate = []; - var duplicatedEntityIDs = []; - var duplicatedChildrenWithOldParents = []; - var originalEntityToNewEntityID = []; - - SelectionManager.saveProperties(); - - // build list of entities to duplicate by including any unselected children of selected parent entities - var originalEntityIDs = Object.keys(that.savedProperties); - var entityHostTypes = Entities.getMultipleEntityProperties(originalEntityIDs, 'entityHostType'); - for (var i = 0; i < originalEntityIDs.length; i++) { - var originalEntityID = originalEntityIDs[i]; - if (entitiesToDuplicate.indexOf(originalEntityID) === -1) { - entitiesToDuplicate.push(originalEntityID); - } - that.addChildrenEntities(originalEntityID, entitiesToDuplicate, entityHostTypes[i].entityHostType); - } - - // duplicate entities from above and store their original to new entity mappings and children needing re-parenting - for (var i = 0; i < entitiesToDuplicate.length; i++) { - var originalEntityID = entitiesToDuplicate[i]; - var properties = that.savedProperties[originalEntityID]; - if (properties === undefined) { - properties = Entities.getEntityProperties(originalEntityID); - } - if (!properties.locked && (!properties.avatarEntity || properties.owningAvatarID === MyAvatar.sessionUUID)) { - if (nonDynamicEntityIsBeingGrabbedByAvatar(properties)) { - properties.parentID = null; - properties.parentJointIndex = null; - properties.localPosition = properties.position; - properties.localRotation = properties.rotation; - } - - properties.localVelocity = Vec3.ZERO; - properties.localAngularVelocity = Vec3.ZERO; - - delete properties.actionData; - var newEntityID = Entities.addEntity(properties); - - // Re-apply actions from the original entity - var actionIDs = Entities.getActionIDs(properties.id); - for (var j = 0; j < actionIDs.length; ++j) { - var actionID = actionIDs[j]; - var actionArguments = Entities.getActionArguments(properties.id, actionID); - if (actionArguments) { - var type = actionArguments.type; - if (type === 'hold' || type === 'far-grab') { - continue; - } - delete actionArguments.ttl; - Entities.addAction(type, newEntityID, actionArguments); - } - } - - duplicatedEntityIDs.push({ - entityID: newEntityID, - properties: properties - }); - if (properties.parentID !== Uuid.NULL) { - duplicatedChildrenWithOldParents[newEntityID] = properties.parentID; - } - originalEntityToNewEntityID[originalEntityID] = newEntityID; - } - } - - // re-parent duplicated children to the duplicate entities of their original parents (if they were duplicated) - Object.keys(duplicatedChildrenWithOldParents).forEach(function(childIDNeedingNewParent) { - var originalParentID = duplicatedChildrenWithOldParents[childIDNeedingNewParent]; - var newParentID = originalEntityToNewEntityID[originalParentID]; - if (newParentID) { - Entities.editEntity(childIDNeedingNewParent, { parentID: newParentID }); - for (var i = 0; i < duplicatedEntityIDs.length; i++) { - var duplicatedEntity = duplicatedEntityIDs[i]; - if (duplicatedEntity.entityID === childIDNeedingNewParent) { - duplicatedEntity.properties.parentID = newParentID; - } - } - } - }); - - return duplicatedEntityIDs; - }; - - // Create the entities in entityProperties, maintaining parent-child relationships. - // @param entityProperties {array} - Array of entity property objects - that.createEntities = function(entityProperties) { - var entitiesToCreate = []; - var createdEntityIDs = []; - var createdChildrenWithOldParents = []; - var originalEntityToNewEntityID = []; - - that.saveProperties(); - - for (var i = 0; i < entityProperties.length; ++i) { - var properties = entityProperties[i]; - if (properties.parentID in originalEntityToNewEntityID) { - properties.parentID = originalEntityToNewEntityID[properties.parentID]; - } else { - delete properties.parentID; - } - - delete properties.actionData; - var newEntityID = Entities.addEntity(properties); - - if (newEntityID) { - createdEntityIDs.push({ - entityID: newEntityID, - properties: properties - }); - if (properties.parentID !== Uuid.NULL) { - createdChildrenWithOldParents[newEntityID] = properties.parentID; - } - originalEntityToNewEntityID[properties.id] = newEntityID; - properties.id = newEntityID; - } - } - - return createdEntityIDs; - }; - - that.cutSelectedEntities = function() { - that.copySelectedEntities(); - deleteSelectedEntities(); - }; - - that.copySelectedEntities = function() { - var entityProperties = Entities.getMultipleEntityProperties(that.selections); - var entityHostTypes = Entities.getMultipleEntityProperties(that.selections, 'entityHostType'); - var entities = {}; - entityProperties.forEach(function(props) { - entities[props.id] = props; - }); - - function appendChildren(entityID, entities, entityHostType) { - var wantDebug = false; - var childrenIDs = Entities.getChildrenIDs(entityID); - var entityHostTypes = Entities.getMultipleEntityProperties(childrenIDs, 'entityHostType'); - for (var i = 0; i < childrenIDs.length; ++i) { - var id = childrenIDs[i]; - - if (entityHostTypes[i].entityHostType !== entityHostType) { - if (wantDebug) { - console.warn("Skipping addition of entity " + id + " with conflicting entityHostType: " + - entityHostTypes[i].entityHostType + ", expected: " + entityHostType); - } - continue; - } - - if (!(id in entities)) { - entities[id] = Entities.getEntityProperties(id); - appendChildren(id, entities, entityHostType); - } - } - } - - var len = entityProperties.length; - for (var i = 0; i < len; ++i) { - appendChildren(entityProperties[i].id, entities, entityHostTypes[i].entityHostType); - } - - for (var id in entities) { - var parentID = entities[id].parentID; - entities[id].root = !(parentID in entities); - } - - entityClipboard.entities = []; - - var ids = Object.keys(entities); - while (ids.length > 0) { - // Go through all remaining entities. - // If an entity does not have a parent left, move it into the list - for (var i = 0; i < ids.length; ++i) { - var id = ids[i]; - var parentID = entities[id].parentID; - if (parentID in entities) { - continue; - } - entityClipboard.entities.push(entities[id]); - delete entities[id]; - } - ids = Object.keys(entities); - } - - // Calculate size - if (entityClipboard.entities.length === 0) { - entityClipboard.dimensions = { x: 0, y: 0, z: 0 }; - entityClipboard.position = { x: 0, y: 0, z: 0 }; - } else { - var properties = entityClipboard.entities; - var brn = properties[0].boundingBox.brn; - var tfl = properties[0].boundingBox.tfl; - for (var i = 1; i < properties.length; i++) { - var bb = properties[i].boundingBox; - brn.x = Math.min(bb.brn.x, brn.x); - brn.y = Math.min(bb.brn.y, brn.y); - brn.z = Math.min(bb.brn.z, brn.z); - tfl.x = Math.max(bb.tfl.x, tfl.x); - tfl.y = Math.max(bb.tfl.y, tfl.y); - tfl.z = Math.max(bb.tfl.z, tfl.z); - } - entityClipboard.dimensions = { - x: tfl.x - brn.x, - y: tfl.y - brn.y, - z: tfl.z - brn.z - }; - entityClipboard.position = { - x: brn.x + entityClipboard.dimensions.x / 2, - y: brn.y + entityClipboard.dimensions.y / 2, - z: brn.z + entityClipboard.dimensions.z / 2 - }; - } - }; - - that.pasteEntities = function() { - var dimensions = entityClipboard.dimensions; - var maxDimension = Math.max(dimensions.x, dimensions.y, dimensions.z); - var pastePosition = getPositionToCreateEntity(maxDimension); - var deltaPosition = Vec3.subtract(pastePosition, entityClipboard.position); - - var copiedProperties = []; - var ids = []; - entityClipboard.entities.forEach(function(originalProperties) { - var properties = deepCopy(originalProperties); - if (properties.root) { - properties.position = Vec3.sum(properties.position, deltaPosition); - delete properties.localPosition; - } else { - delete properties.position; - } - copiedProperties.push(properties); - }); - - var currentSelections = deepCopy(SelectionManager.selections); - - function redo(copiedProperties) { - var created = that.createEntities(copiedProperties); - var ids = []; - for (var i = 0; i < created.length; ++i) { - ids.push(created[i].entityID); - } - SelectionManager.setSelections(ids); - } - - function undo(copiedProperties) { - for (var i = 0; i < copiedProperties.length; ++i) { - Entities.deleteEntity(copiedProperties[i].id); - } - SelectionManager.setSelections(currentSelections); - } - - redo(copiedProperties); - undoHistory.pushCommand(undo, copiedProperties, redo, copiedProperties); - }; - - that._update = function(selectionUpdated, caller) { - var properties = null; - if (that.selections.length === 0) { - that.localDimensions = null; - that.localPosition = null; - that.worldDimensions = null; - that.worldPosition = null; - that.worldRotation = null; - } else if (that.selections.length === 1) { - properties = Entities.getEntityProperties(that.selections[0], - ['dimensions', 'position', 'rotation', 'registrationPoint', 'boundingBox', 'type']); - that.localDimensions = properties.dimensions; - that.localPosition = properties.position; - that.localRotation = properties.rotation; - that.localRegistrationPoint = properties.registrationPoint; - - that.worldDimensions = properties.boundingBox.dimensions; - that.worldPosition = properties.boundingBox.center; - that.worldRotation = Quat.IDENTITY; - - that.entityType = properties.type; - - if (selectionUpdated) { - SelectionDisplay.useDesiredSpaceMode(); - } - } else { - properties = Entities.getEntityProperties(that.selections[0], ['type', 'boundingBox']); - - that.entityType = properties.type; - - var brn = properties.boundingBox.brn; - var tfl = properties.boundingBox.tfl; - - for (var i = 1; i < that.selections.length; i++) { - properties = Entities.getEntityProperties(that.selections[i], 'boundingBox'); - var bb = properties.boundingBox; - brn.x = Math.min(bb.brn.x, brn.x); - brn.y = Math.min(bb.brn.y, brn.y); - brn.z = Math.min(bb.brn.z, brn.z); - tfl.x = Math.max(bb.tfl.x, tfl.x); - tfl.y = Math.max(bb.tfl.y, tfl.y); - tfl.z = Math.max(bb.tfl.z, tfl.z); - } - - that.localRotation = null; - that.localDimensions = null; - that.localPosition = null; - that.worldDimensions = { - x: tfl.x - brn.x, - y: tfl.y - brn.y, - z: tfl.z - brn.z - }; - that.worldRotation = Quat.IDENTITY; - that.worldPosition = { - x: brn.x + (that.worldDimensions.x / 2), - y: brn.y + (that.worldDimensions.y / 2), - z: brn.z + (that.worldDimensions.z / 2) - }; - - // For 1+ selections we can only modify selections in world space - SelectionDisplay.setSpaceMode(SPACE_WORLD, false); - } - - for (var j = 0; j < listeners.length; j++) { - try { - listeners[j].callback.call(listeners[j].thisContext, selectionUpdated === true, caller); - } catch (e) { - print("ERROR: entitySelectionTool.update got exception: " + JSON.stringify(e)); - } - } - }; - - return that; -})(); - -// Normalize degrees to be in the range (-180, 180) -function normalizeDegrees(degrees) { - var maxDegrees = 360; - var halfMaxDegrees = maxDegrees / 2; - degrees = ((degrees + halfMaxDegrees) % maxDegrees) - halfMaxDegrees; - if (degrees <= -halfMaxDegrees) { - degrees += maxDegrees; - } - return degrees; -} - -// SELECTION DISPLAY DEFINITION -SelectionDisplay = (function() { - var that = {}; - - const COLOR_GREEN = { red: 31, green: 198, blue: 166 }; - const COLOR_BLUE = { red: 0, green: 147, blue: 197 }; - const COLOR_RED = { red: 226, green: 51, blue: 77 }; - const COLOR_HOVER = { red: 227, green: 227, blue: 227 }; - const COLOR_ROTATE_CURRENT_RING = { red: 255, green: 99, blue: 9 }; - const COLOR_BOUNDING_EDGE = { red: 87, green: 87, blue: 87 }; - const COLOR_SCALE_CUBE = { red: 106, green: 106, blue: 106 }; - const COLOR_SCALE_CUBE_SELECTED = { red: 18, green: 18, blue: 18 }; - const COLOR_DEBUG_PICK_PLANE = { red: 255, green: 255, blue: 255 }; - const COLOR_DEBUG_PICK_PLANE_HIT = { red: 255, green: 165, blue: 0 }; - - const TRANSLATE_ARROW_CYLINDER_OFFSET = 0.1; - const TRANSLATE_ARROW_CYLINDER_CAMERA_DISTANCE_MULTIPLE = 0.005; - const TRANSLATE_ARROW_CYLINDER_Y_MULTIPLE = 7.5; - const TRANSLATE_ARROW_CONE_CAMERA_DISTANCE_MULTIPLE = 0.025; - const TRANSLATE_ARROW_CONE_OFFSET_CYLINDER_DIMENSION_MULTIPLE = 0.83; - - const ROTATE_RING_CAMERA_DISTANCE_MULTIPLE = 0.15; - const ROTATE_CTRL_SNAP_ANGLE = 22.5; - const ROTATE_DEFAULT_SNAP_ANGLE = 1; - const ROTATE_DEFAULT_TICK_MARKS_ANGLE = 5; - const ROTATE_RING_IDLE_INNER_RADIUS = 0.92; - const ROTATE_RING_SELECTED_INNER_RADIUS = 0.9; - - // These are multipliers for sizing the rotation degrees display while rotating an entity - const ROTATE_DISPLAY_DISTANCE_MULTIPLIER = 2; - const ROTATE_DISPLAY_SIZE_X_MULTIPLIER = 0.2; - const ROTATE_DISPLAY_SIZE_Y_MULTIPLIER = 0.09; - const ROTATE_DISPLAY_LINE_HEIGHT_MULTIPLIER = 0.07; - - const STRETCH_CUBE_OFFSET = 0.06; - const STRETCH_CUBE_CAMERA_DISTANCE_MULTIPLE = 0.02; - const STRETCH_PANEL_WIDTH = 0.01; - - const SCALE_OVERLAY_CAMERA_DISTANCE_MULTIPLE = 0.02; - const SCALE_DIMENSIONS_CAMERA_DISTANCE_MULTIPLE = 0.5; - - const BOUNDING_EDGE_OFFSET = 0.5; - - const DUPLICATOR_OFFSET = { x: 0.9, y: -0.9, z: 0.9 }; - - const CTRL_KEY_CODE = 16777249; - - const RAIL_AXIS_LENGTH = 10000; - - const NEGATE_VECTOR = -1; - const NO_HAND = -1; - - const DEBUG_PICK_PLANE_HIT_LIMIT = 200; - const DEBUG_PICK_PLANE_HIT_CAMERA_DISTANCE_MULTIPLE = 0.01; - - const TRANSLATE_DIRECTION = { - X: 0, - Y: 1, - Z: 2 - }; - - const STRETCH_DIRECTION = { - X: 0, - Y: 1, - Z: 2, - ALL: 3 - }; - - const ROTATE_DIRECTION = { - PITCH: 0, - YAW: 1, - ROLL: 2 - }; - - const INEDIT_STATUS_CHANNEL = "Hifi-InEdit-Status"; - - /** - * The current space mode, this could have been a forced space mode since we do not support multi selection while in - * local space mode. - * @type {string} - should only be set to SPACE_LOCAL or SPACE_WORLD - */ - var spaceMode = SPACE_LOCAL; - - /** - * The desired space mode, this is the user set space mode, which should be respected whenever it is possible. In the case - * of multi entity selection this space mode may differ from the actual spaceMode. - * @type {string} - should only be set to SPACE_LOCAL or SPACE_WORLD - */ - var desiredSpaceMode = SPACE_LOCAL; - - var overlayNames = []; - var lastControllerPoses = [ - getControllerWorldLocation(Controller.Standard.LeftHand, true), - getControllerWorldLocation(Controller.Standard.RightHand, true) - ]; - - var worldRotationX; - var worldRotationY; - var worldRotationZ; - - var activeStretchCubePanelOffset = null; - - var previousHandle = null; - var previousHandleHelper = null; - var previousHandleColor; - - var ctrlPressed = false; - - that.replaceCollisionsAfterStretch = false; - - var handlePropertiesTranslateArrowCones = { - alpha: 1, - shape: "Cone", - solid: true, - visible: false, - ignorePickIntersection: true, - drawInFront: true - }; - var handlePropertiesTranslateArrowCylinders = { - alpha: 1, - shape: "Cylinder", - solid: true, - visible: false, - ignorePickIntersection: true, - drawInFront: true - }; - var handleTranslateXCone = Overlays.addOverlay("shape", handlePropertiesTranslateArrowCones); - var handleTranslateXCylinder = Overlays.addOverlay("shape", handlePropertiesTranslateArrowCylinders); - Overlays.editOverlay(handleTranslateXCone, { color: COLOR_RED }); - Overlays.editOverlay(handleTranslateXCylinder, { color: COLOR_RED }); - var handleTranslateYCone = Overlays.addOverlay("shape", handlePropertiesTranslateArrowCones); - var handleTranslateYCylinder = Overlays.addOverlay("shape", handlePropertiesTranslateArrowCylinders); - Overlays.editOverlay(handleTranslateYCone, { color: COLOR_GREEN }); - Overlays.editOverlay(handleTranslateYCylinder, { color: COLOR_GREEN }); - var handleTranslateZCone = Overlays.addOverlay("shape", handlePropertiesTranslateArrowCones); - var handleTranslateZCylinder = Overlays.addOverlay("shape", handlePropertiesTranslateArrowCylinders); - Overlays.editOverlay(handleTranslateZCone, { color: COLOR_BLUE }); - Overlays.editOverlay(handleTranslateZCylinder, { color: COLOR_BLUE }); - - var handlePropertiesRotateRings = { - alpha: 1, - solid: true, - startAt: 0, - endAt: 360, - innerRadius: ROTATE_RING_IDLE_INNER_RADIUS, - majorTickMarksAngle: ROTATE_DEFAULT_TICK_MARKS_ANGLE, - majorTickMarksLength: 0.1, - visible: false, - ignorePickIntersection: true, - drawInFront: true - }; - var handleRotatePitchRing = Overlays.addOverlay("circle3d", handlePropertiesRotateRings); - Overlays.editOverlay(handleRotatePitchRing, { - color: COLOR_RED, - majorTickMarksColor: COLOR_RED - }); - var handleRotateYawRing = Overlays.addOverlay("circle3d", handlePropertiesRotateRings); - Overlays.editOverlay(handleRotateYawRing, { - color: COLOR_GREEN, - majorTickMarksColor: COLOR_GREEN - }); - var handleRotateRollRing = Overlays.addOverlay("circle3d", handlePropertiesRotateRings); - Overlays.editOverlay(handleRotateRollRing, { - color: COLOR_BLUE, - majorTickMarksColor: COLOR_BLUE - }); - - var handleRotateCurrentRing = Overlays.addOverlay("circle3d", { - alpha: 1, - color: COLOR_ROTATE_CURRENT_RING, - solid: true, - innerRadius: 0.9, - visible: false, - ignorePickIntersection: true, - drawInFront: true - }); - - var rotationDegreesDisplay = Overlays.addOverlay("text3d", { - text: "", - color: { red: 0, green: 0, blue: 0 }, - backgroundColor: { red: 255, green: 255, blue: 255 }, - alpha: 0.7, - backgroundAlpha: 0.7, - visible: false, - isFacingAvatar: true, - drawInFront: true, - ignorePickIntersection: true, - dimensions: { x: 0, y: 0 }, - lineHeight: 0.0, - topMargin: 0, - rightMargin: 0, - bottomMargin: 0, - leftMargin: 0 - }); - - var handlePropertiesStretchCubes = { - solid: true, - visible: false, - ignorePickIntersection: true, - drawInFront: true - }; - var handleStretchXCube = Overlays.addOverlay("cube", handlePropertiesStretchCubes); - Overlays.editOverlay(handleStretchXCube, { color: COLOR_RED }); - var handleStretchYCube = Overlays.addOverlay("cube", handlePropertiesStretchCubes); - Overlays.editOverlay(handleStretchYCube, { color: COLOR_GREEN }); - var handleStretchZCube = Overlays.addOverlay("cube", handlePropertiesStretchCubes); - Overlays.editOverlay(handleStretchZCube, { color: COLOR_BLUE }); - - var handlePropertiesStretchPanel = { - alpha: 0.5, - solid: true, - visible: false, - ignorePickIntersection: true, - drawInFront: true - }; - var handleStretchXPanel = Overlays.addOverlay("cube", handlePropertiesStretchPanel); - Overlays.editOverlay(handleStretchXPanel, { color: COLOR_RED }); - var handleStretchYPanel = Overlays.addOverlay("cube", handlePropertiesStretchPanel); - Overlays.editOverlay(handleStretchYPanel, { color: COLOR_GREEN }); - var handleStretchZPanel = Overlays.addOverlay("cube", handlePropertiesStretchPanel); - Overlays.editOverlay(handleStretchZPanel, { color: COLOR_BLUE }); - - var handleScaleCube = Overlays.addOverlay("cube", { - size: 0.025, - color: COLOR_SCALE_CUBE, - solid: true, - visible: false, - ignorePickIntersection: true, - drawInFront: true, - borderSize: 1.4 - }); - - var handleBoundingBox = Overlays.addOverlay("cube", { - alpha: 1, - color: COLOR_BOUNDING_EDGE, - visible: false, - ignorePickIntersection: true, - drawInFront: true, - isSolid: false - }); - - var handleDuplicator = Overlays.addOverlay("cube", { - alpha: 1, - size: 0.05, - color: COLOR_GREEN, - solid: true, - visible: false, - ignorePickIntersection: true, - drawInFront: true, - borderSize: 1.4 - }); - - // setting to 0 alpha for now to keep this hidden vs using visible false - // because its used as the translate xz tool handle overlay - var selectionBox = Overlays.addOverlay("cube", { - size: 1, - color: COLOR_RED, - alpha: 0, - solid: false, - visible: false, - ignorePickIntersection: true, - dashed: false - }); - - // Handle for x-z translation of particle effect and light entities while inside the bounding box. - // Limitation: If multiple entities are selected, only the first entity's icon translates the selection. - var iconSelectionBox = Overlays.addOverlay("cube", { - size: 0.3, // Match entity icon size. - color: COLOR_RED, - alpha: 0, - solid: false, - visible: false, - ignorePickIntersection: true, - dashed: false - }); - - var xRailOverlay = Overlays.addOverlay("line3d", { - visible: false, - start: Vec3.ZERO, - end: Vec3.ZERO, - color: { - red: 255, - green: 0, - blue: 0 - }, - ignorePickIntersection: true // always ignore this - }); - var yRailOverlay = Overlays.addOverlay("line3d", { - visible: false, - start: Vec3.ZERO, - end: Vec3.ZERO, - color: { - red: 0, - green: 255, - blue: 0 - }, - ignorePickIntersection: true // always ignore this - }); - var zRailOverlay = Overlays.addOverlay("line3d", { - visible: false, - start: Vec3.ZERO, - end: Vec3.ZERO, - color: { - red: 0, - green: 0, - blue: 255 - }, - ignorePickIntersection: true // always ignore this - }); - - var allOverlays = [ - handleTranslateXCone, - handleTranslateXCylinder, - handleTranslateYCone, - handleTranslateYCylinder, - handleTranslateZCone, - handleTranslateZCylinder, - handleRotatePitchRing, - handleRotateYawRing, - handleRotateRollRing, - handleRotateCurrentRing, - rotationDegreesDisplay, - handleStretchXCube, - handleStretchYCube, - handleStretchZCube, - handleStretchXPanel, - handleStretchYPanel, - handleStretchZPanel, - handleScaleCube, - handleBoundingBox, - handleDuplicator, - selectionBox, - iconSelectionBox, - xRailOverlay, - yRailOverlay, - zRailOverlay - ]; - - const nonLayeredOverlays = [selectionBox, iconSelectionBox]; - - var maximumHandleInAllOverlays = handleDuplicator; - - overlayNames[handleTranslateXCone] = "handleTranslateXCone"; - overlayNames[handleTranslateXCylinder] = "handleTranslateXCylinder"; - overlayNames[handleTranslateYCone] = "handleTranslateYCone"; - overlayNames[handleTranslateYCylinder] = "handleTranslateYCylinder"; - overlayNames[handleTranslateZCone] = "handleTranslateZCone"; - overlayNames[handleTranslateZCylinder] = "handleTranslateZCylinder"; - - overlayNames[handleRotatePitchRing] = "handleRotatePitchRing"; - overlayNames[handleRotateYawRing] = "handleRotateYawRing"; - overlayNames[handleRotateRollRing] = "handleRotateRollRing"; - overlayNames[handleRotateCurrentRing] = "handleRotateCurrentRing"; - overlayNames[rotationDegreesDisplay] = "rotationDegreesDisplay"; - - overlayNames[handleStretchXCube] = "handleStretchXCube"; - overlayNames[handleStretchYCube] = "handleStretchYCube"; - overlayNames[handleStretchZCube] = "handleStretchZCube"; - overlayNames[handleStretchXPanel] = "handleStretchXPanel"; - overlayNames[handleStretchYPanel] = "handleStretchYPanel"; - overlayNames[handleStretchZPanel] = "handleStretchZPanel"; - - overlayNames[handleScaleCube] = "handleScaleCube"; - - overlayNames[handleBoundingBox] = "handleBoundingBox"; - - overlayNames[handleDuplicator] = "handleDuplicator"; - overlayNames[selectionBox] = "selectionBox"; - overlayNames[iconSelectionBox] = "iconSelectionBox"; - - var activeTool = null; - var handleTools = {}; - - var debugPickPlaneEnabled = false; - var debugPickPlane = Overlays.addOverlay("shape", { - shape: "Quad", - alpha: 0.25, - color: COLOR_DEBUG_PICK_PLANE, - solid: true, - visible: false, - ignorePickIntersection: true, - drawInFront: false - }); - var debugPickPlaneHits = []; - - // We get mouseMoveEvents from the handControllers, via handControllerPointer. - // But we dont' get mousePressEvents. - that.triggerClickMapping = Controller.newMapping(Script.resolvePath('') + '-click'); - that.triggerPressMapping = Controller.newMapping(Script.resolvePath('') + '-press'); - that.triggeredHand = NO_HAND; - that.pressedHand = NO_HAND; - that.editingHand = NO_HAND; - that.triggered = function() { - return that.triggeredHand !== NO_HAND; - }; - function pointingAtDesktopWindowOrTablet(hand) { - var pointingAtDesktopWindow = (hand === Controller.Standard.RightHand && - SelectionManager.pointingAtDesktopWindowRight) || - (hand === Controller.Standard.LeftHand && - SelectionManager.pointingAtDesktopWindowLeft); - var pointingAtTablet = (hand === Controller.Standard.RightHand && SelectionManager.pointingAtTabletRight) || - (hand === Controller.Standard.LeftHand && SelectionManager.pointingAtTabletLeft); - return pointingAtDesktopWindow || pointingAtTablet; - } - function makeClickHandler(hand) { - return function (clicked) { - // Don't allow both hands to trigger at the same time - if (that.triggered() && hand !== that.triggeredHand) { - return; - } - if (!that.triggered() && clicked && !pointingAtDesktopWindowOrTablet(hand)) { - that.triggeredHand = hand; - that.mousePressEvent({}); - } else if (that.triggered() && !clicked) { - that.triggeredHand = NO_HAND; - that.mouseReleaseEvent({}); - } - }; - } - function makePressHandler(hand) { - return function (value) { - if (value >= TRIGGER_ON_VALUE && !that.triggered() && !pointingAtDesktopWindowOrTablet(hand)) { - that.pressedHand = hand; - that.updateHighlight({}); - } else { - that.pressedHand = NO_HAND; - that.resetPreviousHandleColor(); - } - } - } - that.triggerClickMapping.from(Controller.Standard.RTClick).peek().to(makeClickHandler(Controller.Standard.RightHand)); - that.triggerClickMapping.from(Controller.Standard.LTClick).peek().to(makeClickHandler(Controller.Standard.LeftHand)); - that.triggerPressMapping.from(Controller.Standard.RT).peek().to(makePressHandler(Controller.Standard.RightHand)); - that.triggerPressMapping.from(Controller.Standard.LT).peek().to(makePressHandler(Controller.Standard.LeftHand)); - that.enableTriggerMapping = function() { - that.triggerClickMapping.enable(); - that.triggerPressMapping.enable(); - }; - that.disableTriggerMapping = function() { - that.triggerClickMapping.disable(); - that.triggerPressMapping.disable(); - }; - Script.scriptEnding.connect(that.disableTriggerMapping); - - // FUNCTION DEF(s): Intersection Check Helpers - function testRayIntersect(queryRay, overlayIncludes, overlayExcludes) { - var wantDebug = false; - if ((queryRay === undefined) || (queryRay === null)) { - if (wantDebug) { - print("testRayIntersect - EARLY EXIT -> queryRay is undefined OR null!"); - } - return null; - } - - // We want to first check the drawInFront overlays (i.e. the handles, but really everything except the selectionBoxes) - // so that you can click on them even when they're behind things - var overlayIncludesLayered = []; - var overlayIncludesNonLayered = []; - for (var i = 0; i < overlayIncludes.length; i++) { - var value = overlayIncludes[i]; - var contains = false; - for (var j = 0; j < nonLayeredOverlays.length; j++) { - if (nonLayeredOverlays[j] === value) { - contains = true; - break; - } - } - if (contains) { - overlayIncludesNonLayered.push(value); - } else { - overlayIncludesLayered.push(value); - } - } - - var intersectObj = Overlays.findRayIntersection(queryRay, true, overlayIncludesLayered, overlayExcludes); - - if (!intersectObj.intersects && overlayIncludesNonLayered.length > 0) { - intersectObj = Overlays.findRayIntersection(queryRay, true, overlayIncludesNonLayered, overlayExcludes); - } - - if (wantDebug) { - if (!overlayIncludes) { - print("testRayIntersect - no overlayIncludes provided."); - } - if (!overlayExcludes) { - print("testRayIntersect - no overlayExcludes provided."); - } - print("testRayIntersect - Hit: " + intersectObj.intersects); - print(" intersectObj.overlayID:" + intersectObj.overlayID + "[" + overlayNames[intersectObj.overlayID] + "]"); - print(" OverlayName: " + overlayNames[intersectObj.overlayID]); - print(" intersectObj.distance:" + intersectObj.distance); - print(" intersectObj.face:" + intersectObj.face); - Vec3.print(" intersectObj.intersection:", intersectObj.intersection); - } - - return intersectObj; - } - - function isPointInsideBox(point, box) { - var position = Vec3.subtract(point, box.position); - position = Vec3.multiplyQbyV(Quat.inverse(box.rotation), position); - return Math.abs(position.x) <= box.dimensions.x / 2 && Math.abs(position.y) <= box.dimensions.y / 2 - && Math.abs(position.z) <= box.dimensions.z / 2; - } - - that.isEditHandle = function(overlayID) { - var overlayIndex = allOverlays.indexOf(overlayID); - var maxHandleIndex = allOverlays.indexOf(maximumHandleInAllOverlays); - return overlayIndex >= 0 && overlayIndex <= maxHandleIndex; - }; - - // FUNCTION: MOUSE PRESS EVENT - that.mousePressEvent = function (event) { - var wantDebug = false; - if (wantDebug) { - print("=============== eST::MousePressEvent BEG ======================="); - } - if (!event.isLeftButton && !that.triggered()) { - // EARLY EXIT-(if another mouse button than left is pressed ignore it) - return false; - } - - var pickRay = generalComputePickRay(event.x, event.y); - // TODO_Case6491: Move this out to setup just to make it once - var interactiveOverlays = getMainTabletIDs(); - for (var key in handleTools) { - if (handleTools.hasOwnProperty(key)) { - interactiveOverlays.push(key); - } - } - - // Start with unknown mode, in case no tool can handle this. - activeTool = null; - - var results = testRayIntersect(pickRay, interactiveOverlays); - if (results.intersects) { - var hitOverlayID = results.overlayID; - if ((HMD.tabletID && hitOverlayID === HMD.tabletID) || (HMD.tabletScreenID && hitOverlayID === HMD.tabletScreenID) - || (HMD.homeButtonID && hitOverlayID === HMD.homeButtonID)) { - // EARLY EXIT-(mouse clicks on the tablet should override the edit affordances) - return false; - } - - var hitTool = handleTools[ hitOverlayID ]; - if (hitTool) { - activeTool = hitTool; - that.clearDebugPickPlane(); - if (activeTool.onBegin) { - that.editingHand = that.triggeredHand; - Messages.sendLocalMessage(INEDIT_STATUS_CHANNEL, JSON.stringify({ - method: "editing", - hand: that.editingHand === Controller.Standard.LeftHand ? LEFT_HAND : RIGHT_HAND, - editing: true - })); - activeTool.onBegin(event, pickRay, results); - } else { - print("ERROR: entitySelectionTool.mousePressEvent - ActiveTool(" + activeTool.mode + ") missing onBegin"); - } - } else { - print("ERROR: entitySelectionTool.mousePressEvent - Hit unexpected object, check interactiveOverlays"); - }// End_if (hitTool) - }// End_If(results.intersects) - - if (wantDebug) { - print(" DisplayMode: " + getMode()); - print("=============== eST::MousePressEvent END ======================="); - } - - // If mode is known then we successfully handled this; - // otherwise, we're missing a tool. - return activeTool; - }; - - that.resetPreviousHandleColor = function() { - if (previousHandle !== null) { - Overlays.editOverlay(previousHandle, { color: previousHandleColor }); - previousHandle = null; - } - if (previousHandleHelper !== null) { - Overlays.editOverlay(previousHandleHelper, { color: previousHandleColor }); - previousHandleHelper = null; - } - }; - - that.getHandleHelper = function(overlay) { - if (overlay === handleTranslateXCone) { - return handleTranslateXCylinder; - } else if (overlay === handleTranslateXCylinder) { - return handleTranslateXCone; - } else if (overlay === handleTranslateYCone) { - return handleTranslateYCylinder; - } else if (overlay === handleTranslateYCylinder) { - return handleTranslateYCone; - } else if (overlay === handleTranslateZCone) { - return handleTranslateZCylinder; - } else if (overlay === handleTranslateZCylinder) { - return handleTranslateZCone; - } - return Uuid.NULL; - }; - - that.updateHighlight = function(event) { - // if no tool is active, then just look for handles to highlight... - var pickRay = generalComputePickRay(event.x, event.y); - var result = testRayIntersect(pickRay, allOverlays); - var pickedColor; - var highlightNeeded = false; - - if (result.intersects) { - switch (result.overlayID) { - case handleTranslateXCone: - case handleTranslateXCylinder: - case handleRotatePitchRing: - case handleStretchXCube: - pickedColor = COLOR_RED; - highlightNeeded = true; - break; - case handleTranslateYCone: - case handleTranslateYCylinder: - case handleRotateYawRing: - case handleStretchYCube: - pickedColor = COLOR_GREEN; - highlightNeeded = true; - break; - case handleTranslateZCone: - case handleTranslateZCylinder: - case handleRotateRollRing: - case handleStretchZCube: - pickedColor = COLOR_BLUE; - highlightNeeded = true; - break; - case handleScaleCube: - pickedColor = COLOR_SCALE_CUBE; - highlightNeeded = true; - break; - default: - that.resetPreviousHandleColor(); - break; - } - - if (highlightNeeded) { - that.resetPreviousHandleColor(); - Overlays.editOverlay(result.overlayID, { color: COLOR_HOVER }); - previousHandle = result.overlayID; - previousHandleHelper = that.getHandleHelper(result.overlayID); - if (previousHandleHelper !== null) { - Overlays.editOverlay(previousHandleHelper, { color: COLOR_HOVER }); - } - previousHandleColor = pickedColor; - } - - } else { - that.resetPreviousHandleColor(); - } - }; - - // FUNCTION: MOUSE MOVE EVENT - var lastMouseEvent = null; - that.mouseMoveEvent = function(event) { - var wantDebug = false; - if (wantDebug) { - print("=============== eST::MouseMoveEvent BEG ======================="); - } - lastMouseEvent = event; - if (activeTool) { - if (wantDebug) { - print(" Trigger ActiveTool(" + activeTool.mode + ")'s onMove"); - } - activeTool.onMove(event); - - if (wantDebug) { - print(" Trigger SelectionManager::update"); - } - SelectionManager._update(false, that); - - if (wantDebug) { - print("=============== eST::MouseMoveEvent END ======================="); - } - // EARLY EXIT--(Move handled via active tool) - return true; - } - - that.updateHighlight(event); - - if (wantDebug) { - print("=============== eST::MouseMoveEvent END ======================="); - } - return false; - }; - - // FUNCTION: MOUSE RELEASE EVENT - that.mouseReleaseEvent = function(event) { - var wantDebug = false; - if (wantDebug) { - print("=============== eST::MouseReleaseEvent BEG ======================="); - } - var showHandles = false; - if (activeTool) { - if (activeTool.onEnd) { - if (wantDebug) { - print(" Triggering ActiveTool(" + activeTool.mode + ")'s onEnd"); - } - Messages.sendLocalMessage(INEDIT_STATUS_CHANNEL, JSON.stringify({ - method: "editing", - hand: that.editingHand === Controller.Standard.LeftHand ? LEFT_HAND : RIGHT_HAND, - editing: false - })); - that.editingHand = NO_HAND; - activeTool.onEnd(event); - } else if (wantDebug) { - print(" ActiveTool(" + activeTool.mode + ")'s missing onEnd"); - } - } - - showHandles = activeTool; // base on prior tool value - activeTool = null; - - // if something is selected, then reset the "original" properties for any potential next click+move operation - if (SelectionManager.hasSelection()) { - if (showHandles) { - if (wantDebug) { - print(" Triggering that.select"); - } - that.select(SelectionManager.selections[0], event); - } - } - - if (wantDebug) { - print("=============== eST::MouseReleaseEvent END ======================="); - } - }; - - // Control key remains active only while key is held down - that.keyReleaseEvent = function(event) { - if (event.key === CTRL_KEY_CODE) { - ctrlPressed = false; - that.updateActiveRotateRing(); - } - that.updateLastMouseEvent(event); - }; - - // Triggers notification on specific key driven events - that.keyPressEvent = function(event) { - if (event.key === CTRL_KEY_CODE) { - ctrlPressed = true; - that.updateActiveRotateRing(); - } - that.updateLastMouseEvent(event); - }; - - that.updateLastMouseEvent = function(event) { - if (activeTool && lastMouseEvent !== null) { - var change = lastMouseEvent.isShifted !== event.isShifted || lastMouseEvent.isMeta !== event.isMeta || - lastMouseEvent.isControl !== event.isControl || lastMouseEvent.isAlt !== event.isAlt; - lastMouseEvent.isShifted = event.isShifted; - lastMouseEvent.isMeta = event.isMeta; - lastMouseEvent.isControl = event.isControl; - lastMouseEvent.isAlt = event.isAlt; - if (change) { - activeTool.onMove(lastMouseEvent); - } - } - }; - - // NOTE: mousePressEvent and mouseMoveEvent from the main script should call us., so we don't hook these: - // Controller.mousePressEvent.connect(that.mousePressEvent); - // Controller.mouseMoveEvent.connect(that.mouseMoveEvent); - Controller.mouseReleaseEvent.connect(that.mouseReleaseEvent); - Controller.keyPressEvent.connect(that.keyPressEvent); - Controller.keyReleaseEvent.connect(that.keyReleaseEvent); - - that.checkControllerMove = function() { - if (SelectionManager.hasSelection()) { - var controllerPose = getControllerWorldLocation(that.triggeredHand, true); - var hand = (that.triggeredHand === Controller.Standard.LeftHand) ? 0 : 1; - if (controllerPose.valid && lastControllerPoses[hand].valid && that.triggered()) { - if (!Vec3.equal(controllerPose.position, lastControllerPoses[hand].position) || - !Vec3.equal(controllerPose.rotation, lastControllerPoses[hand].rotation)) { - that.mouseMoveEvent({}); - } - } - lastControllerPoses[hand] = controllerPose; - } - }; - - function controllerComputePickRay() { - var hand = that.triggered() ? that.triggeredHand : that.pressedHand; - var controllerPose = getControllerWorldLocation(hand, true); - if (controllerPose.valid) { - var controllerPosition = controllerPose.translation; - // This gets point direction right, but if you want general quaternion it would be more complicated: - var controllerDirection = Quat.getUp(controllerPose.rotation); - return {origin: controllerPosition, direction: controllerDirection}; - } - } - - function generalComputePickRay(x, y) { - return controllerComputePickRay() || Camera.computePickRay(x, y); - } - - function getControllerAvatarFramePositionFromPickRay(pickRay) { - var controllerPosition = Vec3.subtract(pickRay.origin, MyAvatar.position); - controllerPosition = Vec3.multiplyQbyV(Quat.inverse(MyAvatar.orientation), controllerPosition); - return controllerPosition; - } - - function getDistanceToCamera(position) { - var cameraPosition = Camera.getPosition(); - var toCameraDistance = Vec3.length(Vec3.subtract(cameraPosition, position)); - return toCameraDistance; - } - - function usePreviousPickRay(pickRayDirection, previousPickRayDirection, normal) { - return (Vec3.dot(pickRayDirection, normal) > 0 && Vec3.dot(previousPickRayDirection, normal) < 0) || - (Vec3.dot(pickRayDirection, normal) < 0 && Vec3.dot(previousPickRayDirection, normal) > 0); - } - - // @return string - The mode of the currently active tool; - // otherwise, "UNKNOWN" if there's no active tool. - function getMode() { - return (activeTool ? activeTool.mode : "UNKNOWN"); - } - - that.cleanup = function() { - for (var i = 0; i < allOverlays.length; i++) { - Overlays.deleteOverlay(allOverlays[i]); - } - that.clearDebugPickPlane(); - }; - - that.select = function(entityID, event) { - var properties = Entities.getEntityProperties(SelectionManager.selections[0]); - - if (event !== false) { - var wantDebug = false; - if (wantDebug) { - print("select() with EVENT...... "); - print(" event.y:" + event.y); - Vec3.print(" current position:", properties.position); - } - } - - that.updateHandles(); - }; - - - /** - * This callback is used for spaceMode changes. - * @callback spaceModeChangedCallback - * @param {string} spaceMode - */ - - /** - * set this property with a callback to keep track of spaceMode changes. - * @type {spaceModeChangedCallback} - */ - that.onSpaceModeChange = null; - - // FUNCTION: SET SPACE MODE - that.setSpaceMode = function(newSpaceMode, isDesiredChange) { - var wantDebug = false; - if (wantDebug) { - print("======> SetSpaceMode called. ========"); - } - - if (spaceMode !== newSpaceMode) { - if (wantDebug) { - print(" Updating SpaceMode From: " + spaceMode + " To: " + newSpaceMode); - } - if (isDesiredChange) { - desiredSpaceMode = newSpaceMode; - } - spaceMode = newSpaceMode; - - if (that.onSpaceModeChange !== null) { - that.onSpaceModeChange(newSpaceMode); - } - - that.updateHandles(); - } else if (wantDebug) { - print("WARNING: entitySelectionTool.setSpaceMode - Can't update SpaceMode. CurrentMode: " + - spaceMode + " DesiredMode: " + newSpaceMode); - } - if (wantDebug) { - print("====== SetSpaceMode called. <========"); - } - }; - - // FUNCTION: TOGGLE SPACE MODE - that.toggleSpaceMode = function() { - var wantDebug = false; - if (wantDebug) { - print("========> ToggleSpaceMode called. ========="); - } - if ((spaceMode === SPACE_WORLD) && (SelectionManager.selections.length > 1)) { - if (wantDebug) { - print("Local space editing is not available with multiple selections"); - } - return; - } - if (wantDebug) { - print("PreToggle: " + spaceMode); - } - that.setSpaceMode((spaceMode === SPACE_LOCAL) ? SPACE_WORLD : SPACE_LOCAL, true); - if (wantDebug) { - print("PostToggle: " + spaceMode); - print("======== ToggleSpaceMode called. <========="); - } - }; - - /** - * Switches the display mode back to the set desired display mode - */ - that.useDesiredSpaceMode = function() { - var wantDebug = false; - if (wantDebug) { - print("========> UseDesiredSpaceMode called. ========="); - } - that.setSpaceMode(desiredSpaceMode, false); - if (wantDebug) { - print("PostToggle: " + spaceMode); - print("======== UseDesiredSpaceMode called. <========="); - } - }; - - /** - * Get the currently set SpaceMode - * @returns {string} spaceMode - */ - that.getSpaceMode = function() { - return spaceMode; - }; - - function addHandleTool(overlay, tool) { - handleTools[overlay] = tool; - return tool; - } - - // @param: toolHandle: The overlayID associated with the tool - // that correlates to the tool you wish to query. - // @note: If toolHandle is null or undefined then activeTool - // will be checked against those values as opposed to - // the tool registered under toolHandle. Null & Undefined - // are treated as separate values. - // @return: bool - Indicates if the activeTool is that queried. - function isActiveTool(toolHandle) { - if (!toolHandle) { - // Allow isActiveTool(null) and similar to return true if there's - // no active tool - return (activeTool === toolHandle); - } - - if (!handleTools.hasOwnProperty(toolHandle)) { - print("WARNING: entitySelectionTool.isActiveTool - Encountered unknown grabberToolHandle: " + - toolHandle + ". Tools should be registered via addHandleTool."); - // EARLY EXIT - return false; - } - - return (activeTool === handleTools[ toolHandle ]); - } - - // FUNCTION: UPDATE HANDLES - that.updateHandles = function() { - var wantDebug = false; - if (wantDebug) { - print("======> Update Handles ======="); - print(" Selections Count: " + SelectionManager.selections.length); - print(" SpaceMode: " + spaceMode); - print(" DisplayMode: " + getMode()); - } - - if (SelectionManager.selections.length === 0) { - that.setOverlaysVisible(false); - that.clearDebugPickPlane(); - return; - } - - if (SelectionManager.hasSelection()) { - var position = SelectionManager.worldPosition; - var rotation = spaceMode === SPACE_LOCAL ? SelectionManager.localRotation : SelectionManager.worldRotation; - var dimensions = spaceMode === SPACE_LOCAL ? SelectionManager.localDimensions : SelectionManager.worldDimensions; - var rotationInverse = Quat.inverse(rotation); - var toCameraDistance = getDistanceToCamera(position); - - var rotationDegrees = 90; - var localRotationX = Quat.fromPitchYawRollDegrees(0, 0, -rotationDegrees); - var rotationX = Quat.multiply(rotation, localRotationX); - worldRotationX = rotationX; - var localRotationY = Quat.fromPitchYawRollDegrees(0, rotationDegrees, 0); - var rotationY = Quat.multiply(rotation, localRotationY); - worldRotationY = rotationY; - var localRotationZ = Quat.fromPitchYawRollDegrees(rotationDegrees, 0, 0); - var rotationZ = Quat.multiply(rotation, localRotationZ); - worldRotationZ = rotationZ; - - var selectionBoxGeometry = { - position: position, - rotation: rotation, - dimensions: dimensions - }; - var isCameraInsideBox = isPointInsideBox(Camera.position, selectionBoxGeometry); - - // in HMD if outside the bounding box clamp the overlays to the bounding box for now so lasers can hit them - var maxHandleDimension = 0; - if (HMD.active && !isCameraInsideBox) { - maxHandleDimension = Math.max(dimensions.x, dimensions.y, dimensions.z); - } - - // UPDATE ROTATION RINGS - // rotateDimension is used as the base dimension for all overlays - var rotateDimension = Math.max(maxHandleDimension, toCameraDistance * ROTATE_RING_CAMERA_DISTANCE_MULTIPLE); - var rotateDimensions = { x: rotateDimension, y: rotateDimension, z: rotateDimension }; - if (!isActiveTool(handleRotatePitchRing)) { - Overlays.editOverlay(handleRotatePitchRing, { - position: position, - rotation: rotationY, - dimensions: rotateDimensions, - majorTickMarksAngle: ROTATE_DEFAULT_TICK_MARKS_ANGLE - }); - } - if (!isActiveTool(handleRotateYawRing)) { - Overlays.editOverlay(handleRotateYawRing, { - position: position, - rotation: rotationZ, - dimensions: rotateDimensions, - majorTickMarksAngle: ROTATE_DEFAULT_TICK_MARKS_ANGLE - }); - } - if (!isActiveTool(handleRotateRollRing)) { - Overlays.editOverlay(handleRotateRollRing, { - position: position, - rotation: rotationX, - dimensions: rotateDimensions, - majorTickMarksAngle: ROTATE_DEFAULT_TICK_MARKS_ANGLE - }); - } - Overlays.editOverlay(handleRotateCurrentRing, { dimensions: rotateDimensions }); - that.updateActiveRotateRing(); - - // UPDATE TRANSLATION ARROWS - var arrowCylinderDimension = rotateDimension * TRANSLATE_ARROW_CYLINDER_CAMERA_DISTANCE_MULTIPLE / - ROTATE_RING_CAMERA_DISTANCE_MULTIPLE; - var arrowCylinderDimensions = { - x: arrowCylinderDimension, - y: arrowCylinderDimension * TRANSLATE_ARROW_CYLINDER_Y_MULTIPLE, - z: arrowCylinderDimension - }; - var arrowConeDimension = rotateDimension * TRANSLATE_ARROW_CONE_CAMERA_DISTANCE_MULTIPLE / - ROTATE_RING_CAMERA_DISTANCE_MULTIPLE; - var arrowConeDimensions = { x: arrowConeDimension, y: arrowConeDimension, z: arrowConeDimension }; - var arrowCylinderOffset = rotateDimension * TRANSLATE_ARROW_CYLINDER_OFFSET / ROTATE_RING_CAMERA_DISTANCE_MULTIPLE; - var arrowConeOffset = arrowCylinderDimensions.y * TRANSLATE_ARROW_CONE_OFFSET_CYLINDER_DIMENSION_MULTIPLE; - var cylinderXPosition = { x: arrowCylinderOffset, y: 0, z: 0 }; - cylinderXPosition = Vec3.sum(position, Vec3.multiplyQbyV(rotation, cylinderXPosition)); - Overlays.editOverlay(handleTranslateXCylinder, { - position: cylinderXPosition, - rotation: rotationX, - dimensions: arrowCylinderDimensions - }); - var cylinderXOffset = Vec3.subtract(cylinderXPosition, position); - var coneXPosition = Vec3.sum(cylinderXPosition, Vec3.multiply(Vec3.normalize(cylinderXOffset), arrowConeOffset)); - Overlays.editOverlay(handleTranslateXCone, { - position: coneXPosition, - rotation: rotationX, - dimensions: arrowConeDimensions - }); - var cylinderYPosition = { x: 0, y: arrowCylinderOffset, z: 0 }; - cylinderYPosition = Vec3.sum(position, Vec3.multiplyQbyV(rotation, cylinderYPosition)); - Overlays.editOverlay(handleTranslateYCylinder, { - position: cylinderYPosition, - rotation: rotationY, - dimensions: arrowCylinderDimensions - }); - var cylinderYOffset = Vec3.subtract(cylinderYPosition, position); - var coneYPosition = Vec3.sum(cylinderYPosition, Vec3.multiply(Vec3.normalize(cylinderYOffset), arrowConeOffset)); - Overlays.editOverlay(handleTranslateYCone, { - position: coneYPosition, - rotation: rotationY, - dimensions: arrowConeDimensions - }); - var cylinderZPosition = { x: 0, y: 0, z: arrowCylinderOffset }; - cylinderZPosition = Vec3.sum(position, Vec3.multiplyQbyV(rotation, cylinderZPosition)); - Overlays.editOverlay(handleTranslateZCylinder, { - position: cylinderZPosition, - rotation: rotationZ, - dimensions: arrowCylinderDimensions - }); - var cylinderZOffset = Vec3.subtract(cylinderZPosition, position); - var coneZPosition = Vec3.sum(cylinderZPosition, Vec3.multiply(Vec3.normalize(cylinderZOffset), arrowConeOffset)); - Overlays.editOverlay(handleTranslateZCone, { - position: coneZPosition, - rotation: rotationZ, - dimensions: arrowConeDimensions - }); - - // UPDATE SCALE CUBE - var scaleCubeRotation = spaceMode === SPACE_LOCAL ? rotation : Quat.IDENTITY; - var scaleCubeDimension = rotateDimension * SCALE_OVERLAY_CAMERA_DISTANCE_MULTIPLE / - ROTATE_RING_CAMERA_DISTANCE_MULTIPLE; - var scaleCubeDimensions = { x: scaleCubeDimension, y: scaleCubeDimension, z: scaleCubeDimension }; - Overlays.editOverlay(handleScaleCube, { - position: position, - rotation: scaleCubeRotation, - dimensions: scaleCubeDimensions - }); - - // UPDATE BOUNDING BOX - Overlays.editOverlay(handleBoundingBox, { - position: position, - rotation: rotation, - dimensions: dimensions - }); - - // UPDATE STRETCH HIGHLIGHT PANELS - var edgeOffsetX = BOUNDING_EDGE_OFFSET * dimensions.x; - var edgeOffsetY = BOUNDING_EDGE_OFFSET * dimensions.y; - var edgeOffsetZ = BOUNDING_EDGE_OFFSET * dimensions.z; - var RBFPosition = { x: edgeOffsetX, y: -edgeOffsetY, z: edgeOffsetZ }; - RBFPosition = Vec3.sum(position, Vec3.multiplyQbyV(rotation, RBFPosition)); - var RTFPosition = { x: edgeOffsetX, y: edgeOffsetY, z: edgeOffsetZ }; - RTFPosition = Vec3.sum(position, Vec3.multiplyQbyV(rotation, RTFPosition)); - var LTNPosition = { x: -edgeOffsetX, y: edgeOffsetY, z: -edgeOffsetZ }; - LTNPosition = Vec3.sum(position, Vec3.multiplyQbyV(rotation, LTNPosition)); - var RTNPosition = { x: edgeOffsetX, y: edgeOffsetY, z: -edgeOffsetZ }; - RTNPosition = Vec3.sum(position, Vec3.multiplyQbyV(rotation, RTNPosition)); - - var RBFPositionRotated = Vec3.multiplyQbyV(rotationInverse, RBFPosition); - var RTFPositionRotated = Vec3.multiplyQbyV(rotationInverse, RTFPosition); - var LTNPositionRotated = Vec3.multiplyQbyV(rotationInverse, LTNPosition); - var RTNPositionRotated = Vec3.multiplyQbyV(rotationInverse, RTNPosition); - var stretchPanelXDimensions = Vec3.subtract(RTNPositionRotated, RBFPositionRotated); - var tempY = Math.abs(stretchPanelXDimensions.y); - stretchPanelXDimensions.x = STRETCH_PANEL_WIDTH; - stretchPanelXDimensions.y = Math.abs(stretchPanelXDimensions.z); - stretchPanelXDimensions.z = tempY; - var stretchPanelXPosition = Vec3.sum(position, Vec3.multiplyQbyV(rotation, { x: dimensions.x / 2, y: 0, z: 0 })); - Overlays.editOverlay(handleStretchXPanel, { - position: stretchPanelXPosition, - rotation: rotationZ, - dimensions: stretchPanelXDimensions - }); - var stretchPanelYDimensions = Vec3.subtract(LTNPositionRotated, RTFPositionRotated); - var tempX = Math.abs(stretchPanelYDimensions.x); - stretchPanelYDimensions.x = Math.abs(stretchPanelYDimensions.z); - stretchPanelYDimensions.y = STRETCH_PANEL_WIDTH; - stretchPanelYDimensions.z = tempX; - var stretchPanelYPosition = Vec3.sum(position, Vec3.multiplyQbyV(rotation, { x: 0, y: dimensions.y / 2, z: 0 })); - Overlays.editOverlay(handleStretchYPanel, { - position: stretchPanelYPosition, - rotation: rotationY, - dimensions: stretchPanelYDimensions - }); - var stretchPanelZDimensions = Vec3.subtract(LTNPositionRotated, RBFPositionRotated); - tempX = Math.abs(stretchPanelZDimensions.x); - stretchPanelZDimensions.x = Math.abs(stretchPanelZDimensions.y); - stretchPanelZDimensions.y = tempX; - stretchPanelZDimensions.z = STRETCH_PANEL_WIDTH; - var stretchPanelZPosition = Vec3.sum(position, Vec3.multiplyQbyV(rotation, { x: 0, y: 0, z: dimensions.z / 2 })); - Overlays.editOverlay(handleStretchZPanel, { - position: stretchPanelZPosition, - rotation: rotationX, - dimensions: stretchPanelZDimensions - }); - - // UPDATE STRETCH CUBES - var stretchCubeDimension = rotateDimension * STRETCH_CUBE_CAMERA_DISTANCE_MULTIPLE / - ROTATE_RING_CAMERA_DISTANCE_MULTIPLE; - var stretchCubeDimensions = { x: stretchCubeDimension, y: stretchCubeDimension, z: stretchCubeDimension }; - var stretchCubeOffset = rotateDimension * STRETCH_CUBE_OFFSET / ROTATE_RING_CAMERA_DISTANCE_MULTIPLE; - var stretchXPosition, stretchYPosition, stretchZPosition; - if (isActiveTool(handleStretchXCube)) { - stretchXPosition = Vec3.subtract(stretchPanelXPosition, activeStretchCubePanelOffset); - } else { - stretchXPosition = { x: stretchCubeOffset, y: 0, z: 0 }; - stretchXPosition = Vec3.sum(position, Vec3.multiplyQbyV(rotation, stretchXPosition)); - } - if (isActiveTool(handleStretchYCube)) { - stretchYPosition = Vec3.subtract(stretchPanelYPosition, activeStretchCubePanelOffset); - } else { - stretchYPosition = { x: 0, y: stretchCubeOffset, z: 0 }; - stretchYPosition = Vec3.sum(position, Vec3.multiplyQbyV(rotation, stretchYPosition)); - } - if (isActiveTool(handleStretchZCube)) { - stretchZPosition = Vec3.subtract(stretchPanelZPosition, activeStretchCubePanelOffset); - } else { - stretchZPosition = { x: 0, y: 0, z: stretchCubeOffset }; - stretchZPosition = Vec3.sum(position, Vec3.multiplyQbyV(rotation, stretchZPosition)); - } - Overlays.editOverlay(handleStretchXCube, { - position: stretchXPosition, - rotation: rotationX, - dimensions: stretchCubeDimensions - }); - Overlays.editOverlay(handleStretchYCube, { - position: stretchYPosition, - rotation: rotationY, - dimensions: stretchCubeDimensions - }); - Overlays.editOverlay(handleStretchZCube, { - position: stretchZPosition, - rotation: rotationZ, - dimensions: stretchCubeDimensions - }); - - // UPDATE SELECTION BOX (CURRENTLY INVISIBLE WITH 0 ALPHA FOR TRANSLATE XZ TOOL) - var inModeRotate = isActiveTool(handleRotatePitchRing) || - isActiveTool(handleRotateYawRing) || - isActiveTool(handleRotateRollRing); - selectionBoxGeometry.visible = !inModeRotate && !isCameraInsideBox; - selectionBoxGeometry.ignorePickIntersection = !selectionBoxGeometry.visible; - Overlays.editOverlay(selectionBox, selectionBoxGeometry); - - // UPDATE ICON TRANSLATE HANDLE - if (SelectionManager.entityType === "ParticleEffect" || SelectionManager.entityType === "Light") { - var iconSelectionBoxGeometry = { - position: position, - rotation: rotation - }; - iconSelectionBoxGeometry.visible = !inModeRotate && isCameraInsideBox; - iconSelectionBoxGeometry.ignorePickIntersection = !iconSelectionBoxGeometry.visible; - Overlays.editOverlay(iconSelectionBox, iconSelectionBoxGeometry); - } else { - Overlays.editOverlay(iconSelectionBox, { - visible: false, - ignorePickIntersection: true - }); - } - - // UPDATE DUPLICATOR (CURRENTLY HIDDEN FOR NOW) - var handleDuplicatorOffset = { - x: DUPLICATOR_OFFSET.x * dimensions.x, - y: DUPLICATOR_OFFSET.y * dimensions.y, - z: DUPLICATOR_OFFSET.z * dimensions.z - }; - var handleDuplicatorPos = Vec3.sum(position, Vec3.multiplyQbyV(rotation, handleDuplicatorOffset)); - Overlays.editOverlay(handleDuplicator, { - position: handleDuplicatorPos, - rotation: rotation, - dimensions: scaleCubeDimensions - }); - } - - that.setHandleTranslateXVisible(!activeTool || isActiveTool(handleTranslateXCone) || - isActiveTool(handleTranslateXCylinder)); - that.setHandleTranslateYVisible(!activeTool || isActiveTool(handleTranslateYCone) || - isActiveTool(handleTranslateYCylinder)); - that.setHandleTranslateZVisible(!activeTool || isActiveTool(handleTranslateZCone) || - isActiveTool(handleTranslateZCylinder)); - that.setHandleRotatePitchVisible(!activeTool || isActiveTool(handleRotatePitchRing)); - that.setHandleRotateYawVisible(!activeTool || isActiveTool(handleRotateYawRing)); - that.setHandleRotateRollVisible(!activeTool || isActiveTool(handleRotateRollRing)); - - var showScaleStretch = !activeTool && SelectionManager.selections.length === 1 && spaceMode === SPACE_LOCAL; - that.setHandleStretchXVisible(showScaleStretch || isActiveTool(handleStretchXCube)); - that.setHandleStretchYVisible(showScaleStretch || isActiveTool(handleStretchYCube)); - that.setHandleStretchZVisible(showScaleStretch || isActiveTool(handleStretchZCube)); - that.setHandleScaleVisible(showScaleStretch || isActiveTool(handleScaleCube)); - - var showOutlineForZone = (SelectionManager.selections.length === 1 && - typeof SelectionManager.savedProperties[SelectionManager.selections[0]] !== "undefined" && - SelectionManager.savedProperties[SelectionManager.selections[0]].type === "Zone"); - that.setHandleBoundingBoxVisible(showOutlineForZone || (!isActiveTool(handleRotatePitchRing) && - !isActiveTool(handleRotateYawRing) && - !isActiveTool(handleRotateRollRing))); - - // keep duplicator always hidden for now since you can hold Alt to duplicate while - // translating an entity - we may bring duplicator back for HMD only later - // that.setHandleDuplicatorVisible(!activeTool || isActiveTool(handleDuplicator)); - - if (wantDebug) { - print("====== Update Handles <======="); - } - }; - Script.update.connect(that.updateHandles); - - // FUNCTION: UPDATE ACTIVE ROTATE RING - that.updateActiveRotateRing = function() { - var activeRotateRing = null; - if (isActiveTool(handleRotatePitchRing)) { - activeRotateRing = handleRotatePitchRing; - } else if (isActiveTool(handleRotateYawRing)) { - activeRotateRing = handleRotateYawRing; - } else if (isActiveTool(handleRotateRollRing)) { - activeRotateRing = handleRotateRollRing; - } - if (activeRotateRing !== null) { - var tickMarksAngle = ctrlPressed ? ROTATE_CTRL_SNAP_ANGLE : ROTATE_DEFAULT_TICK_MARKS_ANGLE; - Overlays.editOverlay(activeRotateRing, { majorTickMarksAngle: tickMarksAngle }); - } - }; - - // FUNCTION: SET OVERLAYS VISIBLE - that.setOverlaysVisible = function(isVisible) { - for (var i = 0, length = allOverlays.length; i < length; i++) { - Overlays.editOverlay(allOverlays[i], { visible: isVisible, ignorePickIntersection: !isVisible }); - } - }; - - // FUNCTION: SET HANDLE TRANSLATE VISIBLE - that.setHandleTranslateVisible = function(isVisible) { - that.setHandleTranslateXVisible(isVisible); - that.setHandleTranslateYVisible(isVisible); - that.setHandleTranslateZVisible(isVisible); - }; - - that.setHandleTranslateXVisible = function(isVisible) { - Overlays.editOverlay(handleTranslateXCone, { visible: isVisible, ignorePickIntersection: !isVisible }); - Overlays.editOverlay(handleTranslateXCylinder, { visible: isVisible, ignorePickIntersection: !isVisible }); - }; - - that.setHandleTranslateYVisible = function(isVisible) { - Overlays.editOverlay(handleTranslateYCone, { visible: isVisible, ignorePickIntersection: !isVisible }); - Overlays.editOverlay(handleTranslateYCylinder, { visible: isVisible, ignorePickIntersection: !isVisible }); - }; - - that.setHandleTranslateZVisible = function(isVisible) { - Overlays.editOverlay(handleTranslateZCone, { visible: isVisible, ignorePickIntersection: !isVisible }); - Overlays.editOverlay(handleTranslateZCylinder, { visible: isVisible, ignorePickIntersection: !isVisible }); - }; - - // FUNCTION: SET HANDLE ROTATE VISIBLE - that.setHandleRotateVisible = function(isVisible) { - that.setHandleRotatePitchVisible(isVisible); - that.setHandleRotateYawVisible(isVisible); - that.setHandleRotateRollVisible(isVisible); - }; - - that.setHandleRotatePitchVisible = function(isVisible) { - Overlays.editOverlay(handleRotatePitchRing, { visible: isVisible, ignorePickIntersection: !isVisible }); - }; - - that.setHandleRotateYawVisible = function(isVisible) { - Overlays.editOverlay(handleRotateYawRing, { visible: isVisible, ignorePickIntersection: !isVisible }); - }; - - that.setHandleRotateRollVisible = function(isVisible) { - Overlays.editOverlay(handleRotateRollRing, { visible: isVisible, ignorePickIntersection: !isVisible }); - }; - - // FUNCTION: SET HANDLE STRETCH VISIBLE - that.setHandleStretchVisible = function(isVisible) { - that.setHandleStretchXVisible(isVisible); - that.setHandleStretchYVisible(isVisible); - that.setHandleStretchZVisible(isVisible); - }; - - that.setHandleStretchXVisible = function(isVisible) { - Overlays.editOverlay(handleStretchXCube, { visible: isVisible, ignorePickIntersection: !isVisible }); - }; - - that.setHandleStretchYVisible = function(isVisible) { - Overlays.editOverlay(handleStretchYCube, { visible: isVisible, ignorePickIntersection: !isVisible }); - }; - - that.setHandleStretchZVisible = function(isVisible) { - Overlays.editOverlay(handleStretchZCube, { visible: isVisible, ignorePickIntersection: !isVisible }); - }; - - // FUNCTION: SET HANDLE SCALE VISIBLE - that.setHandleScaleVisible = function(isVisible) { - that.setHandleScaleVisible(isVisible); - that.setHandleBoundingBoxVisible(isVisible); - }; - - that.setHandleScaleVisible = function(isVisible) { - Overlays.editOverlay(handleScaleCube, { visible: isVisible, ignorePickIntersection: !isVisible }); - }; - - that.setHandleBoundingBoxVisible = function(isVisible) { - Overlays.editOverlay(handleBoundingBox, { visible: isVisible, ignorePickIntersection: true }); - }; - - // FUNCTION: SET HANDLE DUPLICATOR VISIBLE - that.setHandleDuplicatorVisible = function(isVisible) { - Overlays.editOverlay(handleDuplicator, { visible: isVisible, ignorePickIntersection: !isVisible }); - }; - - // FUNCTION: DEBUG PICK PLANE - that.showDebugPickPlane = function(pickPlanePosition, pickPlaneNormal) { - var planePlusNormal = Vec3.sum(pickPlanePosition, pickPlaneNormal); - var rotation = Quat.lookAtSimple(planePlusNormal, pickPlanePosition); - var dimensionXZ = getDistanceToCamera(pickPlanePosition) * 1.25; - var dimensions = { x:dimensionXZ, y:dimensionXZ, z:STRETCH_PANEL_WIDTH }; - Overlays.editOverlay(debugPickPlane, { - position: pickPlanePosition, - rotation: rotation, - dimensions: dimensions, - visible: true - }); - }; - - that.showDebugPickPlaneHit = function(pickHitPosition) { - var dimension = getDistanceToCamera(pickHitPosition) * DEBUG_PICK_PLANE_HIT_CAMERA_DISTANCE_MULTIPLE; - var pickPlaneHit = Overlays.addOverlay("shape", { - alpha: 0.5, - shape: "Sphere", - solid: true, - visible: true, - ignorePickIntersection: true, - drawInFront: false, - color: COLOR_DEBUG_PICK_PLANE_HIT, - position: pickHitPosition, - dimensions: { x: dimension, y: dimension, z: dimension } - }); - debugPickPlaneHits.push(pickPlaneHit); - if (debugPickPlaneHits.length > DEBUG_PICK_PLANE_HIT_LIMIT) { - var removedPickPlaneHit = debugPickPlaneHits.shift(); - Overlays.deleteOverlay(removedPickPlaneHit); - } - }; - - that.clearDebugPickPlane = function() { - Overlays.editOverlay(debugPickPlane, { visible: false }); - for (var i = 0; i < debugPickPlaneHits.length; i++) { - Overlays.deleteOverlay(debugPickPlaneHits[i]); - } - debugPickPlaneHits = []; - }; - - // TOOL DEFINITION: HANDLE TRANSLATE XZ TOOL - function addHandleTranslateXZTool(overlay, mode, doDuplicate) { - var initialPick = null; - var isConstrained = false; - var constrainMajorOnly = false; - var startPosition = null; - var duplicatedEntityIDs = null; - var pickPlanePosition = null; - var pickPlaneNormal = { x: 0, y: 1, z: 0 }; - var greatestDimension = 0.0; - var startingDistance = 0.0; - var startingElevation = 0.0; - addHandleTool(overlay, { - mode: mode, - onBegin: function(event, pickRay, pickResult) { - var wantDebug = false; - if (wantDebug) { - print("================== TRANSLATE_XZ(Beg) -> ======================="); - Vec3.print(" pickRay", pickRay); - Vec3.print(" pickRay.origin", pickRay.origin); - Vec3.print(" pickResult.intersection", pickResult.intersection); - } - - // Duplicate entities if alt is pressed. This will make a - // copy of the selected entities and move the _original_ entities, not - // the new ones. - if (event.isAlt || doDuplicate) { - duplicatedEntityIDs = SelectionManager.duplicateSelection(); - var ids = []; - for (var i = 0; i < duplicatedEntityIDs.length; ++i) { - ids.push(duplicatedEntityIDs[i].entityID); - } - SelectionManager.setSelections(ids); - } else { - duplicatedEntityIDs = null; - } - - SelectionManager.saveProperties(); - that.resetPreviousHandleColor(); - - that.setHandleTranslateVisible(false); - that.setHandleRotateVisible(false); - that.setHandleScaleVisible(false); - that.setHandleStretchVisible(false); - that.setHandleDuplicatorVisible(false); - - startPosition = SelectionManager.worldPosition; - pickPlanePosition = pickResult.intersection; - greatestDimension = Math.max(Math.max(SelectionManager.worldDimensions.x, - SelectionManager.worldDimensions.y), - SelectionManager.worldDimensions.z); - startingDistance = Vec3.distance(pickRay.origin, SelectionManager.position); - startingElevation = this.elevation(pickRay.origin, pickPlanePosition); - if (wantDebug) { - print(" longest dimension: " + greatestDimension); - print(" starting distance: " + startingDistance); - print(" starting elevation: " + startingElevation); - } - - initialPick = rayPlaneIntersection(pickRay, pickPlanePosition, pickPlaneNormal); - - if (debugPickPlaneEnabled) { - that.showDebugPickPlane(pickPlanePosition, pickPlaneNormal); - that.showDebugPickPlaneHit(initialPick); - } - - isConstrained = false; - if (wantDebug) { - print("================== TRANSLATE_XZ(End) <- ======================="); - } - }, - onEnd: function(event, reason) { - pushCommandForSelections(duplicatedEntityIDs); - if (isConstrained) { - Overlays.editOverlay(xRailOverlay, { - visible: false, - ignorePickIntersection: true - }); - Overlays.editOverlay(zRailOverlay, { - visible: false, - ignorePickIntersection: true - }); - } - }, - elevation: function(origin, intersection) { - return (origin.y - intersection.y) / Vec3.distance(origin, intersection); - }, - onMove: function(event) { - var wantDebug = false; - var pickRay = generalComputePickRay(event.x, event.y); - - var newPick = rayPlaneIntersection2(pickRay, pickPlanePosition, pickPlaneNormal); - - // If the pick ray doesn't hit the pick plane in this direction, do nothing. - // this will happen when someone drags across the horizon from the side they started on. - if (!newPick) { - if (wantDebug) { - print(" "+ mode + "Pick ray does not intersect XZ plane."); - } - - // EARLY EXIT--(Invalid ray detected.) - return; - } - - if (debugPickPlaneEnabled) { - that.showDebugPickPlaneHit(newPick); - } - - var vector = Vec3.subtract(newPick, initialPick); - - // If the mouse is too close to the horizon of the pick plane, stop moving - var MIN_ELEVATION = 0.02; // largest dimension of object divided by distance to it - var elevation = this.elevation(pickRay.origin, newPick); - if (wantDebug) { - print("Start Elevation: " + startingElevation + ", elevation: " + elevation); - } - if ((startingElevation > 0.0 && elevation < MIN_ELEVATION) || - (startingElevation < 0.0 && elevation > -MIN_ELEVATION)) { - if (wantDebug) { - print(" "+ mode + " - too close to horizon!"); - } - - // EARLY EXIT--(Don't proceed past the reached limit.) - return; - } - - // If the angular size of the object is too small, stop moving - var MIN_ANGULAR_SIZE = 0.01; // Radians - if (greatestDimension > 0) { - var angularSize = Math.atan(greatestDimension / Vec3.distance(pickRay.origin, newPick)); - if (wantDebug) { - print("Angular size = " + angularSize); - } - if (angularSize < MIN_ANGULAR_SIZE) { - return; - } - } - - // If shifted, constrain to one axis - if (event.isShifted) { - if (Math.abs(vector.x) > Math.abs(vector.z)) { - vector.z = 0; - } else { - vector.x = 0; - } - if (!isConstrained) { - var xStart = Vec3.sum(startPosition, { - x: -RAIL_AXIS_LENGTH, - y: 0, - z: 0 - }); - var xEnd = Vec3.sum(startPosition, { - x: RAIL_AXIS_LENGTH, - y: 0, - z: 0 - }); - var zStart = Vec3.sum(startPosition, { - x: 0, - y: 0, - z: -RAIL_AXIS_LENGTH - }); - var zEnd = Vec3.sum(startPosition, { - x: 0, - y: 0, - z: RAIL_AXIS_LENGTH - }); - Overlays.editOverlay(xRailOverlay, { - start: xStart, - end: xEnd, - visible: true, - ignorePickIntersection: true - }); - Overlays.editOverlay(zRailOverlay, { - start: zStart, - end: zEnd, - visible: true, - ignorePickIntersection: true - }); - isConstrained = true; - } - } else { - if (isConstrained) { - Overlays.editOverlay(xRailOverlay, { - visible: false, - ignorePickIntersection: true - }); - Overlays.editOverlay(zRailOverlay, { - visible: false, - ignorePickIntersection: true - }); - isConstrained = false; - } - } - - constrainMajorOnly = event.isControl; - var negateAndHalve = -0.5; - var cornerPosition = Vec3.sum(startPosition, Vec3.multiply(negateAndHalve, SelectionManager.worldDimensions)); - vector = Vec3.subtract( - grid.snapToGrid(Vec3.sum(cornerPosition, vector), constrainMajorOnly), - cornerPosition); - - // editing a parent will cause all the children to automatically follow along, so don't - // edit any entity who has an ancestor in SelectionManager.selections - var toMove = SelectionManager.selections.filter(function (selection) { - if (SelectionManager.selections.indexOf(SelectionManager.savedProperties[selection].parentID) >= 0) { - return false; // a parent is also being moved, so don't issue an edit for this entity - } else { - return true; - } - }); - - for (var i = 0; i < toMove.length; i++) { - var properties = SelectionManager.savedProperties[toMove[i]]; - if (!properties) { - continue; - } - var newPosition = Vec3.sum(properties.position, { - x: vector.x, - y: 0, - z: vector.z - }); - Entities.editEntity(toMove[i], { - position: newPosition - }); - - if (wantDebug) { - print("translateXZ... "); - Vec3.print(" vector:", vector); - Vec3.print(" newPosition:", properties.position); - Vec3.print(" newPosition:", newPosition); - } - } - - SelectionManager._update(false, this); - } - }); - } - - // TOOL DEFINITION: HANDLE TRANSLATE TOOL - function addHandleTranslateTool(overlay, mode, direction) { - var pickPlanePosition = null; - var pickPlaneNormal = null; - var initialPick = null; - var projectionVector = null; - var previousPickRay = null; - var rotation = null; - addHandleTool(overlay, { - mode: mode, - onBegin: function(event, pickRay, pickResult) { - // Duplicate entities if alt is pressed. This will make a - // copy of the selected entities and move the _original_ entities, not - // the new ones. - if (event.isAlt) { - duplicatedEntityIDs = SelectionManager.duplicateSelection(); - var ids = []; - for (var i = 0; i < duplicatedEntityIDs.length; ++i) { - ids.push(duplicatedEntityIDs[i].entityID); - } - SelectionManager.setSelections(ids); - } else { - duplicatedEntityIDs = null; - } - - var axisVector; - if (direction === TRANSLATE_DIRECTION.X) { - axisVector = { x: 1, y: 0, z: 0 }; - } else if (direction === TRANSLATE_DIRECTION.Y) { - axisVector = { x: 0, y: 1, z: 0 }; - } else if (direction === TRANSLATE_DIRECTION.Z) { - axisVector = { x: 0, y: 0, z: 1 }; - } - - rotation = spaceMode === SPACE_LOCAL ? SelectionManager.localRotation : SelectionManager.worldRotation; - axisVector = Vec3.multiplyQbyV(rotation, axisVector); - pickPlaneNormal = Vec3.cross(Vec3.cross(pickRay.direction, axisVector), axisVector); - pickPlanePosition = SelectionManager.worldPosition; - initialPick = rayPlaneIntersection(pickRay, pickPlanePosition, pickPlaneNormal); - - SelectionManager.saveProperties(); - that.resetPreviousHandleColor(); - - that.setHandleTranslateXVisible(direction === TRANSLATE_DIRECTION.X); - that.setHandleTranslateYVisible(direction === TRANSLATE_DIRECTION.Y); - that.setHandleTranslateZVisible(direction === TRANSLATE_DIRECTION.Z); - that.setHandleRotateVisible(false); - that.setHandleStretchVisible(false); - that.setHandleScaleVisible(false); - that.setHandleDuplicatorVisible(false); - - previousPickRay = pickRay; - - if (debugPickPlaneEnabled) { - that.showDebugPickPlane(pickPlanePosition, pickPlaneNormal); - that.showDebugPickPlaneHit(initialPick); - } - }, - onEnd: function(event, reason) { - pushCommandForSelections(duplicatedEntityIDs); - }, - onMove: function(event) { - var pickRay = generalComputePickRay(event.x, event.y); - - // Use previousPickRay if new pickRay will cause resulting rayPlaneIntersection values to wrap around - if (usePreviousPickRay(pickRay.direction, previousPickRay.direction, pickPlaneNormal)) { - pickRay = previousPickRay; - } - - var newPick = rayPlaneIntersection(pickRay, pickPlanePosition, pickPlaneNormal); - if (debugPickPlaneEnabled) { - that.showDebugPickPlaneHit(newPick); - } - - var vector = Vec3.subtract(newPick, initialPick); - - if (direction === TRANSLATE_DIRECTION.X) { - projectionVector = { x: 1, y: 0, z: 0 }; - } else if (direction === TRANSLATE_DIRECTION.Y) { - projectionVector = { x: 0, y: 1, z: 0 }; - } else if (direction === TRANSLATE_DIRECTION.Z) { - projectionVector = { x: 0, y: 0, z: 1 }; - } - projectionVector = Vec3.multiplyQbyV(rotation, projectionVector); - - var dotVector = Vec3.dot(vector, projectionVector); - vector = Vec3.multiply(dotVector, projectionVector); - var gridOrigin = grid.getOrigin(); - vector = Vec3.subtract(grid.snapToGrid(Vec3.sum(vector, gridOrigin)), gridOrigin); - - var wantDebug = false; - if (wantDebug) { - print("translateUpDown... "); - print(" event.y:" + event.y); - Vec3.print(" newIntersection:", newIntersection); - Vec3.print(" vector:", vector); - } - - // editing a parent will cause all the children to automatically follow along, so don't - // edit any entity who has an ancestor in SelectionManager.selections - var toMove = SelectionManager.selections.filter(function (selection) { - if (SelectionManager.selections.indexOf(SelectionManager.savedProperties[selection].parentID) >= 0) { - return false; // a parent is also being moved, so don't issue an edit for this entity - } else { - return true; - } - }); - - for (var i = 0; i < toMove.length; i++) { - var id = toMove[i]; - var properties = SelectionManager.savedProperties[id]; - var newPosition = Vec3.sum(properties.position, vector); - Entities.editEntity(id, { position: newPosition }); - } - - previousPickRay = pickRay; - - SelectionManager._update(false, this); - } - }); - } - - // TOOL DEFINITION: HANDLE STRETCH TOOL - function addHandleStretchTool(overlay, mode, directionEnum) { - var initialPick = null; - var initialPosition = null; - var initialDimensions = null; - var rotation = null; - var registrationPoint = null; - var pickPlanePosition = null; - var pickPlaneNormal = null; - var previousPickRay = null; - var directionVector = null; - var axisVector = null; - var signs = null; - var mask = null; - var stretchPanel = null; - var handleStretchCube = null; - var deltaPivot = null; - addHandleTool(overlay, { - mode: mode, - onBegin: function(event, pickRay, pickResult) { - if (directionEnum === STRETCH_DIRECTION.X) { - stretchPanel = handleStretchXPanel; - handleStretchCube = handleStretchXCube; - directionVector = { x: -1, y: 0, z: 0 }; - } else if (directionEnum === STRETCH_DIRECTION.Y) { - stretchPanel = handleStretchYPanel; - handleStretchCube = handleStretchYCube; - directionVector = { x: 0, y: -1, z: 0 }; - } else if (directionEnum === STRETCH_DIRECTION.Z) { - stretchPanel = handleStretchZPanel; - handleStretchCube = handleStretchZCube; - directionVector = { x: 0, y: 0, z: -1 }; - } - - rotation = SelectionManager.localRotation; - initialPosition = SelectionManager.localPosition; - initialDimensions = SelectionManager.localDimensions; - registrationPoint = SelectionManager.localRegistrationPoint; - - axisVector = Vec3.multiply(NEGATE_VECTOR, directionVector); - axisVector = Vec3.multiplyQbyV(rotation, axisVector); - - signs = { - x: directionVector.x < 0 ? -1 : (directionVector.x > 0 ? 1 : 0), - y: directionVector.y < 0 ? -1 : (directionVector.y > 0 ? 1 : 0), - z: directionVector.z < 0 ? -1 : (directionVector.z > 0 ? 1 : 0) - }; - mask = { - x: Math.abs(directionVector.x) > 0 ? 1 : 0, - y: Math.abs(directionVector.y) > 0 ? 1 : 0, - z: Math.abs(directionVector.z) > 0 ? 1 : 0 - }; - - var pivot = directionVector; - var offset = Vec3.multiply(directionVector, NEGATE_VECTOR); - - // Modify range of registrationPoint to be [-0.5, 0.5] - var centeredRP = Vec3.subtract(registrationPoint, { - x: 0.5, - y: 0.5, - z: 0.5 - }); - - // Scale pivot to be in the same range as registrationPoint - var scaledPivot = Vec3.multiply(0.5, pivot); - deltaPivot = Vec3.subtract(centeredRP, scaledPivot); - - var scaledOffset = Vec3.multiply(0.5, offset); - - // Offset from the registration point - var offsetRP = Vec3.subtract(scaledOffset, centeredRP); - - // Scaled offset in world coordinates - var scaledOffsetWorld = Vec3.multiplyVbyV(initialDimensions, offsetRP); - - pickPlaneNormal = Vec3.cross(Vec3.cross(pickRay.direction, axisVector), axisVector); - pickPlanePosition = Vec3.sum(initialPosition, Vec3.multiplyQbyV(rotation, scaledOffsetWorld)); - initialPick = rayPlaneIntersection(pickRay, pickPlanePosition, pickPlaneNormal); - - that.setHandleTranslateVisible(false); - that.setHandleRotateVisible(false); - that.setHandleScaleVisible(true); - that.setHandleStretchXVisible(directionEnum === STRETCH_DIRECTION.X); - that.setHandleStretchYVisible(directionEnum === STRETCH_DIRECTION.Y); - that.setHandleStretchZVisible(directionEnum === STRETCH_DIRECTION.Z); - that.setHandleDuplicatorVisible(false); - - SelectionManager.saveProperties(); - that.resetPreviousHandleColor(); - - var collisionToRemove = "myAvatar"; - var properties = Entities.getEntityProperties(SelectionManager.selections[0]); - if (properties.collidesWith.indexOf(collisionToRemove) > -1) { - var newCollidesWith = properties.collidesWith.replace(collisionToRemove, ""); - Entities.editEntity(SelectionManager.selections[0], {collidesWith: newCollidesWith}); - that.replaceCollisionsAfterStretch = true; - } - - if (stretchPanel !== null) { - Overlays.editOverlay(stretchPanel, { visible: true, ignorePickIntersection: false }); - } - var stretchCubePosition = Overlays.getProperty(handleStretchCube, "position"); - var stretchPanelPosition = Overlays.getProperty(stretchPanel, "position"); - activeStretchCubePanelOffset = Vec3.subtract(stretchPanelPosition, stretchCubePosition); - - previousPickRay = pickRay; - - if (debugPickPlaneEnabled) { - that.showDebugPickPlane(pickPlanePosition, pickPlaneNormal); - that.showDebugPickPlaneHit(initialPick); - } - }, - onEnd: function(event, reason) { - if (that.replaceCollisionsAfterStretch) { - var newCollidesWith = SelectionManager.savedProperties[SelectionManager.selections[0]].collidesWith; - Entities.editEntity(SelectionManager.selections[0], {collidesWith: newCollidesWith}); - that.replaceCollisionsAfterStretch = false; - } - - if (stretchPanel !== null) { - Overlays.editOverlay(stretchPanel, { visible: false, ignorePickIntersection: true }); - } - activeStretchCubePanelOffset = null; - - pushCommandForSelections(); - }, - onMove: function(event) { - var pickRay = generalComputePickRay(event.x, event.y); - - // Use previousPickRay if new pickRay will cause resulting rayPlaneIntersection values to wrap around - if (usePreviousPickRay(pickRay.direction, previousPickRay.direction, pickPlaneNormal)) { - pickRay = previousPickRay; - } - - var newPick = rayPlaneIntersection(pickRay, pickPlanePosition, pickPlaneNormal); - if (debugPickPlaneEnabled) { - that.showDebugPickPlaneHit(newPick); - } - - var changeInDimensions = Vec3.subtract(newPick, initialPick); - var dotVector = Vec3.dot(changeInDimensions, axisVector); - changeInDimensions = Vec3.multiply(dotVector, axisVector); - changeInDimensions = Vec3.multiplyQbyV(Quat.inverse(rotation), changeInDimensions); - changeInDimensions = Vec3.multiplyVbyV(mask, changeInDimensions); - changeInDimensions = grid.snapToSpacing(changeInDimensions); - changeInDimensions = Vec3.multiply(NEGATE_VECTOR, Vec3.multiplyVbyV(signs, changeInDimensions)); - - var newDimensions = Vec3.sum(initialDimensions, changeInDimensions); - - var minimumDimension = Entities.getPropertyInfo("dimensions").minimum; - if (newDimensions.x < minimumDimension) { - newDimensions.x = minimumDimension; - changeInDimensions.x = minimumDimension - initialDimensions.x; - } - if (newDimensions.y < minimumDimension) { - newDimensions.y = minimumDimension; - changeInDimensions.y = minimumDimension - initialDimensions.y; - } - if (newDimensions.z < minimumDimension) { - newDimensions.z = minimumDimension; - changeInDimensions.z = minimumDimension - initialDimensions.z; - } - - var changeInPosition = Vec3.multiplyQbyV(rotation, Vec3.multiplyVbyV(deltaPivot, changeInDimensions)); - var newPosition = Vec3.sum(initialPosition, changeInPosition); - - Entities.editEntity(SelectionManager.selections[0], { - position: newPosition, - dimensions: newDimensions - }); - - var wantDebug = false; - if (wantDebug) { - print(mode); - Vec3.print(" changeInDimensions:", changeInDimensions); - Vec3.print(" newDimensions:", newDimensions); - Vec3.print(" changeInPosition:", changeInPosition); - Vec3.print(" newPosition:", newPosition); - } - - previousPickRay = pickRay; - - SelectionManager._update(false, this); - } - }); - } - - // TOOL DEFINITION: HANDLE SCALE TOOL - function addHandleScaleTool(overlay, mode) { - var initialPick = null; - var initialPosition = null; - var initialDimensions = null; - var pickPlanePosition = null; - var pickPlaneNormal = null; - var previousPickRay = null; - addHandleTool(overlay, { - mode: mode, - onBegin: function(event, pickRay, pickResult) { - initialPosition = SelectionManager.localPosition; - initialDimensions = SelectionManager.localDimensions; - - pickPlanePosition = initialPosition; - pickPlaneNormal = Vec3.subtract(pickRay.origin, pickPlanePosition); - initialPick = rayPlaneIntersection(pickRay, pickPlanePosition, pickPlaneNormal); - - that.setHandleTranslateVisible(false); - that.setHandleRotateVisible(false); - that.setHandleScaleVisible(true); - that.setHandleStretchVisible(false); - that.setHandleDuplicatorVisible(false); - - SelectionManager.saveProperties(); - that.resetPreviousHandleColor(); - - var collisionToRemove = "myAvatar"; - var properties = Entities.getEntityProperties(SelectionManager.selections[0]); - if (properties.collidesWith.indexOf(collisionToRemove) > -1) { - var newCollidesWith = properties.collidesWith.replace(collisionToRemove, ""); - Entities.editEntity(SelectionManager.selections[0], {collidesWith: newCollidesWith}); - that.replaceCollisionsAfterStretch = true; - } - - previousPickRay = pickRay; - - if (debugPickPlaneEnabled) { - that.showDebugPickPlane(pickPlanePosition, pickPlaneNormal); - that.showDebugPickPlaneHit(initialPick); - } - }, - onEnd: function(event, reason) { - if (that.replaceCollisionsAfterStretch) { - var newCollidesWith = SelectionManager.savedProperties[SelectionManager.selections[0]].collidesWith; - Entities.editEntity(SelectionManager.selections[0], {collidesWith: newCollidesWith}); - that.replaceCollisionsAfterStretch = false; - } - - pushCommandForSelections(); - }, - onMove: function(event) { - var pickRay = generalComputePickRay(event.x, event.y); - - // Use previousPickRay if new pickRay will cause resulting rayPlaneIntersection values to wrap around - if (usePreviousPickRay(pickRay.direction, previousPickRay.direction, pickPlaneNormal)) { - pickRay = previousPickRay; - } - - var newPick = rayPlaneIntersection(pickRay, pickPlanePosition, pickPlaneNormal); - if (debugPickPlaneEnabled) { - that.showDebugPickPlaneHit(newPick); - } - - var toCameraDistance = getDistanceToCamera(initialPosition); - var dimensionsMultiple = toCameraDistance * SCALE_DIMENSIONS_CAMERA_DISTANCE_MULTIPLE; - var changeInDimensions = Vec3.subtract(newPick, initialPick); - changeInDimensions = Vec3.multiplyQbyV(Quat.inverse(Camera.orientation), changeInDimensions); - changeInDimensions = grid.snapToSpacing(changeInDimensions); - changeInDimensions = Vec3.multiply(changeInDimensions, dimensionsMultiple); - - var averageDimensionChange = (changeInDimensions.x + changeInDimensions.y + changeInDimensions.z) / 3; - var averageInitialDimension = (initialDimensions.x + initialDimensions.y + initialDimensions.z) / 3; - percentChange = averageDimensionChange / averageInitialDimension; - percentChange += 1.0; - - var newDimensions = Vec3.multiply(percentChange, initialDimensions); - newDimensions.x = Math.abs(newDimensions.x); - newDimensions.y = Math.abs(newDimensions.y); - newDimensions.z = Math.abs(newDimensions.z); - - var minimumDimension = Entities.getPropertyInfo("dimensions").minimum; - if (newDimensions.x < minimumDimension) { - newDimensions.x = minimumDimension; - changeInDimensions.x = minimumDimension - initialDimensions.x; - } - if (newDimensions.y < minimumDimension) { - newDimensions.y = minimumDimension; - changeInDimensions.y = minimumDimension - initialDimensions.y; - } - if (newDimensions.z < minimumDimension) { - newDimensions.z = minimumDimension; - changeInDimensions.z = minimumDimension - initialDimensions.z; - } - - Entities.editEntity(SelectionManager.selections[0], { dimensions: newDimensions }); - - var wantDebug = false; - if (wantDebug) { - print(mode); - Vec3.print(" changeInDimensions:", changeInDimensions); - Vec3.print(" newDimensions:", newDimensions); - } - - previousPickRay = pickRay; - - SelectionManager._update(false, this); - } - }); - } - - // FUNCTION: UPDATE ROTATION DEGREES OVERLAY - function updateRotationDegreesOverlay(angleFromZero, position) { - var toCameraDistance = getDistanceToCamera(position); - var overlayProps = { - position: position, - dimensions: { - x: toCameraDistance * ROTATE_DISPLAY_SIZE_X_MULTIPLIER, - y: toCameraDistance * ROTATE_DISPLAY_SIZE_Y_MULTIPLIER - }, - lineHeight: toCameraDistance * ROTATE_DISPLAY_LINE_HEIGHT_MULTIPLIER, - text: normalizeDegrees(-angleFromZero) + "°" - }; - Overlays.editOverlay(rotationDegreesDisplay, overlayProps); - } - - // FUNCTION DEF: updateSelectionsRotation - // Helper func used by rotation handle tools - function updateSelectionsRotation(rotationChange, initialPosition) { - if (!rotationChange) { - print("ERROR: entitySelectionTool.updateSelectionsRotation - Invalid arg specified!!"); - - // EARLY EXIT - return; - } - - // Entities should only reposition if we are rotating multiple selections around - // the selections center point. Otherwise, the rotation will be around the entities - // registration point which does not need repositioning. - var reposition = (SelectionManager.selections.length > 1); - - // editing a parent will cause all the children to automatically follow along, so don't - // edit any entity who has an ancestor in SelectionManager.selections - var toRotate = SelectionManager.selections.filter(function (selection) { - if (SelectionManager.selections.indexOf(SelectionManager.savedProperties[selection].parentID) >= 0) { - return false; // a parent is also being moved, so don't issue an edit for this entity - } else { - return true; - } - }); - - for (var i = 0; i < toRotate.length; i++) { - var entityID = toRotate[i]; - var initialProperties = SelectionManager.savedProperties[entityID]; - - var newProperties = { - rotation: Quat.multiply(rotationChange, initialProperties.rotation) - }; - - if (reposition) { - var dPos = Vec3.subtract(initialProperties.position, initialPosition); - dPos = Vec3.multiplyQbyV(rotationChange, dPos); - newProperties.position = Vec3.sum(initialPosition, dPos); - } - - Entities.editEntity(entityID, newProperties); - } - } - - // TOOL DEFINITION: HANDLE ROTATION TOOL - function addHandleRotateTool(overlay, mode, direction) { - var selectedHandle = null; - var worldRotation = null; - var initialRotation = null; - var rotationCenter = null; - var rotationNormal = null; - var rotationZero = null; - var rotationDegreesPosition = null; - addHandleTool(overlay, { - mode: mode, - onBegin: function(event, pickRay, pickResult) { - var wantDebug = false; - if (wantDebug) { - print("================== " + getMode() + "(addHandleRotateTool onBegin) -> ======================="); - } - - if (direction === ROTATE_DIRECTION.PITCH) { - rotationNormal = { x: 1, y: 0, z: 0 }; - worldRotation = worldRotationY; - selectedHandle = handleRotatePitchRing; - } else if (direction === ROTATE_DIRECTION.YAW) { - rotationNormal = { x: 0, y: 1, z: 0 }; - worldRotation = worldRotationZ; - selectedHandle = handleRotateYawRing; - } else if (direction === ROTATE_DIRECTION.ROLL) { - rotationNormal = { x: 0, y: 0, z: 1 }; - worldRotation = worldRotationX; - selectedHandle = handleRotateRollRing; - } - - initialRotation = spaceMode === SPACE_LOCAL ? SelectionManager.localRotation : SelectionManager.worldRotation; - rotationNormal = Vec3.multiplyQbyV(initialRotation, rotationNormal); - rotationCenter = SelectionManager.worldPosition; - - SelectionManager.saveProperties(); - that.resetPreviousHandleColor(); - - that.setHandleTranslateVisible(false); - that.setHandleRotatePitchVisible(direction === ROTATE_DIRECTION.PITCH); - that.setHandleRotateYawVisible(direction === ROTATE_DIRECTION.YAW); - that.setHandleRotateRollVisible(direction === ROTATE_DIRECTION.ROLL); - that.setHandleStretchVisible(false); - that.setHandleScaleVisible(false); - that.setHandleDuplicatorVisible(false); - - Overlays.editOverlay(selectedHandle, { - hasTickMarks: true, - solid: false, - innerRadius: ROTATE_RING_SELECTED_INNER_RADIUS - }); - - Overlays.editOverlay(rotationDegreesDisplay, { visible: true }); - Overlays.editOverlay(handleRotateCurrentRing, { - position: rotationCenter, - rotation: worldRotation, - startAt: 0, - endAt: 0, - visible: true, - ignorePickIntersection: false - }); - - // editOverlays may not have committed rotation changes. - // Compute zero position based on where the overlay will be eventually. - var initialPick = rayPlaneIntersection(pickRay, rotationCenter, rotationNormal); - // In case of a parallel ray, this will be null, which will cause early-out - // in the onMove helper. - rotationZero = initialPick; - - var rotationCenterToZero = Vec3.subtract(rotationZero, rotationCenter); - var rotationCenterToZeroLength = Vec3.length(rotationCenterToZero); - rotationDegreesPosition = Vec3.sum(rotationCenter, Vec3.multiply(Vec3.normalize(rotationCenterToZero), - rotationCenterToZeroLength * ROTATE_DISPLAY_DISTANCE_MULTIPLIER)); - updateRotationDegreesOverlay(0, rotationDegreesPosition); - - if (debugPickPlaneEnabled) { - that.showDebugPickPlane(rotationCenter, rotationNormal); - that.showDebugPickPlaneHit(initialPick); - } - - if (wantDebug) { - print("================== " + getMode() + "(addHandleRotateTool onBegin) <- ======================="); - } - }, - onEnd: function(event, reason) { - var wantDebug = false; - if (wantDebug) { - print("================== " + getMode() + "(addHandleRotateTool onEnd) -> ======================="); - } - Overlays.editOverlay(rotationDegreesDisplay, { visible: false, ignorePickIntersection: true }); - Overlays.editOverlay(selectedHandle, { - hasTickMarks: false, - solid: true, - innerRadius: ROTATE_RING_IDLE_INNER_RADIUS - }); - Overlays.editOverlay(handleRotateCurrentRing, { visible: false, ignorePickIntersection: true }); - pushCommandForSelections(); - if (wantDebug) { - print("================== " + getMode() + "(addHandleRotateTool onEnd) <- ======================="); - } - }, - onMove: function(event) { - if (!rotationZero) { - print("ERROR: entitySelectionTool.addHandleRotateTool.onMove - " + - "Invalid RotationZero Specified (missed rotation target plane?)"); - - // EARLY EXIT - return; - } - - var wantDebug = false; - if (wantDebug) { - print("================== "+ getMode() + "(addHandleRotateTool onMove) -> ======================="); - Vec3.print(" rotationZero: ", rotationZero); - } - - var pickRay = generalComputePickRay(event.x, event.y); - var result = rayPlaneIntersection(pickRay, rotationCenter, rotationNormal); - if (result) { - var centerToZero = Vec3.subtract(rotationZero, rotationCenter); - var centerToIntersect = Vec3.subtract(result, rotationCenter); - - if (wantDebug) { - Vec3.print(" RotationNormal: ", rotationNormal); - Vec3.print(" rotationZero: ", rotationZero); - Vec3.print(" rotationCenter: ", rotationCenter); - Vec3.print(" intersect: ", result); - Vec3.print(" centerToZero: ", centerToZero); - Vec3.print(" centerToIntersect: ", centerToIntersect); - } - - // Note: orientedAngle which wants normalized centerToZero and centerToIntersect - // handles that internally, so it's to pass unnormalized vectors here. - var angleFromZero = Vec3.orientedAngle(centerToZero, centerToIntersect, rotationNormal); - var snapAngle = ctrlPressed ? ROTATE_CTRL_SNAP_ANGLE : ROTATE_DEFAULT_SNAP_ANGLE; - angleFromZero = Math.floor(angleFromZero / snapAngle) * snapAngle; - var rotationChange = Quat.angleAxis(angleFromZero, rotationNormal); - updateSelectionsRotation(rotationChange, rotationCenter); - updateRotationDegreesOverlay(-angleFromZero, rotationDegreesPosition); - - if (direction === ROTATE_DIRECTION.YAW) { - angleFromZero *= -1; - } - - var startAtCurrent = 0; - var endAtCurrent = angleFromZero; - var maxDegrees = 360; - if (angleFromZero < 0) { - startAtCurrent = maxDegrees + angleFromZero; - endAtCurrent = maxDegrees; - } - Overlays.editOverlay(handleRotateCurrentRing, { - startAt: startAtCurrent, - endAt: endAtCurrent - }); - - if (debugPickPlaneEnabled) { - that.showDebugPickPlaneHit(result); - } - } - - if (wantDebug) { - print("================== "+ getMode() + "(addHandleRotateTool onMove) <- ======================="); - } - } - }); - } - - addHandleTranslateXZTool(selectionBox, "TRANSLATE_XZ", false); - addHandleTranslateXZTool(iconSelectionBox, "TRANSLATE_XZ", false); - addHandleTranslateXZTool(handleDuplicator, "DUPLICATE", true); - - addHandleTranslateTool(handleTranslateXCone, "TRANSLATE_X", TRANSLATE_DIRECTION.X); - addHandleTranslateTool(handleTranslateXCylinder, "TRANSLATE_X", TRANSLATE_DIRECTION.X); - addHandleTranslateTool(handleTranslateYCone, "TRANSLATE_Y", TRANSLATE_DIRECTION.Y); - addHandleTranslateTool(handleTranslateYCylinder, "TRANSLATE_Y", TRANSLATE_DIRECTION.Y); - addHandleTranslateTool(handleTranslateZCone, "TRANSLATE_Z", TRANSLATE_DIRECTION.Z); - addHandleTranslateTool(handleTranslateZCylinder, "TRANSLATE_Z", TRANSLATE_DIRECTION.Z); - - addHandleRotateTool(handleRotatePitchRing, "ROTATE_PITCH", ROTATE_DIRECTION.PITCH); - addHandleRotateTool(handleRotateYawRing, "ROTATE_YAW", ROTATE_DIRECTION.YAW); - addHandleRotateTool(handleRotateRollRing, "ROTATE_ROLL", ROTATE_DIRECTION.ROLL); - - addHandleStretchTool(handleStretchXCube, "STRETCH_X", STRETCH_DIRECTION.X); - addHandleStretchTool(handleStretchYCube, "STRETCH_Y", STRETCH_DIRECTION.Y); - addHandleStretchTool(handleStretchZCube, "STRETCH_Z", STRETCH_DIRECTION.Z); - - addHandleScaleTool(handleScaleCube, "SCALE"); - - return that; -}()); diff --git a/tests/shared/src/BlendshapePackingTests.cpp b/tests/shared/src/BlendshapePackingTests.cpp new file mode 100644 index 0000000000..a751a5ca02 --- /dev/null +++ b/tests/shared/src/BlendshapePackingTests.cpp @@ -0,0 +1,148 @@ +// +// BlendshapePackingTests.cpp +// tests/shared/src +// +// Created by Ken Cooke on 6/24/19. +// Copyright 2019 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include "BlendshapePackingTests.h" + +#include + +#include + +#include +#include + +struct BlendshapeOffsetUnpacked { + glm::vec3 positionOffset; + glm::vec3 normalOffset; + glm::vec3 tangentOffset; +}; + +struct BlendshapeOffsetPacked { + glm::uvec4 packedPosNorTan; +}; + +QTEST_MAIN(BlendshapePackingTests) + +static void packBlendshapeOffsetTo_Pos_F32_3xSN10_Nor_3xSN10_Tan_3xSN10(glm::uvec4& packed, const BlendshapeOffsetUnpacked& unpacked) { + float len = glm::compMax(glm::abs(unpacked.positionOffset)); + glm::vec3 normalizedPos(unpacked.positionOffset); + if (len > 0.0f) { + normalizedPos /= len; + } else { + len = 1.0f; + } + + packed = glm::uvec4( + glm::floatBitsToUint(len), + glm_packSnorm3x10_1x2(glm::vec4(normalizedPos, 0.0f)), + glm_packSnorm3x10_1x2(glm::vec4(unpacked.normalOffset, 0.0f)), + glm_packSnorm3x10_1x2(glm::vec4(unpacked.tangentOffset, 0.0f)) + ); +} + +static void packBlendshapeOffsets_ref(BlendshapeOffsetUnpacked* unpacked, BlendshapeOffsetPacked* packed, int size) { + for (int i = 0; i < size; ++i) { + packBlendshapeOffsetTo_Pos_F32_3xSN10_Nor_3xSN10_Tan_3xSN10((*packed).packedPosNorTan, (*unpacked)); + ++unpacked; + ++packed; + } +} + +#if defined(_M_IX86) || defined(_M_X64) || defined(__i386__) || defined(__x86_64__) +// +// Runtime CPU dispatch +// +#include + +void packBlendshapeOffsets_AVX2(float (*unpacked)[9], uint32_t (*packed)[4], int size); + +static void packBlendshapeOffsets(BlendshapeOffsetUnpacked* unpacked, BlendshapeOffsetPacked* packed, int size) { + static bool _cpuSupportsAVX2 = cpuSupportsAVX2(); + if (_cpuSupportsAVX2) { + static_assert(sizeof(BlendshapeOffsetUnpacked) == 9 * sizeof(float), "struct BlendshapeOffsetUnpacked size doesn't match."); + static_assert(sizeof(BlendshapeOffsetPacked) == 4 * sizeof(uint32_t), "struct BlendshapeOffsetPacked size doesn't match."); + packBlendshapeOffsets_AVX2((float(*)[9])unpacked, (uint32_t(*)[4])packed, size); + } else { + packBlendshapeOffsets_ref(unpacked, packed, size); + } +} + +#else // portable reference code +static auto& packBlendshapeOffsets = packBlendshapeOffsets_ref; +#endif + +void comparePacked(BlendshapeOffsetPacked& ref, BlendshapeOffsetPacked& tst) { + union i10i10i10i2 { + struct { + int x : 10; + int y : 10; + int z : 10; + int w : 2; + } data; + uint32_t pack; + } Ref[4], Tst[4]; + + for (int i = 0; i < 4; i++) { + Ref[i].pack = ref.packedPosNorTan[i]; + Tst[i].pack = tst.packedPosNorTan[i]; + } + + // allow 1 ULP due to rounding differences + QCOMPARE_WITH_ABS_ERROR(Tst[0].pack, Ref[0].pack, 1); + + QCOMPARE_WITH_ABS_ERROR(Tst[1].data.x, Ref[1].data.x, 1); + QCOMPARE_WITH_ABS_ERROR(Tst[1].data.y, Ref[1].data.y, 1); + QCOMPARE_WITH_ABS_ERROR(Tst[1].data.z, Ref[1].data.z, 1); + + QCOMPARE_WITH_ABS_ERROR(Tst[2].data.x, Ref[2].data.x, 1); + QCOMPARE_WITH_ABS_ERROR(Tst[2].data.y, Ref[2].data.y, 1); + QCOMPARE_WITH_ABS_ERROR(Tst[2].data.z, Ref[2].data.z, 1); + + QCOMPARE_WITH_ABS_ERROR(Tst[3].data.x, Ref[3].data.x, 1); + QCOMPARE_WITH_ABS_ERROR(Tst[3].data.y, Ref[3].data.y, 1); + QCOMPARE_WITH_ABS_ERROR(Tst[3].data.z, Ref[3].data.z, 1); +} + +void BlendshapePackingTests::testAVX2() { + + for (int numBlendshapeOffsets = 0; numBlendshapeOffsets < 4096; ++numBlendshapeOffsets) { + + std::vector unpackedBlendshapeOffsets(numBlendshapeOffsets); + std::vector packedBlendshapeOffsets1(numBlendshapeOffsets); + std::vector packedBlendshapeOffsets2(numBlendshapeOffsets); + + // init test data + if (numBlendshapeOffsets > 0) { + unpackedBlendshapeOffsets[0] = { + glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f, 0.0f, 0.0f), + }; + } + for (int i = 1; i < numBlendshapeOffsets; ++i) { + unpackedBlendshapeOffsets[i] = { + glm::linearRand(glm::vec3(-2.0f, -2.0f, -2.0f), glm::vec3(2.0f, 2.0f, 2.0f)), + glm::linearRand(glm::vec3(-2.0f, -2.0f, -2.0f), glm::vec3(2.0f, 2.0f, 2.0f)), + glm::linearRand(glm::vec3(-2.0f, -2.0f, -2.0f), glm::vec3(2.0f, 2.0f, 2.0f)), + }; + } + + // ref version + packBlendshapeOffsets_ref(unpackedBlendshapeOffsets.data(), packedBlendshapeOffsets1.data(), numBlendshapeOffsets); + + // AVX2 version, if supported by CPU + packBlendshapeOffsets(unpackedBlendshapeOffsets.data(), packedBlendshapeOffsets2.data(), numBlendshapeOffsets); + + // verify + for (int i = 0; i < numBlendshapeOffsets; ++i) { + auto ref = packedBlendshapeOffsets1.at(i); + auto tst = packedBlendshapeOffsets2.at(i); + comparePacked(ref, tst); + } + } +} diff --git a/tests/shared/src/BlendshapePackingTests.h b/tests/shared/src/BlendshapePackingTests.h new file mode 100644 index 0000000000..d6d0ef8b10 --- /dev/null +++ b/tests/shared/src/BlendshapePackingTests.h @@ -0,0 +1,23 @@ +// +// BlendshapePackingTests.h +// tests/shared/src +// +// Created by Ken Cooke on 6/24/19. +// Copyright 2019 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_BlendshapePackingTests_h +#define hifi_BlendshapePackingTests_h + +#include + +class BlendshapePackingTests : public QObject { + Q_OBJECT +private slots: + void testAVX2(); +}; + +#endif // hifi_BlendshapePackingTests_h