mirror of
https://github.com/overte-org/overte.git
synced 2025-08-08 02:17:11 +02:00
Merge branch 'master' of git://github.com/highfidelity/hifi into fade
This commit is contained in:
commit
0123e2936b
25 changed files with 735 additions and 388 deletions
|
@ -1,7 +1,96 @@
|
||||||
|
# Linux build guide
|
||||||
|
|
||||||
Please read the [general build guide](BUILD.md) for information on dependencies required for all platforms. Only Linux specific instructions are found in this file.
|
Please read the [general build guide](BUILD.md) for information on dependencies required for all platforms. Only Linux specific instructions are found in this file.
|
||||||
|
|
||||||
### Qt5 Dependencies
|
## Qt5 Dependencies
|
||||||
|
|
||||||
Should you choose not to install Qt5 via a package manager that handles dependencies for you, you may be missing some Qt5 dependencies. On Ubuntu, for example, the following additional packages are required:
|
Should you choose not to install Qt5 via a package manager that handles dependencies for you, you may be missing some Qt5 dependencies. On Ubuntu, for example, the following additional packages are required:
|
||||||
|
|
||||||
libasound2 libxmu-dev libxi-dev freeglut3-dev libasound2-dev libjack0 libjack-dev libxrandr-dev libudev-dev libssl-dev
|
libasound2 libxmu-dev libxi-dev freeglut3-dev libasound2-dev libjack0 libjack-dev libxrandr-dev libudev-dev libssl-dev
|
||||||
|
|
||||||
|
## Ubuntu 16.04 specific build guide
|
||||||
|
|
||||||
|
### Prepare environment
|
||||||
|
|
||||||
|
Install qt:
|
||||||
|
```bash
|
||||||
|
wget http://debian.highfidelity.com/pool/h/hi/hifi-qt5.6.1_5.6.1_amd64.deb
|
||||||
|
sudo dpkg -i hifi-qt5.6.1_5.6.1_amd64.deb
|
||||||
|
```
|
||||||
|
|
||||||
|
Install build dependencies:
|
||||||
|
```bash
|
||||||
|
sudo apt-get install libasound2 libxmu-dev libxi-dev freeglut3-dev libasound2-dev libjack0 libjack-dev libxrandr-dev libudev-dev libssl-dev
|
||||||
|
```
|
||||||
|
|
||||||
|
To compile interface in a server you must install:
|
||||||
|
```bash
|
||||||
|
sudo apt -y install libpulse0 libnss3 libnspr4 libfontconfig1 libxcursor1 libxcomposite1 libxtst6 libxslt1.1
|
||||||
|
```
|
||||||
|
|
||||||
|
Install build tools:
|
||||||
|
```bash
|
||||||
|
sudo apt install cmake
|
||||||
|
```
|
||||||
|
|
||||||
|
### Get code and checkout the tag you need
|
||||||
|
|
||||||
|
Clone this repository:
|
||||||
|
```bash
|
||||||
|
git clone https://github.com/highfidelity/hifi.git
|
||||||
|
```
|
||||||
|
|
||||||
|
To compile a RELEASE version checkout the tag you need getting a list of all tags:
|
||||||
|
```bash
|
||||||
|
git fetch -a
|
||||||
|
git tags
|
||||||
|
```
|
||||||
|
|
||||||
|
Then checkout last tag with:
|
||||||
|
```bash
|
||||||
|
git checkout tags/RELEASE-6819
|
||||||
|
```
|
||||||
|
|
||||||
|
Or go to the highfidelity download page (https://highfidelity.com/download) to get the release version. For example, if there is a BETA 6731 type:
|
||||||
|
```bash
|
||||||
|
git checkout tags/RELEASE-6731
|
||||||
|
```
|
||||||
|
|
||||||
|
### Compiling
|
||||||
|
|
||||||
|
Create the build directory:
|
||||||
|
```bash
|
||||||
|
mkdir -p hifi/build
|
||||||
|
cd hifi/build
|
||||||
|
```
|
||||||
|
|
||||||
|
Prepare makefiles:
|
||||||
|
```bash
|
||||||
|
cmake -DQT_CMAKE_PREFIX_PATH=/usr/local/Qt5.6.1/5.6/gcc_64/lib/cmake ..
|
||||||
|
```
|
||||||
|
|
||||||
|
Start compilation and get a cup of coffee:
|
||||||
|
```bash
|
||||||
|
make domain-server assignment-client interface
|
||||||
|
```
|
||||||
|
|
||||||
|
In a server does not make sense to compile interface
|
||||||
|
|
||||||
|
### Running the software
|
||||||
|
|
||||||
|
Running domain server:
|
||||||
|
```bash
|
||||||
|
./domain-server/domain-server
|
||||||
|
```
|
||||||
|
|
||||||
|
Running assignment client:
|
||||||
|
```bash
|
||||||
|
./assignment-client/assignment-client -n 6
|
||||||
|
```
|
||||||
|
|
||||||
|
Running interface:
|
||||||
|
```bash
|
||||||
|
./interface/interface
|
||||||
|
```
|
||||||
|
|
||||||
|
Go to localhost in running interface.
|
||||||
|
|
|
@ -438,6 +438,7 @@ Var DesktopServerCheckbox
|
||||||
Var ServerStartupCheckbox
|
Var ServerStartupCheckbox
|
||||||
Var LaunchServerNowCheckbox
|
Var LaunchServerNowCheckbox
|
||||||
Var LaunchClientNowCheckbox
|
Var LaunchClientNowCheckbox
|
||||||
|
Var CleanInstallCheckbox
|
||||||
Var CurrentOffset
|
Var CurrentOffset
|
||||||
Var OffsetUnits
|
Var OffsetUnits
|
||||||
Var CopyFromProductionCheckbox
|
Var CopyFromProductionCheckbox
|
||||||
|
@ -483,19 +484,10 @@ Function PostInstallOptionsPage
|
||||||
${If} ${SectionIsSelected} ${@SERVER_COMPONENT_NAME@}
|
${If} ${SectionIsSelected} ${@SERVER_COMPONENT_NAME@}
|
||||||
${NSD_CreateCheckbox} 0 $CurrentOffset$OffsetUnits 100% 10u "&Create a desktop shortcut for @CONSOLE_HF_SHORTCUT_NAME@"
|
${NSD_CreateCheckbox} 0 $CurrentOffset$OffsetUnits 100% 10u "&Create a desktop shortcut for @CONSOLE_HF_SHORTCUT_NAME@"
|
||||||
Pop $DesktopServerCheckbox
|
Pop $DesktopServerCheckbox
|
||||||
|
IntOp $CurrentOffset $CurrentOffset + 15
|
||||||
|
|
||||||
; set the checkbox state depending on what is present in the registry
|
; set the checkbox state depending on what is present in the registry
|
||||||
!insertmacro SetPostInstallOption $DesktopServerCheckbox @CONSOLE_DESKTOP_SHORTCUT_REG_KEY@ ${BST_UNCHECKED}
|
!insertmacro SetPostInstallOption $DesktopServerCheckbox @CONSOLE_DESKTOP_SHORTCUT_REG_KEY@ ${BST_UNCHECKED}
|
||||||
|
|
||||||
IntOp $CurrentOffset $CurrentOffset + 15
|
|
||||||
|
|
||||||
${NSD_CreateCheckbox} 0 $CurrentOffset$OffsetUnits 100% 10u "&Launch @CONSOLE_HF_SHORTCUT_NAME@ on startup"
|
|
||||||
Pop $ServerStartupCheckbox
|
|
||||||
|
|
||||||
; set the checkbox state depending on what is present in the registry
|
|
||||||
!insertmacro SetPostInstallOption $ServerStartupCheckbox @CONSOLE_STARTUP_REG_KEY@ ${BST_CHECKED}
|
|
||||||
|
|
||||||
IntOp $CurrentOffset $CurrentOffset + 15
|
|
||||||
${EndIf}
|
${EndIf}
|
||||||
|
|
||||||
${If} ${SectionIsSelected} ${@SERVER_COMPONENT_NAME@}
|
${If} ${SectionIsSelected} ${@SERVER_COMPONENT_NAME@}
|
||||||
|
@ -515,6 +507,7 @@ Function PostInstallOptionsPage
|
||||||
${If} ${SectionIsSelected} ${@CLIENT_COMPONENT_NAME@}
|
${If} ${SectionIsSelected} ${@CLIENT_COMPONENT_NAME@}
|
||||||
${NSD_CreateCheckbox} 0 $CurrentOffset$OffsetUnits 100% 10u "&Launch @INTERFACE_HF_SHORTCUT_NAME@ after install"
|
${NSD_CreateCheckbox} 0 $CurrentOffset$OffsetUnits 100% 10u "&Launch @INTERFACE_HF_SHORTCUT_NAME@ after install"
|
||||||
Pop $LaunchClientNowCheckbox
|
Pop $LaunchClientNowCheckbox
|
||||||
|
IntOp $CurrentOffset $CurrentOffset + 30
|
||||||
|
|
||||||
; set the checkbox state depending on what is present in the registry
|
; set the checkbox state depending on what is present in the registry
|
||||||
!insertmacro SetPostInstallOption $LaunchClientNowCheckbox @CLIENT_LAUNCH_NOW_REG_KEY@ ${BST_CHECKED}
|
!insertmacro SetPostInstallOption $LaunchClientNowCheckbox @CLIENT_LAUNCH_NOW_REG_KEY@ ${BST_CHECKED}
|
||||||
|
@ -524,6 +517,21 @@ Function PostInstallOptionsPage
|
||||||
${EndIf}
|
${EndIf}
|
||||||
${EndIf}
|
${EndIf}
|
||||||
|
|
||||||
|
${If} ${SectionIsSelected} ${@SERVER_COMPONENT_NAME@}
|
||||||
|
${NSD_CreateCheckbox} 0 $CurrentOffset$OffsetUnits 100% 10u "&Launch @CONSOLE_HF_SHORTCUT_NAME@ on startup"
|
||||||
|
Pop $ServerStartupCheckbox
|
||||||
|
IntOp $CurrentOffset $CurrentOffset + 15
|
||||||
|
|
||||||
|
; set the checkbox state depending on what is present in the registry
|
||||||
|
!insertmacro SetPostInstallOption $ServerStartupCheckbox @CONSOLE_STARTUP_REG_KEY@ ${BST_CHECKED}
|
||||||
|
${EndIf}
|
||||||
|
|
||||||
|
${If} ${SectionIsSelected} ${@CLIENT_COMPONENT_NAME@}
|
||||||
|
${NSD_CreateCheckbox} 0 $CurrentOffset$OffsetUnits 100% 10u "&Perform a clean install (Delete older settings and content)"
|
||||||
|
Pop $CleanInstallCheckbox
|
||||||
|
IntOp $CurrentOffset $CurrentOffset + 15
|
||||||
|
${EndIf}
|
||||||
|
|
||||||
${If} @PR_BUILD@ == 1
|
${If} @PR_BUILD@ == 1
|
||||||
; a PR build defaults all install options expect LaunchServerNowCheckbox, LaunchClientNowCheckbox and the settings copy to unchecked
|
; a PR build defaults all install options expect LaunchServerNowCheckbox, LaunchClientNowCheckbox and the settings copy to unchecked
|
||||||
${If} ${SectionIsSelected} ${@CLIENT_COMPONENT_NAME@}
|
${If} ${SectionIsSelected} ${@CLIENT_COMPONENT_NAME@}
|
||||||
|
@ -558,6 +566,7 @@ Var ServerStartupState
|
||||||
Var LaunchServerNowState
|
Var LaunchServerNowState
|
||||||
Var LaunchClientNowState
|
Var LaunchClientNowState
|
||||||
Var CopyFromProductionState
|
Var CopyFromProductionState
|
||||||
|
Var CleanInstallState
|
||||||
|
|
||||||
Function ReadPostInstallOptions
|
Function ReadPostInstallOptions
|
||||||
${If} ${SectionIsSelected} ${@CLIENT_COMPONENT_NAME@}
|
${If} ${SectionIsSelected} ${@CLIENT_COMPONENT_NAME@}
|
||||||
|
@ -587,6 +596,11 @@ Function ReadPostInstallOptions
|
||||||
; check if we need to launch the client post-install
|
; check if we need to launch the client post-install
|
||||||
${NSD_GetState} $LaunchClientNowCheckbox $LaunchClientNowState
|
${NSD_GetState} $LaunchClientNowCheckbox $LaunchClientNowState
|
||||||
${EndIf}
|
${EndIf}
|
||||||
|
|
||||||
|
${If} ${SectionIsSelected} ${@CLIENT_COMPONENT_NAME@}
|
||||||
|
; check if the user asked for a clean install
|
||||||
|
${NSD_GetState} $CleanInstallCheckbox $CleanInstallState
|
||||||
|
${EndIf}
|
||||||
FunctionEnd
|
FunctionEnd
|
||||||
|
|
||||||
Function HandlePostInstallOptions
|
Function HandlePostInstallOptions
|
||||||
|
@ -629,6 +643,15 @@ Function HandlePostInstallOptions
|
||||||
${EndIf}
|
${EndIf}
|
||||||
${EndIf}
|
${EndIf}
|
||||||
|
|
||||||
|
${If} ${SectionIsSelected} ${@CLIENT_COMPONENT_NAME@}
|
||||||
|
; check if the user asked for a clean install
|
||||||
|
${If} $CleanInstallState == ${BST_CHECKED}
|
||||||
|
SetShellVarContext current
|
||||||
|
RMDir /r "$APPDATA\@BUILD_ORGANIZATION@"
|
||||||
|
RMDir /r "$LOCALAPPDATA\@BUILD_ORGANIZATION@"
|
||||||
|
${EndIf}
|
||||||
|
${EndIf}
|
||||||
|
|
||||||
${If} @PR_BUILD@ == 1
|
${If} @PR_BUILD@ == 1
|
||||||
|
|
||||||
; check if we need to copy settings/content from production for this PR build
|
; check if we need to copy settings/content from production for this PR build
|
||||||
|
|
25
interface/resources/icons/tablet-icons/edit-disabled.svg
Normal file
25
interface/resources/icons/tablet-icons/edit-disabled.svg
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!-- Generator: Adobe Illustrator 19.2.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||||
|
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||||
|
viewBox="0 0 50 50" style="enable-background:new 0 0 50 50;" xml:space="preserve" opacity="0.33">
|
||||||
|
<style type="text/css">
|
||||||
|
.st0{fill:#FFFFFF;}
|
||||||
|
</style>
|
||||||
|
<g id="Layer_2">
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<path class="st0" d="M20.7,29.7c-2.2,2.2-4.4,4.4-6.7,6.7c-0.5-0.5-1.1-1.1-1.6-1.6c2.2-2.2,4.4-4.4,6.7-6.7l-1.8-1.8
|
||||||
|
c-2.6,2.5-5.1,5.1-7.7,7.6c-0.5,0.5-0.9,1.1-1,1.8C8.3,37.8,8,39.8,7.7,42c0.2,0,0.4,0,0.5,0c2-0.4,4-0.8,5.9-1.2
|
||||||
|
c0.4-0.1,0.8-0.3,1.1-0.6c2.7-2.6,5.3-5.3,8-8L20.7,29.7z"/>
|
||||||
|
<path class="st0" d="M31.1,11c0.8-0.8,1.8-1.8,2.7-2.7C34.2,8,34.6,8,34.9,8.4c1.6,1.6,3.1,3.1,4.7,4.7c0.4,0.4,0.4,0.8,0,1.2
|
||||||
|
c-0.9,0.9-1.8,1.8-2.7,2.7C35,15,33.1,13,31.1,11z"/>
|
||||||
|
<path class="st0" d="M33,25.9c-0.4,0.1-0.6,0-0.9-0.2c-0.6-0.6-1.3-1.3-1.9-1.9c1.5-1.5,3.1-3.1,4.6-4.6c0.1-0.1,0.3-0.3,0.3-0.3
|
||||||
|
c-2-2-3.9-4-5.9-6.1c-0.1,0.2-0.2,0.3-0.4,0.5c-1.5,1.5-3,3-4.6,4.6c-2.8-2.8-5.6-5.6-8.4-8.4c-0.2-0.2-0.4-0.4-0.6-0.6
|
||||||
|
c-1.5-1.2-3.5-1-4.8,0.4c-1.2,1.4-1.1,3.5,0.2,4.8c4.2,4.2,8.3,8.3,12.5,12.5c1.4,1.4,2.7,2.7,4.1,4.1c0.2,0.2,0.2,0.4,0.2,0.7
|
||||||
|
c-0.2,0.6-0.3,1.2-0.3,1.9c-0.3,4,2.3,7.5,6.1,8.5c1.6,0.4,3.2,0.3,4.8-0.3c-0.1-0.2-0.3-0.2-0.4-0.4c-1.2-1.2-2.3-2.3-3.5-3.5
|
||||||
|
c-0.8-0.9-0.9-2.1-0.1-3c0.6-0.7,1.3-1.3,2-2c0.9-0.8,2-0.8,2.9,0c0.2,0.2,0.3,0.3,0.5,0.5c1.2,1.2,2.3,2.3,3.5,3.5
|
||||||
|
c0.1,0,0.1,0,0.2,0c0.1-0.7,0.3-1.3,0.3-2C43.9,28.7,38.5,24.3,33,25.9z M12.9,12.6c-0.6,0-1.2-0.5-1.2-1.2s0.5-1.2,1.2-1.2
|
||||||
|
c0.6,0,1.2,0.6,1.2,1.2C14.1,12,13.6,12.6,12.9,12.6z M29.3,16.3c0.5,0.5,1,1.1,1.6,1.6c-1.1,1.1-2.2,2.2-3.3,3.3
|
||||||
|
c-0.5-0.5-1.1-1.1-1.6-1.6C27.1,18.5,28.2,17.4,29.3,16.3z"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 1.8 KiB |
|
@ -36,7 +36,7 @@ Slider {
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
width: parent.height - 2
|
width: parent.height - 2
|
||||||
height: slider.value * (slider.width/(slider.maximumValue - slider.minimumValue)) - 1
|
height: slider.width * (slider.value - slider.minimumValue) / (slider.maximumValue - slider.minimumValue) - 1
|
||||||
radius: height / 2
|
radius: height / 2
|
||||||
anchors {
|
anchors {
|
||||||
top: parent.top
|
top: parent.top
|
||||||
|
|
|
@ -72,6 +72,7 @@ Preference {
|
||||||
property var avatarBuilder: Component { AvatarPreference { } }
|
property var avatarBuilder: Component { AvatarPreference { } }
|
||||||
property var buttonBuilder: Component { ButtonPreference { } }
|
property var buttonBuilder: Component { ButtonPreference { } }
|
||||||
property var comboBoxBuilder: Component { ComboBoxPreference { } }
|
property var comboBoxBuilder: Component { ComboBoxPreference { } }
|
||||||
|
property var spinnerSliderBuilder: Component { SpinnerSliderPreference { } }
|
||||||
property var preferences: []
|
property var preferences: []
|
||||||
property int checkBoxCount: 0
|
property int checkBoxCount: 0
|
||||||
|
|
||||||
|
@ -86,7 +87,7 @@ Preference {
|
||||||
}
|
}
|
||||||
|
|
||||||
function buildPreference(preference) {
|
function buildPreference(preference) {
|
||||||
console.log("\tPreference type " + preference.type + " name " + preference.name)
|
console.log("\tPreference type " + preference.type + " name " + preference.name);
|
||||||
var builder;
|
var builder;
|
||||||
switch (preference.type) {
|
switch (preference.type) {
|
||||||
case Preference.Editable:
|
case Preference.Editable:
|
||||||
|
@ -128,6 +129,11 @@ Preference {
|
||||||
checkBoxCount = 0;
|
checkBoxCount = 0;
|
||||||
builder = comboBoxBuilder;
|
builder = comboBoxBuilder;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case Preference.SpinnerSlider:
|
||||||
|
checkBoxCount = 0;
|
||||||
|
builder = spinnerSliderBuilder;
|
||||||
|
break;
|
||||||
};
|
};
|
||||||
|
|
||||||
if (builder) {
|
if (builder) {
|
||||||
|
|
|
@ -0,0 +1,111 @@
|
||||||
|
//
|
||||||
|
// SpinnerSliderPreference.qml
|
||||||
|
//
|
||||||
|
// Created by Cain Kilgore on 11th July 2017
|
||||||
|
// Copyright 2016 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
|
||||||
|
//
|
||||||
|
|
||||||
|
import QtQuick 2.5
|
||||||
|
|
||||||
|
import "../../dialogs"
|
||||||
|
import "../../controls-uit"
|
||||||
|
|
||||||
|
Preference {
|
||||||
|
id: root
|
||||||
|
property alias slider: slider
|
||||||
|
property alias spinner: spinner
|
||||||
|
height: control.height + hifi.dimensions.controlInterlineHeight
|
||||||
|
|
||||||
|
Component.onCompleted: {
|
||||||
|
slider.value = preference.value;
|
||||||
|
spinner.value = preference.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
function save() {
|
||||||
|
preference.value = slider.value;
|
||||||
|
preference.save();
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: control
|
||||||
|
anchors {
|
||||||
|
left: parent.left
|
||||||
|
right: parent.right
|
||||||
|
bottom: parent.bottom
|
||||||
|
}
|
||||||
|
height: Math.max(labelText.height, slider.height, spinner.height, button.height)
|
||||||
|
|
||||||
|
Label {
|
||||||
|
id: labelText
|
||||||
|
text: root.label + ":"
|
||||||
|
colorScheme: hifi.colorSchemes.dark
|
||||||
|
anchors {
|
||||||
|
left: parent.left
|
||||||
|
right: slider.left
|
||||||
|
rightMargin: hifi.dimensions.labelPadding
|
||||||
|
verticalCenter: parent.verticalCenter
|
||||||
|
}
|
||||||
|
horizontalAlignment: Text.AlignRight
|
||||||
|
wrapMode: Text.Wrap
|
||||||
|
}
|
||||||
|
|
||||||
|
Slider {
|
||||||
|
id: slider
|
||||||
|
value: preference.value
|
||||||
|
width: 100
|
||||||
|
minimumValue: MyAvatar.getDomainMinScale()
|
||||||
|
maximumValue: MyAvatar.getDomainMaxScale()
|
||||||
|
stepSize: preference.step
|
||||||
|
onValueChanged: {
|
||||||
|
spinner.value = value
|
||||||
|
}
|
||||||
|
anchors {
|
||||||
|
right: spinner.left
|
||||||
|
rightMargin: 10
|
||||||
|
verticalCenter: parent.verticalCenter
|
||||||
|
}
|
||||||
|
colorScheme: hifi.colorSchemes.dark
|
||||||
|
}
|
||||||
|
|
||||||
|
SpinBox {
|
||||||
|
id: spinner
|
||||||
|
decimals: preference.decimals
|
||||||
|
value: preference.value
|
||||||
|
minimumValue: MyAvatar.getDomainMinScale()
|
||||||
|
maximumValue: MyAvatar.getDomainMaxScale()
|
||||||
|
width: 100
|
||||||
|
onValueChanged: {
|
||||||
|
slider.value = value;
|
||||||
|
}
|
||||||
|
anchors {
|
||||||
|
right: button.left
|
||||||
|
rightMargin: 10
|
||||||
|
verticalCenter: parent.verticalCenter
|
||||||
|
}
|
||||||
|
colorScheme: hifi.colorSchemes.dark
|
||||||
|
}
|
||||||
|
|
||||||
|
GlyphButton {
|
||||||
|
id: button
|
||||||
|
onClicked: {
|
||||||
|
if (spinner.maximumValue >= 1) {
|
||||||
|
spinner.value = 1
|
||||||
|
slider.value = 1
|
||||||
|
} else {
|
||||||
|
spinner.value = spinner.maximumValue
|
||||||
|
slider.value = spinner.maximumValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
width: 30
|
||||||
|
glyph: hifi.glyphs.reload
|
||||||
|
anchors {
|
||||||
|
right: parent.right
|
||||||
|
verticalCenter: parent.verticalCenter
|
||||||
|
}
|
||||||
|
colorScheme: hifi.colorSchemes.dark
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -81,6 +81,7 @@ Preference {
|
||||||
property var avatarBuilder: Component { AvatarPreference { } }
|
property var avatarBuilder: Component { AvatarPreference { } }
|
||||||
property var buttonBuilder: Component { ButtonPreference { } }
|
property var buttonBuilder: Component { ButtonPreference { } }
|
||||||
property var comboBoxBuilder: Component { ComboBoxPreference { } }
|
property var comboBoxBuilder: Component { ComboBoxPreference { } }
|
||||||
|
property var spinnerSliderBuilder: Component { SpinnerSliderPreference { } }
|
||||||
property var preferences: []
|
property var preferences: []
|
||||||
property int checkBoxCount: 0
|
property int checkBoxCount: 0
|
||||||
|
|
||||||
|
@ -143,6 +144,10 @@ Preference {
|
||||||
//to be not overlapped when drop down is active
|
//to be not overlapped when drop down is active
|
||||||
zpos = root.z + 1000 - itemNum
|
zpos = root.z + 1000 - itemNum
|
||||||
break;
|
break;
|
||||||
|
case Preference.SpinnerSlider:
|
||||||
|
checkBoxCount = 0;
|
||||||
|
builder = spinnerSliderBuilder;
|
||||||
|
break;
|
||||||
};
|
};
|
||||||
|
|
||||||
if (builder) {
|
if (builder) {
|
||||||
|
|
|
@ -1752,9 +1752,7 @@ QString Application::getUserAgent() {
|
||||||
void Application::toggleTabletUI(bool shouldOpen) const {
|
void Application::toggleTabletUI(bool shouldOpen) const {
|
||||||
auto tabletScriptingInterface = DependencyManager::get<TabletScriptingInterface>();
|
auto tabletScriptingInterface = DependencyManager::get<TabletScriptingInterface>();
|
||||||
auto hmd = DependencyManager::get<HMDScriptingInterface>();
|
auto hmd = DependencyManager::get<HMDScriptingInterface>();
|
||||||
TabletProxy* tablet = dynamic_cast<TabletProxy*>(tabletScriptingInterface->getTablet(SYSTEM_TABLET));
|
if (!(shouldOpen && hmd->getShouldShowTablet())) {
|
||||||
bool messageOpen = tablet->isMessageDialogOpen();
|
|
||||||
if ((!messageOpen || (messageOpen && !hmd->getShouldShowTablet())) && !(shouldOpen && hmd->getShouldShowTablet())) {
|
|
||||||
auto HMD = DependencyManager::get<HMDScriptingInterface>();
|
auto HMD = DependencyManager::get<HMDScriptingInterface>();
|
||||||
HMD->toggleShouldShowTablet();
|
HMD->toggleShouldShowTablet();
|
||||||
}
|
}
|
||||||
|
@ -5476,6 +5474,10 @@ void Application::updateWindowTitle() const {
|
||||||
qCDebug(interfaceapp, "Application title set to: %s", title.toStdString().c_str());
|
qCDebug(interfaceapp, "Application title set to: %s", title.toStdString().c_str());
|
||||||
#endif
|
#endif
|
||||||
_window->setWindowTitle(title);
|
_window->setWindowTitle(title);
|
||||||
|
|
||||||
|
// updateTitleWindow gets called whenever there's a change regarding the domain, so rather
|
||||||
|
// than placing this within domainChanged, it's placed here to cover the other potential cases.
|
||||||
|
DependencyManager::get< MessagesClient >()->sendLocalMessage("Toolbar-DomainChanged", "");
|
||||||
}
|
}
|
||||||
|
|
||||||
void Application::clearDomainOctreeDetails() {
|
void Application::clearDomainOctreeDetails() {
|
||||||
|
|
|
@ -2227,6 +2227,14 @@ void MyAvatar::clampScaleChangeToDomainLimits(float desiredScale) {
|
||||||
qCDebug(interfaceapp, "Changed scale to %f", (double)_targetScale);
|
qCDebug(interfaceapp, "Changed scale to %f", (double)_targetScale);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
float MyAvatar::getDomainMinScale() {
|
||||||
|
return _domainMinimumScale;
|
||||||
|
}
|
||||||
|
|
||||||
|
float MyAvatar::getDomainMaxScale() {
|
||||||
|
return _domainMaximumScale;
|
||||||
|
}
|
||||||
|
|
||||||
void MyAvatar::increaseSize() {
|
void MyAvatar::increaseSize() {
|
||||||
// make sure we're starting from an allowable scale
|
// make sure we're starting from an allowable scale
|
||||||
clampTargetScaleToDomainLimits();
|
clampTargetScaleToDomainLimits();
|
||||||
|
|
|
@ -556,6 +556,8 @@ public slots:
|
||||||
void increaseSize();
|
void increaseSize();
|
||||||
void decreaseSize();
|
void decreaseSize();
|
||||||
void resetSize();
|
void resetSize();
|
||||||
|
float getDomainMinScale();
|
||||||
|
float getDomainMaxScale();
|
||||||
|
|
||||||
void goToLocation(const glm::vec3& newPosition,
|
void goToLocation(const glm::vec3& newPosition,
|
||||||
bool hasOrientation = false, const glm::quat& newOrientation = glm::quat(),
|
bool hasOrientation = false, const glm::quat& newOrientation = glm::quat(),
|
||||||
|
|
|
@ -184,12 +184,15 @@ void setupPreferences() {
|
||||||
{
|
{
|
||||||
auto getter = [=]()->float { return myAvatar->getUniformScale(); };
|
auto getter = [=]()->float { return myAvatar->getUniformScale(); };
|
||||||
auto setter = [=](float value) { myAvatar->setTargetScale(value); };
|
auto setter = [=](float value) { myAvatar->setTargetScale(value); };
|
||||||
auto preference = new SpinnerPreference(AVATAR_TUNING, "Avatar scale (default is 1.0)", getter, setter);
|
auto preference = new SpinnerSliderPreference(AVATAR_TUNING, "Avatar Scale", getter, setter);
|
||||||
preference->setMin(0.01f);
|
preference->setStep(0.05f);
|
||||||
preference->setMax(99.9f);
|
|
||||||
preference->setDecimals(2);
|
preference->setDecimals(2);
|
||||||
preference->setStep(1);
|
|
||||||
preferences->addPreference(preference);
|
preferences->addPreference(preference);
|
||||||
|
|
||||||
|
// When the Interface is first loaded, this section setupPreferences(); is loaded -
|
||||||
|
// causing the myAvatar->getDomainMinScale() and myAvatar->getDomainMaxScale() to get set to incorrect values
|
||||||
|
// which can't be changed across domain switches. Having these values loaded up when you load the Dialog each time
|
||||||
|
// is a way around this, therefore they're not specified here but in the QML.
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
auto getter = []()->float { return DependencyManager::get<DdeFaceTracker>()->getEyeClosingThreshold(); };
|
auto getter = []()->float { return DependencyManager::get<DdeFaceTracker>()->getEyeClosingThreshold(); };
|
||||||
|
|
|
@ -23,20 +23,23 @@
|
||||||
#include "CubicHermiteSpline.h"
|
#include "CubicHermiteSpline.h"
|
||||||
#include "AnimUtil.h"
|
#include "AnimUtil.h"
|
||||||
|
|
||||||
static void lookupJointChainInfo(AnimInverseKinematics::JointChainInfo* jointChainInfos, size_t numJointChainInfos,
|
static const float JOINT_CHAIN_INTERP_TIME = 0.25f;
|
||||||
|
|
||||||
|
static void lookupJointInfo(const AnimInverseKinematics::JointChainInfo& jointChainInfo,
|
||||||
int indexA, int indexB,
|
int indexA, int indexB,
|
||||||
AnimInverseKinematics::JointChainInfo** jointChainInfoA,
|
const AnimInverseKinematics::JointInfo** jointInfoA,
|
||||||
AnimInverseKinematics::JointChainInfo** jointChainInfoB) {
|
const AnimInverseKinematics::JointInfo** jointInfoB) {
|
||||||
*jointChainInfoA = nullptr;
|
*jointInfoA = nullptr;
|
||||||
*jointChainInfoB = nullptr;
|
*jointInfoB = nullptr;
|
||||||
for (size_t i = 0; i < numJointChainInfos; i++) {
|
for (size_t i = 0; i < jointChainInfo.jointInfoVec.size(); i++) {
|
||||||
if (jointChainInfos[i].jointIndex == indexA) {
|
const AnimInverseKinematics::JointInfo* jointInfo = &jointChainInfo.jointInfoVec[i];
|
||||||
*jointChainInfoA = jointChainInfos + i;
|
if (jointInfo->jointIndex == indexA) {
|
||||||
|
*jointInfoA = jointInfo;
|
||||||
}
|
}
|
||||||
if (jointChainInfos[i].jointIndex == indexB) {
|
if (jointInfo->jointIndex == indexB) {
|
||||||
*jointChainInfoB = jointChainInfos + i;
|
*jointInfoB = jointInfo;
|
||||||
}
|
}
|
||||||
if (*jointChainInfoA && *jointChainInfoB) {
|
if (*jointInfoA && *jointInfoB) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -149,25 +152,28 @@ void AnimInverseKinematics::setTargetVars(const QString& jointName, const QStrin
|
||||||
}
|
}
|
||||||
|
|
||||||
void AnimInverseKinematics::computeTargets(const AnimVariantMap& animVars, std::vector<IKTarget>& targets, const AnimPoseVec& underPoses) {
|
void AnimInverseKinematics::computeTargets(const AnimVariantMap& animVars, std::vector<IKTarget>& targets, const AnimPoseVec& underPoses) {
|
||||||
// build a list of valid targets from _targetVarVec and animVars
|
|
||||||
_maxTargetIndex = -1;
|
|
||||||
_hipsTargetIndex = -1;
|
_hipsTargetIndex = -1;
|
||||||
bool removeUnfoundJoints = false;
|
|
||||||
|
targets.reserve(_targetVarVec.size());
|
||||||
|
|
||||||
for (auto& targetVar : _targetVarVec) {
|
for (auto& targetVar : _targetVarVec) {
|
||||||
|
|
||||||
|
// update targetVar jointIndex cache
|
||||||
if (targetVar.jointIndex == -1) {
|
if (targetVar.jointIndex == -1) {
|
||||||
// this targetVar hasn't been validated yet...
|
|
||||||
int jointIndex = _skeleton->nameToJointIndex(targetVar.jointName);
|
int jointIndex = _skeleton->nameToJointIndex(targetVar.jointName);
|
||||||
if (jointIndex >= 0) {
|
if (jointIndex >= 0) {
|
||||||
// this targetVar has a valid joint --> cache the indices
|
// this targetVar has a valid joint --> cache the indices
|
||||||
targetVar.jointIndex = jointIndex;
|
targetVar.jointIndex = jointIndex;
|
||||||
} else {
|
} else {
|
||||||
qCWarning(animation) << "AnimInverseKinematics could not find jointName" << targetVar.jointName << "in skeleton";
|
qCWarning(animation) << "AnimInverseKinematics could not find jointName" << targetVar.jointName << "in skeleton";
|
||||||
removeUnfoundJoints = true;
|
|
||||||
}
|
}
|
||||||
} else {
|
}
|
||||||
|
|
||||||
IKTarget target;
|
IKTarget target;
|
||||||
|
if (targetVar.jointIndex != -1) {
|
||||||
target.setType(animVars.lookup(targetVar.typeVar, (int)IKTarget::Type::RotationAndPosition));
|
target.setType(animVars.lookup(targetVar.typeVar, (int)IKTarget::Type::RotationAndPosition));
|
||||||
|
target.setIndex(targetVar.jointIndex);
|
||||||
if (target.getType() != IKTarget::Type::Unknown) {
|
if (target.getType() != IKTarget::Type::Unknown) {
|
||||||
AnimPose absPose = _skeleton->getAbsolutePose(targetVar.jointIndex, underPoses);
|
AnimPose absPose = _skeleton->getAbsolutePose(targetVar.jointIndex, underPoses);
|
||||||
glm::quat rotation = animVars.lookupRigToGeometry(targetVar.rotationVar, absPose.rot());
|
glm::quat rotation = animVars.lookupRigToGeometry(targetVar.rotationVar, absPose.rot());
|
||||||
|
@ -175,7 +181,6 @@ void AnimInverseKinematics::computeTargets(const AnimVariantMap& animVars, std::
|
||||||
float weight = animVars.lookup(targetVar.weightVar, targetVar.weight);
|
float weight = animVars.lookup(targetVar.weightVar, targetVar.weight);
|
||||||
|
|
||||||
target.setPose(rotation, translation);
|
target.setPose(rotation, translation);
|
||||||
target.setIndex(targetVar.jointIndex);
|
|
||||||
target.setWeight(weight);
|
target.setWeight(weight);
|
||||||
target.setFlexCoefficients(targetVar.numFlexCoefficients, targetVar.flexCoefficients);
|
target.setFlexCoefficients(targetVar.numFlexCoefficients, targetVar.flexCoefficients);
|
||||||
|
|
||||||
|
@ -188,39 +193,20 @@ void AnimInverseKinematics::computeTargets(const AnimVariantMap& animVars, std::
|
||||||
glm::vec3 poleReferenceVector = animVars.lookupRigToGeometryVector(targetVar.poleReferenceVectorVar, Vectors::UNIT_Z);
|
glm::vec3 poleReferenceVector = animVars.lookupRigToGeometryVector(targetVar.poleReferenceVectorVar, Vectors::UNIT_Z);
|
||||||
target.setPoleReferenceVector(glm::normalize(poleReferenceVector));
|
target.setPoleReferenceVector(glm::normalize(poleReferenceVector));
|
||||||
|
|
||||||
targets.push_back(target);
|
|
||||||
|
|
||||||
if (targetVar.jointIndex > _maxTargetIndex) {
|
|
||||||
_maxTargetIndex = targetVar.jointIndex;
|
|
||||||
}
|
|
||||||
|
|
||||||
// record the index of the hips ik target.
|
// record the index of the hips ik target.
|
||||||
if (target.getIndex() == _hipsIndex) {
|
if (target.getIndex() == _hipsIndex) {
|
||||||
_hipsTargetIndex = (int)targets.size() - 1;
|
_hipsTargetIndex = (int)targets.size();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (removeUnfoundJoints) {
|
|
||||||
int numVars = (int)_targetVarVec.size();
|
|
||||||
int i = 0;
|
|
||||||
while (i < numVars) {
|
|
||||||
if (_targetVarVec[i].jointIndex == -1) {
|
|
||||||
if (numVars > 1) {
|
|
||||||
// swap i for last element
|
|
||||||
_targetVarVec[i] = _targetVarVec[numVars - 1];
|
|
||||||
}
|
|
||||||
_targetVarVec.pop_back();
|
|
||||||
--numVars;
|
|
||||||
} else {
|
} else {
|
||||||
++i;
|
target.setType((int)IKTarget::Type::Unknown);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
targets.push_back(target);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void AnimInverseKinematics::solve(const AnimContext& context, const std::vector<IKTarget>& targets) {
|
void AnimInverseKinematics::solve(const AnimContext& context, const std::vector<IKTarget>& targets, float dt, JointChainInfoVec& jointChainInfoVec) {
|
||||||
// compute absolute poses that correspond to relative target poses
|
// compute absolute poses that correspond to relative target poses
|
||||||
AnimPoseVec absolutePoses;
|
AnimPoseVec absolutePoses;
|
||||||
absolutePoses.resize(_relativePoses.size());
|
absolutePoses.resize(_relativePoses.size());
|
||||||
|
@ -234,26 +220,75 @@ void AnimInverseKinematics::solve(const AnimContext& context, const std::vector<
|
||||||
accumulator.clearAndClean();
|
accumulator.clearAndClean();
|
||||||
}
|
}
|
||||||
|
|
||||||
float maxError = FLT_MAX;
|
float maxError = 0.0f;
|
||||||
int numLoops = 0;
|
int numLoops = 0;
|
||||||
const int MAX_IK_LOOPS = 16;
|
const int MAX_IK_LOOPS = 16;
|
||||||
const float MAX_ERROR_TOLERANCE = 0.1f; // cm
|
while (numLoops < MAX_IK_LOOPS) {
|
||||||
while (maxError > MAX_ERROR_TOLERANCE && numLoops < MAX_IK_LOOPS) {
|
|
||||||
++numLoops;
|
++numLoops;
|
||||||
|
|
||||||
bool debug = context.getEnableDebugDrawIKChains() && numLoops == MAX_IK_LOOPS;
|
bool debug = context.getEnableDebugDrawIKChains() && numLoops == MAX_IK_LOOPS;
|
||||||
|
|
||||||
// solve all targets
|
// solve all targets
|
||||||
for (auto& target: targets) {
|
for (size_t i = 0; i < targets.size(); i++) {
|
||||||
if (target.getType() == IKTarget::Type::Spline) {
|
switch (targets[i].getType()) {
|
||||||
solveTargetWithSpline(context, target, absolutePoses, debug);
|
case IKTarget::Type::Unknown:
|
||||||
} else {
|
break;
|
||||||
solveTargetWithCCD(context, target, absolutePoses, debug);
|
case IKTarget::Type::Spline:
|
||||||
|
solveTargetWithSpline(context, targets[i], absolutePoses, debug, jointChainInfoVec[i]);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
solveTargetWithCCD(context, targets[i], absolutePoses, debug, jointChainInfoVec[i]);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// on last iteration, interpolate jointChains, if necessary
|
||||||
|
if (numLoops == MAX_IK_LOOPS) {
|
||||||
|
for (size_t i = 0; i < _prevJointChainInfoVec.size(); i++) {
|
||||||
|
if (_prevJointChainInfoVec[i].timer > 0.0f) {
|
||||||
|
float alpha = (JOINT_CHAIN_INTERP_TIME - _prevJointChainInfoVec[i].timer) / JOINT_CHAIN_INTERP_TIME;
|
||||||
|
size_t chainSize = std::min(_prevJointChainInfoVec[i].jointInfoVec.size(), jointChainInfoVec[i].jointInfoVec.size());
|
||||||
|
for (size_t j = 0; j < chainSize; j++) {
|
||||||
|
jointChainInfoVec[i].jointInfoVec[j].rot = safeMix(_prevJointChainInfoVec[i].jointInfoVec[j].rot, jointChainInfoVec[i].jointInfoVec[j].rot, alpha);
|
||||||
|
jointChainInfoVec[i].jointInfoVec[j].trans = lerp(_prevJointChainInfoVec[i].jointInfoVec[j].trans, jointChainInfoVec[i].jointInfoVec[j].trans, alpha);
|
||||||
|
}
|
||||||
|
|
||||||
|
// if joint chain was just disabled, ramp the weight toward zero.
|
||||||
|
if (_prevJointChainInfoVec[i].target.getType() != IKTarget::Type::Unknown &&
|
||||||
|
jointChainInfoVec[i].target.getType() == IKTarget::Type::Unknown) {
|
||||||
|
IKTarget newTarget = _prevJointChainInfoVec[i].target;
|
||||||
|
newTarget.setWeight((1.0f - alpha) * _prevJointChainInfoVec[i].target.getWeight());
|
||||||
|
jointChainInfoVec[i].target = newTarget;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// copy jointChainInfoVecs into accumulators
|
||||||
|
for (size_t i = 0; i < targets.size(); i++) {
|
||||||
|
const std::vector<JointInfo>& jointInfoVec = jointChainInfoVec[i].jointInfoVec;
|
||||||
|
|
||||||
|
// don't accumulate disabled or rotation only ik targets.
|
||||||
|
IKTarget::Type type = jointChainInfoVec[i].target.getType();
|
||||||
|
if (type != IKTarget::Type::Unknown && type != IKTarget::Type::RotationOnly) {
|
||||||
|
float weight = jointChainInfoVec[i].target.getWeight();
|
||||||
|
if (weight > 0.0f) {
|
||||||
|
for (size_t j = 0; j < jointInfoVec.size(); j++) {
|
||||||
|
const JointInfo& info = jointInfoVec[j];
|
||||||
|
if (info.jointIndex >= 0) {
|
||||||
|
_rotationAccumulators[info.jointIndex].add(info.rot, weight);
|
||||||
|
_translationAccumulators[info.jointIndex].add(info.trans, weight);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// harvest accumulated rotations and apply the average
|
// harvest accumulated rotations and apply the average
|
||||||
for (int i = 0; i < (int)_relativePoses.size(); ++i) {
|
for (int i = 0; i < (int)_relativePoses.size(); ++i) {
|
||||||
|
if (i == _hipsIndex) {
|
||||||
|
continue; // don't apply accumulators to hips
|
||||||
|
}
|
||||||
if (_rotationAccumulators[i].size() > 0) {
|
if (_rotationAccumulators[i].size() > 0) {
|
||||||
_relativePoses[i].rot() = _rotationAccumulators[i].getAverage();
|
_relativePoses[i].rot() = _rotationAccumulators[i].getAverage();
|
||||||
_rotationAccumulators[i].clear();
|
_rotationAccumulators[i].clear();
|
||||||
|
@ -289,7 +324,7 @@ void AnimInverseKinematics::solve(const AnimContext& context, const std::vector<
|
||||||
// finally set the relative rotation of each tip to agree with absolute target rotation
|
// finally set the relative rotation of each tip to agree with absolute target rotation
|
||||||
for (auto& target: targets) {
|
for (auto& target: targets) {
|
||||||
int tipIndex = target.getIndex();
|
int tipIndex = target.getIndex();
|
||||||
int parentIndex = _skeleton->getParentIndex(tipIndex);
|
int parentIndex = (tipIndex >= 0) ? _skeleton->getParentIndex(tipIndex) : -1;
|
||||||
|
|
||||||
// update rotationOnly targets that don't lie on the ik chain of other ik targets.
|
// update rotationOnly targets that don't lie on the ik chain of other ik targets.
|
||||||
if (parentIndex != -1 && !_rotationAccumulators[tipIndex].isDirty() && target.getType() == IKTarget::Type::RotationOnly) {
|
if (parentIndex != -1 && !_rotationAccumulators[tipIndex].isDirty() && target.getType() == IKTarget::Type::RotationOnly) {
|
||||||
|
@ -308,9 +343,34 @@ void AnimInverseKinematics::solve(const AnimContext& context, const std::vector<
|
||||||
absolutePoses[tipIndex].rot() = targetRotation;
|
absolutePoses[tipIndex].rot() = targetRotation;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// copy jointChainInfoVec into _prevJointChainInfoVec, and update timers
|
||||||
|
for (size_t i = 0; i < jointChainInfoVec.size(); i++) {
|
||||||
|
_prevJointChainInfoVec[i].timer = _prevJointChainInfoVec[i].timer - dt;
|
||||||
|
if (_prevJointChainInfoVec[i].timer <= 0.0f) {
|
||||||
|
_prevJointChainInfoVec[i] = jointChainInfoVec[i];
|
||||||
|
_prevJointChainInfoVec[i].target = targets[i];
|
||||||
|
// store relative poses into unknown/rotation only joint chains.
|
||||||
|
// so we have something to interpolate from if this chain is activated.
|
||||||
|
IKTarget::Type type = _prevJointChainInfoVec[i].target.getType();
|
||||||
|
if (type == IKTarget::Type::Unknown || type == IKTarget::Type::RotationOnly) {
|
||||||
|
for (size_t j = 0; j < _prevJointChainInfoVec[i].jointInfoVec.size(); j++) {
|
||||||
|
auto& info = _prevJointChainInfoVec[i].jointInfoVec[j];
|
||||||
|
if (info.jointIndex >= 0) {
|
||||||
|
info.rot = _relativePoses[info.jointIndex].rot();
|
||||||
|
info.trans = _relativePoses[info.jointIndex].trans();
|
||||||
|
} else {
|
||||||
|
info.rot = Quaternions::IDENTITY;
|
||||||
|
info.trans = glm::vec3(0.0f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void AnimInverseKinematics::solveTargetWithCCD(const AnimContext& context, const IKTarget& target, const AnimPoseVec& absolutePoses, bool debug) {
|
void AnimInverseKinematics::solveTargetWithCCD(const AnimContext& context, const IKTarget& target, const AnimPoseVec& absolutePoses,
|
||||||
|
bool debug, JointChainInfo& jointChainInfoOut) const {
|
||||||
size_t chainDepth = 0;
|
size_t chainDepth = 0;
|
||||||
|
|
||||||
IKTarget::Type targetType = target.getType();
|
IKTarget::Type targetType = target.getType();
|
||||||
|
@ -338,9 +398,6 @@ void AnimInverseKinematics::solveTargetWithCCD(const AnimContext& context, const
|
||||||
// the tip's parent-relative as we proceed up the chain
|
// the tip's parent-relative as we proceed up the chain
|
||||||
glm::quat tipParentOrientation = absolutePoses[pivotIndex].rot();
|
glm::quat tipParentOrientation = absolutePoses[pivotIndex].rot();
|
||||||
|
|
||||||
const size_t MAX_CHAIN_DEPTH = 30;
|
|
||||||
JointChainInfo jointChainInfos[MAX_CHAIN_DEPTH];
|
|
||||||
|
|
||||||
// NOTE: if this code is removed, the head will remain rigid, causing the spine/hips to thrust forward backward
|
// NOTE: if this code is removed, the head will remain rigid, causing the spine/hips to thrust forward backward
|
||||||
// as the head is nodded.
|
// as the head is nodded.
|
||||||
if (targetType == IKTarget::Type::HmdHead ||
|
if (targetType == IKTarget::Type::HmdHead ||
|
||||||
|
@ -368,7 +425,7 @@ void AnimInverseKinematics::solveTargetWithCCD(const AnimContext& context, const
|
||||||
}
|
}
|
||||||
|
|
||||||
glm::vec3 tipRelativeTranslation = _relativePoses[target.getIndex()].trans();
|
glm::vec3 tipRelativeTranslation = _relativePoses[target.getIndex()].trans();
|
||||||
jointChainInfos[chainDepth] = { tipRelativeRotation, tipRelativeTranslation, target.getWeight(), tipIndex, constrained };
|
jointChainInfoOut.jointInfoVec[chainDepth] = { tipRelativeRotation, tipRelativeTranslation, tipIndex, constrained };
|
||||||
}
|
}
|
||||||
|
|
||||||
// cache tip absolute position
|
// cache tip absolute position
|
||||||
|
@ -379,7 +436,7 @@ void AnimInverseKinematics::solveTargetWithCCD(const AnimContext& context, const
|
||||||
// descend toward root, pivoting each joint to get tip closer to target position
|
// descend toward root, pivoting each joint to get tip closer to target position
|
||||||
while (pivotIndex != _hipsIndex && pivotsParentIndex != -1) {
|
while (pivotIndex != _hipsIndex && pivotsParentIndex != -1) {
|
||||||
|
|
||||||
assert(chainDepth < MAX_CHAIN_DEPTH);
|
assert(chainDepth < jointChainInfoOut.jointInfoVec.size());
|
||||||
|
|
||||||
// compute the two lines that should be aligned
|
// compute the two lines that should be aligned
|
||||||
glm::vec3 jointPosition = absolutePoses[pivotIndex].trans();
|
glm::vec3 jointPosition = absolutePoses[pivotIndex].trans();
|
||||||
|
@ -444,9 +501,8 @@ void AnimInverseKinematics::solveTargetWithCCD(const AnimContext& context, const
|
||||||
glm::quat twistPart;
|
glm::quat twistPart;
|
||||||
glm::vec3 axis = glm::normalize(deltaRotation * leverArm);
|
glm::vec3 axis = glm::normalize(deltaRotation * leverArm);
|
||||||
swingTwistDecomposition(missingRotation, axis, swingPart, twistPart);
|
swingTwistDecomposition(missingRotation, axis, swingPart, twistPart);
|
||||||
float dotSign = copysignf(1.0f, twistPart.w);
|
|
||||||
const float LIMIT_LEAK_FRACTION = 0.1f;
|
const float LIMIT_LEAK_FRACTION = 0.1f;
|
||||||
deltaRotation = glm::normalize(glm::lerp(glm::quat(), dotSign * twistPart, LIMIT_LEAK_FRACTION)) * deltaRotation;
|
deltaRotation = safeLerp(glm::quat(), twistPart, LIMIT_LEAK_FRACTION);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -455,9 +511,8 @@ void AnimInverseKinematics::solveTargetWithCCD(const AnimContext& context, const
|
||||||
// An HmdHead target slaves the orientation of the end-effector by distributing rotation
|
// An HmdHead target slaves the orientation of the end-effector by distributing rotation
|
||||||
// deltas up the hierarchy. Its target position is enforced later (by shifting the hips).
|
// deltas up the hierarchy. Its target position is enforced later (by shifting the hips).
|
||||||
deltaRotation = target.getRotation() * glm::inverse(tipOrientation);
|
deltaRotation = target.getRotation() * glm::inverse(tipOrientation);
|
||||||
float dotSign = copysignf(1.0f, deltaRotation.w);
|
|
||||||
const float ANGLE_DISTRIBUTION_FACTOR = 0.45f;
|
const float ANGLE_DISTRIBUTION_FACTOR = 0.45f;
|
||||||
deltaRotation = glm::normalize(glm::lerp(glm::quat(), dotSign * deltaRotation, ANGLE_DISTRIBUTION_FACTOR));
|
deltaRotation = safeLerp(glm::quat(), deltaRotation, ANGLE_DISTRIBUTION_FACTOR);
|
||||||
}
|
}
|
||||||
|
|
||||||
// compute joint's new parent-relative rotation after swing
|
// compute joint's new parent-relative rotation after swing
|
||||||
|
@ -480,7 +535,7 @@ void AnimInverseKinematics::solveTargetWithCCD(const AnimContext& context, const
|
||||||
}
|
}
|
||||||
|
|
||||||
glm::vec3 newTrans = _relativePoses[pivotIndex].trans();
|
glm::vec3 newTrans = _relativePoses[pivotIndex].trans();
|
||||||
jointChainInfos[chainDepth] = { newRot, newTrans, target.getWeight(), pivotIndex, constrained };
|
jointChainInfoOut.jointInfoVec[chainDepth] = { newRot, newTrans, pivotIndex, constrained };
|
||||||
|
|
||||||
// keep track of tip's new transform as we descend towards root
|
// keep track of tip's new transform as we descend towards root
|
||||||
tipPosition = jointPosition + deltaRotation * (tipPosition - jointPosition);
|
tipPosition = jointPosition + deltaRotation * (tipPosition - jointPosition);
|
||||||
|
@ -502,24 +557,25 @@ void AnimInverseKinematics::solveTargetWithCCD(const AnimContext& context, const
|
||||||
int baseParentJointIndex = _skeleton->getParentIndex(baseJointIndex);
|
int baseParentJointIndex = _skeleton->getParentIndex(baseJointIndex);
|
||||||
AnimPose topPose, midPose, basePose;
|
AnimPose topPose, midPose, basePose;
|
||||||
int topChainIndex = -1, baseChainIndex = -1;
|
int topChainIndex = -1, baseChainIndex = -1;
|
||||||
|
const size_t MAX_CHAIN_DEPTH = 30;
|
||||||
AnimPose postAbsPoses[MAX_CHAIN_DEPTH];
|
AnimPose postAbsPoses[MAX_CHAIN_DEPTH];
|
||||||
AnimPose accum = absolutePoses[_hipsIndex];
|
AnimPose accum = absolutePoses[_hipsIndex];
|
||||||
AnimPose baseParentPose = absolutePoses[_hipsIndex];
|
AnimPose baseParentPose = absolutePoses[_hipsIndex];
|
||||||
for (int i = (int)chainDepth - 1; i >= 0; i--) {
|
for (int i = (int)chainDepth - 1; i >= 0; i--) {
|
||||||
accum = accum * AnimPose(glm::vec3(1.0f), jointChainInfos[i].relRot, jointChainInfos[i].relTrans);
|
accum = accum * AnimPose(glm::vec3(1.0f), jointChainInfoOut.jointInfoVec[i].rot, jointChainInfoOut.jointInfoVec[i].trans);
|
||||||
postAbsPoses[i] = accum;
|
postAbsPoses[i] = accum;
|
||||||
if (jointChainInfos[i].jointIndex == topJointIndex) {
|
if (jointChainInfoOut.jointInfoVec[i].jointIndex == topJointIndex) {
|
||||||
topChainIndex = i;
|
topChainIndex = i;
|
||||||
topPose = accum;
|
topPose = accum;
|
||||||
}
|
}
|
||||||
if (jointChainInfos[i].jointIndex == midJointIndex) {
|
if (jointChainInfoOut.jointInfoVec[i].jointIndex == midJointIndex) {
|
||||||
midPose = accum;
|
midPose = accum;
|
||||||
}
|
}
|
||||||
if (jointChainInfos[i].jointIndex == baseJointIndex) {
|
if (jointChainInfoOut.jointInfoVec[i].jointIndex == baseJointIndex) {
|
||||||
baseChainIndex = i;
|
baseChainIndex = i;
|
||||||
basePose = accum;
|
basePose = accum;
|
||||||
}
|
}
|
||||||
if (jointChainInfos[i].jointIndex == baseParentJointIndex) {
|
if (jointChainInfoOut.jointInfoVec[i].jointIndex == baseParentJointIndex) {
|
||||||
baseParentPose = accum;
|
baseParentPose = accum;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -599,21 +655,16 @@ void AnimInverseKinematics::solveTargetWithCCD(const AnimContext& context, const
|
||||||
}
|
}
|
||||||
|
|
||||||
glm::quat newBaseRelRot = glm::inverse(baseParentPose.rot()) * poleRot * basePose.rot();
|
glm::quat newBaseRelRot = glm::inverse(baseParentPose.rot()) * poleRot * basePose.rot();
|
||||||
jointChainInfos[baseChainIndex].relRot = newBaseRelRot;
|
jointChainInfoOut.jointInfoVec[baseChainIndex].rot = newBaseRelRot;
|
||||||
|
|
||||||
glm::quat newTopRelRot = glm::inverse(midPose.rot()) * glm::inverse(poleRot) * topPose.rot();
|
glm::quat newTopRelRot = glm::inverse(midPose.rot()) * glm::inverse(poleRot) * topPose.rot();
|
||||||
jointChainInfos[topChainIndex].relRot = newTopRelRot;
|
jointChainInfoOut.jointInfoVec[topChainIndex].rot = newTopRelRot;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (size_t i = 0; i < chainDepth; i++) {
|
|
||||||
_rotationAccumulators[jointChainInfos[i].jointIndex].add(jointChainInfos[i].relRot, jointChainInfos[i].weight);
|
|
||||||
_translationAccumulators[jointChainInfos[i].jointIndex].add(jointChainInfos[i].relTrans, jointChainInfos[i].weight);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (debug) {
|
if (debug) {
|
||||||
debugDrawIKChain(jointChainInfos, chainDepth, context);
|
debugDrawIKChain(jointChainInfoOut, context);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -628,7 +679,7 @@ static CubicHermiteSplineFunctorWithArcLength computeSplineFromTipAndBase(const
|
||||||
}
|
}
|
||||||
|
|
||||||
// pre-compute information about each joint influeced by this spline IK target.
|
// pre-compute information about each joint influeced by this spline IK target.
|
||||||
void AnimInverseKinematics::computeSplineJointInfosForIKTarget(const AnimContext& context, const IKTarget& target) {
|
void AnimInverseKinematics::computeAndCacheSplineJointInfosForIKTarget(const AnimContext& context, const IKTarget& target) const {
|
||||||
std::vector<SplineJointInfo> splineJointInfoVec;
|
std::vector<SplineJointInfo> splineJointInfoVec;
|
||||||
|
|
||||||
// build spline between the default poses.
|
// build spline between the default poses.
|
||||||
|
@ -681,13 +732,13 @@ void AnimInverseKinematics::computeSplineJointInfosForIKTarget(const AnimContext
|
||||||
_splineJointInfoMap[target.getIndex()] = splineJointInfoVec;
|
_splineJointInfoMap[target.getIndex()] = splineJointInfoVec;
|
||||||
}
|
}
|
||||||
|
|
||||||
const std::vector<AnimInverseKinematics::SplineJointInfo>* AnimInverseKinematics::findOrCreateSplineJointInfo(const AnimContext& context, const IKTarget& target) {
|
const std::vector<AnimInverseKinematics::SplineJointInfo>* AnimInverseKinematics::findOrCreateSplineJointInfo(const AnimContext& context, const IKTarget& target) const {
|
||||||
// find or create splineJointInfo for this target
|
// find or create splineJointInfo for this target
|
||||||
auto iter = _splineJointInfoMap.find(target.getIndex());
|
auto iter = _splineJointInfoMap.find(target.getIndex());
|
||||||
if (iter != _splineJointInfoMap.end()) {
|
if (iter != _splineJointInfoMap.end()) {
|
||||||
return &(iter->second);
|
return &(iter->second);
|
||||||
} else {
|
} else {
|
||||||
computeSplineJointInfosForIKTarget(context, target);
|
computeAndCacheSplineJointInfosForIKTarget(context, target);
|
||||||
auto iter = _splineJointInfoMap.find(target.getIndex());
|
auto iter = _splineJointInfoMap.find(target.getIndex());
|
||||||
if (iter != _splineJointInfoMap.end()) {
|
if (iter != _splineJointInfoMap.end()) {
|
||||||
return &(iter->second);
|
return &(iter->second);
|
||||||
|
@ -697,10 +748,8 @@ const std::vector<AnimInverseKinematics::SplineJointInfo>* AnimInverseKinematics
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
void AnimInverseKinematics::solveTargetWithSpline(const AnimContext& context, const IKTarget& target, const AnimPoseVec& absolutePoses, bool debug) {
|
void AnimInverseKinematics::solveTargetWithSpline(const AnimContext& context, const IKTarget& target, const AnimPoseVec& absolutePoses,
|
||||||
|
bool debug, JointChainInfo& jointChainInfoOut) const {
|
||||||
const size_t MAX_CHAIN_DEPTH = 30;
|
|
||||||
JointChainInfo jointChainInfos[MAX_CHAIN_DEPTH];
|
|
||||||
|
|
||||||
const int baseIndex = _hipsIndex;
|
const int baseIndex = _hipsIndex;
|
||||||
|
|
||||||
|
@ -720,7 +769,7 @@ void AnimInverseKinematics::solveTargetWithSpline(const AnimContext& context, co
|
||||||
|
|
||||||
// This prevents the rotation interpolation from rotating the wrong physical way (but correct mathematical way)
|
// This prevents the rotation interpolation from rotating the wrong physical way (but correct mathematical way)
|
||||||
// when the head is arched backwards very far.
|
// when the head is arched backwards very far.
|
||||||
glm::quat halfRot = glm::normalize(glm::lerp(basePose.rot(), tipPose.rot(), 0.5f));
|
glm::quat halfRot = safeLerp(basePose.rot(), tipPose.rot(), 0.5f);
|
||||||
if (glm::dot(halfRot * Vectors::UNIT_Z, basePose.rot() * Vectors::UNIT_Z) < 0.0f) {
|
if (glm::dot(halfRot * Vectors::UNIT_Z, basePose.rot() * Vectors::UNIT_Z) < 0.0f) {
|
||||||
tipPose.rot() = -tipPose.rot();
|
tipPose.rot() = -tipPose.rot();
|
||||||
}
|
}
|
||||||
|
@ -743,7 +792,7 @@ void AnimInverseKinematics::solveTargetWithSpline(const AnimContext& context, co
|
||||||
if (target.getIndex() == _headIndex) {
|
if (target.getIndex() == _headIndex) {
|
||||||
rotT = t * t;
|
rotT = t * t;
|
||||||
}
|
}
|
||||||
glm::quat twistRot = glm::normalize(glm::lerp(basePose.rot(), tipPose.rot(), rotT));
|
glm::quat twistRot = safeLerp(basePose.rot(), tipPose.rot(), rotT);
|
||||||
|
|
||||||
// compute the rotation by using the derivative of the spline as the y-axis, and the twistRot x-axis
|
// compute the rotation by using the derivative of the spline as the y-axis, and the twistRot x-axis
|
||||||
glm::vec3 y = glm::normalize(spline.d(t));
|
glm::vec3 y = glm::normalize(spline.d(t));
|
||||||
|
@ -783,19 +832,14 @@ void AnimInverseKinematics::solveTargetWithSpline(const AnimContext& context, co
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
jointChainInfos[i] = { relPose.rot(), relPose.trans(), target.getWeight(), splineJointInfo.jointIndex, constrained };
|
jointChainInfoOut.jointInfoVec[i] = { relPose.rot(), relPose.trans(), splineJointInfo.jointIndex, constrained };
|
||||||
|
|
||||||
parentAbsPose = flexedAbsPose;
|
parentAbsPose = flexedAbsPose;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (size_t i = 0; i < splineJointInfoVec->size(); i++) {
|
|
||||||
_rotationAccumulators[jointChainInfos[i].jointIndex].add(jointChainInfos[i].relRot, jointChainInfos[i].weight);
|
|
||||||
_translationAccumulators[jointChainInfos[i].jointIndex].add(jointChainInfos[i].relTrans, jointChainInfos[i].weight);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (debug) {
|
if (debug) {
|
||||||
debugDrawIKChain(jointChainInfos, splineJointInfoVec->size(), context);
|
debugDrawIKChain(jointChainInfoOut, context);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -806,6 +850,24 @@ const AnimPoseVec& AnimInverseKinematics::evaluate(const AnimVariantMap& animVar
|
||||||
return _relativePoses;
|
return _relativePoses;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
AnimPose AnimInverseKinematics::applyHipsOffset() const {
|
||||||
|
glm::vec3 hipsOffset = _hipsOffset;
|
||||||
|
AnimPose relHipsPose = _relativePoses[_hipsIndex];
|
||||||
|
float offsetLength = glm::length(hipsOffset);
|
||||||
|
const float MIN_HIPS_OFFSET_LENGTH = 0.03f;
|
||||||
|
if (offsetLength > MIN_HIPS_OFFSET_LENGTH) {
|
||||||
|
float scaleFactor = ((offsetLength - MIN_HIPS_OFFSET_LENGTH) / offsetLength);
|
||||||
|
glm::vec3 scaledHipsOffset = scaleFactor * hipsOffset;
|
||||||
|
if (_hipsParentIndex == -1) {
|
||||||
|
relHipsPose.trans() = _relativePoses[_hipsIndex].trans() + scaledHipsOffset;
|
||||||
|
} else {
|
||||||
|
AnimPose absHipsPose = _skeleton->getAbsolutePose(_hipsIndex, _relativePoses);
|
||||||
|
absHipsPose.trans() += scaledHipsOffset;
|
||||||
|
relHipsPose = _skeleton->getAbsolutePose(_hipsParentIndex, _relativePoses).inverse() * absHipsPose;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return relHipsPose;
|
||||||
|
}
|
||||||
|
|
||||||
//virtual
|
//virtual
|
||||||
const AnimPoseVec& AnimInverseKinematics::overlay(const AnimVariantMap& animVars, const AnimContext& context, float dt, Triggers& triggersOut, const AnimPoseVec& underPoses) {
|
const AnimPoseVec& AnimInverseKinematics::overlay(const AnimVariantMap& animVars, const AnimContext& context, float dt, Triggers& triggersOut, const AnimPoseVec& underPoses) {
|
||||||
|
@ -850,33 +912,88 @@ const AnimPoseVec& AnimInverseKinematics::overlay(const AnimVariantMap& animVars
|
||||||
_relativePoses = underPoses;
|
_relativePoses = underPoses;
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
|
JointChainInfoVec jointChainInfoVec(targets.size());
|
||||||
|
{
|
||||||
|
PROFILE_RANGE_EX(simulation_animation, "ik/jointChainInfo", 0xffff00ff, 0);
|
||||||
|
|
||||||
|
// initialize a new jointChainInfoVec, this will hold the results for solving each ik chain.
|
||||||
|
JointInfo defaultJointInfo = { glm::quat(), glm::vec3(), -1, false };
|
||||||
|
for (size_t i = 0; i < targets.size(); i++) {
|
||||||
|
size_t chainDepth = (size_t)_skeleton->getChainDepth(targets[i].getIndex());
|
||||||
|
jointChainInfoVec[i].jointInfoVec.reserve(chainDepth);
|
||||||
|
jointChainInfoVec[i].target = targets[i];
|
||||||
|
int index = targets[i].getIndex();
|
||||||
|
for (size_t j = 0; j < chainDepth; j++) {
|
||||||
|
jointChainInfoVec[i].jointInfoVec.push_back(defaultJointInfo);
|
||||||
|
jointChainInfoVec[i].jointInfoVec[j].jointIndex = index;
|
||||||
|
index = _skeleton->getParentIndex(index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// identify joint chains that have changed types this frame.
|
||||||
|
_prevJointChainInfoVec.resize(jointChainInfoVec.size());
|
||||||
|
for (size_t i = 0; i < _prevJointChainInfoVec.size(); i++) {
|
||||||
|
if (_prevJointChainInfoVec[i].timer <= 0.0f &&
|
||||||
|
(jointChainInfoVec[i].target.getType() != _prevJointChainInfoVec[i].target.getType() ||
|
||||||
|
jointChainInfoVec[i].target.getPoleVectorEnabled() != _prevJointChainInfoVec[i].target.getPoleVectorEnabled())) {
|
||||||
|
_prevJointChainInfoVec[i].timer = JOINT_CHAIN_INTERP_TIME;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
PROFILE_RANGE_EX(simulation_animation, "ik/shiftHips", 0xffff00ff, 0);
|
PROFILE_RANGE_EX(simulation_animation, "ik/shiftHips", 0xffff00ff, 0);
|
||||||
|
|
||||||
if (_hipsTargetIndex >= 0 && _hipsTargetIndex < (int)targets.size()) {
|
if (_hipsTargetIndex >= 0) {
|
||||||
|
assert(_hipsTargetIndex < (int)targets.size());
|
||||||
|
|
||||||
// slam the hips to match the _hipsTarget
|
// slam the hips to match the _hipsTarget
|
||||||
|
|
||||||
AnimPose absPose = targets[_hipsTargetIndex].getPose();
|
AnimPose absPose = targets[_hipsTargetIndex].getPose();
|
||||||
|
|
||||||
int parentIndex = _skeleton->getParentIndex(targets[_hipsTargetIndex].getIndex());
|
int parentIndex = _skeleton->getParentIndex(targets[_hipsTargetIndex].getIndex());
|
||||||
if (parentIndex != -1) {
|
AnimPose parentAbsPose = _skeleton->getAbsolutePose(parentIndex, _relativePoses);
|
||||||
_relativePoses[_hipsIndex] = _skeleton->getAbsolutePose(parentIndex, _relativePoses).inverse() * absPose;
|
|
||||||
} else {
|
// do smooth interpolation of hips, if necessary.
|
||||||
_relativePoses[_hipsIndex] = absPose;
|
if (_prevJointChainInfoVec[_hipsTargetIndex].timer > 0.0f && _prevJointChainInfoVec[_hipsTargetIndex].jointInfoVec.size() > 0) {
|
||||||
|
float alpha = (JOINT_CHAIN_INTERP_TIME - _prevJointChainInfoVec[_hipsTargetIndex].timer) / JOINT_CHAIN_INTERP_TIME;
|
||||||
|
|
||||||
|
auto& info = _prevJointChainInfoVec[_hipsTargetIndex].jointInfoVec[0];
|
||||||
|
AnimPose prevHipsRelPose(info.rot, info.trans);
|
||||||
|
AnimPose prevHipsAbsPose = parentAbsPose * prevHipsRelPose;
|
||||||
|
::blend(1, &prevHipsAbsPose, &absPose, alpha, &absPose);
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
|
_relativePoses[_hipsIndex] = parentAbsPose.inverse() * absPose;
|
||||||
|
_relativePoses[_hipsIndex].scale() = glm::vec3(1.0f);
|
||||||
|
_hipsOffset = Vectors::ZERO;
|
||||||
|
|
||||||
|
} else if (_hipsIndex >= 0) {
|
||||||
|
|
||||||
// if there is no hips target, shift hips according to the _hipsOffset from the previous frame
|
// if there is no hips target, shift hips according to the _hipsOffset from the previous frame
|
||||||
float offsetLength = glm::length(_hipsOffset);
|
AnimPose relHipsPose = applyHipsOffset();
|
||||||
const float MIN_HIPS_OFFSET_LENGTH = 0.03f;
|
|
||||||
if (offsetLength > MIN_HIPS_OFFSET_LENGTH && _hipsIndex >= 0) {
|
// determine if we should begin interpolating the hips.
|
||||||
float scaleFactor = ((offsetLength - MIN_HIPS_OFFSET_LENGTH) / offsetLength);
|
for (size_t i = 0; i < targets.size(); i++) {
|
||||||
glm::vec3 hipsOffset = scaleFactor * _hipsOffset;
|
if (_prevJointChainInfoVec[i].target.getIndex() == _hipsIndex) {
|
||||||
if (_hipsParentIndex == -1) {
|
if (_prevJointChainInfoVec[i].timer > 0.0f) {
|
||||||
_relativePoses[_hipsIndex].trans() = _relativePoses[_hipsIndex].trans() + hipsOffset;
|
// smoothly lerp in hipsOffset
|
||||||
} else {
|
float alpha = (JOINT_CHAIN_INTERP_TIME - _prevJointChainInfoVec[i].timer) / JOINT_CHAIN_INTERP_TIME;
|
||||||
auto absHipsPose = _skeleton->getAbsolutePose(_hipsIndex, _relativePoses);
|
AnimPose prevRelHipsPose(_prevJointChainInfoVec[i].jointInfoVec[0].rot, _prevJointChainInfoVec[i].jointInfoVec[0].trans);
|
||||||
absHipsPose.trans() += hipsOffset;
|
::blend(1, &prevRelHipsPose, &relHipsPose, alpha, &relHipsPose);
|
||||||
_relativePoses[_hipsIndex] = _skeleton->getAbsolutePose(_hipsParentIndex, _relativePoses).inverse() * absHipsPose;
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_relativePoses[_hipsIndex] = relHipsPose;
|
||||||
|
}
|
||||||
|
|
||||||
|
// if there is an active jointChainInfo for the hips store the post shifted hips into it.
|
||||||
|
// This is so we have a valid pose to interplate from when the hips target is disabled.
|
||||||
|
if (_hipsTargetIndex >= 0) {
|
||||||
|
jointChainInfoVec[_hipsTargetIndex].jointInfoVec[0].rot = _relativePoses[_hipsIndex].rot();
|
||||||
|
jointChainInfoVec[_hipsTargetIndex].jointInfoVec[0].trans = _relativePoses[_hipsIndex].trans();
|
||||||
}
|
}
|
||||||
|
|
||||||
// update all HipsRelative targets to account for the hips shift/ik target.
|
// update all HipsRelative targets to account for the hips shift/ik target.
|
||||||
|
@ -920,15 +1037,14 @@ const AnimPoseVec& AnimInverseKinematics::overlay(const AnimVariantMap& animVars
|
||||||
|
|
||||||
{
|
{
|
||||||
PROFILE_RANGE_EX(simulation_animation, "ik/ccd", 0xffff00ff, 0);
|
PROFILE_RANGE_EX(simulation_animation, "ik/ccd", 0xffff00ff, 0);
|
||||||
|
|
||||||
preconditionRelativePosesToAvoidLimbLock(context, targets);
|
preconditionRelativePosesToAvoidLimbLock(context, targets);
|
||||||
solve(context, targets);
|
solve(context, targets, dt, jointChainInfoVec);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_hipsTargetIndex < 0) {
|
if (_hipsTargetIndex < 0) {
|
||||||
PROFILE_RANGE_EX(simulation_animation, "ik/measureHipsOffset", 0xffff00ff, 0);
|
PROFILE_RANGE_EX(simulation_animation, "ik/measureHipsOffset", 0xffff00ff, 0);
|
||||||
computeHipsOffset(targets, underPoses, dt);
|
_hipsOffset = computeHipsOffset(targets, underPoses, dt, _hipsOffset);
|
||||||
} else {
|
|
||||||
_hipsOffset = Vectors::ZERO;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -937,23 +1053,15 @@ const AnimPoseVec& AnimInverseKinematics::overlay(const AnimVariantMap& animVars
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_leftHandIndex > -1) {
|
|
||||||
_uncontrolledLeftHandPose = _skeleton->getAbsolutePose(_leftHandIndex, underPoses);
|
|
||||||
}
|
|
||||||
if (_rightHandIndex > -1) {
|
|
||||||
_uncontrolledRightHandPose = _skeleton->getAbsolutePose(_rightHandIndex, underPoses);
|
|
||||||
}
|
|
||||||
if (_hipsIndex > -1) {
|
|
||||||
_uncontrolledHipsPose = _skeleton->getAbsolutePose(_hipsIndex, underPoses);
|
|
||||||
}
|
|
||||||
|
|
||||||
return _relativePoses;
|
return _relativePoses;
|
||||||
}
|
}
|
||||||
|
|
||||||
void AnimInverseKinematics::computeHipsOffset(const std::vector<IKTarget>& targets, const AnimPoseVec& underPoses, float dt) {
|
glm::vec3 AnimInverseKinematics::computeHipsOffset(const std::vector<IKTarget>& targets, const AnimPoseVec& underPoses, float dt, glm::vec3 prevHipsOffset) const {
|
||||||
|
|
||||||
// measure new _hipsOffset for next frame
|
// measure new _hipsOffset for next frame
|
||||||
// by looking for discrepancies between where a targeted endEffector is
|
// by looking for discrepancies between where a targeted endEffector is
|
||||||
// and where it wants to be (after IK solutions are done)
|
// and where it wants to be (after IK solutions are done)
|
||||||
|
glm::vec3 hipsOffset = prevHipsOffset;
|
||||||
glm::vec3 newHipsOffset = Vectors::ZERO;
|
glm::vec3 newHipsOffset = Vectors::ZERO;
|
||||||
for (auto& target: targets) {
|
for (auto& target: targets) {
|
||||||
int targetIndex = target.getIndex();
|
int targetIndex = target.getIndex();
|
||||||
|
@ -969,9 +1077,9 @@ void AnimInverseKinematics::computeHipsOffset(const std::vector<IKTarget>& targe
|
||||||
} else if (target.getType() == IKTarget::Type::HmdHead) {
|
} else if (target.getType() == IKTarget::Type::HmdHead) {
|
||||||
// we want to shift the hips to bring the head to its designated position
|
// we want to shift the hips to bring the head to its designated position
|
||||||
glm::vec3 actual = _skeleton->getAbsolutePose(_headIndex, _relativePoses).trans();
|
glm::vec3 actual = _skeleton->getAbsolutePose(_headIndex, _relativePoses).trans();
|
||||||
_hipsOffset += target.getTranslation() - actual;
|
hipsOffset += target.getTranslation() - actual;
|
||||||
// and ignore all other targets
|
// and ignore all other targets
|
||||||
newHipsOffset = _hipsOffset;
|
newHipsOffset = hipsOffset;
|
||||||
break;
|
break;
|
||||||
} else if (target.getType() == IKTarget::Type::RotationAndPosition) {
|
} else if (target.getType() == IKTarget::Type::RotationAndPosition) {
|
||||||
glm::vec3 actualPosition = _skeleton->getAbsolutePose(targetIndex, _relativePoses).trans();
|
glm::vec3 actualPosition = _skeleton->getAbsolutePose(targetIndex, _relativePoses).trans();
|
||||||
|
@ -991,16 +1099,18 @@ void AnimInverseKinematics::computeHipsOffset(const std::vector<IKTarget>& targe
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// smooth transitions by relaxing _hipsOffset toward the new value
|
// smooth transitions by relaxing hipsOffset toward the new value
|
||||||
const float HIPS_OFFSET_SLAVE_TIMESCALE = 0.10f;
|
const float HIPS_OFFSET_SLAVE_TIMESCALE = 0.10f;
|
||||||
float tau = dt < HIPS_OFFSET_SLAVE_TIMESCALE ? dt / HIPS_OFFSET_SLAVE_TIMESCALE : 1.0f;
|
float tau = dt < HIPS_OFFSET_SLAVE_TIMESCALE ? dt / HIPS_OFFSET_SLAVE_TIMESCALE : 1.0f;
|
||||||
_hipsOffset += (newHipsOffset - _hipsOffset) * tau;
|
hipsOffset += (newHipsOffset - hipsOffset) * tau;
|
||||||
|
|
||||||
// clamp the hips offset
|
// clamp the hips offset
|
||||||
float hipsOffsetLength = glm::length(_hipsOffset);
|
float hipsOffsetLength = glm::length(hipsOffset);
|
||||||
if (hipsOffsetLength > _maxHipsOffsetLength) {
|
if (hipsOffsetLength > _maxHipsOffsetLength) {
|
||||||
_hipsOffset *= _maxHipsOffsetLength / hipsOffsetLength;
|
hipsOffset *= _maxHipsOffsetLength / hipsOffsetLength;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return hipsOffset;
|
||||||
}
|
}
|
||||||
|
|
||||||
void AnimInverseKinematics::setMaxHipsOffsetLength(float maxLength) {
|
void AnimInverseKinematics::setMaxHipsOffsetLength(float maxLength) {
|
||||||
|
@ -1414,8 +1524,6 @@ void AnimInverseKinematics::setSkeletonInternal(AnimSkeleton::ConstPointer skele
|
||||||
targetVar.jointIndex = -1;
|
targetVar.jointIndex = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
_maxTargetIndex = -1;
|
|
||||||
|
|
||||||
for (auto& accumulator: _rotationAccumulators) {
|
for (auto& accumulator: _rotationAccumulators) {
|
||||||
accumulator.clearAndClean();
|
accumulator.clearAndClean();
|
||||||
}
|
}
|
||||||
|
@ -1446,10 +1554,6 @@ void AnimInverseKinematics::setSkeletonInternal(AnimSkeleton::ConstPointer skele
|
||||||
_leftHandIndex = -1;
|
_leftHandIndex = -1;
|
||||||
_rightHandIndex = -1;
|
_rightHandIndex = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
_uncontrolledLeftHandPose = AnimPose();
|
|
||||||
_uncontrolledRightHandPose = AnimPose();
|
|
||||||
_uncontrolledHipsPose = AnimPose();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static glm::vec3 sphericalToCartesian(float phi, float theta) {
|
static glm::vec3 sphericalToCartesian(float phi, float theta) {
|
||||||
|
@ -1495,14 +1599,14 @@ void AnimInverseKinematics::debugDrawRelativePoses(const AnimContext& context) c
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void AnimInverseKinematics::debugDrawIKChain(JointChainInfo* jointChainInfos, size_t numJointChainInfos, const AnimContext& context) const {
|
void AnimInverseKinematics::debugDrawIKChain(const JointChainInfo& jointChainInfo, const AnimContext& context) const {
|
||||||
AnimPoseVec poses = _relativePoses;
|
AnimPoseVec poses = _relativePoses;
|
||||||
|
|
||||||
// copy debug joint rotations into the relative poses
|
// copy debug joint rotations into the relative poses
|
||||||
for (size_t i = 0; i < numJointChainInfos; i++) {
|
for (size_t i = 0; i < jointChainInfo.jointInfoVec.size(); i++) {
|
||||||
const JointChainInfo& info = jointChainInfos[i];
|
const JointInfo& info = jointChainInfo.jointInfoVec[i];
|
||||||
poses[info.jointIndex].rot() = info.relRot;
|
poses[info.jointIndex].rot() = info.rot;
|
||||||
poses[info.jointIndex].trans() = info.relTrans;
|
poses[info.jointIndex].trans() = info.trans;
|
||||||
}
|
}
|
||||||
|
|
||||||
// convert relative poses to absolute
|
// convert relative poses to absolute
|
||||||
|
@ -1519,9 +1623,9 @@ void AnimInverseKinematics::debugDrawIKChain(JointChainInfo* jointChainInfos, si
|
||||||
// draw each pose
|
// draw each pose
|
||||||
for (int i = 0; i < (int)poses.size(); i++) {
|
for (int i = 0; i < (int)poses.size(); i++) {
|
||||||
int parentIndex = _skeleton->getParentIndex(i);
|
int parentIndex = _skeleton->getParentIndex(i);
|
||||||
JointChainInfo* jointInfo = nullptr;
|
const JointInfo* jointInfo = nullptr;
|
||||||
JointChainInfo* parentJointInfo = nullptr;
|
const JointInfo* parentJointInfo = nullptr;
|
||||||
lookupJointChainInfo(jointChainInfos, numJointChainInfos, i, parentIndex, &jointInfo, &parentJointInfo);
|
lookupJointInfo(jointChainInfo, i, parentIndex, &jointInfo, &parentJointInfo);
|
||||||
if (jointInfo && parentJointInfo) {
|
if (jointInfo && parentJointInfo) {
|
||||||
|
|
||||||
// transform local axes into world space.
|
// transform local axes into world space.
|
||||||
|
@ -1608,7 +1712,7 @@ void AnimInverseKinematics::debugDrawConstraints(const AnimContext& context) con
|
||||||
|
|
||||||
const int NUM_SWING_STEPS = 10;
|
const int NUM_SWING_STEPS = 10;
|
||||||
for (int i = 0; i < NUM_SWING_STEPS + 1; i++) {
|
for (int i = 0; i < NUM_SWING_STEPS + 1; i++) {
|
||||||
glm::quat rot = glm::normalize(glm::lerp(minRot, maxRot, i * (1.0f / NUM_SWING_STEPS)));
|
glm::quat rot = safeLerp(minRot, maxRot, i * (1.0f / NUM_SWING_STEPS));
|
||||||
glm::vec3 axis = transformVectorFast(geomToWorldMatrix, parentAbsRot * rot * refRot * Vectors::UNIT_Y);
|
glm::vec3 axis = transformVectorFast(geomToWorldMatrix, parentAbsRot * rot * refRot * Vectors::UNIT_Y);
|
||||||
DebugDraw::getInstance().drawRay(pos, pos + TWIST_LENGTH * axis, CYAN);
|
DebugDraw::getInstance().drawRay(pos, pos + TWIST_LENGTH * axis, CYAN);
|
||||||
}
|
}
|
||||||
|
@ -1626,7 +1730,7 @@ void AnimInverseKinematics::debugDrawConstraints(const AnimContext& context) con
|
||||||
|
|
||||||
const int NUM_SWING_STEPS = 10;
|
const int NUM_SWING_STEPS = 10;
|
||||||
for (int i = 0; i < NUM_SWING_STEPS + 1; i++) {
|
for (int i = 0; i < NUM_SWING_STEPS + 1; i++) {
|
||||||
glm::quat rot = glm::normalize(glm::lerp(minRot, maxRot, i * (1.0f / NUM_SWING_STEPS)));
|
glm::quat rot = safeLerp(minRot, maxRot, i * (1.0f / NUM_SWING_STEPS));
|
||||||
glm::vec3 axis = transformVectorFast(geomToWorldMatrix, parentAbsRot * rot * refRot * Vectors::UNIT_X);
|
glm::vec3 axis = transformVectorFast(geomToWorldMatrix, parentAbsRot * rot * refRot * Vectors::UNIT_X);
|
||||||
DebugDraw::getInstance().drawRay(pos, pos + TWIST_LENGTH * axis, CYAN);
|
DebugDraw::getInstance().drawRay(pos, pos + TWIST_LENGTH * axis, CYAN);
|
||||||
}
|
}
|
||||||
|
@ -1666,10 +1770,9 @@ void AnimInverseKinematics::blendToPoses(const AnimPoseVec& targetPoses, const A
|
||||||
// relax toward poses
|
// relax toward poses
|
||||||
int numJoints = (int)_relativePoses.size();
|
int numJoints = (int)_relativePoses.size();
|
||||||
for (int i = 0; i < numJoints; ++i) {
|
for (int i = 0; i < numJoints; ++i) {
|
||||||
float dotSign = copysignf(1.0f, glm::dot(_relativePoses[i].rot(), targetPoses[i].rot()));
|
|
||||||
if (_rotationAccumulators[i].isDirty()) {
|
if (_rotationAccumulators[i].isDirty()) {
|
||||||
// this joint is affected by IK --> blend toward the targetPoses rotation
|
// this joint is affected by IK --> blend toward the targetPoses rotation
|
||||||
_relativePoses[i].rot() = glm::normalize(glm::lerp(_relativePoses[i].rot(), dotSign * targetPoses[i].rot(), blendFactor));
|
_relativePoses[i].rot() = safeLerp(_relativePoses[i].rot(), targetPoses[i].rot(), blendFactor);
|
||||||
} else {
|
} else {
|
||||||
// this joint is NOT affected by IK --> slam to underPoses rotation
|
// this joint is NOT affected by IK --> slam to underPoses rotation
|
||||||
_relativePoses[i].rot() = underPoses[i].rot();
|
_relativePoses[i].rot() = underPoses[i].rot();
|
||||||
|
|
|
@ -26,14 +26,21 @@ class RotationConstraint;
|
||||||
class AnimInverseKinematics : public AnimNode {
|
class AnimInverseKinematics : public AnimNode {
|
||||||
public:
|
public:
|
||||||
|
|
||||||
struct JointChainInfo {
|
struct JointInfo {
|
||||||
glm::quat relRot;
|
glm::quat rot;
|
||||||
glm::vec3 relTrans;
|
glm::vec3 trans;
|
||||||
float weight;
|
|
||||||
int jointIndex;
|
int jointIndex;
|
||||||
bool constrained;
|
bool constrained;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct JointChainInfo {
|
||||||
|
std::vector<JointInfo> jointInfoVec;
|
||||||
|
IKTarget target;
|
||||||
|
float timer { 0.0f };
|
||||||
|
};
|
||||||
|
|
||||||
|
using JointChainInfoVec = std::vector<JointChainInfo>;
|
||||||
|
|
||||||
explicit AnimInverseKinematics(const QString& id);
|
explicit AnimInverseKinematics(const QString& id);
|
||||||
virtual ~AnimInverseKinematics() override;
|
virtual ~AnimInverseKinematics() override;
|
||||||
|
|
||||||
|
@ -66,23 +73,22 @@ public:
|
||||||
void setSolutionSource(SolutionSource solutionSource) { _solutionSource = solutionSource; }
|
void setSolutionSource(SolutionSource solutionSource) { _solutionSource = solutionSource; }
|
||||||
void setSolutionSourceVar(const QString& solutionSourceVar) { _solutionSourceVar = solutionSourceVar; }
|
void setSolutionSourceVar(const QString& solutionSourceVar) { _solutionSourceVar = solutionSourceVar; }
|
||||||
|
|
||||||
const AnimPose& getUncontrolledLeftHandPose() { return _uncontrolledLeftHandPose; }
|
|
||||||
const AnimPose& getUncontrolledRightHandPose() { return _uncontrolledRightHandPose; }
|
|
||||||
const AnimPose& getUncontrolledHipPose() { return _uncontrolledHipsPose; }
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void computeTargets(const AnimVariantMap& animVars, std::vector<IKTarget>& targets, const AnimPoseVec& underPoses);
|
void computeTargets(const AnimVariantMap& animVars, std::vector<IKTarget>& targets, const AnimPoseVec& underPoses);
|
||||||
void solve(const AnimContext& context, const std::vector<IKTarget>& targets);
|
void solve(const AnimContext& context, const std::vector<IKTarget>& targets, float dt, JointChainInfoVec& jointChainInfoVec);
|
||||||
void solveTargetWithCCD(const AnimContext& context, const IKTarget& target, const AnimPoseVec& absolutePoses, bool debug);
|
void solveTargetWithCCD(const AnimContext& context, const IKTarget& target, const AnimPoseVec& absolutePoses,
|
||||||
void solveTargetWithSpline(const AnimContext& context, const IKTarget& target, const AnimPoseVec& absolutePoses, bool debug);
|
bool debug, JointChainInfo& jointChainInfoOut) const;
|
||||||
|
void solveTargetWithSpline(const AnimContext& context, const IKTarget& target, const AnimPoseVec& absolutePoses,
|
||||||
|
bool debug, JointChainInfo& jointChainInfoOut) const;
|
||||||
virtual void setSkeletonInternal(AnimSkeleton::ConstPointer skeleton) override;
|
virtual void setSkeletonInternal(AnimSkeleton::ConstPointer skeleton) override;
|
||||||
void debugDrawIKChain(JointChainInfo* jointChainInfos, size_t numJointChainInfos, const AnimContext& context) const;
|
void debugDrawIKChain(const JointChainInfo& jointChainInfo, const AnimContext& context) const;
|
||||||
void debugDrawRelativePoses(const AnimContext& context) const;
|
void debugDrawRelativePoses(const AnimContext& context) const;
|
||||||
void debugDrawConstraints(const AnimContext& context) const;
|
void debugDrawConstraints(const AnimContext& context) const;
|
||||||
void debugDrawSpineSplines(const AnimContext& context, const std::vector<IKTarget>& targets) const;
|
void debugDrawSpineSplines(const AnimContext& context, const std::vector<IKTarget>& targets) const;
|
||||||
void initRelativePosesFromSolutionSource(SolutionSource solutionSource, const AnimPoseVec& underPose);
|
void initRelativePosesFromSolutionSource(SolutionSource solutionSource, const AnimPoseVec& underPose);
|
||||||
void blendToPoses(const AnimPoseVec& targetPoses, const AnimPoseVec& underPose, float blendFactor);
|
void blendToPoses(const AnimPoseVec& targetPoses, const AnimPoseVec& underPose, float blendFactor);
|
||||||
void preconditionRelativePosesToAvoidLimbLock(const AnimContext& context, const std::vector<IKTarget>& targets);
|
void preconditionRelativePosesToAvoidLimbLock(const AnimContext& context, const std::vector<IKTarget>& targets);
|
||||||
|
AnimPose applyHipsOffset() const;
|
||||||
|
|
||||||
// used to pre-compute information about each joint influeced by a spline IK target.
|
// used to pre-compute information about each joint influeced by a spline IK target.
|
||||||
struct SplineJointInfo {
|
struct SplineJointInfo {
|
||||||
|
@ -91,8 +97,8 @@ protected:
|
||||||
AnimPose offsetPose; // local offset from the spline to the joint.
|
AnimPose offsetPose; // local offset from the spline to the joint.
|
||||||
};
|
};
|
||||||
|
|
||||||
void computeSplineJointInfosForIKTarget(const AnimContext& context, const IKTarget& target);
|
void computeAndCacheSplineJointInfosForIKTarget(const AnimContext& context, const IKTarget& target) const;
|
||||||
const std::vector<SplineJointInfo>* findOrCreateSplineJointInfo(const AnimContext& context, const IKTarget& target);
|
const std::vector<SplineJointInfo>* findOrCreateSplineJointInfo(const AnimContext& context, const IKTarget& target) const;
|
||||||
|
|
||||||
// for AnimDebugDraw rendering
|
// for AnimDebugDraw rendering
|
||||||
virtual const AnimPoseVec& getPosesInternal() const override { return _relativePoses; }
|
virtual const AnimPoseVec& getPosesInternal() const override { return _relativePoses; }
|
||||||
|
@ -101,7 +107,7 @@ protected:
|
||||||
void clearConstraints();
|
void clearConstraints();
|
||||||
void initConstraints();
|
void initConstraints();
|
||||||
void initLimitCenterPoses();
|
void initLimitCenterPoses();
|
||||||
void computeHipsOffset(const std::vector<IKTarget>& targets, const AnimPoseVec& underPoses, float dt);
|
glm::vec3 computeHipsOffset(const std::vector<IKTarget>& targets, const AnimPoseVec& underPoses, float dt, glm::vec3 prevHipsOffset) const;
|
||||||
|
|
||||||
// no copies
|
// no copies
|
||||||
AnimInverseKinematics(const AnimInverseKinematics&) = delete;
|
AnimInverseKinematics(const AnimInverseKinematics&) = delete;
|
||||||
|
@ -136,7 +142,7 @@ protected:
|
||||||
AnimPoseVec _relativePoses; // current relative poses
|
AnimPoseVec _relativePoses; // current relative poses
|
||||||
AnimPoseVec _limitCenterPoses; // relative
|
AnimPoseVec _limitCenterPoses; // relative
|
||||||
|
|
||||||
std::map<int, std::vector<SplineJointInfo>> _splineJointInfoMap;
|
mutable std::map<int, std::vector<SplineJointInfo>> _splineJointInfoMap;
|
||||||
|
|
||||||
// experimental data for moving hips during IK
|
// experimental data for moving hips during IK
|
||||||
glm::vec3 _hipsOffset { Vectors::ZERO };
|
glm::vec3 _hipsOffset { Vectors::ZERO };
|
||||||
|
@ -148,18 +154,12 @@ protected:
|
||||||
int _leftHandIndex { -1 };
|
int _leftHandIndex { -1 };
|
||||||
int _rightHandIndex { -1 };
|
int _rightHandIndex { -1 };
|
||||||
|
|
||||||
// _maxTargetIndex is tracked to help optimize the recalculation of absolute poses
|
|
||||||
// during the the cyclic coordinate descent algorithm
|
|
||||||
int _maxTargetIndex { 0 };
|
|
||||||
|
|
||||||
float _maxErrorOnLastSolve { FLT_MAX };
|
float _maxErrorOnLastSolve { FLT_MAX };
|
||||||
bool _previousEnableDebugIKTargets { false };
|
bool _previousEnableDebugIKTargets { false };
|
||||||
SolutionSource _solutionSource { SolutionSource::RelaxToUnderPoses };
|
SolutionSource _solutionSource { SolutionSource::RelaxToUnderPoses };
|
||||||
QString _solutionSourceVar;
|
QString _solutionSourceVar;
|
||||||
|
|
||||||
AnimPose _uncontrolledLeftHandPose { AnimPose() };
|
JointChainInfoVec _prevJointChainInfoVec;
|
||||||
AnimPose _uncontrolledRightHandPose { AnimPose() };
|
|
||||||
AnimPose _uncontrolledHipsPose { AnimPose() };
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // hifi_AnimInverseKinematics_h
|
#endif // hifi_AnimInverseKinematics_h
|
||||||
|
|
|
@ -42,6 +42,20 @@ int AnimSkeleton::getNumJoints() const {
|
||||||
return _jointsSize;
|
return _jointsSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int AnimSkeleton::getChainDepth(int jointIndex) const {
|
||||||
|
if (jointIndex >= 0) {
|
||||||
|
int chainDepth = 0;
|
||||||
|
int index = jointIndex;
|
||||||
|
do {
|
||||||
|
chainDepth++;
|
||||||
|
index = _joints[index].parentIndex;
|
||||||
|
} while (index != -1);
|
||||||
|
return chainDepth;
|
||||||
|
} else {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const AnimPose& AnimSkeleton::getAbsoluteBindPose(int jointIndex) const {
|
const AnimPose& AnimSkeleton::getAbsoluteBindPose(int jointIndex) const {
|
||||||
return _absoluteBindPoses[jointIndex];
|
return _absoluteBindPoses[jointIndex];
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,6 +28,7 @@ public:
|
||||||
int nameToJointIndex(const QString& jointName) const;
|
int nameToJointIndex(const QString& jointName) const;
|
||||||
const QString& getJointName(int jointIndex) const;
|
const QString& getJointName(int jointIndex) const;
|
||||||
int getNumJoints() const;
|
int getNumJoints() const;
|
||||||
|
int getChainDepth(int jointIndex) const;
|
||||||
|
|
||||||
// absolute pose, not relative to parent
|
// absolute pose, not relative to parent
|
||||||
const AnimPose& getAbsoluteBindPose(int jointIndex) const;
|
const AnimPose& getAbsoluteBindPose(int jointIndex) const;
|
||||||
|
|
|
@ -28,7 +28,7 @@ void blend(size_t numPoses, const AnimPose* a, const AnimPose* b, float alpha, A
|
||||||
}
|
}
|
||||||
|
|
||||||
result[i].scale() = lerp(aPose.scale(), bPose.scale(), alpha);
|
result[i].scale() = lerp(aPose.scale(), bPose.scale(), alpha);
|
||||||
result[i].rot() = glm::normalize(glm::lerp(aPose.rot(), q2, alpha));
|
result[i].rot() = safeLerp(aPose.rot(), bPose.rot(), alpha);
|
||||||
result[i].trans() = lerp(aPose.trans(), bPose.trans(), alpha);
|
result[i].trans() = lerp(aPose.trans(), bPose.trans(), alpha);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,4 +21,14 @@ glm::quat averageQuats(size_t numQuats, const glm::quat* quats);
|
||||||
float accumulateTime(float startFrame, float endFrame, float timeScale, float currentFrame, float dt, bool loopFlag,
|
float accumulateTime(float startFrame, float endFrame, float timeScale, float currentFrame, float dt, bool loopFlag,
|
||||||
const QString& id, AnimNode::Triggers& triggersOut);
|
const QString& id, AnimNode::Triggers& triggersOut);
|
||||||
|
|
||||||
|
inline glm::quat safeLerp(const glm::quat& a, const glm::quat& b, float alpha) {
|
||||||
|
// adjust signs if necessary
|
||||||
|
glm::quat bTemp = b;
|
||||||
|
float dot = glm::dot(a, bTemp);
|
||||||
|
if (dot < 0.0f) {
|
||||||
|
bTemp = -bTemp;
|
||||||
|
}
|
||||||
|
return glm::normalize(glm::lerp(a, bTemp, alpha));
|
||||||
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -56,8 +56,8 @@ private:
|
||||||
glm::vec3 _poleReferenceVector;
|
glm::vec3 _poleReferenceVector;
|
||||||
bool _poleVectorEnabled { false };
|
bool _poleVectorEnabled { false };
|
||||||
int _index { -1 };
|
int _index { -1 };
|
||||||
Type _type { Type::RotationAndPosition };
|
Type _type { Type::Unknown };
|
||||||
float _weight;
|
float _weight { 0.0f };
|
||||||
float _flexCoefficients[MAX_FLEX_COEFFICIENTS];
|
float _flexCoefficients[MAX_FLEX_COEFFICIENTS];
|
||||||
size_t _numFlexCoefficients;
|
size_t _numFlexCoefficients;
|
||||||
};
|
};
|
||||||
|
|
|
@ -1115,36 +1115,13 @@ void Rig::updateHands(bool leftHandEnabled, bool rightHandEnabled, bool hipsEnab
|
||||||
const glm::vec3 bodyCapsuleEnd = bodyCapsuleCenter + glm::vec3(0, bodyCapsuleHalfHeight, 0);
|
const glm::vec3 bodyCapsuleEnd = bodyCapsuleCenter + glm::vec3(0, bodyCapsuleHalfHeight, 0);
|
||||||
|
|
||||||
const float HAND_RADIUS = 0.05f;
|
const float HAND_RADIUS = 0.05f;
|
||||||
|
|
||||||
const float RELAX_DURATION = 0.6f;
|
|
||||||
const float CONTROL_DURATION = 0.4f;
|
|
||||||
const bool TO_CONTROLLED = true;
|
|
||||||
const bool FROM_CONTROLLED = false;
|
|
||||||
const bool LEFT_HAND = true;
|
|
||||||
const bool RIGHT_HAND = false;
|
|
||||||
|
|
||||||
const float ELBOW_POLE_VECTOR_BLEND_FACTOR = 0.95f;
|
const float ELBOW_POLE_VECTOR_BLEND_FACTOR = 0.95f;
|
||||||
|
|
||||||
if (leftHandEnabled) {
|
if (leftHandEnabled) {
|
||||||
if (!_isLeftHandControlled) {
|
|
||||||
_leftHandControlTimeRemaining = CONTROL_DURATION;
|
|
||||||
_isLeftHandControlled = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
glm::vec3 handPosition = leftHandPose.trans();
|
glm::vec3 handPosition = leftHandPose.trans();
|
||||||
glm::quat handRotation = leftHandPose.rot();
|
glm::quat handRotation = leftHandPose.rot();
|
||||||
|
|
||||||
if (_leftHandControlTimeRemaining > 0.0f) {
|
|
||||||
// Move hand from non-controlled position to controlled position.
|
|
||||||
_leftHandControlTimeRemaining = std::max(_leftHandControlTimeRemaining - dt, 0.0f);
|
|
||||||
AnimPose handPose(Vectors::ONE, handRotation, handPosition);
|
|
||||||
if (transitionHandPose(_leftHandControlTimeRemaining, CONTROL_DURATION, handPose,
|
|
||||||
LEFT_HAND, TO_CONTROLLED, handPose)) {
|
|
||||||
handPosition = handPose.trans();
|
|
||||||
handRotation = handPose.rot();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!hipsEnabled) {
|
if (!hipsEnabled) {
|
||||||
// prevent the hand IK targets from intersecting the body capsule
|
// prevent the hand IK targets from intersecting the body capsule
|
||||||
glm::vec3 displacement;
|
glm::vec3 displacement;
|
||||||
|
@ -1157,9 +1134,6 @@ void Rig::updateHands(bool leftHandEnabled, bool rightHandEnabled, bool hipsEnab
|
||||||
_animVars.set("leftHandRotation", handRotation);
|
_animVars.set("leftHandRotation", handRotation);
|
||||||
_animVars.set("leftHandType", (int)IKTarget::Type::RotationAndPosition);
|
_animVars.set("leftHandType", (int)IKTarget::Type::RotationAndPosition);
|
||||||
|
|
||||||
_lastLeftHandControlledPose = AnimPose(Vectors::ONE, handRotation, handPosition);
|
|
||||||
_isLeftHandControlled = true;
|
|
||||||
|
|
||||||
// compute pole vector
|
// compute pole vector
|
||||||
int handJointIndex = _animSkeleton->nameToJointIndex("LeftHand");
|
int handJointIndex = _animSkeleton->nameToJointIndex("LeftHand");
|
||||||
int armJointIndex = _animSkeleton->nameToJointIndex("LeftArm");
|
int armJointIndex = _animSkeleton->nameToJointIndex("LeftArm");
|
||||||
|
@ -1187,47 +1161,17 @@ void Rig::updateHands(bool leftHandEnabled, bool rightHandEnabled, bool hipsEnab
|
||||||
_prevLeftHandPoleVectorValid = false;
|
_prevLeftHandPoleVectorValid = false;
|
||||||
_animVars.set("leftHandPoleVectorEnabled", false);
|
_animVars.set("leftHandPoleVectorEnabled", false);
|
||||||
|
|
||||||
if (_isLeftHandControlled) {
|
|
||||||
_leftHandRelaxTimeRemaining = RELAX_DURATION;
|
|
||||||
_isLeftHandControlled = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_leftHandRelaxTimeRemaining > 0.0f) {
|
|
||||||
// Move hand from controlled position to non-controlled position.
|
|
||||||
_leftHandRelaxTimeRemaining = std::max(_leftHandRelaxTimeRemaining - dt, 0.0f);
|
|
||||||
AnimPose handPose;
|
|
||||||
if (transitionHandPose(_leftHandRelaxTimeRemaining, RELAX_DURATION, _lastLeftHandControlledPose,
|
|
||||||
LEFT_HAND, FROM_CONTROLLED, handPose)) {
|
|
||||||
_animVars.set("leftHandPosition", handPose.trans());
|
|
||||||
_animVars.set("leftHandRotation", handPose.rot());
|
|
||||||
_animVars.set("leftHandType", (int)IKTarget::Type::RotationAndPosition);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
_animVars.unset("leftHandPosition");
|
_animVars.unset("leftHandPosition");
|
||||||
_animVars.unset("leftHandRotation");
|
_animVars.unset("leftHandRotation");
|
||||||
_animVars.set("leftHandType", (int)IKTarget::Type::HipsRelativeRotationAndPosition);
|
_animVars.set("leftHandType", (int)IKTarget::Type::HipsRelativeRotationAndPosition);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (rightHandEnabled) {
|
if (rightHandEnabled) {
|
||||||
if (!_isRightHandControlled) {
|
|
||||||
_rightHandControlTimeRemaining = CONTROL_DURATION;
|
|
||||||
_isRightHandControlled = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
glm::vec3 handPosition = rightHandPose.trans();
|
glm::vec3 handPosition = rightHandPose.trans();
|
||||||
glm::quat handRotation = rightHandPose.rot();
|
glm::quat handRotation = rightHandPose.rot();
|
||||||
|
|
||||||
if (_rightHandControlTimeRemaining > 0.0f) {
|
|
||||||
// Move hand from non-controlled position to controlled position.
|
|
||||||
_rightHandControlTimeRemaining = std::max(_rightHandControlTimeRemaining - dt, 0.0f);
|
|
||||||
AnimPose handPose(Vectors::ONE, handRotation, handPosition);
|
|
||||||
if (transitionHandPose(_rightHandControlTimeRemaining, CONTROL_DURATION, handPose, RIGHT_HAND, TO_CONTROLLED, handPose)) {
|
|
||||||
handPosition = handPose.trans();
|
|
||||||
handRotation = handPose.rot();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!hipsEnabled) {
|
if (!hipsEnabled) {
|
||||||
// prevent the hand IK targets from intersecting the body capsule
|
// prevent the hand IK targets from intersecting the body capsule
|
||||||
glm::vec3 displacement;
|
glm::vec3 displacement;
|
||||||
|
@ -1240,9 +1184,6 @@ void Rig::updateHands(bool leftHandEnabled, bool rightHandEnabled, bool hipsEnab
|
||||||
_animVars.set("rightHandRotation", handRotation);
|
_animVars.set("rightHandRotation", handRotation);
|
||||||
_animVars.set("rightHandType", (int)IKTarget::Type::RotationAndPosition);
|
_animVars.set("rightHandType", (int)IKTarget::Type::RotationAndPosition);
|
||||||
|
|
||||||
_lastRightHandControlledPose = AnimPose(Vectors::ONE, handRotation, handPosition);
|
|
||||||
_isRightHandControlled = true;
|
|
||||||
|
|
||||||
// compute pole vector
|
// compute pole vector
|
||||||
int handJointIndex = _animSkeleton->nameToJointIndex("RightHand");
|
int handJointIndex = _animSkeleton->nameToJointIndex("RightHand");
|
||||||
int armJointIndex = _animSkeleton->nameToJointIndex("RightArm");
|
int armJointIndex = _animSkeleton->nameToJointIndex("RightArm");
|
||||||
|
@ -1270,27 +1211,11 @@ void Rig::updateHands(bool leftHandEnabled, bool rightHandEnabled, bool hipsEnab
|
||||||
_prevRightHandPoleVectorValid = false;
|
_prevRightHandPoleVectorValid = false;
|
||||||
_animVars.set("rightHandPoleVectorEnabled", false);
|
_animVars.set("rightHandPoleVectorEnabled", false);
|
||||||
|
|
||||||
if (_isRightHandControlled) {
|
|
||||||
_rightHandRelaxTimeRemaining = RELAX_DURATION;
|
|
||||||
_isRightHandControlled = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_rightHandRelaxTimeRemaining > 0.0f) {
|
|
||||||
// Move hand from controlled position to non-controlled position.
|
|
||||||
_rightHandRelaxTimeRemaining = std::max(_rightHandRelaxTimeRemaining - dt, 0.0f);
|
|
||||||
AnimPose handPose;
|
|
||||||
if (transitionHandPose(_rightHandRelaxTimeRemaining, RELAX_DURATION, _lastRightHandControlledPose, RIGHT_HAND, FROM_CONTROLLED, handPose)) {
|
|
||||||
_animVars.set("rightHandPosition", handPose.trans());
|
|
||||||
_animVars.set("rightHandRotation", handPose.rot());
|
|
||||||
_animVars.set("rightHandType", (int)IKTarget::Type::RotationAndPosition);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
_animVars.unset("rightHandPosition");
|
_animVars.unset("rightHandPosition");
|
||||||
_animVars.unset("rightHandRotation");
|
_animVars.unset("rightHandRotation");
|
||||||
_animVars.set("rightHandType", (int)IKTarget::Type::HipsRelativeRotationAndPosition);
|
_animVars.set("rightHandType", (int)IKTarget::Type::HipsRelativeRotationAndPosition);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
void Rig::updateFeet(bool leftFootEnabled, bool rightFootEnabled, const AnimPose& leftFootPose, const AnimPose& rightFootPose) {
|
void Rig::updateFeet(bool leftFootEnabled, bool rightFootEnabled, const AnimPose& leftFootPose, const AnimPose& rightFootPose) {
|
||||||
|
|
||||||
|
@ -1737,39 +1662,38 @@ void Rig::computeAvatarBoundingCapsule(
|
||||||
ikNode.setTargetVars("RightFoot", "rightFootPosition", "rightFootRotation",
|
ikNode.setTargetVars("RightFoot", "rightFootPosition", "rightFootRotation",
|
||||||
"rightFootType", "rightFootWeight", 1.0f, {},
|
"rightFootType", "rightFootWeight", 1.0f, {},
|
||||||
QString(), QString(), QString());
|
QString(), QString(), QString());
|
||||||
|
glm::vec3 hipsPosition(0.0f);
|
||||||
AnimPose geometryToRig = _modelOffset * _geometryOffset;
|
|
||||||
|
|
||||||
AnimPose hips(glm::vec3(1), glm::quat(), glm::vec3());
|
|
||||||
int hipsIndex = indexOfJoint("Hips");
|
int hipsIndex = indexOfJoint("Hips");
|
||||||
if (hipsIndex >= 0) {
|
if (hipsIndex >= 0) {
|
||||||
hips = geometryToRig * _animSkeleton->getAbsoluteBindPose(hipsIndex);
|
hipsPosition = transformPoint(_geometryToRigTransform, _animSkeleton->getAbsoluteDefaultPose(hipsIndex).trans());
|
||||||
}
|
}
|
||||||
AnimVariantMap animVars;
|
AnimVariantMap animVars;
|
||||||
|
animVars.setRigToGeometryTransform(_rigToGeometryTransform);
|
||||||
glm::quat handRotation = glm::angleAxis(PI, Vectors::UNIT_X);
|
glm::quat handRotation = glm::angleAxis(PI, Vectors::UNIT_X);
|
||||||
animVars.set("leftHandPosition", hips.trans());
|
animVars.set("leftHandPosition", hipsPosition);
|
||||||
animVars.set("leftHandRotation", handRotation);
|
animVars.set("leftHandRotation", handRotation);
|
||||||
animVars.set("leftHandType", (int)IKTarget::Type::RotationAndPosition);
|
animVars.set("leftHandType", (int)IKTarget::Type::RotationAndPosition);
|
||||||
animVars.set("rightHandPosition", hips.trans());
|
animVars.set("rightHandPosition", hipsPosition);
|
||||||
animVars.set("rightHandRotation", handRotation);
|
animVars.set("rightHandRotation", handRotation);
|
||||||
animVars.set("rightHandType", (int)IKTarget::Type::RotationAndPosition);
|
animVars.set("rightHandType", (int)IKTarget::Type::RotationAndPosition);
|
||||||
|
|
||||||
int rightFootIndex = indexOfJoint("RightFoot");
|
int rightFootIndex = indexOfJoint("RightFoot");
|
||||||
int leftFootIndex = indexOfJoint("LeftFoot");
|
int leftFootIndex = indexOfJoint("LeftFoot");
|
||||||
if (rightFootIndex != -1 && leftFootIndex != -1) {
|
if (rightFootIndex != -1 && leftFootIndex != -1) {
|
||||||
glm::vec3 foot = Vectors::ZERO;
|
glm::vec3 geomFootPosition = glm::vec3(0.0f, _animSkeleton->getAbsoluteDefaultPose(rightFootIndex).trans().y, 0.0f);
|
||||||
|
glm::vec3 footPosition = transformPoint(_geometryToRigTransform, geomFootPosition);
|
||||||
glm::quat footRotation = glm::angleAxis(0.5f * PI, Vectors::UNIT_X);
|
glm::quat footRotation = glm::angleAxis(0.5f * PI, Vectors::UNIT_X);
|
||||||
animVars.set("leftFootPosition", foot);
|
animVars.set("leftFootPosition", footPosition);
|
||||||
animVars.set("leftFootRotation", footRotation);
|
animVars.set("leftFootRotation", footRotation);
|
||||||
animVars.set("leftFootType", (int)IKTarget::Type::RotationAndPosition);
|
animVars.set("leftFootType", (int)IKTarget::Type::RotationAndPosition);
|
||||||
animVars.set("rightFootPosition", foot);
|
animVars.set("rightFootPosition", footPosition);
|
||||||
animVars.set("rightFootRotation", footRotation);
|
animVars.set("rightFootRotation", footRotation);
|
||||||
animVars.set("rightFootType", (int)IKTarget::Type::RotationAndPosition);
|
animVars.set("rightFootType", (int)IKTarget::Type::RotationAndPosition);
|
||||||
}
|
}
|
||||||
|
|
||||||
// call overlay twice: once to verify AnimPoseVec joints and again to do the IK
|
// call overlay twice: once to verify AnimPoseVec joints and again to do the IK
|
||||||
AnimNode::Triggers triggersOut;
|
AnimNode::Triggers triggersOut;
|
||||||
AnimContext context(false, false, false, glm::mat4(), glm::mat4());
|
AnimContext context(false, false, false, _geometryToRigTransform, _rigToGeometryTransform);
|
||||||
float dt = 1.0f; // the value of this does not matter
|
float dt = 1.0f; // the value of this does not matter
|
||||||
ikNode.overlay(animVars, context, dt, triggersOut, _animSkeleton->getRelativeBindPoses());
|
ikNode.overlay(animVars, context, dt, triggersOut, _animSkeleton->getRelativeBindPoses());
|
||||||
AnimPoseVec finalPoses = ikNode.overlay(animVars, context, dt, triggersOut, _animSkeleton->getRelativeBindPoses());
|
AnimPoseVec finalPoses = ikNode.overlay(animVars, context, dt, triggersOut, _animSkeleton->getRelativeBindPoses());
|
||||||
|
@ -1802,34 +1726,13 @@ void Rig::computeAvatarBoundingCapsule(
|
||||||
|
|
||||||
// compute bounding shape parameters
|
// compute bounding shape parameters
|
||||||
// NOTE: we assume that the longest side of totalExtents is the yAxis...
|
// NOTE: we assume that the longest side of totalExtents is the yAxis...
|
||||||
glm::vec3 diagonal = (geometryToRig * totalExtents.maximum) - (geometryToRig * totalExtents.minimum);
|
glm::vec3 diagonal = (transformPoint(_geometryToRigTransform, totalExtents.maximum) -
|
||||||
|
transformPoint(_geometryToRigTransform, totalExtents.minimum));
|
||||||
// ... and assume the radiusOut is half the RMS of the X and Z sides:
|
// ... and assume the radiusOut is half the RMS of the X and Z sides:
|
||||||
radiusOut = 0.5f * sqrtf(0.5f * (diagonal.x * diagonal.x + diagonal.z * diagonal.z));
|
radiusOut = 0.5f * sqrtf(0.5f * (diagonal.x * diagonal.x + diagonal.z * diagonal.z));
|
||||||
heightOut = diagonal.y - 2.0f * radiusOut;
|
heightOut = diagonal.y - 2.0f * radiusOut;
|
||||||
|
|
||||||
glm::vec3 rootPosition = finalPoses[geometry.rootJointIndex].trans();
|
glm::vec3 rootPosition = finalPoses[geometry.rootJointIndex].trans();
|
||||||
glm::vec3 rigCenter = (geometryToRig * (0.5f * (totalExtents.maximum + totalExtents.minimum)));
|
glm::vec3 rigCenter = transformPoint(_geometryToRigTransform, (0.5f * (totalExtents.maximum + totalExtents.minimum)));
|
||||||
localOffsetOut = rigCenter - (geometryToRig * rootPosition);
|
localOffsetOut = rigCenter - transformPoint(_geometryToRigTransform, rootPosition);
|
||||||
}
|
|
||||||
|
|
||||||
bool Rig::transitionHandPose(float deltaTime, float totalDuration, AnimPose& controlledHandPose, bool isLeftHand,
|
|
||||||
bool isToControlled, AnimPose& returnHandPose) {
|
|
||||||
auto ikNode = getAnimInverseKinematicsNode();
|
|
||||||
if (ikNode) {
|
|
||||||
float alpha = 1.0f - deltaTime / totalDuration;
|
|
||||||
const AnimPose geometryToRigTransform(_geometryToRigTransform);
|
|
||||||
AnimPose uncontrolledHandPose;
|
|
||||||
if (isLeftHand) {
|
|
||||||
uncontrolledHandPose = geometryToRigTransform * ikNode->getUncontrolledLeftHandPose();
|
|
||||||
} else {
|
|
||||||
uncontrolledHandPose = geometryToRigTransform * ikNode->getUncontrolledRightHandPose();
|
|
||||||
}
|
|
||||||
if (isToControlled) {
|
|
||||||
::blend(1, &uncontrolledHandPose, &controlledHandPose, alpha, &returnHandPose);
|
|
||||||
} else {
|
|
||||||
::blend(1, &controlledHandPose, &uncontrolledHandPose, alpha, &returnHandPose);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -340,18 +340,6 @@ protected:
|
||||||
int _nextStateHandlerId { 0 };
|
int _nextStateHandlerId { 0 };
|
||||||
QMutex _stateMutex;
|
QMutex _stateMutex;
|
||||||
|
|
||||||
bool transitionHandPose(float deltaTime, float totalDuration, AnimPose& controlledHandPose, bool isLeftHand,
|
|
||||||
bool isToControlled, AnimPose& returnHandPose);
|
|
||||||
|
|
||||||
bool _isLeftHandControlled { false };
|
|
||||||
bool _isRightHandControlled { false };
|
|
||||||
float _leftHandControlTimeRemaining { 0.0f };
|
|
||||||
float _rightHandControlTimeRemaining { 0.0f };
|
|
||||||
float _leftHandRelaxTimeRemaining { 0.0f };
|
|
||||||
float _rightHandRelaxTimeRemaining { 0.0f };
|
|
||||||
AnimPose _lastLeftHandControlledPose;
|
|
||||||
AnimPose _lastRightHandControlledPose;
|
|
||||||
|
|
||||||
glm::vec3 _prevRightFootPoleVector { Vectors::UNIT_Z };
|
glm::vec3 _prevRightFootPoleVector { Vectors::UNIT_Z };
|
||||||
bool _prevRightFootPoleVectorValid { false };
|
bool _prevRightFootPoleVectorValid { false };
|
||||||
|
|
||||||
|
|
|
@ -102,6 +102,8 @@ QSharedPointer<Resource> ResourceCacheSharedItems::getHighestPendingRequest() {
|
||||||
QSharedPointer<Resource> highestResource;
|
QSharedPointer<Resource> highestResource;
|
||||||
Lock lock(_mutex);
|
Lock lock(_mutex);
|
||||||
|
|
||||||
|
bool currentHighestIsFile = false;
|
||||||
|
|
||||||
for (int i = 0; i < _pendingRequests.size();) {
|
for (int i = 0; i < _pendingRequests.size();) {
|
||||||
// Clear any freed resources
|
// Clear any freed resources
|
||||||
auto resource = _pendingRequests.at(i).lock();
|
auto resource = _pendingRequests.at(i).lock();
|
||||||
|
@ -112,10 +114,12 @@ QSharedPointer<Resource> ResourceCacheSharedItems::getHighestPendingRequest() {
|
||||||
|
|
||||||
// Check load priority
|
// Check load priority
|
||||||
float priority = resource->getLoadPriority();
|
float priority = resource->getLoadPriority();
|
||||||
if (priority >= highestPriority) {
|
bool isFile = resource->getURL().scheme() == URL_SCHEME_FILE;
|
||||||
|
if (priority >= highestPriority && (isFile || !currentHighestIsFile)) {
|
||||||
highestPriority = priority;
|
highestPriority = priority;
|
||||||
highestIndex = i;
|
highestIndex = i;
|
||||||
highestResource = resource;
|
highestResource = resource;
|
||||||
|
currentHighestIsFile = isFile;
|
||||||
}
|
}
|
||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
|
|
|
@ -52,6 +52,7 @@ public:
|
||||||
Browsable,
|
Browsable,
|
||||||
Slider,
|
Slider,
|
||||||
Spinner,
|
Spinner,
|
||||||
|
SpinnerSlider,
|
||||||
Checkbox,
|
Checkbox,
|
||||||
Button,
|
Button,
|
||||||
ComboBox,
|
ComboBox,
|
||||||
|
@ -254,6 +255,15 @@ public:
|
||||||
Type getType() override { return Spinner; }
|
Type getType() override { return Spinner; }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class SpinnerSliderPreference : public FloatPreference {
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
SpinnerSliderPreference(const QString& category, const QString& name, Getter getter, Setter setter)
|
||||||
|
: FloatPreference(category, name, getter, setter) { }
|
||||||
|
|
||||||
|
Type getType() override { return SpinnerSlider; }
|
||||||
|
};
|
||||||
|
|
||||||
class IntSpinnerPreference : public IntPreference {
|
class IntSpinnerPreference : public IntPreference {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
|
|
|
@ -227,7 +227,6 @@ void TabletProxy::setToolbarMode(bool toolbarMode) {
|
||||||
// forward qml surface events to interface js
|
// forward qml surface events to interface js
|
||||||
connect(tabletRootWindow, &QmlWindowClass::fromQml, this, &TabletProxy::fromQml);
|
connect(tabletRootWindow, &QmlWindowClass::fromQml, this, &TabletProxy::fromQml);
|
||||||
} else {
|
} else {
|
||||||
_state = State::Home;
|
|
||||||
removeButtonsFromToolbar();
|
removeButtonsFromToolbar();
|
||||||
addButtonsToHomeScreen();
|
addButtonsToHomeScreen();
|
||||||
emit screenChanged(QVariant("Home"), QVariant(TABLET_SOURCE_URL));
|
emit screenChanged(QVariant("Home"), QVariant(TABLET_SOURCE_URL));
|
||||||
|
|
|
@ -370,14 +370,14 @@
|
||||||
// Change the avatar size to bigger.
|
// Change the avatar size to bigger.
|
||||||
function biggerSize() {
|
function biggerSize() {
|
||||||
//print("biggerSize");
|
//print("biggerSize");
|
||||||
logMessage("Increasing avatar size bigger!", null);
|
logMessage("Increasing avatar size", null);
|
||||||
MyAvatar.increaseSize();
|
MyAvatar.increaseSize();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Change the avatar size to smaller.
|
// Change the avatar size to smaller.
|
||||||
function smallerSize() {
|
function smallerSize() {
|
||||||
//print("smallerSize");
|
//print("smallerSize");
|
||||||
logMessage("Decreasing avatar size smaler!", null);
|
logMessage("Decreasing avatar size", null);
|
||||||
MyAvatar.decreaseSize();
|
MyAvatar.decreaseSize();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -470,14 +470,13 @@
|
||||||
|
|
||||||
case '?':
|
case '?':
|
||||||
case 'help':
|
case 'help':
|
||||||
logMessage('Type "/?" or "/help" for help, which is this!', null);
|
logMessage('Type "/?" or "/help" for help', null);
|
||||||
logMessage('Type "/name <name>" to set your chat name, or "/name" to use your display name, or a random name if that is not defined.', null);
|
logMessage('Type "/name <name>" to set your chat name, or "/name" to use your display name. If your display name is not defined, a random name will be used.', null);
|
||||||
logMessage('Type "/shutup" to shut up your overhead chat message.', null);
|
logMessage('Type "/close" to close your overhead chat message.', null);
|
||||||
logMessage('Type "/say <something>" to say something.', null);
|
logMessage('Type "/say <something>" to display a new message.', null);
|
||||||
logMessage('Type "/clear" to clear your cha, nullt log.', null);
|
logMessage('Type "/clear" to clear your chat log.', null);
|
||||||
logMessage('Type "/who" to ask who is h, nullere to chat.', null);
|
logMessage('Type "/who" to ask who is in the chat session.', null);
|
||||||
logMessage('Type "/bigger", "/smaller" or "/normal" to change, null your avatar size.', null);
|
logMessage('Type "/bigger", "/smaller" or "/normal" to change your avatar size.', null);
|
||||||
logMessage('(Sorry, that\'s all there is so far!)', null);
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'name':
|
case 'name':
|
||||||
|
@ -498,9 +497,9 @@
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'shutup':
|
case 'close':
|
||||||
popDownSpeechBubble();
|
popDownSpeechBubble();
|
||||||
logMessage('Overhead chat message shut up.', null);
|
logMessage('Overhead chat message closed.', null);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'say':
|
case 'say':
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
"use strict";
|
|
||||||
|
|
||||||
// edit.js
|
// edit.js
|
||||||
//
|
//
|
||||||
// Created by Brad Hefta-Gaub on 10/2/14.
|
// Created by Brad Hefta-Gaub on 10/2/14.
|
||||||
|
@ -17,6 +15,8 @@
|
||||||
|
|
||||||
(function() { // BEGIN LOCAL_SCOPE
|
(function() { // BEGIN LOCAL_SCOPE
|
||||||
|
|
||||||
|
"use strict";
|
||||||
|
|
||||||
var HIFI_PUBLIC_BUCKET = "http://s3.amazonaws.com/hifi-public/";
|
var HIFI_PUBLIC_BUCKET = "http://s3.amazonaws.com/hifi-public/";
|
||||||
var EDIT_TOGGLE_BUTTON = "com.highfidelity.interface.system.editButton";
|
var EDIT_TOGGLE_BUTTON = "com.highfidelity.interface.system.editButton";
|
||||||
var SYSTEM_TOOLBAR = "com.highfidelity.interface.toolbar.system";
|
var SYSTEM_TOOLBAR = "com.highfidelity.interface.toolbar.system";
|
||||||
|
@ -41,19 +41,19 @@ Script.include([
|
||||||
var selectionDisplay = SelectionDisplay;
|
var selectionDisplay = SelectionDisplay;
|
||||||
var selectionManager = SelectionManager;
|
var selectionManager = SelectionManager;
|
||||||
|
|
||||||
const PARTICLE_SYSTEM_URL = Script.resolvePath("assets/images/icon-particles.svg");
|
var PARTICLE_SYSTEM_URL = Script.resolvePath("assets/images/icon-particles.svg");
|
||||||
const POINT_LIGHT_URL = Script.resolvePath("assets/images/icon-point-light.svg");
|
var POINT_LIGHT_URL = Script.resolvePath("assets/images/icon-point-light.svg");
|
||||||
const SPOT_LIGHT_URL = Script.resolvePath("assets/images/icon-spot-light.svg");
|
var SPOT_LIGHT_URL = Script.resolvePath("assets/images/icon-spot-light.svg");
|
||||||
entityIconOverlayManager = new EntityIconOverlayManager(['Light', 'ParticleEffect'], function(entityID) {
|
entityIconOverlayManager = new EntityIconOverlayManager(['Light', 'ParticleEffect'], function(entityID) {
|
||||||
var properties = Entities.getEntityProperties(entityID, ['type', 'isSpotlight']);
|
var properties = Entities.getEntityProperties(entityID, ['type', 'isSpotlight']);
|
||||||
if (properties.type === 'Light') {
|
if (properties.type === 'Light') {
|
||||||
return {
|
return {
|
||||||
url: properties.isSpotlight ? SPOT_LIGHT_URL : POINT_LIGHT_URL,
|
url: properties.isSpotlight ? SPOT_LIGHT_URL : POINT_LIGHT_URL,
|
||||||
}
|
};
|
||||||
} else {
|
} else {
|
||||||
return {
|
return {
|
||||||
url: PARTICLE_SYSTEM_URL,
|
url: PARTICLE_SYSTEM_URL,
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -94,7 +94,7 @@ selectionManager.addEventListener(function () {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const KEY_P = 80; //Key code for letter p used for Parenting hotkey.
|
var KEY_P = 80; //Key code for letter p used for Parenting hotkey.
|
||||||
var DEGREES_TO_RADIANS = Math.PI / 180.0;
|
var DEGREES_TO_RADIANS = Math.PI / 180.0;
|
||||||
var RADIANS_TO_DEGREES = 180.0 / Math.PI;
|
var RADIANS_TO_DEGREES = 180.0 / Math.PI;
|
||||||
|
|
||||||
|
@ -123,6 +123,8 @@ var SETTING_EASE_ON_FOCUS = "cameraEaseOnFocus";
|
||||||
var SETTING_SHOW_LIGHTS_AND_PARTICLES_IN_EDIT_MODE = "showLightsAndParticlesInEditMode";
|
var SETTING_SHOW_LIGHTS_AND_PARTICLES_IN_EDIT_MODE = "showLightsAndParticlesInEditMode";
|
||||||
var SETTING_SHOW_ZONES_IN_EDIT_MODE = "showZonesInEditMode";
|
var SETTING_SHOW_ZONES_IN_EDIT_MODE = "showZonesInEditMode";
|
||||||
|
|
||||||
|
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.
|
// marketplace info, etc. not quite ready yet.
|
||||||
var SHOULD_SHOW_PROPERTY_MENU = false;
|
var SHOULD_SHOW_PROPERTY_MENU = false;
|
||||||
|
@ -130,6 +132,7 @@ var INSUFFICIENT_PERMISSIONS_ERROR_MSG = "You do not have the necessary permissi
|
||||||
var INSUFFICIENT_PERMISSIONS_IMPORT_ERROR_MSG = "You do not have the necessary permissions to place items 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 isActive = false;
|
||||||
|
var createButton = null;
|
||||||
|
|
||||||
var IMPORTING_SVO_OVERLAY_WIDTH = 144;
|
var IMPORTING_SVO_OVERLAY_WIDTH = 144;
|
||||||
var IMPORTING_SVO_OVERLAY_HEIGHT = 30;
|
var IMPORTING_SVO_OVERLAY_HEIGHT = 30;
|
||||||
|
@ -397,13 +400,15 @@ var toolBar = (function () {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
var createButtonIconRsrc = ((Entities.canRez() || Entities.canRezTmp()) ? CREATE_ENABLED_ICON : CREATE_DISABLED_ICON);
|
||||||
tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system");
|
tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system");
|
||||||
activeButton = tablet.addButton({
|
activeButton = tablet.addButton({
|
||||||
icon: "icons/tablet-icons/edit-i.svg",
|
icon: createButtonIconRsrc,
|
||||||
activeIcon: "icons/tablet-icons/edit-a.svg",
|
activeIcon: "icons/tablet-icons/edit-a.svg",
|
||||||
text: "CREATE",
|
text: "CREATE",
|
||||||
sortOrder: 10
|
sortOrder: 10
|
||||||
});
|
});
|
||||||
|
createButton = activeButton;
|
||||||
tablet.screenChanged.connect(function (type, url) {
|
tablet.screenChanged.connect(function (type, url) {
|
||||||
if (isActive && (type !== "QML" || url !== "Edit.qml")) {
|
if (isActive && (type !== "QML" || url !== "Edit.qml")) {
|
||||||
that.toggle();
|
that.toggle();
|
||||||
|
@ -411,7 +416,12 @@ var toolBar = (function () {
|
||||||
});
|
});
|
||||||
tablet.fromQml.connect(fromQml);
|
tablet.fromQml.connect(fromQml);
|
||||||
|
|
||||||
activeButton.clicked.connect(function() {
|
createButton.clicked.connect(function() {
|
||||||
|
if ( ! (Entities.canRez() || Entities.canRezTmp()) ) {
|
||||||
|
Window.notifyEditError(INSUFFICIENT_PERMISSIONS_ERROR_MSG);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
that.toggle();
|
that.toggle();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -642,7 +652,7 @@ var toolBar = (function () {
|
||||||
grid.setEnabled(true);
|
grid.setEnabled(true);
|
||||||
propertiesTool.setVisible(true);
|
propertiesTool.setVisible(true);
|
||||||
selectionDisplay.triggerMapping.enable();
|
selectionDisplay.triggerMapping.enable();
|
||||||
print("starting tablet in landscape mode")
|
print("starting tablet in landscape mode");
|
||||||
tablet.landscape = true;
|
tablet.landscape = true;
|
||||||
// Not sure what the following was meant to accomplish, but it currently causes
|
// 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
|
// everybody else to think that Interface has lost focus overall. fogbugzid:558
|
||||||
|
@ -760,8 +770,38 @@ function handleOverlaySelectionToolUpdates(channel, message, sender) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Handles any edit mode updates required when domains have switched
|
||||||
|
function handleDomainChange() {
|
||||||
|
if ( (createButton === null) || (createButton === undefined) ){
|
||||||
|
//--EARLY EXIT--( nothing to safely update )
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var hasRezPermissions = (Entities.canRez() || Entities.canRezTmp());
|
||||||
|
createButton.editProperties({
|
||||||
|
icon: (hasRezPermissions ? CREATE_ENABLED_ICON : CREATE_DISABLED_ICON),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleMessagesReceived(channel, message, sender) {
|
||||||
|
switch( channel ){
|
||||||
|
case 'entityToolUpdates': {
|
||||||
|
handleOverlaySelectionToolUpdates( channel, message, sender );
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'Toolbar-DomainChanged': {
|
||||||
|
handleDomainChange();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Messages.subscribe('Toolbar-DomainChanged');
|
||||||
Messages.subscribe("entityToolUpdates");
|
Messages.subscribe("entityToolUpdates");
|
||||||
Messages.messageReceived.connect(handleOverlaySelectionToolUpdates);
|
Messages.messageReceived.connect(handleMessagesReceived);
|
||||||
|
|
||||||
var mouseHasMovedSincePress = false;
|
var mouseHasMovedSincePress = false;
|
||||||
var mousePressStartTime = 0;
|
var mousePressStartTime = 0;
|
||||||
|
@ -1180,6 +1220,8 @@ Script.scriptEnding.connect(function () {
|
||||||
|
|
||||||
Messages.messageReceived.disconnect(handleOverlaySelectionToolUpdates);
|
Messages.messageReceived.disconnect(handleOverlaySelectionToolUpdates);
|
||||||
Messages.unsubscribe("entityToolUpdates");
|
Messages.unsubscribe("entityToolUpdates");
|
||||||
|
Messages.unsubscribe("Toolbar-DomainChanged");
|
||||||
|
createButton = null;
|
||||||
});
|
});
|
||||||
|
|
||||||
var lastOrientation = null;
|
var lastOrientation = null;
|
||||||
|
@ -1305,7 +1347,7 @@ function unparentSelectedEntities() {
|
||||||
if (parentId !== null && parentId.length > 0 && parentId !== "{00000000-0000-0000-0000-000000000000}") {
|
if (parentId !== null && parentId.length > 0 && parentId !== "{00000000-0000-0000-0000-000000000000}") {
|
||||||
parentCheck = true;
|
parentCheck = true;
|
||||||
}
|
}
|
||||||
Entities.editEntity(id, {parentID: null})
|
Entities.editEntity(id, {parentID: null});
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
if (parentCheck) {
|
if (parentCheck) {
|
||||||
|
@ -1340,7 +1382,7 @@ function parentSelectedEntities() {
|
||||||
if (parentId !== lastEntityId) {
|
if (parentId !== lastEntityId) {
|
||||||
parentCheck = true;
|
parentCheck = true;
|
||||||
}
|
}
|
||||||
Entities.editEntity(id, {parentID: lastEntityId})
|
Entities.editEntity(id, {parentID: lastEntityId});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -1513,9 +1555,9 @@ function importSVO(importURL) {
|
||||||
var entityPositions = [];
|
var entityPositions = [];
|
||||||
var entityParentIDs = [];
|
var entityParentIDs = [];
|
||||||
|
|
||||||
var properties = Entities.getEntityProperties(pastedEntityIDs[0], ["type"]);
|
var propType = Entities.getEntityProperties(pastedEntityIDs[0], ["type"]).type;
|
||||||
var NO_ADJUST_ENTITY_TYPES = ["Zone", "Light", "ParticleEffect"];
|
var NO_ADJUST_ENTITY_TYPES = ["Zone", "Light", "ParticleEffect"];
|
||||||
if (NO_ADJUST_ENTITY_TYPES.indexOf(properties.type) === -1) {
|
if (NO_ADJUST_ENTITY_TYPES.indexOf(propType) === -1) {
|
||||||
var targetDirection;
|
var targetDirection;
|
||||||
if (Camera.mode === "entity" || Camera.mode === "independent") {
|
if (Camera.mode === "entity" || Camera.mode === "independent") {
|
||||||
targetDirection = Camera.orientation;
|
targetDirection = Camera.orientation;
|
||||||
|
@ -1528,36 +1570,36 @@ function importSVO(importURL) {
|
||||||
var deltaParallel = HALF_TREE_SCALE; // Distance to move entities parallel to targetDirection.
|
var deltaParallel = HALF_TREE_SCALE; // Distance to move entities parallel to targetDirection.
|
||||||
var deltaPerpendicular = Vec3.ZERO; // Distance to move entities perpendicular to targetDirection.
|
var deltaPerpendicular = Vec3.ZERO; // Distance to move entities perpendicular to targetDirection.
|
||||||
for (var i = 0, length = pastedEntityIDs.length; i < length; i++) {
|
for (var i = 0, length = pastedEntityIDs.length; i < length; i++) {
|
||||||
var properties = Entities.getEntityProperties(pastedEntityIDs[i], ["position", "dimensions",
|
var curLoopEntityProps = Entities.getEntityProperties(pastedEntityIDs[i], ["position", "dimensions",
|
||||||
"registrationPoint", "rotation", "parentID"]);
|
"registrationPoint", "rotation", "parentID"]);
|
||||||
var adjustedPosition = adjustPositionPerBoundingBox(targetPosition, targetDirection,
|
var adjustedPosition = adjustPositionPerBoundingBox(targetPosition, targetDirection,
|
||||||
properties.registrationPoint, properties.dimensions, properties.rotation);
|
curLoopEntityProps.registrationPoint, curLoopEntityProps.dimensions, curLoopEntityProps.rotation);
|
||||||
var delta = Vec3.subtract(adjustedPosition, properties.position);
|
var delta = Vec3.subtract(adjustedPosition, curLoopEntityProps.position);
|
||||||
var distance = Vec3.dot(delta, targetDirection);
|
var distance = Vec3.dot(delta, targetDirection);
|
||||||
deltaParallel = Math.min(distance, deltaParallel);
|
deltaParallel = Math.min(distance, deltaParallel);
|
||||||
deltaPerpendicular = Vec3.sum(Vec3.subtract(delta, Vec3.multiply(distance, targetDirection)),
|
deltaPerpendicular = Vec3.sum(Vec3.subtract(delta, Vec3.multiply(distance, targetDirection)),
|
||||||
deltaPerpendicular);
|
deltaPerpendicular);
|
||||||
entityPositions[i] = properties.position;
|
entityPositions[i] = curLoopEntityProps.position;
|
||||||
entityParentIDs[i] = properties.parentID;
|
entityParentIDs[i] = curLoopEntityProps.parentID;
|
||||||
}
|
}
|
||||||
deltaPerpendicular = Vec3.multiply(1 / pastedEntityIDs.length, deltaPerpendicular);
|
deltaPerpendicular = Vec3.multiply(1 / pastedEntityIDs.length, deltaPerpendicular);
|
||||||
deltaPosition = Vec3.sum(Vec3.multiply(deltaParallel, targetDirection), deltaPerpendicular);
|
deltaPosition = Vec3.sum(Vec3.multiply(deltaParallel, targetDirection), deltaPerpendicular);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (grid.getSnapToGrid()) {
|
if (grid.getSnapToGrid()) {
|
||||||
var properties = Entities.getEntityProperties(pastedEntityIDs[0], ["position", "dimensions",
|
var firstEntityProps = Entities.getEntityProperties(pastedEntityIDs[0], ["position", "dimensions",
|
||||||
"registrationPoint"]);
|
"registrationPoint"]);
|
||||||
var position = Vec3.sum(deltaPosition, properties.position);
|
var positionPreSnap = Vec3.sum(deltaPosition, firstEntityProps.position);
|
||||||
position = grid.snapToSurface(grid.snapToGrid(position, false, properties.dimensions,
|
position = grid.snapToSurface(grid.snapToGrid(positionPreSnap, false, firstEntityProps.dimensions,
|
||||||
properties.registrationPoint), properties.dimensions, properties.registrationPoint);
|
firstEntityProps.registrationPoint), firstEntityProps.dimensions, firstEntityProps.registrationPoint);
|
||||||
deltaPosition = Vec3.subtract(position, properties.position);
|
deltaPosition = Vec3.subtract(position, firstEntityProps.position);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!Vec3.equal(deltaPosition, Vec3.ZERO)) {
|
if (!Vec3.equal(deltaPosition, Vec3.ZERO)) {
|
||||||
for (var i = 0, length = pastedEntityIDs.length; i < length; i++) {
|
for (var editEntityIndex = 0, numEntities = pastedEntityIDs.length; editEntityIndex < numEntities; editEntityIndex++) {
|
||||||
if (Uuid.isNull(entityParentIDs[i])) {
|
if (Uuid.isNull(entityParentIDs[editEntityIndex])) {
|
||||||
Entities.editEntity(pastedEntityIDs[i], {
|
Entities.editEntity(pastedEntityIDs[editEntityIndex], {
|
||||||
position: Vec3.sum(deltaPosition, entityPositions[i])
|
position: Vec3.sum(deltaPosition, entityPositions[editEntityIndex])
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2203,7 +2245,7 @@ entityListTool.webView.webEventReceived.connect(function (data) {
|
||||||
try {
|
try {
|
||||||
data = JSON.parse(data);
|
data = JSON.parse(data);
|
||||||
} catch(e) {
|
} catch(e) {
|
||||||
print("edit.js: Error parsing JSON: " + e.name + " data " + data)
|
print("edit.js: Error parsing JSON: " + e.name + " data " + data);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue