mirror of
https://github.com/overte-org/overte.git
synced 2025-08-09 17:49:27 +02:00
Merge branch 'master' of https://github.com/highfidelity/hifi into exportToTestRail
This commit is contained in:
commit
64f651a266
76 changed files with 4250 additions and 1660 deletions
|
@ -19,6 +19,7 @@
|
||||||
#include <QtNetwork/QNetworkReply>
|
#include <QtNetwork/QNetworkReply>
|
||||||
#include <QThread>
|
#include <QThread>
|
||||||
|
|
||||||
|
#include <AnimationCacheScriptingInterface.h>
|
||||||
#include <AssetClient.h>
|
#include <AssetClient.h>
|
||||||
#include <AvatarHashMap.h>
|
#include <AvatarHashMap.h>
|
||||||
#include <AudioInjectorManager.h>
|
#include <AudioInjectorManager.h>
|
||||||
|
@ -32,6 +33,7 @@
|
||||||
#include <ResourceCache.h>
|
#include <ResourceCache.h>
|
||||||
#include <ScriptCache.h>
|
#include <ScriptCache.h>
|
||||||
#include <ScriptEngines.h>
|
#include <ScriptEngines.h>
|
||||||
|
#include <SoundCacheScriptingInterface.h>
|
||||||
#include <SoundCache.h>
|
#include <SoundCache.h>
|
||||||
#include <UsersScriptingInterface.h>
|
#include <UsersScriptingInterface.h>
|
||||||
#include <UUID.h>
|
#include <UUID.h>
|
||||||
|
@ -71,6 +73,7 @@ Agent::Agent(ReceivedMessage& message) :
|
||||||
|
|
||||||
DependencyManager::set<ResourceCacheSharedItems>();
|
DependencyManager::set<ResourceCacheSharedItems>();
|
||||||
DependencyManager::set<SoundCache>();
|
DependencyManager::set<SoundCache>();
|
||||||
|
DependencyManager::set<SoundCacheScriptingInterface>();
|
||||||
DependencyManager::set<AudioScriptingInterface>();
|
DependencyManager::set<AudioScriptingInterface>();
|
||||||
DependencyManager::set<AudioInjectorManager>();
|
DependencyManager::set<AudioInjectorManager>();
|
||||||
|
|
||||||
|
@ -453,8 +456,8 @@ void Agent::executeScript() {
|
||||||
// register ourselves to the script engine
|
// register ourselves to the script engine
|
||||||
_scriptEngine->registerGlobalObject("Agent", new AgentScriptingInterface(this));
|
_scriptEngine->registerGlobalObject("Agent", new AgentScriptingInterface(this));
|
||||||
|
|
||||||
_scriptEngine->registerGlobalObject("SoundCache", DependencyManager::get<SoundCache>().data());
|
_scriptEngine->registerGlobalObject("AnimationCache", DependencyManager::get<AnimationCacheScriptingInterface>().data());
|
||||||
_scriptEngine->registerGlobalObject("AnimationCache", DependencyManager::get<AnimationCache>().data());
|
_scriptEngine->registerGlobalObject("SoundCache", DependencyManager::get<SoundCacheScriptingInterface>().data());
|
||||||
|
|
||||||
QScriptValue webSocketServerConstructorValue = _scriptEngine->newFunction(WebSocketServerClass::constructor);
|
QScriptValue webSocketServerConstructorValue = _scriptEngine->newFunction(WebSocketServerClass::constructor);
|
||||||
_scriptEngine->globalObject().setProperty("WebSocketServer", webSocketServerConstructorValue);
|
_scriptEngine->globalObject().setProperty("WebSocketServer", webSocketServerConstructorValue);
|
||||||
|
@ -843,6 +846,7 @@ void Agent::aboutToFinish() {
|
||||||
DependencyManager::destroy<ScriptEngines>();
|
DependencyManager::destroy<ScriptEngines>();
|
||||||
|
|
||||||
DependencyManager::destroy<ResourceCacheSharedItems>();
|
DependencyManager::destroy<ResourceCacheSharedItems>();
|
||||||
|
DependencyManager::destroy<SoundCacheScriptingInterface>();
|
||||||
DependencyManager::destroy<SoundCache>();
|
DependencyManager::destroy<SoundCache>();
|
||||||
DependencyManager::destroy<AudioScriptingInterface>();
|
DependencyManager::destroy<AudioScriptingInterface>();
|
||||||
|
|
||||||
|
|
|
@ -21,6 +21,7 @@
|
||||||
#include <shared/QtHelpers.h>
|
#include <shared/QtHelpers.h>
|
||||||
#include <AccountManager.h>
|
#include <AccountManager.h>
|
||||||
#include <AddressManager.h>
|
#include <AddressManager.h>
|
||||||
|
#include <AnimationCacheScriptingInterface.h>
|
||||||
#include <Assignment.h>
|
#include <Assignment.h>
|
||||||
#include <AvatarHashMap.h>
|
#include <AvatarHashMap.h>
|
||||||
#include <EntityScriptingInterface.h>
|
#include <EntityScriptingInterface.h>
|
||||||
|
@ -63,6 +64,7 @@ AssignmentClient::AssignmentClient(Assignment::Type requestAssignmentType, QStri
|
||||||
auto nodeList = DependencyManager::set<NodeList>(NodeType::Unassigned, listenPort);
|
auto nodeList = DependencyManager::set<NodeList>(NodeType::Unassigned, listenPort);
|
||||||
|
|
||||||
auto animationCache = DependencyManager::set<AnimationCache>();
|
auto animationCache = DependencyManager::set<AnimationCache>();
|
||||||
|
DependencyManager::set<AnimationCacheScriptingInterface>();
|
||||||
auto entityScriptingInterface = DependencyManager::set<EntityScriptingInterface>(false);
|
auto entityScriptingInterface = DependencyManager::set<EntityScriptingInterface>(false);
|
||||||
|
|
||||||
DependencyManager::registerInheritance<EntityDynamicFactoryInterface, AssignmentDynamicFactory>();
|
DependencyManager::registerInheritance<EntityDynamicFactoryInterface, AssignmentDynamicFactory>();
|
||||||
|
|
|
@ -26,7 +26,7 @@
|
||||||
#include <ResourceManager.h>
|
#include <ResourceManager.h>
|
||||||
#include <ScriptCache.h>
|
#include <ScriptCache.h>
|
||||||
#include <ScriptEngines.h>
|
#include <ScriptEngines.h>
|
||||||
#include <SoundCache.h>
|
#include <SoundCacheScriptingInterface.h>
|
||||||
#include <UUID.h>
|
#include <UUID.h>
|
||||||
#include <WebSocketServerClass.h>
|
#include <WebSocketServerClass.h>
|
||||||
|
|
||||||
|
@ -66,6 +66,7 @@ EntityScriptServer::EntityScriptServer(ReceivedMessage& message) : ThreadedAssig
|
||||||
|
|
||||||
DependencyManager::set<ResourceCacheSharedItems>();
|
DependencyManager::set<ResourceCacheSharedItems>();
|
||||||
DependencyManager::set<SoundCache>();
|
DependencyManager::set<SoundCache>();
|
||||||
|
DependencyManager::set<SoundCacheScriptingInterface>();
|
||||||
DependencyManager::set<AudioInjectorManager>();
|
DependencyManager::set<AudioInjectorManager>();
|
||||||
|
|
||||||
DependencyManager::set<ScriptCache>();
|
DependencyManager::set<ScriptCache>();
|
||||||
|
@ -438,7 +439,7 @@ void EntityScriptServer::resetEntitiesScriptEngine() {
|
||||||
auto webSocketServerConstructorValue = newEngine->newFunction(WebSocketServerClass::constructor);
|
auto webSocketServerConstructorValue = newEngine->newFunction(WebSocketServerClass::constructor);
|
||||||
newEngine->globalObject().setProperty("WebSocketServer", webSocketServerConstructorValue);
|
newEngine->globalObject().setProperty("WebSocketServer", webSocketServerConstructorValue);
|
||||||
|
|
||||||
newEngine->registerGlobalObject("SoundCache", DependencyManager::get<SoundCache>().data());
|
newEngine->registerGlobalObject("SoundCache", DependencyManager::get<SoundCacheScriptingInterface>().data());
|
||||||
|
|
||||||
// connect this script engines printedMessage signal to the global ScriptEngines these various messages
|
// connect this script engines printedMessage signal to the global ScriptEngines these various messages
|
||||||
auto scriptEngines = DependencyManager::get<ScriptEngines>().data();
|
auto scriptEngines = DependencyManager::get<ScriptEngines>().data();
|
||||||
|
|
|
@ -4,8 +4,8 @@ set(EXTERNAL_NAME serverless-content)
|
||||||
|
|
||||||
ExternalProject_Add(
|
ExternalProject_Add(
|
||||||
${EXTERNAL_NAME}
|
${EXTERNAL_NAME}
|
||||||
URL http://cdn.highfidelity.com/content-sets/serverless-tutorial-RC69.zip
|
URL http://cdn.highfidelity.com/content-sets/serverless-tutorial-RC70v2.zip
|
||||||
URL_MD5 e2467b08de069da7e22ec8e032435592
|
URL_MD5 35fcc8e635e71d0b00a08455a2582448
|
||||||
CONFIGURE_COMMAND ""
|
CONFIGURE_COMMAND ""
|
||||||
BUILD_COMMAND ""
|
BUILD_COMMAND ""
|
||||||
INSTALL_COMMAND ""
|
INSTALL_COMMAND ""
|
||||||
|
|
File diff suppressed because it is too large
Load diff
1228
interface/resources/avatar/old-avatar-animation.json
Normal file
1228
interface/resources/avatar/old-avatar-animation.json
Normal file
File diff suppressed because it is too large
Load diff
|
@ -20,6 +20,7 @@ Original.Button {
|
||||||
property int color: 0
|
property int color: 0
|
||||||
property int colorScheme: hifi.colorSchemes.light
|
property int colorScheme: hifi.colorSchemes.light
|
||||||
property int fontSize: hifi.fontSizes.buttonLabel
|
property int fontSize: hifi.fontSizes.buttonLabel
|
||||||
|
property int radius: hifi.buttons.radius
|
||||||
property alias implicitTextWidth: buttonText.implicitWidth
|
property alias implicitTextWidth: buttonText.implicitWidth
|
||||||
property string buttonGlyph: "";
|
property string buttonGlyph: "";
|
||||||
property int fontCapitalization: Font.AllUppercase
|
property int fontCapitalization: Font.AllUppercase
|
||||||
|
@ -46,7 +47,7 @@ Original.Button {
|
||||||
}
|
}
|
||||||
|
|
||||||
background: Rectangle {
|
background: Rectangle {
|
||||||
radius: hifi.buttons.radius
|
radius: control.radius
|
||||||
|
|
||||||
border.width: (control.color === hifi.buttons.none ||
|
border.width: (control.color === hifi.buttons.none ||
|
||||||
(control.color === hifi.buttons.noneBorderless && control.hovered) ||
|
(control.color === hifi.buttons.noneBorderless && control.hovered) ||
|
||||||
|
|
|
@ -124,6 +124,11 @@ SpinBox {
|
||||||
color: spinBox.up.pressed || spinBox.up.hovered ? (isLightColorScheme ? hifi.colors.black : hifi.colors.white) : hifi.colors.gray
|
color: spinBox.up.pressed || spinBox.up.hovered ? (isLightColorScheme ? hifi.colors.black : hifi.colors.white) : hifi.colors.gray
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
up.onPressedChanged: {
|
||||||
|
if(value) {
|
||||||
|
spinBox.forceActiveFocus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
down.indicator: Item {
|
down.indicator: Item {
|
||||||
x: spinBox.width - implicitWidth - 5
|
x: spinBox.width - implicitWidth - 5
|
||||||
|
@ -138,6 +143,11 @@ SpinBox {
|
||||||
color: spinBox.down.pressed || spinBox.down.hovered ? (isLightColorScheme ? hifi.colors.black : hifi.colors.white) : hifi.colors.gray
|
color: spinBox.down.pressed || spinBox.down.hovered ? (isLightColorScheme ? hifi.colors.black : hifi.colors.white) : hifi.colors.gray
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
down.onPressedChanged: {
|
||||||
|
if(value) {
|
||||||
|
spinBox.forceActiveFocus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
HifiControls.Label {
|
HifiControls.Label {
|
||||||
id: spinBoxLabel
|
id: spinBoxLabel
|
||||||
|
|
|
@ -476,17 +476,13 @@ Rectangle {
|
||||||
anchors.verticalCenter: avatarNameLabel.verticalCenter
|
anchors.verticalCenter: avatarNameLabel.verticalCenter
|
||||||
glyphText: "."
|
glyphText: "."
|
||||||
glyphSize: 22
|
glyphSize: 22
|
||||||
|
onClicked: {
|
||||||
MouseArea {
|
popup.showSpecifyAvatarUrl(currentAvatar.avatarUrl, function() {
|
||||||
anchors.fill: parent
|
var url = popup.inputText.text;
|
||||||
onClicked: {
|
emitSendToScript({'method' : 'applyExternalAvatar', 'avatarURL' : url})
|
||||||
popup.showSpecifyAvatarUrl(currentAvatar.avatarUrl, function() {
|
}, function(link) {
|
||||||
var url = popup.inputText.text;
|
Qt.openUrlExternally(link);
|
||||||
emitSendToScript({'method' : 'applyExternalAvatar', 'avatarURL' : url})
|
});
|
||||||
}, function(link) {
|
|
||||||
Qt.openUrlExternally(link);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -496,12 +492,8 @@ Rectangle {
|
||||||
glyphText: "\ue02e"
|
glyphText: "\ue02e"
|
||||||
|
|
||||||
visible: avatarWearablesCount !== 0
|
visible: avatarWearablesCount !== 0
|
||||||
|
onClicked: {
|
||||||
MouseArea {
|
adjustWearables.open(currentAvatar);
|
||||||
anchors.fill: parent
|
|
||||||
onClicked: {
|
|
||||||
adjustWearables.open(currentAvatar);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -326,7 +326,7 @@ Rectangle {
|
||||||
height: 40
|
height: 40
|
||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
color: hifi.buttons.red;
|
color: hifi.buttons.red;
|
||||||
colorScheme: hifi.colorSchemes.dark;
|
colorScheme: hifi.colorSchemes.light;
|
||||||
text: "TAKE IT OFF"
|
text: "TAKE IT OFF"
|
||||||
onClicked: wearableDeleted(root.avatarName, getCurrentWearable().id);
|
onClicked: wearableDeleted(root.avatarName, getCurrentWearable().id);
|
||||||
enabled: wearablesCombobox.model.count !== 0
|
enabled: wearablesCombobox.model.count !== 0
|
||||||
|
|
|
@ -1,25 +1,42 @@
|
||||||
import "../../styles-uit"
|
import "../../styles-uit"
|
||||||
|
import "../../controls-uit" as HifiControlsUit
|
||||||
import QtQuick 2.9
|
import QtQuick 2.9
|
||||||
import QtGraphicalEffects 1.0
|
import QtGraphicalEffects 1.0
|
||||||
|
|
||||||
ShadowRectangle {
|
Item {
|
||||||
|
id: root
|
||||||
width: 44
|
width: 44
|
||||||
height: 28
|
height: 28
|
||||||
AvatarAppStyle {
|
signal clicked();
|
||||||
id: style
|
|
||||||
|
HifiControlsUit.Button {
|
||||||
|
id: button
|
||||||
|
|
||||||
|
HifiConstants {
|
||||||
|
id: hifi
|
||||||
|
}
|
||||||
|
|
||||||
|
anchors.fill: parent
|
||||||
|
color: hifi.buttons.blue;
|
||||||
|
colorScheme: hifi.colorSchemes.light;
|
||||||
|
radius: 3
|
||||||
|
onClicked: root.clicked();
|
||||||
}
|
}
|
||||||
|
|
||||||
gradient: Gradient {
|
DropShadow {
|
||||||
GradientStop { position: 0.0; color: style.colors.blueHighlight }
|
id: shadow
|
||||||
GradientStop { position: 1.0; color: style.colors.blueAccent }
|
anchors.fill: button
|
||||||
|
radius: 6
|
||||||
|
horizontalOffset: 0
|
||||||
|
verticalOffset: 3
|
||||||
|
color: Qt.rgba(0, 0, 0, 0.25)
|
||||||
|
source: button
|
||||||
}
|
}
|
||||||
|
|
||||||
property alias glyphText: glyph.text
|
property alias glyphText: glyph.text
|
||||||
property alias glyphRotation: glyph.rotation
|
property alias glyphRotation: glyph.rotation
|
||||||
property alias glyphSize: glyph.size
|
property alias glyphSize: glyph.size
|
||||||
|
|
||||||
radius: 3
|
|
||||||
|
|
||||||
HiFiGlyphs {
|
HiFiGlyphs {
|
||||||
id: glyph
|
id: glyph
|
||||||
color: 'white'
|
color: 'white'
|
||||||
|
|
|
@ -63,6 +63,7 @@
|
||||||
#include <AddressManager.h>
|
#include <AddressManager.h>
|
||||||
#include <AnimDebugDraw.h>
|
#include <AnimDebugDraw.h>
|
||||||
#include <BuildInfo.h>
|
#include <BuildInfo.h>
|
||||||
|
#include <AnimationCacheScriptingInterface.h>
|
||||||
#include <AssetClient.h>
|
#include <AssetClient.h>
|
||||||
#include <AssetUpload.h>
|
#include <AssetUpload.h>
|
||||||
#include <AutoUpdater.h>
|
#include <AutoUpdater.h>
|
||||||
|
@ -98,6 +99,8 @@
|
||||||
#include <MainWindow.h>
|
#include <MainWindow.h>
|
||||||
#include <MappingRequest.h>
|
#include <MappingRequest.h>
|
||||||
#include <MessagesClient.h>
|
#include <MessagesClient.h>
|
||||||
|
#include <model-networking/ModelCacheScriptingInterface.h>
|
||||||
|
#include <model-networking/TextureCacheScriptingInterface.h>
|
||||||
#include <ModelEntityItem.h>
|
#include <ModelEntityItem.h>
|
||||||
#include <NetworkAccessManager.h>
|
#include <NetworkAccessManager.h>
|
||||||
#include <NetworkingConstants.h>
|
#include <NetworkingConstants.h>
|
||||||
|
@ -127,7 +130,7 @@
|
||||||
#include <ScriptEngines.h>
|
#include <ScriptEngines.h>
|
||||||
#include <ScriptCache.h>
|
#include <ScriptCache.h>
|
||||||
#include <ShapeEntityItem.h>
|
#include <ShapeEntityItem.h>
|
||||||
#include <SoundCache.h>
|
#include <SoundCacheScriptingInterface.h>
|
||||||
#include <ui/TabletScriptingInterface.h>
|
#include <ui/TabletScriptingInterface.h>
|
||||||
#include <ui/ToolbarScriptingInterface.h>
|
#include <ui/ToolbarScriptingInterface.h>
|
||||||
#include <InteractiveWindow.h>
|
#include <InteractiveWindow.h>
|
||||||
|
@ -143,6 +146,7 @@
|
||||||
#include <QmlFragmentClass.h>
|
#include <QmlFragmentClass.h>
|
||||||
#include <Preferences.h>
|
#include <Preferences.h>
|
||||||
#include <display-plugins/CompositorHelper.h>
|
#include <display-plugins/CompositorHelper.h>
|
||||||
|
#include <display-plugins/hmd/HmdDisplayPlugin.h>
|
||||||
#include <trackers/EyeTracker.h>
|
#include <trackers/EyeTracker.h>
|
||||||
#include <avatars-renderer/ScriptAvatar.h>
|
#include <avatars-renderer/ScriptAvatar.h>
|
||||||
#include <RenderableEntityItem.h>
|
#include <RenderableEntityItem.h>
|
||||||
|
@ -866,16 +870,20 @@ bool setupEssentials(int& argc, char** argv, bool runningMarkerExisted) {
|
||||||
DependencyManager::set<recording::ClipCache>();
|
DependencyManager::set<recording::ClipCache>();
|
||||||
DependencyManager::set<GeometryCache>();
|
DependencyManager::set<GeometryCache>();
|
||||||
DependencyManager::set<ModelCache>();
|
DependencyManager::set<ModelCache>();
|
||||||
|
DependencyManager::set<ModelCacheScriptingInterface>();
|
||||||
DependencyManager::set<ScriptCache>();
|
DependencyManager::set<ScriptCache>();
|
||||||
DependencyManager::set<SoundCache>();
|
DependencyManager::set<SoundCache>();
|
||||||
|
DependencyManager::set<SoundCacheScriptingInterface>();
|
||||||
DependencyManager::set<DdeFaceTracker>();
|
DependencyManager::set<DdeFaceTracker>();
|
||||||
DependencyManager::set<EyeTracker>();
|
DependencyManager::set<EyeTracker>();
|
||||||
DependencyManager::set<AudioClient>();
|
DependencyManager::set<AudioClient>();
|
||||||
DependencyManager::set<AudioScope>();
|
DependencyManager::set<AudioScope>();
|
||||||
DependencyManager::set<DeferredLightingEffect>();
|
DependencyManager::set<DeferredLightingEffect>();
|
||||||
DependencyManager::set<TextureCache>();
|
DependencyManager::set<TextureCache>();
|
||||||
|
DependencyManager::set<TextureCacheScriptingInterface>();
|
||||||
DependencyManager::set<FramebufferCache>();
|
DependencyManager::set<FramebufferCache>();
|
||||||
DependencyManager::set<AnimationCache>();
|
DependencyManager::set<AnimationCache>();
|
||||||
|
DependencyManager::set<AnimationCacheScriptingInterface>();
|
||||||
DependencyManager::set<ModelBlender>();
|
DependencyManager::set<ModelBlender>();
|
||||||
DependencyManager::set<UsersScriptingInterface>();
|
DependencyManager::set<UsersScriptingInterface>();
|
||||||
DependencyManager::set<AvatarManager>();
|
DependencyManager::set<AvatarManager>();
|
||||||
|
@ -2562,12 +2570,18 @@ Application::~Application() {
|
||||||
|
|
||||||
DependencyManager::destroy<CompositorHelper>(); // must be destroyed before the FramebufferCache
|
DependencyManager::destroy<CompositorHelper>(); // must be destroyed before the FramebufferCache
|
||||||
|
|
||||||
|
DependencyManager::destroy<SoundCacheScriptingInterface>();
|
||||||
|
|
||||||
DependencyManager::destroy<AvatarManager>();
|
DependencyManager::destroy<AvatarManager>();
|
||||||
|
DependencyManager::destroy<AnimationCacheScriptingInterface>();
|
||||||
DependencyManager::destroy<AnimationCache>();
|
DependencyManager::destroy<AnimationCache>();
|
||||||
DependencyManager::destroy<FramebufferCache>();
|
DependencyManager::destroy<FramebufferCache>();
|
||||||
|
DependencyManager::destroy<TextureCacheScriptingInterface>();
|
||||||
DependencyManager::destroy<TextureCache>();
|
DependencyManager::destroy<TextureCache>();
|
||||||
|
DependencyManager::destroy<ModelCacheScriptingInterface>();
|
||||||
DependencyManager::destroy<ModelCache>();
|
DependencyManager::destroy<ModelCache>();
|
||||||
DependencyManager::destroy<ScriptCache>();
|
DependencyManager::destroy<ScriptCache>();
|
||||||
|
DependencyManager::destroy<SoundCacheScriptingInterface>();
|
||||||
DependencyManager::destroy<SoundCache>();
|
DependencyManager::destroy<SoundCache>();
|
||||||
DependencyManager::destroy<OctreeStatsProvider>();
|
DependencyManager::destroy<OctreeStatsProvider>();
|
||||||
DependencyManager::destroy<GeometryCache>();
|
DependencyManager::destroy<GeometryCache>();
|
||||||
|
@ -2717,6 +2731,10 @@ void Application::initializeDisplayPlugins() {
|
||||||
QObject::connect(displayPlugin.get(), &DisplayPlugin::recommendedFramebufferSizeChanged,
|
QObject::connect(displayPlugin.get(), &DisplayPlugin::recommendedFramebufferSizeChanged,
|
||||||
[this](const QSize& size) { resizeGL(); });
|
[this](const QSize& size) { resizeGL(); });
|
||||||
QObject::connect(displayPlugin.get(), &DisplayPlugin::resetSensorsRequested, this, &Application::requestReset);
|
QObject::connect(displayPlugin.get(), &DisplayPlugin::resetSensorsRequested, this, &Application::requestReset);
|
||||||
|
if (displayPlugin->isHmd()) {
|
||||||
|
QObject::connect(dynamic_cast<HmdDisplayPlugin*>(displayPlugin.get()), &HmdDisplayPlugin::hmdMountedChanged,
|
||||||
|
DependencyManager::get<HMDScriptingInterface>().data(), &HMDScriptingInterface::mountedChanged);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// The default display plugin needs to be activated first, otherwise the display plugin thread
|
// The default display plugin needs to be activated first, otherwise the display plugin thread
|
||||||
|
@ -2989,10 +3007,11 @@ void Application::onDesktopRootContextCreated(QQmlContext* surfaceContext) {
|
||||||
surfaceContext->setContextProperty("LocationBookmarks", DependencyManager::get<LocationBookmarks>().data());
|
surfaceContext->setContextProperty("LocationBookmarks", DependencyManager::get<LocationBookmarks>().data());
|
||||||
|
|
||||||
// Caches
|
// Caches
|
||||||
surfaceContext->setContextProperty("AnimationCache", DependencyManager::get<AnimationCache>().data());
|
surfaceContext->setContextProperty("AnimationCache", DependencyManager::get<AnimationCacheScriptingInterface>().data());
|
||||||
surfaceContext->setContextProperty("TextureCache", DependencyManager::get<TextureCache>().data());
|
surfaceContext->setContextProperty("TextureCache", DependencyManager::get<TextureCacheScriptingInterface>().data());
|
||||||
surfaceContext->setContextProperty("ModelCache", DependencyManager::get<ModelCache>().data());
|
surfaceContext->setContextProperty("ModelCache", DependencyManager::get<ModelCacheScriptingInterface>().data());
|
||||||
surfaceContext->setContextProperty("SoundCache", DependencyManager::get<SoundCache>().data());
|
surfaceContext->setContextProperty("SoundCache", DependencyManager::get<SoundCacheScriptingInterface>().data());
|
||||||
|
|
||||||
surfaceContext->setContextProperty("InputConfiguration", DependencyManager::get<InputConfiguration>().data());
|
surfaceContext->setContextProperty("InputConfiguration", DependencyManager::get<InputConfiguration>().data());
|
||||||
|
|
||||||
surfaceContext->setContextProperty("Account", AccountServicesScriptingInterface::getInstance()); // DEPRECATED - TO BE REMOVED
|
surfaceContext->setContextProperty("Account", AccountServicesScriptingInterface::getInstance()); // DEPRECATED - TO BE REMOVED
|
||||||
|
@ -6606,10 +6625,10 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEnginePointe
|
||||||
scriptEngine->registerGlobalObject("Pointers", DependencyManager::get<PointerScriptingInterface>().data());
|
scriptEngine->registerGlobalObject("Pointers", DependencyManager::get<PointerScriptingInterface>().data());
|
||||||
|
|
||||||
// Caches
|
// Caches
|
||||||
scriptEngine->registerGlobalObject("AnimationCache", DependencyManager::get<AnimationCache>().data());
|
scriptEngine->registerGlobalObject("AnimationCache", DependencyManager::get<AnimationCacheScriptingInterface>().data());
|
||||||
scriptEngine->registerGlobalObject("TextureCache", DependencyManager::get<TextureCache>().data());
|
scriptEngine->registerGlobalObject("TextureCache", DependencyManager::get<TextureCacheScriptingInterface>().data());
|
||||||
scriptEngine->registerGlobalObject("ModelCache", DependencyManager::get<ModelCache>().data());
|
scriptEngine->registerGlobalObject("ModelCache", DependencyManager::get<ModelCacheScriptingInterface>().data());
|
||||||
scriptEngine->registerGlobalObject("SoundCache", DependencyManager::get<SoundCache>().data());
|
scriptEngine->registerGlobalObject("SoundCache", DependencyManager::get<SoundCacheScriptingInterface>().data());
|
||||||
|
|
||||||
scriptEngine->registerGlobalObject("DialogsManager", _dialogsManagerScriptingInterface);
|
scriptEngine->registerGlobalObject("DialogsManager", _dialogsManagerScriptingInterface);
|
||||||
|
|
||||||
|
|
|
@ -347,17 +347,6 @@ signals:
|
||||||
*/
|
*/
|
||||||
bool shouldShowHandControllersChanged();
|
bool shouldShowHandControllersChanged();
|
||||||
|
|
||||||
/**jsdoc
|
|
||||||
* Triggered when the <code>HMD.mounted</code> property value changes.
|
|
||||||
* @function HMD.mountedChanged
|
|
||||||
* @returns {Signal}
|
|
||||||
* @example <caption>Report when there's a change in the HMD being worn.</caption>
|
|
||||||
* HMD.mountedChanged.connect(function () {
|
|
||||||
* print("Mounted changed. HMD is mounted: " + HMD.mounted);
|
|
||||||
* });
|
|
||||||
*/
|
|
||||||
void mountedChanged();
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
HMDScriptingInterface();
|
HMDScriptingInterface();
|
||||||
static QScriptValue getHUDLookAtPosition2D(QScriptContext* context, QScriptEngine* engine);
|
static QScriptValue getHUDLookAtPosition2D(QScriptContext* context, QScriptEngine* engine);
|
||||||
|
|
|
@ -55,7 +55,7 @@
|
||||||
#include "scripting/AccountServicesScriptingInterface.h"
|
#include "scripting/AccountServicesScriptingInterface.h"
|
||||||
#include <plugins/InputConfiguration.h>
|
#include <plugins/InputConfiguration.h>
|
||||||
#include "ui/Snapshot.h"
|
#include "ui/Snapshot.h"
|
||||||
#include "SoundCache.h"
|
#include "SoundCacheScriptingInterface.h"
|
||||||
#include "raypick/PointerScriptingInterface.h"
|
#include "raypick/PointerScriptingInterface.h"
|
||||||
#include <display-plugins/CompositorHelper.h>
|
#include <display-plugins/CompositorHelper.h>
|
||||||
#include "AboutUtil.h"
|
#include "AboutUtil.h"
|
||||||
|
@ -253,7 +253,7 @@ void Web3DOverlay::setupQmlSurface() {
|
||||||
_webSurface->getSurfaceContext()->setContextProperty("AvatarList", DependencyManager::get<AvatarManager>().data());
|
_webSurface->getSurfaceContext()->setContextProperty("AvatarList", DependencyManager::get<AvatarManager>().data());
|
||||||
_webSurface->getSurfaceContext()->setContextProperty("DialogsManager", DialogsManagerScriptingInterface::getInstance());
|
_webSurface->getSurfaceContext()->setContextProperty("DialogsManager", DialogsManagerScriptingInterface::getInstance());
|
||||||
_webSurface->getSurfaceContext()->setContextProperty("InputConfiguration", DependencyManager::get<InputConfiguration>().data());
|
_webSurface->getSurfaceContext()->setContextProperty("InputConfiguration", DependencyManager::get<InputConfiguration>().data());
|
||||||
_webSurface->getSurfaceContext()->setContextProperty("SoundCache", DependencyManager::get<SoundCache>().data());
|
_webSurface->getSurfaceContext()->setContextProperty("SoundCache", DependencyManager::get<SoundCacheScriptingInterface>().data());
|
||||||
_webSurface->getSurfaceContext()->setContextProperty("MenuInterface", MenuScriptingInterface::getInstance());
|
_webSurface->getSurfaceContext()->setContextProperty("MenuInterface", MenuScriptingInterface::getInstance());
|
||||||
_webSurface->getSurfaceContext()->setContextProperty("Settings", SettingsScriptingInterface::getInstance());
|
_webSurface->getSurfaceContext()->setContextProperty("Settings", SettingsScriptingInterface::getInstance());
|
||||||
_webSurface->getSurfaceContext()->setContextProperty("AvatarBookmarks", DependencyManager::get<AvatarBookmarks>().data());
|
_webSurface->getSurfaceContext()->setContextProperty("AvatarBookmarks", DependencyManager::get<AvatarBookmarks>().data());
|
||||||
|
|
|
@ -24,7 +24,7 @@ AnimBlendLinear::~AnimBlendLinear() {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const AnimPoseVec& AnimBlendLinear::evaluate(const AnimVariantMap& animVars, const AnimContext& context, float dt, Triggers& triggersOut) {
|
const AnimPoseVec& AnimBlendLinear::evaluate(const AnimVariantMap& animVars, const AnimContext& context, float dt, AnimVariantMap& triggersOut) {
|
||||||
|
|
||||||
_alpha = animVars.lookup(_alphaVar, _alpha);
|
_alpha = animVars.lookup(_alphaVar, _alpha);
|
||||||
|
|
||||||
|
@ -43,6 +43,9 @@ const AnimPoseVec& AnimBlendLinear::evaluate(const AnimVariantMap& animVars, con
|
||||||
|
|
||||||
evaluateAndBlendChildren(animVars, context, triggersOut, alpha, prevPoseIndex, nextPoseIndex, dt);
|
evaluateAndBlendChildren(animVars, context, triggersOut, alpha, prevPoseIndex, nextPoseIndex, dt);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
processOutputJoints(triggersOut);
|
||||||
|
|
||||||
return _poses;
|
return _poses;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -51,7 +54,7 @@ const AnimPoseVec& AnimBlendLinear::getPosesInternal() const {
|
||||||
return _poses;
|
return _poses;
|
||||||
}
|
}
|
||||||
|
|
||||||
void AnimBlendLinear::evaluateAndBlendChildren(const AnimVariantMap& animVars, const AnimContext& context, Triggers& triggersOut, float alpha,
|
void AnimBlendLinear::evaluateAndBlendChildren(const AnimVariantMap& animVars, const AnimContext& context, AnimVariantMap& triggersOut, float alpha,
|
||||||
size_t prevPoseIndex, size_t nextPoseIndex, float dt) {
|
size_t prevPoseIndex, size_t nextPoseIndex, float dt) {
|
||||||
if (prevPoseIndex == nextPoseIndex) {
|
if (prevPoseIndex == nextPoseIndex) {
|
||||||
// this can happen if alpha is on an integer boundary
|
// this can happen if alpha is on an integer boundary
|
||||||
|
|
|
@ -30,7 +30,7 @@ public:
|
||||||
AnimBlendLinear(const QString& id, float alpha);
|
AnimBlendLinear(const QString& id, float alpha);
|
||||||
virtual ~AnimBlendLinear() override;
|
virtual ~AnimBlendLinear() override;
|
||||||
|
|
||||||
virtual const AnimPoseVec& evaluate(const AnimVariantMap& animVars, const AnimContext& context, float dt, Triggers& triggersOut) override;
|
virtual const AnimPoseVec& evaluate(const AnimVariantMap& animVars, const AnimContext& context, float dt, AnimVariantMap& triggersOut) override;
|
||||||
|
|
||||||
void setAlphaVar(const QString& alphaVar) { _alphaVar = alphaVar; }
|
void setAlphaVar(const QString& alphaVar) { _alphaVar = alphaVar; }
|
||||||
|
|
||||||
|
@ -38,7 +38,7 @@ protected:
|
||||||
// for AnimDebugDraw rendering
|
// for AnimDebugDraw rendering
|
||||||
virtual const AnimPoseVec& getPosesInternal() const override;
|
virtual const AnimPoseVec& getPosesInternal() const override;
|
||||||
|
|
||||||
void evaluateAndBlendChildren(const AnimVariantMap& animVars, const AnimContext& context, Triggers& triggersOut, float alpha,
|
void evaluateAndBlendChildren(const AnimVariantMap& animVars, const AnimContext& context, AnimVariantMap& triggersOut, float alpha,
|
||||||
size_t prevPoseIndex, size_t nextPoseIndex, float dt);
|
size_t prevPoseIndex, size_t nextPoseIndex, float dt);
|
||||||
|
|
||||||
AnimPoseVec _poses;
|
AnimPoseVec _poses;
|
||||||
|
|
|
@ -26,7 +26,7 @@ AnimBlendLinearMove::~AnimBlendLinearMove() {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const AnimPoseVec& AnimBlendLinearMove::evaluate(const AnimVariantMap& animVars, const AnimContext& context, float dt, Triggers& triggersOut) {
|
const AnimPoseVec& AnimBlendLinearMove::evaluate(const AnimVariantMap& animVars, const AnimContext& context, float dt, AnimVariantMap& triggersOut) {
|
||||||
|
|
||||||
assert(_children.size() == _characteristicSpeeds.size());
|
assert(_children.size() == _characteristicSpeeds.size());
|
||||||
|
|
||||||
|
@ -54,6 +54,9 @@ const AnimPoseVec& AnimBlendLinearMove::evaluate(const AnimVariantMap& animVars,
|
||||||
setFrameAndPhase(dt, alpha, prevPoseIndex, nextPoseIndex, &prevDeltaTime, &nextDeltaTime, triggersOut);
|
setFrameAndPhase(dt, alpha, prevPoseIndex, nextPoseIndex, &prevDeltaTime, &nextDeltaTime, triggersOut);
|
||||||
evaluateAndBlendChildren(animVars, context, triggersOut, alpha, prevPoseIndex, nextPoseIndex, prevDeltaTime, nextDeltaTime);
|
evaluateAndBlendChildren(animVars, context, triggersOut, alpha, prevPoseIndex, nextPoseIndex, prevDeltaTime, nextDeltaTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
processOutputJoints(triggersOut);
|
||||||
|
|
||||||
return _poses;
|
return _poses;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -62,7 +65,7 @@ const AnimPoseVec& AnimBlendLinearMove::getPosesInternal() const {
|
||||||
return _poses;
|
return _poses;
|
||||||
}
|
}
|
||||||
|
|
||||||
void AnimBlendLinearMove::evaluateAndBlendChildren(const AnimVariantMap& animVars, const AnimContext& context, Triggers& triggersOut, float alpha,
|
void AnimBlendLinearMove::evaluateAndBlendChildren(const AnimVariantMap& animVars, const AnimContext& context, AnimVariantMap& triggersOut, float alpha,
|
||||||
size_t prevPoseIndex, size_t nextPoseIndex,
|
size_t prevPoseIndex, size_t nextPoseIndex,
|
||||||
float prevDeltaTime, float nextDeltaTime) {
|
float prevDeltaTime, float nextDeltaTime) {
|
||||||
if (prevPoseIndex == nextPoseIndex) {
|
if (prevPoseIndex == nextPoseIndex) {
|
||||||
|
@ -82,7 +85,7 @@ void AnimBlendLinearMove::evaluateAndBlendChildren(const AnimVariantMap& animVar
|
||||||
}
|
}
|
||||||
|
|
||||||
void AnimBlendLinearMove::setFrameAndPhase(float dt, float alpha, int prevPoseIndex, int nextPoseIndex,
|
void AnimBlendLinearMove::setFrameAndPhase(float dt, float alpha, int prevPoseIndex, int nextPoseIndex,
|
||||||
float* prevDeltaTimeOut, float* nextDeltaTimeOut, Triggers& triggersOut) {
|
float* prevDeltaTimeOut, float* nextDeltaTimeOut, AnimVariantMap& triggersOut) {
|
||||||
|
|
||||||
const float FRAMES_PER_SECOND = 30.0f;
|
const float FRAMES_PER_SECOND = 30.0f;
|
||||||
auto prevClipNode = std::dynamic_pointer_cast<AnimClip>(_children[prevPoseIndex]);
|
auto prevClipNode = std::dynamic_pointer_cast<AnimClip>(_children[prevPoseIndex]);
|
||||||
|
@ -109,7 +112,7 @@ void AnimBlendLinearMove::setFrameAndPhase(float dt, float alpha, int prevPoseIn
|
||||||
|
|
||||||
// detect loop trigger events
|
// detect loop trigger events
|
||||||
if (_phase >= 1.0f) {
|
if (_phase >= 1.0f) {
|
||||||
triggersOut.push_back(_id + "Loop");
|
triggersOut.setTrigger(_id + "Loop");
|
||||||
_phase = glm::fract(_phase);
|
_phase = glm::fract(_phase);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -39,7 +39,7 @@ public:
|
||||||
AnimBlendLinearMove(const QString& id, float alpha, float desiredSpeed, const std::vector<float>& characteristicSpeeds);
|
AnimBlendLinearMove(const QString& id, float alpha, float desiredSpeed, const std::vector<float>& characteristicSpeeds);
|
||||||
virtual ~AnimBlendLinearMove() override;
|
virtual ~AnimBlendLinearMove() override;
|
||||||
|
|
||||||
virtual const AnimPoseVec& evaluate(const AnimVariantMap& animVars, const AnimContext& context, float dt, Triggers& triggersOut) override;
|
virtual const AnimPoseVec& evaluate(const AnimVariantMap& animVars, const AnimContext& context, float dt, AnimVariantMap& triggersOut) override;
|
||||||
|
|
||||||
void setAlphaVar(const QString& alphaVar) { _alphaVar = alphaVar; }
|
void setAlphaVar(const QString& alphaVar) { _alphaVar = alphaVar; }
|
||||||
void setDesiredSpeedVar(const QString& desiredSpeedVar) { _desiredSpeedVar = desiredSpeedVar; }
|
void setDesiredSpeedVar(const QString& desiredSpeedVar) { _desiredSpeedVar = desiredSpeedVar; }
|
||||||
|
@ -48,12 +48,12 @@ protected:
|
||||||
// for AnimDebugDraw rendering
|
// for AnimDebugDraw rendering
|
||||||
virtual const AnimPoseVec& getPosesInternal() const override;
|
virtual const AnimPoseVec& getPosesInternal() const override;
|
||||||
|
|
||||||
void evaluateAndBlendChildren(const AnimVariantMap& animVars, const AnimContext& context, Triggers& triggersOut, float alpha,
|
void evaluateAndBlendChildren(const AnimVariantMap& animVars, const AnimContext& context, AnimVariantMap& triggersOut, float alpha,
|
||||||
size_t prevPoseIndex, size_t nextPoseIndex,
|
size_t prevPoseIndex, size_t nextPoseIndex,
|
||||||
float prevDeltaTime, float nextDeltaTime);
|
float prevDeltaTime, float nextDeltaTime);
|
||||||
|
|
||||||
void setFrameAndPhase(float dt, float alpha, int prevPoseIndex, int nextPoseIndex,
|
void setFrameAndPhase(float dt, float alpha, int prevPoseIndex, int nextPoseIndex,
|
||||||
float* prevDeltaTimeOut, float* nextDeltaTimeOut, Triggers& triggersOut);
|
float* prevDeltaTimeOut, float* nextDeltaTimeOut, AnimVariantMap& triggersOut);
|
||||||
|
|
||||||
virtual void setCurrentFrameInternal(float frame) override;
|
virtual void setCurrentFrameInternal(float frame) override;
|
||||||
|
|
||||||
|
|
159
libraries/animation/src/AnimChain.h
Normal file
159
libraries/animation/src/AnimChain.h
Normal file
|
@ -0,0 +1,159 @@
|
||||||
|
//
|
||||||
|
// AnimChain.h
|
||||||
|
//
|
||||||
|
// Created by Anthony J. Thibault on 7/16/2018.
|
||||||
|
// Copyright (c) 2018 High Fidelity, Inc. All rights reserved.
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef hifi_AnimChain
|
||||||
|
#define hifi_AnimChain
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
#include <glm/glm.hpp>
|
||||||
|
#include <glm/gtc/quaternion.hpp>
|
||||||
|
|
||||||
|
#include <DebugDraw.h>
|
||||||
|
|
||||||
|
template <int N>
|
||||||
|
class AnimChainT {
|
||||||
|
|
||||||
|
public:
|
||||||
|
AnimChainT() {}
|
||||||
|
|
||||||
|
AnimChainT(const AnimChainT& orig) {
|
||||||
|
_top = orig._top;
|
||||||
|
for (int i = 0; i < _top; i++) {
|
||||||
|
_chain[i] = orig._chain[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
AnimChainT& operator=(const AnimChainT& orig) {
|
||||||
|
_top = orig._top;
|
||||||
|
for (int i = 0; i < _top; i++) {
|
||||||
|
_chain[i] = orig._chain[i];
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool buildFromRelativePoses(const AnimSkeleton::ConstPointer& skeleton, const AnimPoseVec& relativePoses, int tipIndex) {
|
||||||
|
_top = 0;
|
||||||
|
// iterate through the skeleton parents, from the tip to the base, copying over relativePoses into the chain.
|
||||||
|
for (int jointIndex = tipIndex; jointIndex != -1; jointIndex = skeleton->getParentIndex(jointIndex)) {
|
||||||
|
if (_top >= N) {
|
||||||
|
assert(_top < N);
|
||||||
|
// stack overflow
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
_chain[_top].relativePose = relativePoses[jointIndex];
|
||||||
|
_chain[_top].jointIndex = jointIndex;
|
||||||
|
_chain[_top].dirty = true;
|
||||||
|
_top++;
|
||||||
|
}
|
||||||
|
|
||||||
|
buildDirtyAbsolutePoses();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const AnimPose& getAbsolutePoseFromJointIndex(int jointIndex) const {
|
||||||
|
for (int i = 0; i < _top; i++) {
|
||||||
|
if (_chain[i].jointIndex == jointIndex) {
|
||||||
|
return _chain[i].absolutePose;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return AnimPose::identity;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool setRelativePoseAtJointIndex(int jointIndex, const AnimPose& relativePose) {
|
||||||
|
bool foundIndex = false;
|
||||||
|
for (int i = _top - 1; i >= 0; i--) {
|
||||||
|
if (_chain[i].jointIndex == jointIndex) {
|
||||||
|
_chain[i].relativePose = relativePose;
|
||||||
|
foundIndex = true;
|
||||||
|
}
|
||||||
|
// all child absolute poses are now dirty
|
||||||
|
if (foundIndex) {
|
||||||
|
_chain[i].dirty = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return foundIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
void buildDirtyAbsolutePoses() {
|
||||||
|
// the relative and absolute pose is the same for the base of the chain.
|
||||||
|
_chain[_top - 1].absolutePose = _chain[_top - 1].relativePose;
|
||||||
|
_chain[_top - 1].dirty = false;
|
||||||
|
|
||||||
|
// iterate chain from base to tip, concatinating the relative poses to build the absolute poses.
|
||||||
|
for (int i = _top - 1; i > 0; i--) {
|
||||||
|
AnimChainElem& parent = _chain[i];
|
||||||
|
AnimChainElem& child = _chain[i - 1];
|
||||||
|
|
||||||
|
if (child.dirty) {
|
||||||
|
child.absolutePose = parent.absolutePose * child.relativePose;
|
||||||
|
child.dirty = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void blend(const AnimChainT& srcChain, float alpha) {
|
||||||
|
// make sure chains have same lengths
|
||||||
|
assert(srcChain._top == _top);
|
||||||
|
if (srcChain._top != _top) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// only blend the relative poses
|
||||||
|
for (int i = 0; i < _top; i++) {
|
||||||
|
_chain[i].relativePose.blend(srcChain._chain[i].relativePose, alpha);
|
||||||
|
_chain[i].dirty = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int size() const {
|
||||||
|
return _top;
|
||||||
|
}
|
||||||
|
|
||||||
|
void outputRelativePoses(AnimPoseVec& relativePoses) {
|
||||||
|
for (int i = 0; i < _top; i++) {
|
||||||
|
relativePoses[_chain[i].jointIndex] = _chain[i].relativePose;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void debugDraw(const glm::mat4& geomToWorldMat, const glm::vec4& color) const {
|
||||||
|
for (int i = 1; i < _top; i++) {
|
||||||
|
glm::vec3 start = transformPoint(geomToWorldMat, _chain[i - 1].absolutePose.trans());
|
||||||
|
glm::vec3 end = transformPoint(geomToWorldMat, _chain[i].absolutePose.trans());
|
||||||
|
DebugDraw::getInstance().drawRay(start, end, color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void dump() const {
|
||||||
|
for (int i = 0; i < _top; i++) {
|
||||||
|
qWarning() << "AJT: AnimPoseElem[" << i << "]";
|
||||||
|
qWarning() << "AJT: relPose =" << _chain[i].relativePose;
|
||||||
|
qWarning() << "AJT: absPose =" << _chain[i].absolutePose;
|
||||||
|
qWarning() << "AJT: jointIndex =" << _chain[i].jointIndex;
|
||||||
|
qWarning() << "AJT: dirty =" << _chain[i].dirty;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
|
||||||
|
struct AnimChainElem {
|
||||||
|
AnimPose relativePose;
|
||||||
|
AnimPose absolutePose;
|
||||||
|
int jointIndex { -1 };
|
||||||
|
bool dirty { true };
|
||||||
|
};
|
||||||
|
|
||||||
|
AnimChainElem _chain[N];
|
||||||
|
int _top { 0 };
|
||||||
|
};
|
||||||
|
|
||||||
|
using AnimChain = AnimChainT<10>;
|
||||||
|
|
||||||
|
#endif
|
|
@ -30,7 +30,7 @@ AnimClip::~AnimClip() {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const AnimPoseVec& AnimClip::evaluate(const AnimVariantMap& animVars, const AnimContext& context, float dt, Triggers& triggersOut) {
|
const AnimPoseVec& AnimClip::evaluate(const AnimVariantMap& animVars, const AnimContext& context, float dt, AnimVariantMap& triggersOut) {
|
||||||
|
|
||||||
// lookup parameters from animVars, using current instance variables as defaults.
|
// lookup parameters from animVars, using current instance variables as defaults.
|
||||||
_startFrame = animVars.lookup(_startFrameVar, _startFrame);
|
_startFrame = animVars.lookup(_startFrameVar, _startFrame);
|
||||||
|
@ -77,6 +77,8 @@ const AnimPoseVec& AnimClip::evaluate(const AnimVariantMap& animVars, const Anim
|
||||||
::blend(_poses.size(), &prevFrame[0], &nextFrame[0], alpha, &_poses[0]);
|
::blend(_poses.size(), &prevFrame[0], &nextFrame[0], alpha, &_poses[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
processOutputJoints(triggersOut);
|
||||||
|
|
||||||
return _poses;
|
return _poses;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -89,7 +91,7 @@ void AnimClip::loadURL(const QString& url) {
|
||||||
void AnimClip::setCurrentFrameInternal(float frame) {
|
void AnimClip::setCurrentFrameInternal(float frame) {
|
||||||
// because dt is 0, we should not encounter any triggers
|
// because dt is 0, we should not encounter any triggers
|
||||||
const float dt = 0.0f;
|
const float dt = 0.0f;
|
||||||
Triggers triggers;
|
AnimVariantMap triggers;
|
||||||
_frame = ::accumulateTime(_startFrame, _endFrame, _timeScale, frame + _startFrame, dt, _loopFlag, _id, triggers);
|
_frame = ::accumulateTime(_startFrame, _endFrame, _timeScale, frame + _startFrame, dt, _loopFlag, _id, triggers);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -28,7 +28,7 @@ public:
|
||||||
AnimClip(const QString& id, const QString& url, float startFrame, float endFrame, float timeScale, bool loopFlag, bool mirrorFlag);
|
AnimClip(const QString& id, const QString& url, float startFrame, float endFrame, float timeScale, bool loopFlag, bool mirrorFlag);
|
||||||
virtual ~AnimClip() override;
|
virtual ~AnimClip() override;
|
||||||
|
|
||||||
virtual const AnimPoseVec& evaluate(const AnimVariantMap& animVars, const AnimContext& context, float dt, Triggers& triggersOut) override;
|
virtual const AnimPoseVec& evaluate(const AnimVariantMap& animVars, const AnimContext& context, float dt, AnimVariantMap& triggersOut) override;
|
||||||
|
|
||||||
void setStartFrameVar(const QString& startFrameVar) { _startFrameVar = startFrameVar; }
|
void setStartFrameVar(const QString& startFrameVar) { _startFrameVar = startFrameVar; }
|
||||||
void setEndFrameVar(const QString& endFrameVar) { _endFrameVar = endFrameVar; }
|
void setEndFrameVar(const QString& endFrameVar) { _endFrameVar = endFrameVar; }
|
||||||
|
|
|
@ -20,12 +20,15 @@ AnimDefaultPose::~AnimDefaultPose() {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const AnimPoseVec& AnimDefaultPose::evaluate(const AnimVariantMap& animVars, const AnimContext& context, float dt, Triggers& triggersOut) {
|
const AnimPoseVec& AnimDefaultPose::evaluate(const AnimVariantMap& animVars, const AnimContext& context, float dt, AnimVariantMap& triggersOut) {
|
||||||
if (_skeleton) {
|
if (_skeleton) {
|
||||||
_poses = _skeleton->getRelativeDefaultPoses();
|
_poses = _skeleton->getRelativeDefaultPoses();
|
||||||
} else {
|
} else {
|
||||||
_poses.clear();
|
_poses.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
processOutputJoints(triggersOut);
|
||||||
|
|
||||||
return _poses;
|
return _poses;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -21,7 +21,7 @@ public:
|
||||||
AnimDefaultPose(const QString& id);
|
AnimDefaultPose(const QString& id);
|
||||||
virtual ~AnimDefaultPose() override;
|
virtual ~AnimDefaultPose() override;
|
||||||
|
|
||||||
virtual const AnimPoseVec& evaluate(const AnimVariantMap& animVars, const AnimContext& context, float dt, Triggers& triggersOut) override;
|
virtual const AnimPoseVec& evaluate(const AnimVariantMap& animVars, const AnimContext& context, float dt, AnimVariantMap& triggersOut) override;
|
||||||
protected:
|
protected:
|
||||||
// for AnimDebugDraw rendering
|
// for AnimDebugDraw rendering
|
||||||
virtual const AnimPoseVec& getPosesInternal() const override;
|
virtual const AnimPoseVec& getPosesInternal() const override;
|
||||||
|
|
|
@ -259,14 +259,6 @@ void AnimInverseKinematics::solve(const AnimContext& context, const std::vector<
|
||||||
jointChainInfoVec[i].jointInfoVec[j].rot = safeMix(_prevJointChainInfoVec[i].jointInfoVec[j].rot, jointChainInfoVec[i].jointInfoVec[j].rot, alpha);
|
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);
|
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -874,14 +866,14 @@ void AnimInverseKinematics::solveTargetWithSpline(const AnimContext& context, co
|
||||||
}
|
}
|
||||||
|
|
||||||
//virtual
|
//virtual
|
||||||
const AnimPoseVec& AnimInverseKinematics::evaluate(const AnimVariantMap& animVars, const AnimContext& context, float dt, AnimNode::Triggers& triggersOut) {
|
const AnimPoseVec& AnimInverseKinematics::evaluate(const AnimVariantMap& animVars, const AnimContext& context, float dt, AnimVariantMap& triggersOut) {
|
||||||
// don't call this function, call overlay() instead
|
// don't call this function, call overlay() instead
|
||||||
assert(false);
|
assert(false);
|
||||||
return _relativePoses;
|
return _relativePoses;
|
||||||
}
|
}
|
||||||
|
|
||||||
//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, AnimVariantMap& triggersOut, const AnimPoseVec& underPoses) {
|
||||||
#ifdef Q_OS_ANDROID
|
#ifdef Q_OS_ANDROID
|
||||||
// disable IK on android
|
// disable IK on android
|
||||||
return underPoses;
|
return underPoses;
|
||||||
|
@ -961,6 +953,7 @@ const AnimPoseVec& AnimInverseKinematics::overlay(const AnimVariantMap& animVars
|
||||||
PROFILE_RANGE_EX(simulation_animation, "ik/shiftHips", 0xffff00ff, 0);
|
PROFILE_RANGE_EX(simulation_animation, "ik/shiftHips", 0xffff00ff, 0);
|
||||||
|
|
||||||
if (_hipsTargetIndex >= 0) {
|
if (_hipsTargetIndex >= 0) {
|
||||||
|
|
||||||
assert(_hipsTargetIndex < (int)targets.size());
|
assert(_hipsTargetIndex < (int)targets.size());
|
||||||
|
|
||||||
// slam the hips to match the _hipsTarget
|
// slam the hips to match the _hipsTarget
|
||||||
|
@ -1045,6 +1038,7 @@ 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);
|
||||||
|
|
||||||
setSecondaryTargets(context);
|
setSecondaryTargets(context);
|
||||||
|
|
||||||
preconditionRelativePosesToAvoidLimbLock(context, targets);
|
preconditionRelativePosesToAvoidLimbLock(context, targets);
|
||||||
|
|
||||||
solve(context, targets, dt, jointChainInfoVec);
|
solve(context, targets, dt, jointChainInfoVec);
|
||||||
|
@ -1056,6 +1050,8 @@ const AnimPoseVec& AnimInverseKinematics::overlay(const AnimVariantMap& animVars
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
processOutputJoints(triggersOut);
|
||||||
|
|
||||||
return _relativePoses;
|
return _relativePoses;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1750,7 +1746,7 @@ void AnimInverseKinematics::preconditionRelativePosesToAvoidLimbLock(const AnimC
|
||||||
const float MIN_AXIS_LENGTH = 1.0e-4f;
|
const float MIN_AXIS_LENGTH = 1.0e-4f;
|
||||||
|
|
||||||
for (auto& target : targets) {
|
for (auto& target : targets) {
|
||||||
if (target.getIndex() != -1) {
|
if (target.getIndex() != -1 && target.getType() == IKTarget::Type::RotationAndPosition) {
|
||||||
for (int i = 0; i < NUM_LIMBS; i++) {
|
for (int i = 0; i < NUM_LIMBS; i++) {
|
||||||
if (limbs[i].first == target.getIndex()) {
|
if (limbs[i].first == target.getIndex()) {
|
||||||
int tipIndex = limbs[i].first;
|
int tipIndex = limbs[i].first;
|
||||||
|
@ -1843,6 +1839,10 @@ void AnimInverseKinematics::initRelativePosesFromSolutionSource(SolutionSource s
|
||||||
default:
|
default:
|
||||||
case SolutionSource::RelaxToUnderPoses:
|
case SolutionSource::RelaxToUnderPoses:
|
||||||
blendToPoses(underPoses, underPoses, RELAX_BLEND_FACTOR);
|
blendToPoses(underPoses, underPoses, RELAX_BLEND_FACTOR);
|
||||||
|
// special case for hips: don't dampen hip motion from underposes
|
||||||
|
if (_hipsIndex >= 0 && _hipsIndex < (int)_relativePoses.size()) {
|
||||||
|
_relativePoses[_hipsIndex] = underPoses[_hipsIndex];
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case SolutionSource::RelaxToLimitCenterPoses:
|
case SolutionSource::RelaxToLimitCenterPoses:
|
||||||
blendToPoses(_limitCenterPoses, underPoses, RELAX_BLEND_FACTOR);
|
blendToPoses(_limitCenterPoses, underPoses, RELAX_BLEND_FACTOR);
|
||||||
|
|
|
@ -52,8 +52,8 @@ public:
|
||||||
const QString& typeVar, const QString& weightVar, float weight, const std::vector<float>& flexCoefficients,
|
const QString& typeVar, const QString& weightVar, float weight, const std::vector<float>& flexCoefficients,
|
||||||
const QString& poleVectorEnabledVar, const QString& poleReferenceVectorVar, const QString& poleVectorVar);
|
const QString& poleVectorEnabledVar, const QString& poleReferenceVectorVar, const QString& poleVectorVar);
|
||||||
|
|
||||||
virtual const AnimPoseVec& evaluate(const AnimVariantMap& animVars, const AnimContext& context, float dt, AnimNode::Triggers& triggersOut) override;
|
virtual const AnimPoseVec& evaluate(const AnimVariantMap& animVars, const AnimContext& context, float dt, AnimVariantMap& triggersOut) override;
|
||||||
virtual const AnimPoseVec& overlay(const AnimVariantMap& animVars, const AnimContext& context, float dt, Triggers& triggersOut, const AnimPoseVec& underPoses) override;
|
virtual const AnimPoseVec& overlay(const AnimVariantMap& animVars, const AnimContext& context, float dt, AnimVariantMap& triggersOut, const AnimPoseVec& underPoses) override;
|
||||||
|
|
||||||
void clearIKJointLimitHistory();
|
void clearIKJointLimitHistory();
|
||||||
|
|
||||||
|
|
|
@ -32,11 +32,11 @@ AnimManipulator::~AnimManipulator() {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const AnimPoseVec& AnimManipulator::evaluate(const AnimVariantMap& animVars, const AnimContext& context, float dt, Triggers& triggersOut) {
|
const AnimPoseVec& AnimManipulator::evaluate(const AnimVariantMap& animVars, const AnimContext& context, float dt, AnimVariantMap& triggersOut) {
|
||||||
return overlay(animVars, context, dt, triggersOut, _skeleton->getRelativeDefaultPoses());
|
return overlay(animVars, context, dt, triggersOut, _skeleton->getRelativeDefaultPoses());
|
||||||
}
|
}
|
||||||
|
|
||||||
const AnimPoseVec& AnimManipulator::overlay(const AnimVariantMap& animVars, const AnimContext& context, float dt, Triggers& triggersOut, const AnimPoseVec& underPoses) {
|
const AnimPoseVec& AnimManipulator::overlay(const AnimVariantMap& animVars, const AnimContext& context, float dt, AnimVariantMap& triggersOut, const AnimPoseVec& underPoses) {
|
||||||
_alpha = animVars.lookup(_alphaVar, _alpha);
|
_alpha = animVars.lookup(_alphaVar, _alpha);
|
||||||
|
|
||||||
_poses = underPoses;
|
_poses = underPoses;
|
||||||
|
@ -74,6 +74,8 @@ const AnimPoseVec& AnimManipulator::overlay(const AnimVariantMap& animVars, cons
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
processOutputJoints(triggersOut);
|
||||||
|
|
||||||
return _poses;
|
return _poses;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -22,8 +22,8 @@ public:
|
||||||
AnimManipulator(const QString& id, float alpha);
|
AnimManipulator(const QString& id, float alpha);
|
||||||
virtual ~AnimManipulator() override;
|
virtual ~AnimManipulator() override;
|
||||||
|
|
||||||
virtual const AnimPoseVec& evaluate(const AnimVariantMap& animVars, const AnimContext& context, float dt, Triggers& triggersOut) override;
|
virtual const AnimPoseVec& evaluate(const AnimVariantMap& animVars, const AnimContext& context, float dt, AnimVariantMap& triggersOut) override;
|
||||||
virtual const AnimPoseVec& overlay(const AnimVariantMap& animVars, const AnimContext& context, float dt, Triggers& triggersOut, const AnimPoseVec& underPoses) override;
|
virtual const AnimPoseVec& overlay(const AnimVariantMap& animVars, const AnimContext& context, float dt, AnimVariantMap& triggersOut, const AnimPoseVec& underPoses) override;
|
||||||
|
|
||||||
void setAlphaVar(const QString& alphaVar) { _alphaVar = alphaVar; }
|
void setAlphaVar(const QString& alphaVar) { _alphaVar = alphaVar; }
|
||||||
|
|
||||||
|
|
|
@ -59,3 +59,19 @@ void AnimNode::setCurrentFrame(float frame) {
|
||||||
child->setCurrentFrameInternal(frame);
|
child->setCurrentFrameInternal(frame);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void AnimNode::processOutputJoints(AnimVariantMap& triggersOut) const {
|
||||||
|
if (!_skeleton) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto&& jointName : _outputJointNames) {
|
||||||
|
// TODO: cache the jointIndices
|
||||||
|
int jointIndex = _skeleton->nameToJointIndex(jointName);
|
||||||
|
if (jointIndex >= 0) {
|
||||||
|
AnimPose pose = _skeleton->getAbsolutePose(jointIndex, getPosesInternal());
|
||||||
|
triggersOut.set(_id + jointName + "Rotation", pose.rot());
|
||||||
|
triggersOut.set(_id + jointName + "Position", pose.trans());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -45,11 +45,12 @@ public:
|
||||||
Manipulator,
|
Manipulator,
|
||||||
InverseKinematics,
|
InverseKinematics,
|
||||||
DefaultPose,
|
DefaultPose,
|
||||||
|
TwoBoneIK,
|
||||||
|
PoleVectorConstraint,
|
||||||
NumTypes
|
NumTypes
|
||||||
};
|
};
|
||||||
using Pointer = std::shared_ptr<AnimNode>;
|
using Pointer = std::shared_ptr<AnimNode>;
|
||||||
using ConstPointer = std::shared_ptr<const AnimNode>;
|
using ConstPointer = std::shared_ptr<const AnimNode>;
|
||||||
using Triggers = std::vector<QString>;
|
|
||||||
|
|
||||||
friend class AnimDebugDraw;
|
friend class AnimDebugDraw;
|
||||||
friend void buildChildMap(std::map<QString, Pointer>& map, Pointer node);
|
friend void buildChildMap(std::map<QString, Pointer>& map, Pointer node);
|
||||||
|
@ -61,6 +62,8 @@ public:
|
||||||
const QString& getID() const { return _id; }
|
const QString& getID() const { return _id; }
|
||||||
Type getType() const { return _type; }
|
Type getType() const { return _type; }
|
||||||
|
|
||||||
|
void addOutputJoint(const QString& outputJointName) { _outputJointNames.push_back(outputJointName); }
|
||||||
|
|
||||||
// hierarchy accessors
|
// hierarchy accessors
|
||||||
Pointer getParent();
|
Pointer getParent();
|
||||||
void addChild(Pointer child);
|
void addChild(Pointer child);
|
||||||
|
@ -74,8 +77,8 @@ public:
|
||||||
|
|
||||||
AnimSkeleton::ConstPointer getSkeleton() const { return _skeleton; }
|
AnimSkeleton::ConstPointer getSkeleton() const { return _skeleton; }
|
||||||
|
|
||||||
virtual const AnimPoseVec& evaluate(const AnimVariantMap& animVars, const AnimContext& context, float dt, Triggers& triggersOut) = 0;
|
virtual const AnimPoseVec& evaluate(const AnimVariantMap& animVars, const AnimContext& context, float dt, AnimVariantMap& triggersOut) = 0;
|
||||||
virtual const AnimPoseVec& overlay(const AnimVariantMap& animVars, const AnimContext& context, float dt, Triggers& triggersOut,
|
virtual const AnimPoseVec& overlay(const AnimVariantMap& animVars, const AnimContext& context, float dt, AnimVariantMap& triggersOut,
|
||||||
const AnimPoseVec& underPoses) {
|
const AnimPoseVec& underPoses) {
|
||||||
return evaluate(animVars, context, dt, triggersOut);
|
return evaluate(animVars, context, dt, triggersOut);
|
||||||
}
|
}
|
||||||
|
@ -114,11 +117,14 @@ protected:
|
||||||
// for AnimDebugDraw rendering
|
// for AnimDebugDraw rendering
|
||||||
virtual const AnimPoseVec& getPosesInternal() const = 0;
|
virtual const AnimPoseVec& getPosesInternal() const = 0;
|
||||||
|
|
||||||
|
void processOutputJoints(AnimVariantMap& triggersOut) const;
|
||||||
|
|
||||||
Type _type;
|
Type _type;
|
||||||
QString _id;
|
QString _id;
|
||||||
std::vector<AnimNode::Pointer> _children;
|
std::vector<AnimNode::Pointer> _children;
|
||||||
AnimSkeleton::ConstPointer _skeleton;
|
AnimSkeleton::ConstPointer _skeleton;
|
||||||
std::weak_ptr<AnimNode> _parent;
|
std::weak_ptr<AnimNode> _parent;
|
||||||
|
std::vector<QString> _outputJointNames;
|
||||||
|
|
||||||
// no copies
|
// no copies
|
||||||
AnimNode(const AnimNode&) = delete;
|
AnimNode(const AnimNode&) = delete;
|
||||||
|
|
|
@ -25,6 +25,8 @@
|
||||||
#include "AnimManipulator.h"
|
#include "AnimManipulator.h"
|
||||||
#include "AnimInverseKinematics.h"
|
#include "AnimInverseKinematics.h"
|
||||||
#include "AnimDefaultPose.h"
|
#include "AnimDefaultPose.h"
|
||||||
|
#include "AnimTwoBoneIK.h"
|
||||||
|
#include "AnimPoleVectorConstraint.h"
|
||||||
|
|
||||||
using NodeLoaderFunc = AnimNode::Pointer (*)(const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl);
|
using NodeLoaderFunc = AnimNode::Pointer (*)(const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl);
|
||||||
using NodeProcessFunc = bool (*)(AnimNode::Pointer node, const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl);
|
using NodeProcessFunc = bool (*)(AnimNode::Pointer node, const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl);
|
||||||
|
@ -38,6 +40,8 @@ static AnimNode::Pointer loadStateMachineNode(const QJsonObject& jsonObj, const
|
||||||
static AnimNode::Pointer loadManipulatorNode(const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl);
|
static AnimNode::Pointer loadManipulatorNode(const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl);
|
||||||
static AnimNode::Pointer loadInverseKinematicsNode(const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl);
|
static AnimNode::Pointer loadInverseKinematicsNode(const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl);
|
||||||
static AnimNode::Pointer loadDefaultPoseNode(const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl);
|
static AnimNode::Pointer loadDefaultPoseNode(const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl);
|
||||||
|
static AnimNode::Pointer loadTwoBoneIKNode(const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl);
|
||||||
|
static AnimNode::Pointer loadPoleVectorConstraintNode(const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl);
|
||||||
|
|
||||||
static const float ANIM_GRAPH_LOAD_PRIORITY = 10.0f;
|
static const float ANIM_GRAPH_LOAD_PRIORITY = 10.0f;
|
||||||
|
|
||||||
|
@ -56,6 +60,8 @@ static const char* animNodeTypeToString(AnimNode::Type type) {
|
||||||
case AnimNode::Type::Manipulator: return "manipulator";
|
case AnimNode::Type::Manipulator: return "manipulator";
|
||||||
case AnimNode::Type::InverseKinematics: return "inverseKinematics";
|
case AnimNode::Type::InverseKinematics: return "inverseKinematics";
|
||||||
case AnimNode::Type::DefaultPose: return "defaultPose";
|
case AnimNode::Type::DefaultPose: return "defaultPose";
|
||||||
|
case AnimNode::Type::TwoBoneIK: return "twoBoneIK";
|
||||||
|
case AnimNode::Type::PoleVectorConstraint: return "poleVectorConstraint";
|
||||||
case AnimNode::Type::NumTypes: return nullptr;
|
case AnimNode::Type::NumTypes: return nullptr;
|
||||||
};
|
};
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
@ -116,6 +122,8 @@ static NodeLoaderFunc animNodeTypeToLoaderFunc(AnimNode::Type type) {
|
||||||
case AnimNode::Type::Manipulator: return loadManipulatorNode;
|
case AnimNode::Type::Manipulator: return loadManipulatorNode;
|
||||||
case AnimNode::Type::InverseKinematics: return loadInverseKinematicsNode;
|
case AnimNode::Type::InverseKinematics: return loadInverseKinematicsNode;
|
||||||
case AnimNode::Type::DefaultPose: return loadDefaultPoseNode;
|
case AnimNode::Type::DefaultPose: return loadDefaultPoseNode;
|
||||||
|
case AnimNode::Type::TwoBoneIK: return loadTwoBoneIKNode;
|
||||||
|
case AnimNode::Type::PoleVectorConstraint: return loadPoleVectorConstraintNode;
|
||||||
case AnimNode::Type::NumTypes: return nullptr;
|
case AnimNode::Type::NumTypes: return nullptr;
|
||||||
};
|
};
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
@ -131,6 +139,8 @@ static NodeProcessFunc animNodeTypeToProcessFunc(AnimNode::Type type) {
|
||||||
case AnimNode::Type::Manipulator: return processDoNothing;
|
case AnimNode::Type::Manipulator: return processDoNothing;
|
||||||
case AnimNode::Type::InverseKinematics: return processDoNothing;
|
case AnimNode::Type::InverseKinematics: return processDoNothing;
|
||||||
case AnimNode::Type::DefaultPose: return processDoNothing;
|
case AnimNode::Type::DefaultPose: return processDoNothing;
|
||||||
|
case AnimNode::Type::TwoBoneIK: return processDoNothing;
|
||||||
|
case AnimNode::Type::PoleVectorConstraint: return processDoNothing;
|
||||||
case AnimNode::Type::NumTypes: return nullptr;
|
case AnimNode::Type::NumTypes: return nullptr;
|
||||||
};
|
};
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
@ -189,6 +199,25 @@ static NodeProcessFunc animNodeTypeToProcessFunc(AnimNode::Type type) {
|
||||||
} \
|
} \
|
||||||
do {} while (0)
|
do {} while (0)
|
||||||
|
|
||||||
|
#define READ_VEC3(NAME, JSON_OBJ, ID, URL, ERROR_RETURN) \
|
||||||
|
auto NAME##_VAL = JSON_OBJ.value(#NAME); \
|
||||||
|
if (!NAME##_VAL.isArray()) { \
|
||||||
|
qCCritical(animation) << "AnimNodeLoader, error reading vector" \
|
||||||
|
<< #NAME << "id =" << ID \
|
||||||
|
<< ", url =" << URL.toDisplayString(); \
|
||||||
|
return ERROR_RETURN; \
|
||||||
|
} \
|
||||||
|
QJsonArray NAME##_ARRAY = NAME##_VAL.toArray(); \
|
||||||
|
if (NAME##_ARRAY.size() != 3) { \
|
||||||
|
qCCritical(animation) << "AnimNodeLoader, vector size != 3" \
|
||||||
|
<< #NAME << "id =" << ID \
|
||||||
|
<< ", url =" << URL.toDisplayString(); \
|
||||||
|
return ERROR_RETURN; \
|
||||||
|
} \
|
||||||
|
glm::vec3 NAME((float)NAME##_ARRAY.at(0).toDouble(), \
|
||||||
|
(float)NAME##_ARRAY.at(1).toDouble(), \
|
||||||
|
(float)NAME##_ARRAY.at(2).toDouble())
|
||||||
|
|
||||||
static AnimNode::Pointer loadNode(const QJsonObject& jsonObj, const QUrl& jsonUrl) {
|
static AnimNode::Pointer loadNode(const QJsonObject& jsonObj, const QUrl& jsonUrl) {
|
||||||
auto idVal = jsonObj.value("id");
|
auto idVal = jsonObj.value("id");
|
||||||
if (!idVal.isString()) {
|
if (!idVal.isString()) {
|
||||||
|
@ -216,6 +245,16 @@ static AnimNode::Pointer loadNode(const QJsonObject& jsonObj, const QUrl& jsonUr
|
||||||
}
|
}
|
||||||
auto dataObj = dataValue.toObject();
|
auto dataObj = dataValue.toObject();
|
||||||
|
|
||||||
|
std::vector<QString> outputJoints;
|
||||||
|
|
||||||
|
auto outputJoints_VAL = dataObj.value("outputJoints");
|
||||||
|
if (outputJoints_VAL.isArray()) {
|
||||||
|
QJsonArray outputJoints_ARRAY = outputJoints_VAL.toArray();
|
||||||
|
for (int i = 0; i < outputJoints_ARRAY.size(); i++) {
|
||||||
|
outputJoints.push_back(outputJoints_ARRAY.at(i).toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
assert((int)type >= 0 && type < AnimNode::Type::NumTypes);
|
assert((int)type >= 0 && type < AnimNode::Type::NumTypes);
|
||||||
auto node = (animNodeTypeToLoaderFunc(type))(dataObj, id, jsonUrl);
|
auto node = (animNodeTypeToLoaderFunc(type))(dataObj, id, jsonUrl);
|
||||||
if (!node) {
|
if (!node) {
|
||||||
|
@ -242,6 +281,9 @@ static AnimNode::Pointer loadNode(const QJsonObject& jsonObj, const QUrl& jsonUr
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((animNodeTypeToProcessFunc(type))(node, dataObj, id, jsonUrl)) {
|
if ((animNodeTypeToProcessFunc(type))(node, dataObj, id, jsonUrl)) {
|
||||||
|
for (auto&& outputJoint : outputJoints) {
|
||||||
|
node->addOutputJoint(outputJoint);
|
||||||
|
}
|
||||||
return node;
|
return node;
|
||||||
} else {
|
} else {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
@ -531,6 +573,41 @@ static AnimNode::Pointer loadDefaultPoseNode(const QJsonObject& jsonObj, const Q
|
||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static AnimNode::Pointer loadTwoBoneIKNode(const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl) {
|
||||||
|
READ_FLOAT(alpha, jsonObj, id, jsonUrl, nullptr);
|
||||||
|
READ_BOOL(enabled, jsonObj, id, jsonUrl, nullptr);
|
||||||
|
READ_FLOAT(interpDuration, jsonObj, id, jsonUrl, nullptr);
|
||||||
|
READ_STRING(baseJointName, jsonObj, id, jsonUrl, nullptr);
|
||||||
|
READ_STRING(midJointName, jsonObj, id, jsonUrl, nullptr);
|
||||||
|
READ_STRING(tipJointName, jsonObj, id, jsonUrl, nullptr);
|
||||||
|
READ_VEC3(midHingeAxis, jsonObj, id, jsonUrl, nullptr);
|
||||||
|
READ_STRING(alphaVar, jsonObj, id, jsonUrl, nullptr);
|
||||||
|
READ_STRING(enabledVar, jsonObj, id, jsonUrl, nullptr);
|
||||||
|
READ_STRING(endEffectorRotationVarVar, jsonObj, id, jsonUrl, nullptr);
|
||||||
|
READ_STRING(endEffectorPositionVarVar, jsonObj, id, jsonUrl, nullptr);
|
||||||
|
|
||||||
|
auto node = std::make_shared<AnimTwoBoneIK>(id, alpha, enabled, interpDuration,
|
||||||
|
baseJointName, midJointName, tipJointName, midHingeAxis,
|
||||||
|
alphaVar, enabledVar,
|
||||||
|
endEffectorRotationVarVar, endEffectorPositionVarVar);
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
static AnimNode::Pointer loadPoleVectorConstraintNode(const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl) {
|
||||||
|
READ_VEC3(referenceVector, jsonObj, id, jsonUrl, nullptr);
|
||||||
|
READ_BOOL(enabled, jsonObj, id, jsonUrl, nullptr);
|
||||||
|
READ_STRING(baseJointName, jsonObj, id, jsonUrl, nullptr);
|
||||||
|
READ_STRING(midJointName, jsonObj, id, jsonUrl, nullptr);
|
||||||
|
READ_STRING(tipJointName, jsonObj, id, jsonUrl, nullptr);
|
||||||
|
READ_STRING(enabledVar, jsonObj, id, jsonUrl, nullptr);
|
||||||
|
READ_STRING(poleVectorVar, jsonObj, id, jsonUrl, nullptr);
|
||||||
|
|
||||||
|
auto node = std::make_shared<AnimPoleVectorConstraint>(id, enabled, referenceVector,
|
||||||
|
baseJointName, midJointName, tipJointName,
|
||||||
|
enabledVar, poleVectorVar);
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
void buildChildMap(std::map<QString, int>& map, AnimNode::Pointer node) {
|
void buildChildMap(std::map<QString, int>& map, AnimNode::Pointer node) {
|
||||||
for (int i = 0; i < (int)node->getChildCount(); ++i) {
|
for (int i = 0; i < (int)node->getChildCount(); ++i) {
|
||||||
map.insert(std::pair<QString, int>(node->getChild(i)->getID(), i));
|
map.insert(std::pair<QString, int>(node->getChild(i)->getID(), i));
|
||||||
|
@ -682,7 +759,8 @@ AnimNode::Pointer AnimNodeLoader::load(const QByteArray& contents, const QUrl& j
|
||||||
QString version = versionVal.toString();
|
QString version = versionVal.toString();
|
||||||
|
|
||||||
// check version
|
// check version
|
||||||
if (version != "1.0") {
|
// AJT: TODO version check
|
||||||
|
if (version != "1.0" && version != "1.1") {
|
||||||
qCCritical(animation) << "AnimNodeLoader, bad version number" << version << "expected \"1.0\", url =" << jsonUrl.toDisplayString();
|
qCCritical(animation) << "AnimNodeLoader, bad version number" << version << "expected \"1.0\", url =" << jsonUrl.toDisplayString();
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
|
@ -41,7 +41,7 @@ void AnimOverlay::buildBoneSet(BoneSet boneSet) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const AnimPoseVec& AnimOverlay::evaluate(const AnimVariantMap& animVars, const AnimContext& context, float dt, Triggers& triggersOut) {
|
const AnimPoseVec& AnimOverlay::evaluate(const AnimVariantMap& animVars, const AnimContext& context, float dt, AnimVariantMap& triggersOut) {
|
||||||
|
|
||||||
// lookup parameters from animVars, using current instance variables as defaults.
|
// lookup parameters from animVars, using current instance variables as defaults.
|
||||||
// NOTE: switching bonesets can be an expensive operation, let's try to avoid it.
|
// NOTE: switching bonesets can be an expensive operation, let's try to avoid it.
|
||||||
|
@ -66,6 +66,9 @@ const AnimPoseVec& AnimOverlay::evaluate(const AnimVariantMap& animVars, const A
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
processOutputJoints(triggersOut);
|
||||||
|
|
||||||
return _poses;
|
return _poses;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -45,7 +45,7 @@ public:
|
||||||
AnimOverlay(const QString& id, BoneSet boneSet, float alpha);
|
AnimOverlay(const QString& id, BoneSet boneSet, float alpha);
|
||||||
virtual ~AnimOverlay() override;
|
virtual ~AnimOverlay() override;
|
||||||
|
|
||||||
virtual const AnimPoseVec& evaluate(const AnimVariantMap& animVars, const AnimContext& context, float dt, Triggers& triggersOut) override;
|
virtual const AnimPoseVec& evaluate(const AnimVariantMap& animVars, const AnimContext& context, float dt, AnimVariantMap& triggersOut) override;
|
||||||
|
|
||||||
void setBoneSetVar(const QString& boneSetVar) { _boneSetVar = boneSetVar; }
|
void setBoneSetVar(const QString& boneSetVar) { _boneSetVar = boneSetVar; }
|
||||||
void setAlphaVar(const QString& alphaVar) { _alphaVar = alphaVar; }
|
void setAlphaVar(const QString& alphaVar) { _alphaVar = alphaVar; }
|
||||||
|
|
244
libraries/animation/src/AnimPoleVectorConstraint.cpp
Normal file
244
libraries/animation/src/AnimPoleVectorConstraint.cpp
Normal file
|
@ -0,0 +1,244 @@
|
||||||
|
//
|
||||||
|
// AnimPoleVectorConstraint.cpp
|
||||||
|
//
|
||||||
|
// Created by Anthony J. Thibault on 5/12/18.
|
||||||
|
// Copyright (c) 2018 High Fidelity, Inc. All rights reserved.
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "AnimPoleVectorConstraint.h"
|
||||||
|
#include "AnimationLogging.h"
|
||||||
|
#include "AnimUtil.h"
|
||||||
|
#include "GLMHelpers.h"
|
||||||
|
|
||||||
|
const float FRAMES_PER_SECOND = 30.0f;
|
||||||
|
const float INTERP_DURATION = 6.0f;
|
||||||
|
|
||||||
|
AnimPoleVectorConstraint::AnimPoleVectorConstraint(const QString& id, bool enabled, glm::vec3 referenceVector,
|
||||||
|
const QString& baseJointName, const QString& midJointName, const QString& tipJointName,
|
||||||
|
const QString& enabledVar, const QString& poleVectorVar) :
|
||||||
|
AnimNode(AnimNode::Type::PoleVectorConstraint, id),
|
||||||
|
_enabled(enabled),
|
||||||
|
_referenceVector(referenceVector),
|
||||||
|
_baseJointName(baseJointName),
|
||||||
|
_midJointName(midJointName),
|
||||||
|
_tipJointName(tipJointName),
|
||||||
|
_enabledVar(enabledVar),
|
||||||
|
_poleVectorVar(poleVectorVar) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
AnimPoleVectorConstraint::~AnimPoleVectorConstraint() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
const AnimPoseVec& AnimPoleVectorConstraint::evaluate(const AnimVariantMap& animVars, const AnimContext& context, float dt, AnimVariantMap& triggersOut) {
|
||||||
|
|
||||||
|
assert(_children.size() == 1);
|
||||||
|
if (_children.size() != 1) {
|
||||||
|
return _poses;
|
||||||
|
}
|
||||||
|
|
||||||
|
// evalute underPoses
|
||||||
|
AnimPoseVec underPoses = _children[0]->evaluate(animVars, context, dt, triggersOut);
|
||||||
|
|
||||||
|
// if we don't have a skeleton, or jointName lookup failed.
|
||||||
|
if (!_skeleton || _baseJointIndex == -1 || _midJointIndex == -1 || _tipJointIndex == -1 || underPoses.size() == 0) {
|
||||||
|
// pass underPoses through unmodified.
|
||||||
|
_poses = underPoses;
|
||||||
|
return _poses;
|
||||||
|
}
|
||||||
|
|
||||||
|
// guard against size changes
|
||||||
|
if (underPoses.size() != _poses.size()) {
|
||||||
|
_poses = underPoses;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Look up poleVector from animVars, make sure to convert into geom space.
|
||||||
|
glm::vec3 poleVector = animVars.lookupRigToGeometryVector(_poleVectorVar, Vectors::UNIT_Z);
|
||||||
|
|
||||||
|
// determine if we should interpolate
|
||||||
|
bool enabled = animVars.lookup(_enabledVar, _enabled);
|
||||||
|
|
||||||
|
const float MIN_LENGTH = 1.0e-4f;
|
||||||
|
if (glm::length(poleVector) < MIN_LENGTH) {
|
||||||
|
enabled = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (enabled != _enabled) {
|
||||||
|
AnimChain poseChain;
|
||||||
|
poseChain.buildFromRelativePoses(_skeleton, _poses, _tipJointIndex);
|
||||||
|
if (enabled) {
|
||||||
|
beginInterp(InterpType::SnapshotToSolve, poseChain);
|
||||||
|
} else {
|
||||||
|
beginInterp(InterpType::SnapshotToUnderPoses, poseChain);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_enabled = enabled;
|
||||||
|
|
||||||
|
// don't build chains or do IK if we are disbled & not interping.
|
||||||
|
if (_interpType == InterpType::None && !enabled) {
|
||||||
|
_poses = underPoses;
|
||||||
|
return _poses;
|
||||||
|
}
|
||||||
|
|
||||||
|
// compute chain
|
||||||
|
AnimChain underChain;
|
||||||
|
underChain.buildFromRelativePoses(_skeleton, underPoses, _tipJointIndex);
|
||||||
|
AnimChain ikChain = underChain;
|
||||||
|
|
||||||
|
AnimPose baseParentPose = ikChain.getAbsolutePoseFromJointIndex(_baseParentJointIndex);
|
||||||
|
AnimPose basePose = ikChain.getAbsolutePoseFromJointIndex(_baseJointIndex);
|
||||||
|
AnimPose midPose = ikChain.getAbsolutePoseFromJointIndex(_midJointIndex);
|
||||||
|
AnimPose tipPose = ikChain.getAbsolutePoseFromJointIndex(_tipJointIndex);
|
||||||
|
|
||||||
|
// Look up refVector from animVars, make sure to convert into geom space.
|
||||||
|
glm::vec3 refVector = midPose.xformVectorFast(_referenceVector);
|
||||||
|
float refVectorLength = glm::length(refVector);
|
||||||
|
|
||||||
|
glm::vec3 axis = basePose.trans() - tipPose.trans();
|
||||||
|
float axisLength = glm::length(axis);
|
||||||
|
glm::vec3 unitAxis = axis / axisLength;
|
||||||
|
|
||||||
|
glm::vec3 sideVector = glm::cross(unitAxis, refVector);
|
||||||
|
float sideVectorLength = glm::length(sideVector);
|
||||||
|
|
||||||
|
// project refVector onto axis plane
|
||||||
|
glm::vec3 refVectorProj = refVector - glm::dot(refVector, unitAxis) * unitAxis;
|
||||||
|
float refVectorProjLength = glm::length(refVectorProj);
|
||||||
|
|
||||||
|
// project poleVector on plane formed by axis.
|
||||||
|
glm::vec3 poleVectorProj = poleVector - glm::dot(poleVector, unitAxis) * unitAxis;
|
||||||
|
float poleVectorProjLength = glm::length(poleVectorProj);
|
||||||
|
|
||||||
|
// double check for zero length vectors or vectors parallel to rotaiton axis.
|
||||||
|
if (axisLength > MIN_LENGTH && refVectorLength > MIN_LENGTH && sideVectorLength > MIN_LENGTH &&
|
||||||
|
refVectorProjLength > MIN_LENGTH && poleVectorProjLength > MIN_LENGTH) {
|
||||||
|
|
||||||
|
float dot = glm::clamp(glm::dot(refVectorProj / refVectorProjLength, poleVectorProj / poleVectorProjLength), 0.0f, 1.0f);
|
||||||
|
float sideDot = glm::dot(poleVector, sideVector);
|
||||||
|
float theta = copysignf(1.0f, sideDot) * acosf(dot);
|
||||||
|
|
||||||
|
glm::quat deltaRot = glm::angleAxis(theta, unitAxis);
|
||||||
|
|
||||||
|
// transform result back into parent relative frame.
|
||||||
|
glm::quat relBaseRot = glm::inverse(baseParentPose.rot()) * deltaRot * basePose.rot();
|
||||||
|
ikChain.setRelativePoseAtJointIndex(_baseJointIndex, AnimPose(relBaseRot, underPoses[_baseJointIndex].trans()));
|
||||||
|
|
||||||
|
glm::quat relTipRot = glm::inverse(midPose.rot()) * glm::inverse(deltaRot) * tipPose.rot();
|
||||||
|
ikChain.setRelativePoseAtJointIndex(_tipJointIndex, AnimPose(relTipRot, underPoses[_tipJointIndex].trans()));
|
||||||
|
}
|
||||||
|
|
||||||
|
// start off by initializing output poses with the underPoses
|
||||||
|
_poses = underPoses;
|
||||||
|
|
||||||
|
// apply smooth interpolation
|
||||||
|
if (_interpType != InterpType::None) {
|
||||||
|
_interpAlpha += _interpAlphaVel * dt;
|
||||||
|
|
||||||
|
if (_interpAlpha < 1.0f) {
|
||||||
|
AnimChain interpChain;
|
||||||
|
if (_interpType == InterpType::SnapshotToUnderPoses) {
|
||||||
|
interpChain = underChain;
|
||||||
|
interpChain.blend(_snapshotChain, _interpAlpha);
|
||||||
|
} else if (_interpType == InterpType::SnapshotToSolve) {
|
||||||
|
interpChain = ikChain;
|
||||||
|
interpChain.blend(_snapshotChain, _interpAlpha);
|
||||||
|
}
|
||||||
|
// copy interpChain into _poses
|
||||||
|
interpChain.outputRelativePoses(_poses);
|
||||||
|
} else {
|
||||||
|
// interpolation complete
|
||||||
|
_interpType = InterpType::None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_interpType == InterpType::None) {
|
||||||
|
if (enabled) {
|
||||||
|
// copy chain into _poses
|
||||||
|
ikChain.outputRelativePoses(_poses);
|
||||||
|
} else {
|
||||||
|
// copy under chain into _poses
|
||||||
|
underChain.outputRelativePoses(_poses);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (context.getEnableDebugDrawIKChains()) {
|
||||||
|
if (_interpType == InterpType::None && enabled) {
|
||||||
|
const vec4 BLUE(0.0f, 0.0f, 1.0f, 1.0f);
|
||||||
|
ikChain.debugDraw(context.getRigToWorldMatrix() * context.getGeometryToRigMatrix(), BLUE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (context.getEnableDebugDrawIKChains()) {
|
||||||
|
if (enabled) {
|
||||||
|
const glm::vec4 RED(1.0f, 0.0f, 0.0f, 1.0f);
|
||||||
|
const glm::vec4 GREEN(0.0f, 1.0f, 0.0f, 1.0f);
|
||||||
|
const glm::vec4 CYAN(0.0f, 1.0f, 1.0f, 1.0f);
|
||||||
|
const glm::vec4 YELLOW(1.0f, 0.0f, 1.0f, 1.0f);
|
||||||
|
const float VECTOR_LENGTH = 0.5f;
|
||||||
|
|
||||||
|
glm::mat4 geomToWorld = context.getRigToWorldMatrix() * context.getGeometryToRigMatrix();
|
||||||
|
|
||||||
|
// draw the pole
|
||||||
|
glm::vec3 start = transformPoint(geomToWorld, basePose.trans());
|
||||||
|
glm::vec3 end = transformPoint(geomToWorld, tipPose.trans());
|
||||||
|
DebugDraw::getInstance().drawRay(start, end, CYAN);
|
||||||
|
|
||||||
|
// draw the poleVector
|
||||||
|
glm::vec3 midPoint = 0.5f * (start + end);
|
||||||
|
glm::vec3 poleVectorEnd = midPoint + VECTOR_LENGTH * glm::normalize(transformVectorFast(geomToWorld, poleVector));
|
||||||
|
DebugDraw::getInstance().drawRay(midPoint, poleVectorEnd, GREEN);
|
||||||
|
|
||||||
|
// draw the refVector
|
||||||
|
glm::vec3 refVectorEnd = midPoint + VECTOR_LENGTH * glm::normalize(transformVectorFast(geomToWorld, refVector));
|
||||||
|
DebugDraw::getInstance().drawRay(midPoint, refVectorEnd, RED);
|
||||||
|
|
||||||
|
// draw the sideVector
|
||||||
|
glm::vec3 sideVector = glm::cross(poleVector, refVector);
|
||||||
|
glm::vec3 sideVectorEnd = midPoint + VECTOR_LENGTH * glm::normalize(transformVectorFast(geomToWorld, sideVector));
|
||||||
|
DebugDraw::getInstance().drawRay(midPoint, sideVectorEnd, YELLOW);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
processOutputJoints(triggersOut);
|
||||||
|
|
||||||
|
return _poses;
|
||||||
|
}
|
||||||
|
|
||||||
|
// for AnimDebugDraw rendering
|
||||||
|
const AnimPoseVec& AnimPoleVectorConstraint::getPosesInternal() const {
|
||||||
|
return _poses;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AnimPoleVectorConstraint::setSkeletonInternal(AnimSkeleton::ConstPointer skeleton) {
|
||||||
|
AnimNode::setSkeletonInternal(skeleton);
|
||||||
|
lookUpIndices();
|
||||||
|
}
|
||||||
|
|
||||||
|
void AnimPoleVectorConstraint::lookUpIndices() {
|
||||||
|
assert(_skeleton);
|
||||||
|
|
||||||
|
// look up bone indices by name
|
||||||
|
std::vector<int> indices = _skeleton->lookUpJointIndices({_baseJointName, _midJointName, _tipJointName});
|
||||||
|
|
||||||
|
// cache the results
|
||||||
|
_baseJointIndex = indices[0];
|
||||||
|
_midJointIndex = indices[1];
|
||||||
|
_tipJointIndex = indices[2];
|
||||||
|
|
||||||
|
if (_baseJointIndex != -1) {
|
||||||
|
_baseParentJointIndex = _skeleton->getParentIndex(_baseJointIndex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AnimPoleVectorConstraint::beginInterp(InterpType interpType, const AnimChain& chain) {
|
||||||
|
// capture the current poses in a snapshot.
|
||||||
|
_snapshotChain = chain;
|
||||||
|
|
||||||
|
_interpType = interpType;
|
||||||
|
_interpAlphaVel = FRAMES_PER_SECOND / INTERP_DURATION;
|
||||||
|
_interpAlpha = 0.0f;
|
||||||
|
}
|
74
libraries/animation/src/AnimPoleVectorConstraint.h
Normal file
74
libraries/animation/src/AnimPoleVectorConstraint.h
Normal file
|
@ -0,0 +1,74 @@
|
||||||
|
//
|
||||||
|
// AnimPoleVectorConstraint.h
|
||||||
|
//
|
||||||
|
// Created by Anthony J. Thibault on 5/25/18.
|
||||||
|
// Copyright (c) 2018 High Fidelity, Inc. All rights reserved.
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef hifi_AnimPoleVectorConstraint_h
|
||||||
|
#define hifi_AnimPoleVectorConstraint_h
|
||||||
|
|
||||||
|
#include "AnimNode.h"
|
||||||
|
#include "AnimChain.h"
|
||||||
|
|
||||||
|
// Three bone IK chain
|
||||||
|
|
||||||
|
class AnimPoleVectorConstraint : public AnimNode {
|
||||||
|
public:
|
||||||
|
friend class AnimTests;
|
||||||
|
|
||||||
|
AnimPoleVectorConstraint(const QString& id, bool enabled, glm::vec3 referenceVector,
|
||||||
|
const QString& baseJointName, const QString& midJointName, const QString& tipJointName,
|
||||||
|
const QString& enabledVar, const QString& poleVectorVar);
|
||||||
|
virtual ~AnimPoleVectorConstraint() override;
|
||||||
|
|
||||||
|
virtual const AnimPoseVec& evaluate(const AnimVariantMap& animVars, const AnimContext& context, float dt, AnimVariantMap& triggersOut) override;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
|
||||||
|
enum class InterpType {
|
||||||
|
None = 0,
|
||||||
|
SnapshotToUnderPoses,
|
||||||
|
SnapshotToSolve,
|
||||||
|
NumTypes
|
||||||
|
};
|
||||||
|
|
||||||
|
// for AnimDebugDraw rendering
|
||||||
|
virtual const AnimPoseVec& getPosesInternal() const override;
|
||||||
|
virtual void setSkeletonInternal(AnimSkeleton::ConstPointer skeleton) override;
|
||||||
|
|
||||||
|
void lookUpIndices();
|
||||||
|
void beginInterp(InterpType interpType, const AnimChain& chain);
|
||||||
|
|
||||||
|
AnimPoseVec _poses;
|
||||||
|
|
||||||
|
bool _enabled;
|
||||||
|
glm::vec3 _referenceVector;
|
||||||
|
|
||||||
|
QString _baseJointName;
|
||||||
|
QString _midJointName;
|
||||||
|
QString _tipJointName;
|
||||||
|
|
||||||
|
QString _enabledVar;
|
||||||
|
QString _poleVectorVar;
|
||||||
|
|
||||||
|
int _baseParentJointIndex { -1 };
|
||||||
|
int _baseJointIndex { -1 };
|
||||||
|
int _midJointIndex { -1 };
|
||||||
|
int _tipJointIndex { -1 };
|
||||||
|
|
||||||
|
InterpType _interpType { InterpType::None };
|
||||||
|
float _interpAlphaVel { 0.0f };
|
||||||
|
float _interpAlpha { 0.0f };
|
||||||
|
|
||||||
|
AnimChain _snapshotChain;
|
||||||
|
|
||||||
|
// no copies
|
||||||
|
AnimPoleVectorConstraint(const AnimPoleVectorConstraint&) = delete;
|
||||||
|
AnimPoleVectorConstraint& operator=(const AnimPoleVectorConstraint&) = delete;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // hifi_AnimPoleVectorConstraint_h
|
|
@ -12,6 +12,7 @@
|
||||||
#include <GLMHelpers.h>
|
#include <GLMHelpers.h>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <glm/gtc/matrix_transform.hpp>
|
#include <glm/gtc/matrix_transform.hpp>
|
||||||
|
#include "AnimUtil.h"
|
||||||
|
|
||||||
const AnimPose AnimPose::identity = AnimPose(glm::vec3(1.0f),
|
const AnimPose AnimPose::identity = AnimPose(glm::vec3(1.0f),
|
||||||
glm::quat(),
|
glm::quat(),
|
||||||
|
@ -77,4 +78,8 @@ AnimPose::operator glm::mat4() const {
|
||||||
glm::vec4(zAxis, 0.0f), glm::vec4(_trans, 1.0f));
|
glm::vec4(zAxis, 0.0f), glm::vec4(_trans, 1.0f));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void AnimPose::blend(const AnimPose& srcPose, float alpha) {
|
||||||
|
_scale = lerp(srcPose._scale, _scale, alpha);
|
||||||
|
_rot = safeLerp(srcPose._rot, _rot, alpha);
|
||||||
|
_trans = lerp(srcPose._trans, _trans, alpha);
|
||||||
|
}
|
||||||
|
|
|
@ -46,6 +46,8 @@ public:
|
||||||
const glm::vec3& trans() const { return _trans; }
|
const glm::vec3& trans() const { return _trans; }
|
||||||
glm::vec3& trans() { return _trans; }
|
glm::vec3& trans() { return _trans; }
|
||||||
|
|
||||||
|
void blend(const AnimPose& srcPose, float alpha);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
friend QDebug operator<<(QDebug debug, const AnimPose& pose);
|
friend QDebug operator<<(QDebug debug, const AnimPose& pose);
|
||||||
glm::vec3 _scale { 1.0f };
|
glm::vec3 _scale { 1.0f };
|
||||||
|
|
|
@ -282,3 +282,17 @@ void AnimSkeleton::dump(const AnimPoseVec& poses) const {
|
||||||
qCDebug(animation) << "]";
|
qCDebug(animation) << "]";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::vector<int> AnimSkeleton::lookUpJointIndices(const std::vector<QString>& jointNames) const {
|
||||||
|
std::vector<int> result;
|
||||||
|
result.reserve(jointNames.size());
|
||||||
|
for (auto& name : jointNames) {
|
||||||
|
int index = nameToJointIndex(name);
|
||||||
|
if (index == -1) {
|
||||||
|
qWarning(animation) << "AnimSkeleton::lookUpJointIndices(): could not find bone with named " << name;
|
||||||
|
}
|
||||||
|
result.push_back(index);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -61,6 +61,8 @@ public:
|
||||||
void dump(bool verbose) const;
|
void dump(bool verbose) const;
|
||||||
void dump(const AnimPoseVec& poses) const;
|
void dump(const AnimPoseVec& poses) const;
|
||||||
|
|
||||||
|
std::vector<int> lookUpJointIndices(const std::vector<QString>& jointNames) const;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void buildSkeletonFromJoints(const std::vector<FBXJoint>& joints);
|
void buildSkeletonFromJoints(const std::vector<FBXJoint>& joints);
|
||||||
|
|
||||||
|
|
|
@ -21,7 +21,7 @@ AnimStateMachine::~AnimStateMachine() {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const AnimPoseVec& AnimStateMachine::evaluate(const AnimVariantMap& animVars, const AnimContext& context, float dt, Triggers& triggersOut) {
|
const AnimPoseVec& AnimStateMachine::evaluate(const AnimVariantMap& animVars, const AnimContext& context, float dt, AnimVariantMap& triggersOut) {
|
||||||
|
|
||||||
QString desiredStateID = animVars.lookup(_currentStateVar, _currentState->getID());
|
QString desiredStateID = animVars.lookup(_currentStateVar, _currentState->getID());
|
||||||
if (_currentState->getID() != desiredStateID) {
|
if (_currentState->getID() != desiredStateID) {
|
||||||
|
@ -81,6 +81,9 @@ const AnimPoseVec& AnimStateMachine::evaluate(const AnimVariantMap& animVars, co
|
||||||
if (!_duringInterp) {
|
if (!_duringInterp) {
|
||||||
_poses = currentStateNode->evaluate(animVars, context, dt, triggersOut);
|
_poses = currentStateNode->evaluate(animVars, context, dt, triggersOut);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
processOutputJoints(triggersOut);
|
||||||
|
|
||||||
return _poses;
|
return _poses;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -107,7 +110,7 @@ void AnimStateMachine::switchState(const AnimVariantMap& animVars, const AnimCon
|
||||||
|
|
||||||
// because dt is 0, we should not encounter any triggers
|
// because dt is 0, we should not encounter any triggers
|
||||||
const float dt = 0.0f;
|
const float dt = 0.0f;
|
||||||
Triggers triggers;
|
AnimVariantMap triggers;
|
||||||
|
|
||||||
if (_interpType == InterpType::SnapshotBoth) {
|
if (_interpType == InterpType::SnapshotBoth) {
|
||||||
// snapshot previous pose.
|
// snapshot previous pose.
|
||||||
|
|
|
@ -113,7 +113,7 @@ public:
|
||||||
explicit AnimStateMachine(const QString& id);
|
explicit AnimStateMachine(const QString& id);
|
||||||
virtual ~AnimStateMachine() override;
|
virtual ~AnimStateMachine() override;
|
||||||
|
|
||||||
virtual const AnimPoseVec& evaluate(const AnimVariantMap& animVars, const AnimContext& context, float dt, Triggers& triggersOut) override;
|
virtual const AnimPoseVec& evaluate(const AnimVariantMap& animVars, const AnimContext& context, float dt, AnimVariantMap& triggersOut) override;
|
||||||
|
|
||||||
void setCurrentStateVar(QString& currentStateVar) { _currentStateVar = currentStateVar; }
|
void setCurrentStateVar(QString& currentStateVar) { _currentStateVar = currentStateVar; }
|
||||||
|
|
||||||
|
|
293
libraries/animation/src/AnimTwoBoneIK.cpp
Normal file
293
libraries/animation/src/AnimTwoBoneIK.cpp
Normal file
|
@ -0,0 +1,293 @@
|
||||||
|
//
|
||||||
|
// AnimTwoBoneIK.cpp
|
||||||
|
//
|
||||||
|
// Created by Anthony J. Thibault on 5/12/18.
|
||||||
|
// Copyright (c) 2018 High Fidelity, Inc. All rights reserved.
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "AnimTwoBoneIK.h"
|
||||||
|
|
||||||
|
#include <DebugDraw.h>
|
||||||
|
|
||||||
|
#include "AnimationLogging.h"
|
||||||
|
#include "AnimUtil.h"
|
||||||
|
|
||||||
|
const float FRAMES_PER_SECOND = 30.0f;
|
||||||
|
|
||||||
|
AnimTwoBoneIK::AnimTwoBoneIK(const QString& id, float alpha, bool enabled, float interpDuration,
|
||||||
|
const QString& baseJointName, const QString& midJointName,
|
||||||
|
const QString& tipJointName, const glm::vec3& midHingeAxis,
|
||||||
|
const QString& alphaVar, const QString& enabledVar,
|
||||||
|
const QString& endEffectorRotationVarVar, const QString& endEffectorPositionVarVar) :
|
||||||
|
AnimNode(AnimNode::Type::TwoBoneIK, id),
|
||||||
|
_alpha(alpha),
|
||||||
|
_enabled(enabled),
|
||||||
|
_interpDuration(interpDuration),
|
||||||
|
_baseJointName(baseJointName),
|
||||||
|
_midJointName(midJointName),
|
||||||
|
_tipJointName(tipJointName),
|
||||||
|
_midHingeAxis(glm::normalize(midHingeAxis)),
|
||||||
|
_alphaVar(alphaVar),
|
||||||
|
_enabledVar(enabledVar),
|
||||||
|
_endEffectorRotationVarVar(endEffectorRotationVarVar),
|
||||||
|
_endEffectorPositionVarVar(endEffectorPositionVarVar),
|
||||||
|
_prevEndEffectorRotationVar(),
|
||||||
|
_prevEndEffectorPositionVar()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
AnimTwoBoneIK::~AnimTwoBoneIK() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
const AnimPoseVec& AnimTwoBoneIK::evaluate(const AnimVariantMap& animVars, const AnimContext& context, float dt, AnimVariantMap& triggersOut) {
|
||||||
|
|
||||||
|
assert(_children.size() == 1);
|
||||||
|
if (_children.size() != 1) {
|
||||||
|
return _poses;
|
||||||
|
}
|
||||||
|
|
||||||
|
// evalute underPoses
|
||||||
|
AnimPoseVec underPoses = _children[0]->evaluate(animVars, context, dt, triggersOut);
|
||||||
|
|
||||||
|
// if we don't have a skeleton, or jointName lookup failed.
|
||||||
|
if (!_skeleton || _baseJointIndex == -1 || _midJointIndex == -1 || _tipJointIndex == -1 || underPoses.size() == 0) {
|
||||||
|
// pass underPoses through unmodified.
|
||||||
|
_poses = underPoses;
|
||||||
|
return _poses;
|
||||||
|
}
|
||||||
|
|
||||||
|
// guard against size changes
|
||||||
|
if (underPoses.size() != _poses.size()) {
|
||||||
|
_poses = underPoses;
|
||||||
|
}
|
||||||
|
|
||||||
|
const float MIN_ALPHA = 0.0f;
|
||||||
|
const float MAX_ALPHA = 1.0f;
|
||||||
|
float alpha = glm::clamp(animVars.lookup(_alphaVar, _alpha), MIN_ALPHA, MAX_ALPHA);
|
||||||
|
|
||||||
|
// don't perform IK if we have bad indices, or alpha is zero
|
||||||
|
if (_tipJointIndex == -1 || _midJointIndex == -1 || _baseJointIndex == -1 || alpha == 0.0f) {
|
||||||
|
_poses = underPoses;
|
||||||
|
return _poses;
|
||||||
|
}
|
||||||
|
|
||||||
|
// determine if we should interpolate
|
||||||
|
bool enabled = animVars.lookup(_enabledVar, _enabled);
|
||||||
|
if (enabled != _enabled) {
|
||||||
|
AnimChain poseChain;
|
||||||
|
poseChain.buildFromRelativePoses(_skeleton, _poses, _tipJointIndex);
|
||||||
|
if (enabled) {
|
||||||
|
beginInterp(InterpType::SnapshotToSolve, poseChain);
|
||||||
|
} else {
|
||||||
|
beginInterp(InterpType::SnapshotToUnderPoses, poseChain);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_enabled = enabled;
|
||||||
|
|
||||||
|
// don't build chains or do IK if we are disbled & not interping.
|
||||||
|
if (_interpType == InterpType::None && !enabled) {
|
||||||
|
_poses = underPoses;
|
||||||
|
return _poses;
|
||||||
|
}
|
||||||
|
|
||||||
|
// compute chain
|
||||||
|
AnimChain underChain;
|
||||||
|
underChain.buildFromRelativePoses(_skeleton, underPoses, _tipJointIndex);
|
||||||
|
AnimChain ikChain = underChain;
|
||||||
|
|
||||||
|
AnimPose baseParentPose = ikChain.getAbsolutePoseFromJointIndex(_baseParentJointIndex);
|
||||||
|
AnimPose basePose = ikChain.getAbsolutePoseFromJointIndex(_baseJointIndex);
|
||||||
|
AnimPose midPose = ikChain.getAbsolutePoseFromJointIndex(_midJointIndex);
|
||||||
|
AnimPose tipPose = ikChain.getAbsolutePoseFromJointIndex(_tipJointIndex);
|
||||||
|
|
||||||
|
QString endEffectorRotationVar = animVars.lookup(_endEffectorRotationVarVar, QString(""));
|
||||||
|
QString endEffectorPositionVar = animVars.lookup(_endEffectorPositionVarVar, QString(""));
|
||||||
|
|
||||||
|
// if either of the endEffectorVars have changed
|
||||||
|
if ((!_prevEndEffectorRotationVar.isEmpty() && (_prevEndEffectorRotationVar != endEffectorRotationVar)) ||
|
||||||
|
(!_prevEndEffectorPositionVar.isEmpty() && (_prevEndEffectorPositionVar != endEffectorPositionVar))) {
|
||||||
|
// begin interp to smooth out transition between prev and new end effector.
|
||||||
|
AnimChain poseChain;
|
||||||
|
poseChain.buildFromRelativePoses(_skeleton, _poses, _tipJointIndex);
|
||||||
|
beginInterp(InterpType::SnapshotToSolve, poseChain);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Look up end effector from animVars, make sure to convert into geom space.
|
||||||
|
// First look in the triggers then look in the animVars, so we can follow output joints underneath us in the anim graph
|
||||||
|
AnimPose targetPose(tipPose);
|
||||||
|
if (triggersOut.hasKey(endEffectorRotationVar)) {
|
||||||
|
targetPose.rot() = triggersOut.lookupRigToGeometry(endEffectorRotationVar, tipPose.rot());
|
||||||
|
} else if (animVars.hasKey(endEffectorRotationVar)) {
|
||||||
|
targetPose.rot() = animVars.lookupRigToGeometry(endEffectorRotationVar, tipPose.rot());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (triggersOut.hasKey(endEffectorPositionVar)) {
|
||||||
|
targetPose.trans() = triggersOut.lookupRigToGeometry(endEffectorPositionVar, tipPose.trans());
|
||||||
|
} else if (animVars.hasKey(endEffectorRotationVar)) {
|
||||||
|
targetPose.trans() = animVars.lookupRigToGeometry(endEffectorPositionVar, tipPose.trans());
|
||||||
|
}
|
||||||
|
|
||||||
|
_prevEndEffectorRotationVar = endEffectorRotationVar;
|
||||||
|
_prevEndEffectorPositionVar = endEffectorPositionVar;
|
||||||
|
|
||||||
|
glm::vec3 bicepVector = midPose.trans() - basePose.trans();
|
||||||
|
float r0 = glm::length(bicepVector);
|
||||||
|
bicepVector = bicepVector / r0;
|
||||||
|
|
||||||
|
glm::vec3 forearmVector = tipPose.trans() - midPose.trans();
|
||||||
|
float r1 = glm::length(forearmVector);
|
||||||
|
forearmVector = forearmVector / r1;
|
||||||
|
|
||||||
|
float d = glm::length(targetPose.trans() - basePose.trans());
|
||||||
|
|
||||||
|
// http://mathworld.wolfram.com/Circle-CircleIntersection.html
|
||||||
|
float midAngle = 0.0f;
|
||||||
|
if (d < r0 + r1) {
|
||||||
|
float y = sqrtf((-d + r1 - r0) * (-d - r1 + r0) * (-d + r1 + r0) * (d + r1 + r0)) / (2.0f * d);
|
||||||
|
midAngle = PI - (acosf(y / r0) + acosf(y / r1));
|
||||||
|
}
|
||||||
|
|
||||||
|
// compute midJoint rotation
|
||||||
|
glm::quat relMidRot = glm::angleAxis(midAngle, _midHingeAxis);
|
||||||
|
|
||||||
|
// insert new relative pose into the chain and rebuild it.
|
||||||
|
ikChain.setRelativePoseAtJointIndex(_midJointIndex, AnimPose(relMidRot, underPoses[_midJointIndex].trans()));
|
||||||
|
ikChain.buildDirtyAbsolutePoses();
|
||||||
|
|
||||||
|
// recompute tip pose after mid joint has been rotated
|
||||||
|
AnimPose newTipPose = ikChain.getAbsolutePoseFromJointIndex(_tipJointIndex);
|
||||||
|
|
||||||
|
glm::vec3 leverArm = newTipPose.trans() - basePose.trans();
|
||||||
|
glm::vec3 targetLine = targetPose.trans() - basePose.trans();
|
||||||
|
|
||||||
|
// compute delta rotation that brings leverArm parallel to targetLine
|
||||||
|
glm::vec3 axis = glm::cross(leverArm, targetLine);
|
||||||
|
float axisLength = glm::length(axis);
|
||||||
|
const float MIN_AXIS_LENGTH = 1.0e-4f;
|
||||||
|
if (axisLength > MIN_AXIS_LENGTH) {
|
||||||
|
axis /= axisLength;
|
||||||
|
float cosAngle = glm::clamp(glm::dot(leverArm, targetLine) / (glm::length(leverArm) * glm::length(targetLine)), -1.0f, 1.0f);
|
||||||
|
float angle = acosf(cosAngle);
|
||||||
|
glm::quat deltaRot = glm::angleAxis(angle, axis);
|
||||||
|
|
||||||
|
// combine deltaRot with basePose.
|
||||||
|
glm::quat absRot = deltaRot * basePose.rot();
|
||||||
|
|
||||||
|
// transform result back into parent relative frame.
|
||||||
|
glm::quat relBaseRot = glm::inverse(baseParentPose.rot()) * absRot;
|
||||||
|
ikChain.setRelativePoseAtJointIndex(_baseJointIndex, AnimPose(relBaseRot, underPoses[_baseJointIndex].trans()));
|
||||||
|
}
|
||||||
|
|
||||||
|
// recompute midJoint pose after base has been rotated.
|
||||||
|
ikChain.buildDirtyAbsolutePoses();
|
||||||
|
AnimPose midJointPose = ikChain.getAbsolutePoseFromJointIndex(_midJointIndex);
|
||||||
|
|
||||||
|
// transform target rotation in to parent relative frame.
|
||||||
|
glm::quat relTipRot = glm::inverse(midJointPose.rot()) * targetPose.rot();
|
||||||
|
ikChain.setRelativePoseAtJointIndex(_tipJointIndex, AnimPose(relTipRot, underPoses[_tipJointIndex].trans()));
|
||||||
|
|
||||||
|
// blend with the underChain
|
||||||
|
ikChain.blend(underChain, alpha);
|
||||||
|
|
||||||
|
// start off by initializing output poses with the underPoses
|
||||||
|
_poses = underPoses;
|
||||||
|
|
||||||
|
// apply smooth interpolation
|
||||||
|
if (_interpType != InterpType::None) {
|
||||||
|
_interpAlpha += _interpAlphaVel * dt;
|
||||||
|
|
||||||
|
if (_interpAlpha < 1.0f) {
|
||||||
|
AnimChain interpChain;
|
||||||
|
if (_interpType == InterpType::SnapshotToUnderPoses) {
|
||||||
|
interpChain = underChain;
|
||||||
|
interpChain.blend(_snapshotChain, _interpAlpha);
|
||||||
|
} else if (_interpType == InterpType::SnapshotToSolve) {
|
||||||
|
interpChain = ikChain;
|
||||||
|
interpChain.blend(_snapshotChain, _interpAlpha);
|
||||||
|
}
|
||||||
|
// copy interpChain into _poses
|
||||||
|
interpChain.outputRelativePoses(_poses);
|
||||||
|
} else {
|
||||||
|
// interpolation complete
|
||||||
|
_interpType = InterpType::None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_interpType == InterpType::None) {
|
||||||
|
if (enabled) {
|
||||||
|
// copy chain into _poses
|
||||||
|
ikChain.outputRelativePoses(_poses);
|
||||||
|
} else {
|
||||||
|
// copy under chain into _poses
|
||||||
|
underChain.outputRelativePoses(_poses);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (context.getEnableDebugDrawIKTargets()) {
|
||||||
|
const vec4 RED(1.0f, 0.0f, 0.0f, 1.0f);
|
||||||
|
const vec4 GREEN(0.0f, 1.0f, 0.0f, 1.0f);
|
||||||
|
glm::mat4 rigToAvatarMat = createMatFromQuatAndPos(Quaternions::Y_180, glm::vec3());
|
||||||
|
|
||||||
|
glm::mat4 geomTargetMat = createMatFromQuatAndPos(targetPose.rot(), targetPose.trans());
|
||||||
|
glm::mat4 avatarTargetMat = rigToAvatarMat * context.getGeometryToRigMatrix() * geomTargetMat;
|
||||||
|
|
||||||
|
QString name = QString("%1_target").arg(_id);
|
||||||
|
DebugDraw::getInstance().addMyAvatarMarker(name, glmExtractRotation(avatarTargetMat),
|
||||||
|
extractTranslation(avatarTargetMat), _enabled ? GREEN : RED);
|
||||||
|
} else if (_lastEnableDebugDrawIKTargets) {
|
||||||
|
QString name = QString("%1_target").arg(_id);
|
||||||
|
DebugDraw::getInstance().removeMyAvatarMarker(name);
|
||||||
|
}
|
||||||
|
_lastEnableDebugDrawIKTargets = context.getEnableDebugDrawIKTargets();
|
||||||
|
|
||||||
|
if (context.getEnableDebugDrawIKChains()) {
|
||||||
|
if (_interpType == InterpType::None && enabled) {
|
||||||
|
const vec4 CYAN(0.0f, 1.0f, 1.0f, 1.0f);
|
||||||
|
ikChain.debugDraw(context.getRigToWorldMatrix() * context.getGeometryToRigMatrix(), CYAN);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
processOutputJoints(triggersOut);
|
||||||
|
|
||||||
|
return _poses;
|
||||||
|
}
|
||||||
|
|
||||||
|
// for AnimDebugDraw rendering
|
||||||
|
const AnimPoseVec& AnimTwoBoneIK::getPosesInternal() const {
|
||||||
|
return _poses;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AnimTwoBoneIK::setSkeletonInternal(AnimSkeleton::ConstPointer skeleton) {
|
||||||
|
AnimNode::setSkeletonInternal(skeleton);
|
||||||
|
lookUpIndices();
|
||||||
|
}
|
||||||
|
|
||||||
|
void AnimTwoBoneIK::lookUpIndices() {
|
||||||
|
assert(_skeleton);
|
||||||
|
|
||||||
|
// look up bone indices by name
|
||||||
|
std::vector<int> indices = _skeleton->lookUpJointIndices({_baseJointName, _midJointName, _tipJointName});
|
||||||
|
|
||||||
|
// cache the results
|
||||||
|
_baseJointIndex = indices[0];
|
||||||
|
_midJointIndex = indices[1];
|
||||||
|
_tipJointIndex = indices[2];
|
||||||
|
|
||||||
|
if (_baseJointIndex != -1) {
|
||||||
|
_baseParentJointIndex = _skeleton->getParentIndex(_baseJointIndex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AnimTwoBoneIK::beginInterp(InterpType interpType, const AnimChain& chain) {
|
||||||
|
// capture the current poses in a snapshot.
|
||||||
|
_snapshotChain = chain;
|
||||||
|
|
||||||
|
_interpType = interpType;
|
||||||
|
_interpAlphaVel = FRAMES_PER_SECOND / _interpDuration;
|
||||||
|
_interpAlpha = 0.0f;
|
||||||
|
}
|
83
libraries/animation/src/AnimTwoBoneIK.h
Normal file
83
libraries/animation/src/AnimTwoBoneIK.h
Normal file
|
@ -0,0 +1,83 @@
|
||||||
|
//
|
||||||
|
// AnimTwoBoneIK.h
|
||||||
|
//
|
||||||
|
// Created by Anthony J. Thibault on 5/12/18.
|
||||||
|
// Copyright (c) 2018 High Fidelity, Inc. All rights reserved.
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef hifi_AnimTwoBoneIK_h
|
||||||
|
#define hifi_AnimTwoBoneIK_h
|
||||||
|
|
||||||
|
#include "AnimNode.h"
|
||||||
|
#include "AnimChain.h"
|
||||||
|
|
||||||
|
// Simple two bone IK chain
|
||||||
|
class AnimTwoBoneIK : public AnimNode {
|
||||||
|
public:
|
||||||
|
friend class AnimTests;
|
||||||
|
|
||||||
|
AnimTwoBoneIK(const QString& id, float alpha, bool enabled, float interpDuration,
|
||||||
|
const QString& baseJointName, const QString& midJointName,
|
||||||
|
const QString& tipJointName, const glm::vec3& midHingeAxis,
|
||||||
|
const QString& alphaVar, const QString& enabledVar,
|
||||||
|
const QString& endEffectorRotationVarVar, const QString& endEffectorPositionVarVar);
|
||||||
|
virtual ~AnimTwoBoneIK() override;
|
||||||
|
|
||||||
|
virtual const AnimPoseVec& evaluate(const AnimVariantMap& animVars, const AnimContext& context, float dt, AnimVariantMap& triggersOut) override;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
|
||||||
|
enum class InterpType {
|
||||||
|
None = 0,
|
||||||
|
SnapshotToUnderPoses,
|
||||||
|
SnapshotToSolve,
|
||||||
|
NumTypes
|
||||||
|
};
|
||||||
|
|
||||||
|
// for AnimDebugDraw rendering
|
||||||
|
virtual const AnimPoseVec& getPosesInternal() const override;
|
||||||
|
virtual void setSkeletonInternal(AnimSkeleton::ConstPointer skeleton) override;
|
||||||
|
|
||||||
|
void lookUpIndices();
|
||||||
|
void beginInterp(InterpType interpType, const AnimChain& chain);
|
||||||
|
|
||||||
|
AnimPoseVec _poses;
|
||||||
|
|
||||||
|
float _alpha;
|
||||||
|
bool _enabled;
|
||||||
|
float _interpDuration; // in frames (1/30 sec)
|
||||||
|
QString _baseJointName;
|
||||||
|
QString _midJointName;
|
||||||
|
QString _tipJointName;
|
||||||
|
glm::vec3 _midHingeAxis; // in baseJoint relative frame, should be normalized
|
||||||
|
|
||||||
|
int _baseParentJointIndex { -1 };
|
||||||
|
int _baseJointIndex { -1 };
|
||||||
|
int _midJointIndex { -1 };
|
||||||
|
int _tipJointIndex { -1 };
|
||||||
|
|
||||||
|
QString _alphaVar; // float - (0, 1) 0 means underPoses only, 1 means IK only.
|
||||||
|
QString _enabledVar; // bool
|
||||||
|
QString _endEffectorRotationVarVar; // string
|
||||||
|
QString _endEffectorPositionVarVar; // string
|
||||||
|
|
||||||
|
QString _prevEndEffectorRotationVar;
|
||||||
|
QString _prevEndEffectorPositionVar;
|
||||||
|
|
||||||
|
InterpType _interpType { InterpType::None };
|
||||||
|
float _interpAlphaVel { 0.0f };
|
||||||
|
float _interpAlpha { 0.0f };
|
||||||
|
|
||||||
|
AnimChain _snapshotChain;
|
||||||
|
|
||||||
|
bool _lastEnableDebugDrawIKTargets { false };
|
||||||
|
|
||||||
|
// no copies
|
||||||
|
AnimTwoBoneIK(const AnimTwoBoneIK&) = delete;
|
||||||
|
AnimTwoBoneIK& operator=(const AnimTwoBoneIK&) = delete;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // hifi_AnimTwoBoneIK_h
|
|
@ -21,14 +21,6 @@ void blend(size_t numPoses, const AnimPose* a, const AnimPose* b, float alpha, A
|
||||||
const AnimPose& aPose = a[i];
|
const AnimPose& aPose = a[i];
|
||||||
const AnimPose& bPose = b[i];
|
const AnimPose& bPose = b[i];
|
||||||
|
|
||||||
// adjust signs if necessary
|
|
||||||
const glm::quat& q1 = aPose.rot();
|
|
||||||
glm::quat q2 = bPose.rot();
|
|
||||||
float dot = glm::dot(q1, q2);
|
|
||||||
if (dot < 0.0f) {
|
|
||||||
q2 = -q2;
|
|
||||||
}
|
|
||||||
|
|
||||||
result[i].scale() = lerp(aPose.scale(), bPose.scale(), alpha);
|
result[i].scale() = lerp(aPose.scale(), bPose.scale(), alpha);
|
||||||
result[i].rot() = safeLerp(aPose.rot(), bPose.rot(), 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);
|
||||||
|
@ -53,7 +45,7 @@ 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, AnimVariantMap& triggersOut) {
|
||||||
|
|
||||||
const float EPSILON = 0.0001f;
|
const float EPSILON = 0.0001f;
|
||||||
float frame = currentFrame;
|
float frame = currentFrame;
|
||||||
|
@ -79,12 +71,12 @@ float accumulateTime(float startFrame, float endFrame, float timeScale, float cu
|
||||||
if (framesRemaining >= framesTillEnd) {
|
if (framesRemaining >= framesTillEnd) {
|
||||||
if (loopFlag) {
|
if (loopFlag) {
|
||||||
// anim loop
|
// anim loop
|
||||||
triggersOut.push_back(id + "OnLoop");
|
triggersOut.setTrigger(id + "OnLoop");
|
||||||
framesRemaining -= framesTillEnd;
|
framesRemaining -= framesTillEnd;
|
||||||
frame = clampedStartFrame;
|
frame = clampedStartFrame;
|
||||||
} else {
|
} else {
|
||||||
// anim end
|
// anim end
|
||||||
triggersOut.push_back(id + "OnDone");
|
triggersOut.setTrigger(id + "OnDone");
|
||||||
frame = endFrame;
|
frame = endFrame;
|
||||||
framesRemaining = 0.0f;
|
framesRemaining = 0.0f;
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,7 +19,7 @@ void blend(size_t numPoses, const AnimPose* a, const AnimPose* b, float alpha, A
|
||||||
glm::quat averageQuats(size_t numQuats, const glm::quat* quats);
|
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, AnimVariantMap& triggersOut);
|
||||||
|
|
||||||
inline glm::quat safeLerp(const glm::quat& a, const glm::quat& b, float alpha) {
|
inline glm::quat safeLerp(const glm::quat& a, const glm::quat& b, float alpha) {
|
||||||
// adjust signs if necessary
|
// adjust signs if necessary
|
||||||
|
|
|
@ -24,71 +24,12 @@ class Animation;
|
||||||
|
|
||||||
typedef QSharedPointer<Animation> AnimationPointer;
|
typedef QSharedPointer<Animation> AnimationPointer;
|
||||||
|
|
||||||
/// Scriptable interface for FBX animation loading.
|
|
||||||
class AnimationCache : public ResourceCache, public Dependency {
|
class AnimationCache : public ResourceCache, public Dependency {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
SINGLETON_DEPENDENCY
|
SINGLETON_DEPENDENCY
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
// Properties are copied over from ResourceCache (see ResourceCache.h for reason).
|
|
||||||
|
|
||||||
/**jsdoc
|
|
||||||
* API to manage animation cache resources.
|
|
||||||
* @namespace AnimationCache
|
|
||||||
*
|
|
||||||
* @hifi-interface
|
|
||||||
* @hifi-client-entity
|
|
||||||
* @hifi-assignment-client
|
|
||||||
*
|
|
||||||
* @property {number} numTotal - Total number of total resources. <em>Read-only.</em>
|
|
||||||
* @property {number} numCached - Total number of cached resource. <em>Read-only.</em>
|
|
||||||
* @property {number} sizeTotal - Size in bytes of all resources. <em>Read-only.</em>
|
|
||||||
* @property {number} sizeCached - Size in bytes of all cached resources. <em>Read-only.</em>
|
|
||||||
*/
|
|
||||||
|
|
||||||
// Functions are copied over from ResourceCache (see ResourceCache.h for reason).
|
|
||||||
|
|
||||||
/**jsdoc
|
|
||||||
* Get the list of all resource URLs.
|
|
||||||
* @function AnimationCache.getResourceList
|
|
||||||
* @returns {string[]}
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**jsdoc
|
|
||||||
* @function AnimationCache.dirty
|
|
||||||
* @returns {Signal}
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**jsdoc
|
|
||||||
* @function AnimationCache.updateTotalSize
|
|
||||||
* @param {number} deltaSize
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**jsdoc
|
|
||||||
* Prefetches a resource.
|
|
||||||
* @function AnimationCache.prefetch
|
|
||||||
* @param {string} url - URL of the resource to prefetch.
|
|
||||||
* @param {object} [extra=null]
|
|
||||||
* @returns {ResourceObject}
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**jsdoc
|
|
||||||
* Asynchronously loads a resource from the specified URL and returns it.
|
|
||||||
* @function AnimationCache.getResource
|
|
||||||
* @param {string} url - URL of the resource to load.
|
|
||||||
* @param {string} [fallback=""] - Fallback URL if load of the desired URL fails.
|
|
||||||
* @param {} [extra=null]
|
|
||||||
* @returns {object}
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
/**jsdoc
|
|
||||||
* Returns animation resource for particular animation.
|
|
||||||
* @function AnimationCache.getAnimation
|
|
||||||
* @param {string} url - URL to load.
|
|
||||||
* @returns {AnimationObject} animation
|
|
||||||
*/
|
|
||||||
Q_INVOKABLE AnimationPointer getAnimation(const QString& url) { return getAnimation(QUrl(url)); }
|
Q_INVOKABLE AnimationPointer getAnimation(const QString& url) { return getAnimation(QUrl(url)); }
|
||||||
Q_INVOKABLE AnimationPointer getAnimation(const QUrl& url);
|
Q_INVOKABLE AnimationPointer getAnimation(const QUrl& url);
|
||||||
|
|
||||||
|
|
20
libraries/animation/src/AnimationCacheScriptingInterface.cpp
Normal file
20
libraries/animation/src/AnimationCacheScriptingInterface.cpp
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
//
|
||||||
|
// AnimationCacheScriptingInterface.cpp
|
||||||
|
// libraries/animation/src
|
||||||
|
//
|
||||||
|
// Created by David Rowe on 25 Jul 2018.
|
||||||
|
// Copyright 2018 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "AnimationCacheScriptingInterface.h"
|
||||||
|
|
||||||
|
AnimationCacheScriptingInterface::AnimationCacheScriptingInterface() :
|
||||||
|
ScriptableResourceCache::ScriptableResourceCache(DependencyManager::get<AnimationCache>())
|
||||||
|
{ }
|
||||||
|
|
||||||
|
AnimationPointer AnimationCacheScriptingInterface::getAnimation(const QString& url) {
|
||||||
|
return DependencyManager::get<AnimationCache>()->getAnimation(QUrl(url));
|
||||||
|
}
|
58
libraries/animation/src/AnimationCacheScriptingInterface.h
Normal file
58
libraries/animation/src/AnimationCacheScriptingInterface.h
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
//
|
||||||
|
// AnimationCacheScriptingInterface.h
|
||||||
|
// libraries/animation/src
|
||||||
|
//
|
||||||
|
// Created by David Rowe on 25 Jul 2018.
|
||||||
|
// Copyright 2018 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
//
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#ifndef hifi_AnimationCacheScriptingInterface_h
|
||||||
|
#define hifi_AnimationCacheScriptingInterface_h
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
|
||||||
|
#include <ResourceCache.h>
|
||||||
|
|
||||||
|
#include "AnimationCache.h"
|
||||||
|
|
||||||
|
class AnimationCacheScriptingInterface : public ScriptableResourceCache, public Dependency {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
// Properties are copied over from ResourceCache (see ResourceCache.h for reason).
|
||||||
|
|
||||||
|
/**jsdoc
|
||||||
|
* API to manage animation cache resources.
|
||||||
|
* @namespace AnimationCache
|
||||||
|
*
|
||||||
|
* @hifi-interface
|
||||||
|
* @hifi-client-entity
|
||||||
|
* @hifi-assignment-client
|
||||||
|
*
|
||||||
|
* @property {number} numTotal - Total number of total resources. <em>Read-only.</em>
|
||||||
|
* @property {number} numCached - Total number of cached resource. <em>Read-only.</em>
|
||||||
|
* @property {number} sizeTotal - Size in bytes of all resources. <em>Read-only.</em>
|
||||||
|
* @property {number} sizeCached - Size in bytes of all cached resources. <em>Read-only.</em>
|
||||||
|
*
|
||||||
|
* @borrows ResourceCache.getResourceList as getResourceList
|
||||||
|
* @borrows ResourceCache.updateTotalSize as updateTotalSize
|
||||||
|
* @borrows ResourceCache.prefetch as prefetch
|
||||||
|
* @borrows ResourceCache.dirty as dirty
|
||||||
|
*/
|
||||||
|
|
||||||
|
public:
|
||||||
|
AnimationCacheScriptingInterface();
|
||||||
|
|
||||||
|
/**jsdoc
|
||||||
|
* Returns animation resource for particular animation.
|
||||||
|
* @function AnimationCache.getAnimation
|
||||||
|
* @param {string} url - URL to load.
|
||||||
|
* @returns {AnimationObject} animation
|
||||||
|
*/
|
||||||
|
Q_INVOKABLE AnimationPointer getAnimation(const QString& url);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // hifi_AnimationCacheScriptingInterface_h
|
|
@ -59,6 +59,21 @@ const glm::vec3 DEFAULT_RIGHT_EYE_POS(-0.3f, 0.9f, 0.0f);
|
||||||
const glm::vec3 DEFAULT_LEFT_EYE_POS(0.3f, 0.9f, 0.0f);
|
const glm::vec3 DEFAULT_LEFT_EYE_POS(0.3f, 0.9f, 0.0f);
|
||||||
const glm::vec3 DEFAULT_HEAD_POS(0.0f, 0.75f, 0.0f);
|
const glm::vec3 DEFAULT_HEAD_POS(0.0f, 0.75f, 0.0f);
|
||||||
|
|
||||||
|
static const QString LEFT_FOOT_POSITION("leftFootPosition");
|
||||||
|
static const QString LEFT_FOOT_ROTATION("leftFootRotation");
|
||||||
|
static const QString LEFT_FOOT_IK_POSITION_VAR("leftFootIKPositionVar");
|
||||||
|
static const QString LEFT_FOOT_IK_ROTATION_VAR("leftFootIKRotationVar");
|
||||||
|
static const QString MAIN_STATE_MACHINE_LEFT_FOOT_POSITION("mainStateMachineLeftFootPosition");
|
||||||
|
static const QString MAIN_STATE_MACHINE_LEFT_FOOT_ROTATION("mainStateMachineLeftFootRotation");
|
||||||
|
|
||||||
|
static const QString RIGHT_FOOT_POSITION("rightFootPosition");
|
||||||
|
static const QString RIGHT_FOOT_ROTATION("rightFootRotation");
|
||||||
|
static const QString RIGHT_FOOT_IK_POSITION_VAR("rightFootIKPositionVar");
|
||||||
|
static const QString RIGHT_FOOT_IK_ROTATION_VAR("rightFootIKRotationVar");
|
||||||
|
static const QString MAIN_STATE_MACHINE_RIGHT_FOOT_ROTATION("mainStateMachineRightFootRotation");
|
||||||
|
static const QString MAIN_STATE_MACHINE_RIGHT_FOOT_POSITION("mainStateMachineRightFootPosition");
|
||||||
|
|
||||||
|
|
||||||
Rig::Rig() {
|
Rig::Rig() {
|
||||||
// Ensure thread-safe access to the rigRegistry.
|
// Ensure thread-safe access to the rigRegistry.
|
||||||
std::lock_guard<std::mutex> guard(rigRegistryMutex);
|
std::lock_guard<std::mutex> guard(rigRegistryMutex);
|
||||||
|
@ -1049,7 +1064,7 @@ void Rig::updateAnimations(float deltaTime, const glm::mat4& rootTransform, cons
|
||||||
getGeometryToRigTransform(), rigToWorldTransform);
|
getGeometryToRigTransform(), rigToWorldTransform);
|
||||||
|
|
||||||
// evaluate the animation
|
// evaluate the animation
|
||||||
AnimNode::Triggers triggersOut;
|
AnimVariantMap triggersOut;
|
||||||
|
|
||||||
_internalPoseSet._relativePoses = _animNode->evaluate(_animVars, context, deltaTime, triggersOut);
|
_internalPoseSet._relativePoses = _animNode->evaluate(_animVars, context, deltaTime, triggersOut);
|
||||||
if ((int)_internalPoseSet._relativePoses.size() != _animSkeleton->getNumJoints()) {
|
if ((int)_internalPoseSet._relativePoses.size() != _animSkeleton->getNumJoints()) {
|
||||||
|
@ -1057,9 +1072,7 @@ void Rig::updateAnimations(float deltaTime, const glm::mat4& rootTransform, cons
|
||||||
_internalPoseSet._relativePoses = _animSkeleton->getRelativeDefaultPoses();
|
_internalPoseSet._relativePoses = _animSkeleton->getRelativeDefaultPoses();
|
||||||
}
|
}
|
||||||
_animVars.clearTriggers();
|
_animVars.clearTriggers();
|
||||||
for (auto& trigger : triggersOut) {
|
_animVars = triggersOut;
|
||||||
_animVars.setTrigger(trigger);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
applyOverridePoses();
|
applyOverridePoses();
|
||||||
buildAbsoluteRigPoses(_internalPoseSet._relativePoses, _internalPoseSet._absolutePoses);
|
buildAbsoluteRigPoses(_internalPoseSet._relativePoses, _internalPoseSet._absolutePoses);
|
||||||
|
@ -1241,7 +1254,7 @@ glm::vec3 Rig::deflectHandFromTorso(const glm::vec3& handPosition, const FBXJoin
|
||||||
}
|
}
|
||||||
|
|
||||||
void Rig::updateHands(bool leftHandEnabled, bool rightHandEnabled, bool hipsEnabled, bool hipsEstimated,
|
void Rig::updateHands(bool leftHandEnabled, bool rightHandEnabled, bool hipsEnabled, bool hipsEstimated,
|
||||||
bool leftArmEnabled, bool rightArmEnabled, float dt,
|
bool leftArmEnabled, bool rightArmEnabled, bool headEnabled, float dt,
|
||||||
const AnimPose& leftHandPose, const AnimPose& rightHandPose,
|
const AnimPose& leftHandPose, const AnimPose& rightHandPose,
|
||||||
const FBXJointShapeInfo& hipsShapeInfo, const FBXJointShapeInfo& spineShapeInfo,
|
const FBXJointShapeInfo& hipsShapeInfo, const FBXJointShapeInfo& spineShapeInfo,
|
||||||
const FBXJointShapeInfo& spine1ShapeInfo, const FBXJointShapeInfo& spine2ShapeInfo,
|
const FBXJointShapeInfo& spine1ShapeInfo, const FBXJointShapeInfo& spine2ShapeInfo,
|
||||||
|
@ -1305,7 +1318,13 @@ void Rig::updateHands(bool leftHandEnabled, bool rightHandEnabled, bool hipsEnab
|
||||||
|
|
||||||
_animVars.unset("leftHandPosition");
|
_animVars.unset("leftHandPosition");
|
||||||
_animVars.unset("leftHandRotation");
|
_animVars.unset("leftHandRotation");
|
||||||
_animVars.set("leftHandType", (int)IKTarget::Type::HipsRelativeRotationAndPosition);
|
|
||||||
|
if (headEnabled) {
|
||||||
|
_animVars.set("leftHandType", (int)IKTarget::Type::HipsRelativeRotationAndPosition);
|
||||||
|
} else {
|
||||||
|
// disable hand IK for desktop mode
|
||||||
|
_animVars.set("leftHandType", (int)IKTarget::Type::Unknown);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (rightHandEnabled) {
|
if (rightHandEnabled) {
|
||||||
|
@ -1364,21 +1383,41 @@ void Rig::updateHands(bool leftHandEnabled, bool rightHandEnabled, bool hipsEnab
|
||||||
|
|
||||||
_animVars.unset("rightHandPosition");
|
_animVars.unset("rightHandPosition");
|
||||||
_animVars.unset("rightHandRotation");
|
_animVars.unset("rightHandRotation");
|
||||||
_animVars.set("rightHandType", (int)IKTarget::Type::HipsRelativeRotationAndPosition);
|
|
||||||
|
if (headEnabled) {
|
||||||
|
_animVars.set("rightHandType", (int)IKTarget::Type::HipsRelativeRotationAndPosition);
|
||||||
|
} else {
|
||||||
|
// disable hand IK for desktop mode
|
||||||
|
_animVars.set("rightHandType", (int)IKTarget::Type::Unknown);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Rig::updateFeet(bool leftFootEnabled, bool rightFootEnabled, const AnimPose& leftFootPose, const AnimPose& rightFootPose,
|
void Rig::updateFeet(bool leftFootEnabled, bool rightFootEnabled, bool headEnabled,
|
||||||
|
const AnimPose& leftFootPose, const AnimPose& rightFootPose,
|
||||||
const glm::mat4& rigToSensorMatrix, const glm::mat4& sensorToRigMatrix) {
|
const glm::mat4& rigToSensorMatrix, const glm::mat4& sensorToRigMatrix) {
|
||||||
|
|
||||||
const float KNEE_POLE_VECTOR_BLEND_FACTOR = 0.95f;
|
|
||||||
|
|
||||||
int hipsIndex = indexOfJoint("Hips");
|
int hipsIndex = indexOfJoint("Hips");
|
||||||
|
const float KNEE_POLE_VECTOR_BLEND_FACTOR = 0.85f;
|
||||||
|
|
||||||
|
if (headEnabled) {
|
||||||
|
// always do IK if head is enabled
|
||||||
|
_animVars.set("leftFootIKEnabled", true);
|
||||||
|
_animVars.set("rightFootIKEnabled", true);
|
||||||
|
} else {
|
||||||
|
// only do IK if we have a valid foot.
|
||||||
|
_animVars.set("leftFootIKEnabled", leftFootEnabled);
|
||||||
|
_animVars.set("rightFootIKEnabled", rightFootEnabled);
|
||||||
|
}
|
||||||
|
|
||||||
if (leftFootEnabled) {
|
if (leftFootEnabled) {
|
||||||
_animVars.set("leftFootPosition", leftFootPose.trans());
|
|
||||||
_animVars.set("leftFootRotation", leftFootPose.rot());
|
_animVars.set(LEFT_FOOT_POSITION, leftFootPose.trans());
|
||||||
_animVars.set("leftFootType", (int)IKTarget::Type::RotationAndPosition);
|
_animVars.set(LEFT_FOOT_ROTATION, leftFootPose.rot());
|
||||||
|
|
||||||
|
// We want to drive the IK directly from the trackers.
|
||||||
|
_animVars.set(LEFT_FOOT_IK_POSITION_VAR, LEFT_FOOT_POSITION);
|
||||||
|
_animVars.set(LEFT_FOOT_IK_ROTATION_VAR, LEFT_FOOT_ROTATION);
|
||||||
|
|
||||||
int footJointIndex = _animSkeleton->nameToJointIndex("LeftFoot");
|
int footJointIndex = _animSkeleton->nameToJointIndex("LeftFoot");
|
||||||
int kneeJointIndex = _animSkeleton->nameToJointIndex("LeftLeg");
|
int kneeJointIndex = _animSkeleton->nameToJointIndex("LeftLeg");
|
||||||
|
@ -1396,20 +1435,25 @@ void Rig::updateFeet(bool leftFootEnabled, bool rightFootEnabled, const AnimPose
|
||||||
_prevLeftFootPoleVector = smoothDeltaRot * _prevLeftFootPoleVector;
|
_prevLeftFootPoleVector = smoothDeltaRot * _prevLeftFootPoleVector;
|
||||||
|
|
||||||
_animVars.set("leftFootPoleVectorEnabled", true);
|
_animVars.set("leftFootPoleVectorEnabled", true);
|
||||||
_animVars.set("leftFootPoleReferenceVector", Vectors::UNIT_Z);
|
|
||||||
_animVars.set("leftFootPoleVector", transformVectorFast(sensorToRigMatrix, _prevLeftFootPoleVector));
|
_animVars.set("leftFootPoleVector", transformVectorFast(sensorToRigMatrix, _prevLeftFootPoleVector));
|
||||||
} else {
|
} else {
|
||||||
_animVars.unset("leftFootPosition");
|
// We want to drive the IK from the underlying animation.
|
||||||
_animVars.unset("leftFootRotation");
|
// This gives us the ability to squat while in the HMD, without the feet from dipping under the floor.
|
||||||
_animVars.set("leftFootType", (int)IKTarget::Type::RotationAndPosition);
|
_animVars.set(LEFT_FOOT_IK_POSITION_VAR, MAIN_STATE_MACHINE_LEFT_FOOT_POSITION);
|
||||||
|
_animVars.set(LEFT_FOOT_IK_ROTATION_VAR, MAIN_STATE_MACHINE_LEFT_FOOT_ROTATION);
|
||||||
|
|
||||||
|
// We want to match the animated knee pose as close as possible, so don't use poleVectors
|
||||||
_animVars.set("leftFootPoleVectorEnabled", false);
|
_animVars.set("leftFootPoleVectorEnabled", false);
|
||||||
_prevLeftFootPoleVectorValid = false;
|
_prevLeftFootPoleVectorValid = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (rightFootEnabled) {
|
if (rightFootEnabled) {
|
||||||
_animVars.set("rightFootPosition", rightFootPose.trans());
|
_animVars.set(RIGHT_FOOT_POSITION, rightFootPose.trans());
|
||||||
_animVars.set("rightFootRotation", rightFootPose.rot());
|
_animVars.set(RIGHT_FOOT_ROTATION, rightFootPose.rot());
|
||||||
_animVars.set("rightFootType", (int)IKTarget::Type::RotationAndPosition);
|
|
||||||
|
// We want to drive the IK directly from the trackers.
|
||||||
|
_animVars.set(RIGHT_FOOT_IK_POSITION_VAR, RIGHT_FOOT_POSITION);
|
||||||
|
_animVars.set(RIGHT_FOOT_IK_ROTATION_VAR, RIGHT_FOOT_ROTATION);
|
||||||
|
|
||||||
int footJointIndex = _animSkeleton->nameToJointIndex("RightFoot");
|
int footJointIndex = _animSkeleton->nameToJointIndex("RightFoot");
|
||||||
int kneeJointIndex = _animSkeleton->nameToJointIndex("RightLeg");
|
int kneeJointIndex = _animSkeleton->nameToJointIndex("RightLeg");
|
||||||
|
@ -1427,13 +1471,16 @@ void Rig::updateFeet(bool leftFootEnabled, bool rightFootEnabled, const AnimPose
|
||||||
_prevRightFootPoleVector = smoothDeltaRot * _prevRightFootPoleVector;
|
_prevRightFootPoleVector = smoothDeltaRot * _prevRightFootPoleVector;
|
||||||
|
|
||||||
_animVars.set("rightFootPoleVectorEnabled", true);
|
_animVars.set("rightFootPoleVectorEnabled", true);
|
||||||
_animVars.set("rightFootPoleReferenceVector", Vectors::UNIT_Z);
|
|
||||||
_animVars.set("rightFootPoleVector", transformVectorFast(sensorToRigMatrix, _prevRightFootPoleVector));
|
_animVars.set("rightFootPoleVector", transformVectorFast(sensorToRigMatrix, _prevRightFootPoleVector));
|
||||||
} else {
|
} else {
|
||||||
_animVars.unset("rightFootPosition");
|
// We want to drive the IK from the underlying animation.
|
||||||
_animVars.unset("rightFootRotation");
|
// This gives us the ability to squat while in the HMD, without the feet from dipping under the floor.
|
||||||
|
_animVars.set(RIGHT_FOOT_IK_POSITION_VAR, MAIN_STATE_MACHINE_RIGHT_FOOT_POSITION);
|
||||||
|
_animVars.set(RIGHT_FOOT_IK_ROTATION_VAR, MAIN_STATE_MACHINE_RIGHT_FOOT_ROTATION);
|
||||||
|
|
||||||
|
// We want to match the animated knee pose as close as possible, so don't use poleVectors
|
||||||
_animVars.set("rightFootPoleVectorEnabled", false);
|
_animVars.set("rightFootPoleVectorEnabled", false);
|
||||||
_animVars.set("rightFootType", (int)IKTarget::Type::RotationAndPosition);
|
_prevRightFootPoleVectorValid = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1475,23 +1522,12 @@ void Rig::updateEyeJoint(int index, const glm::vec3& modelTranslation, const glm
|
||||||
for (int i = 0; i < (int)children.size(); i++) {
|
for (int i = 0; i < (int)children.size(); i++) {
|
||||||
int jointIndex = children[i];
|
int jointIndex = children[i];
|
||||||
int parentIndex = _animSkeleton->getParentIndex(jointIndex);
|
int parentIndex = _animSkeleton->getParentIndex(jointIndex);
|
||||||
_internalPoseSet._absolutePoses[jointIndex] =
|
_internalPoseSet._absolutePoses[jointIndex] =
|
||||||
_internalPoseSet._absolutePoses[parentIndex] * _internalPoseSet._relativePoses[jointIndex];
|
_internalPoseSet._absolutePoses[parentIndex] * _internalPoseSet._relativePoses[jointIndex];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static glm::quat quatLerp(const glm::quat& q1, const glm::quat& q2, float alpha) {
|
|
||||||
float dot = glm::dot(q1, q2);
|
|
||||||
glm::quat temp;
|
|
||||||
if (dot < 0.0f) {
|
|
||||||
temp = -q2;
|
|
||||||
} else {
|
|
||||||
temp = q2;
|
|
||||||
}
|
|
||||||
return glm::normalize(glm::lerp(q1, temp, alpha));
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Rig::calculateElbowPoleVector(int handIndex, int elbowIndex, int armIndex, int oppositeArmIndex, glm::vec3& poleVector) const {
|
bool Rig::calculateElbowPoleVector(int handIndex, int elbowIndex, int armIndex, int oppositeArmIndex, glm::vec3& poleVector) const {
|
||||||
// The resulting Pole Vector is calculated as the sum of a three vectors.
|
// The resulting Pole Vector is calculated as the sum of a three vectors.
|
||||||
// The first is the vector with direction shoulder-hand. The module of this vector is inversely proportional to the strength of the resulting Pole Vector.
|
// The first is the vector with direction shoulder-hand. The module of this vector is inversely proportional to the strength of the resulting Pole Vector.
|
||||||
|
@ -1510,7 +1546,7 @@ bool Rig::calculateElbowPoleVector(int handIndex, int elbowIndex, int armIndex,
|
||||||
|
|
||||||
glm::vec3 backVector = oppositeArmPose.trans() - armPose.trans();
|
glm::vec3 backVector = oppositeArmPose.trans() - armPose.trans();
|
||||||
glm::vec3 backCenter = armPose.trans() + 0.5f * backVector;
|
glm::vec3 backCenter = armPose.trans() + 0.5f * backVector;
|
||||||
|
|
||||||
const float OVER_BACK_HEAD_PERCENTAGE = 0.2f;
|
const float OVER_BACK_HEAD_PERCENTAGE = 0.2f;
|
||||||
|
|
||||||
glm::vec3 headCenter = backCenter + glm::vec3(0, OVER_BACK_HEAD_PERCENTAGE * backVector.length(), 0);
|
glm::vec3 headCenter = backCenter + glm::vec3(0, OVER_BACK_HEAD_PERCENTAGE * backVector.length(), 0);
|
||||||
|
@ -1522,7 +1558,7 @@ bool Rig::calculateElbowPoleVector(int handIndex, int elbowIndex, int armIndex,
|
||||||
glm::vec3 headForward = headCenter + horizontalModule * frontVector;
|
glm::vec3 headForward = headCenter + horizontalModule * frontVector;
|
||||||
|
|
||||||
glm::vec3 armToHead = headForward - armPose.trans();
|
glm::vec3 armToHead = headForward - armPose.trans();
|
||||||
|
|
||||||
float armToHandDistance = glm::length(armToHand);
|
float armToHandDistance = glm::length(armToHand);
|
||||||
float armToElbowDistance = glm::length(armToElbow);
|
float armToElbowDistance = glm::length(armToElbow);
|
||||||
float elbowToHandDistance = glm::length(elbowToHand);
|
float elbowToHandDistance = glm::length(elbowToHand);
|
||||||
|
@ -1533,7 +1569,7 @@ bool Rig::calculateElbowPoleVector(int handIndex, int elbowIndex, int armIndex,
|
||||||
|
|
||||||
// How much the hand is reaching for the opposite side
|
// How much the hand is reaching for the opposite side
|
||||||
float oppositeProjection = glm::dot(armToHandDir, glm::normalize(backVector));
|
float oppositeProjection = glm::dot(armToHandDir, glm::normalize(backVector));
|
||||||
|
|
||||||
// Don't use pole vector when the hands are behind
|
// Don't use pole vector when the hands are behind
|
||||||
if (glm::dot(frontVector, armToHand) < 0 && oppositeProjection < 0.5f * armTotalDistance) {
|
if (glm::dot(frontVector, armToHand) < 0 && oppositeProjection < 0.5f * armTotalDistance) {
|
||||||
return false;
|
return false;
|
||||||
|
@ -1552,7 +1588,7 @@ bool Rig::calculateElbowPoleVector(int handIndex, int elbowIndex, int armIndex,
|
||||||
const float FORWARD_CORRECTOR_WEIGHT = 3.0f;
|
const float FORWARD_CORRECTOR_WEIGHT = 3.0f;
|
||||||
|
|
||||||
float elbowForwardTrigger = FORWARD_TRIGGER_PERCENTAGE * armToHandDistance;
|
float elbowForwardTrigger = FORWARD_TRIGGER_PERCENTAGE * armToHandDistance;
|
||||||
|
|
||||||
if (oppositeProjection > -elbowForwardTrigger) {
|
if (oppositeProjection > -elbowForwardTrigger) {
|
||||||
float forwardAmount = FORWARD_CORRECTOR_WEIGHT * (elbowForwardTrigger + oppositeProjection);
|
float forwardAmount = FORWARD_CORRECTOR_WEIGHT * (elbowForwardTrigger + oppositeProjection);
|
||||||
correctionVector = forwardAmount * frontVector;
|
correctionVector = forwardAmount * frontVector;
|
||||||
|
@ -1561,31 +1597,18 @@ bool Rig::calculateElbowPoleVector(int handIndex, int elbowIndex, int armIndex,
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// returns a poleVector for the knees that is a blend of the foot and the hips.
|
||||||
|
// targetFootPose is in rig space
|
||||||
|
// result poleVector is also in rig space.
|
||||||
glm::vec3 Rig::calculateKneePoleVector(int footJointIndex, int kneeIndex, int upLegIndex, int hipsIndex, const AnimPose& targetFootPose) const {
|
glm::vec3 Rig::calculateKneePoleVector(int footJointIndex, int kneeIndex, int upLegIndex, int hipsIndex, const AnimPose& targetFootPose) const {
|
||||||
|
const float FOOT_THETA = 0.8969f; // 51.39 degrees
|
||||||
|
const glm::vec3 localFootForward(0.0f, cosf(FOOT_THETA), sinf(FOOT_THETA));
|
||||||
|
|
||||||
|
glm::vec3 footForward = targetFootPose.rot() * localFootForward;
|
||||||
AnimPose hipsPose = _externalPoseSet._absolutePoses[hipsIndex];
|
AnimPose hipsPose = _externalPoseSet._absolutePoses[hipsIndex];
|
||||||
AnimPose footPose = targetFootPose;
|
glm::vec3 hipsForward = hipsPose.rot() * Vectors::UNIT_Z;
|
||||||
AnimPose kneePose = _externalPoseSet._absolutePoses[kneeIndex];
|
|
||||||
AnimPose upLegPose = _externalPoseSet._absolutePoses[upLegIndex];
|
|
||||||
|
|
||||||
// ray from foot to upLeg
|
return glm::normalize(lerp(hipsForward, footForward, 0.75f));
|
||||||
glm::vec3 d = glm::normalize(footPose.trans() - upLegPose.trans());
|
|
||||||
|
|
||||||
// form a plane normal to the hips x-axis
|
|
||||||
glm::vec3 n = hipsPose.rot() * Vectors::UNIT_X;
|
|
||||||
|
|
||||||
// project d onto this plane
|
|
||||||
glm::vec3 dProj = d - glm::dot(d, n) * n;
|
|
||||||
|
|
||||||
// rotate dProj by 90 degrees to get the poleVector.
|
|
||||||
glm::vec3 poleVector = glm::angleAxis(-PI / 2.0f, n) * dProj;
|
|
||||||
|
|
||||||
// blend the foot oreintation into the pole vector
|
|
||||||
glm::quat kneeToFootDelta = footPose.rot() * glm::inverse(kneePose.rot());
|
|
||||||
const float WRIST_POLE_ADJUST_FACTOR = 0.5f;
|
|
||||||
glm::quat poleAdjust = quatLerp(Quaternions::IDENTITY, kneeToFootDelta, WRIST_POLE_ADJUST_FACTOR);
|
|
||||||
|
|
||||||
return glm::normalize(poleAdjust * poleVector);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Rig::updateFromControllerParameters(const ControllerParameters& params, float dt) {
|
void Rig::updateFromControllerParameters(const ControllerParameters& params, float dt) {
|
||||||
|
@ -1610,12 +1633,12 @@ void Rig::updateFromControllerParameters(const ControllerParameters& params, flo
|
||||||
|
|
||||||
updateHead(headEnabled, hipsEnabled, params.primaryControllerPoses[PrimaryControllerType_Head]);
|
updateHead(headEnabled, hipsEnabled, params.primaryControllerPoses[PrimaryControllerType_Head]);
|
||||||
|
|
||||||
updateHands(leftHandEnabled, rightHandEnabled, hipsEnabled, hipsEstimated, leftArmEnabled, rightArmEnabled, dt,
|
updateHands(leftHandEnabled, rightHandEnabled, hipsEnabled, hipsEstimated, leftArmEnabled, rightArmEnabled, headEnabled, dt,
|
||||||
params.primaryControllerPoses[PrimaryControllerType_LeftHand], params.primaryControllerPoses[PrimaryControllerType_RightHand],
|
params.primaryControllerPoses[PrimaryControllerType_LeftHand], params.primaryControllerPoses[PrimaryControllerType_RightHand],
|
||||||
params.hipsShapeInfo, params.spineShapeInfo, params.spine1ShapeInfo, params.spine2ShapeInfo,
|
params.hipsShapeInfo, params.spineShapeInfo, params.spine1ShapeInfo, params.spine2ShapeInfo,
|
||||||
params.rigToSensorMatrix, sensorToRigMatrix);
|
params.rigToSensorMatrix, sensorToRigMatrix);
|
||||||
|
|
||||||
updateFeet(leftFootEnabled, rightFootEnabled,
|
updateFeet(leftFootEnabled, rightFootEnabled, headEnabled,
|
||||||
params.primaryControllerPoses[PrimaryControllerType_LeftFoot], params.primaryControllerPoses[PrimaryControllerType_RightFoot],
|
params.primaryControllerPoses[PrimaryControllerType_LeftFoot], params.primaryControllerPoses[PrimaryControllerType_RightFoot],
|
||||||
params.rigToSensorMatrix, sensorToRigMatrix);
|
params.rigToSensorMatrix, sensorToRigMatrix);
|
||||||
|
|
||||||
|
|
|
@ -75,6 +75,10 @@ public:
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ControllerParameters {
|
struct ControllerParameters {
|
||||||
|
ControllerParameters() {
|
||||||
|
memset(primaryControllerFlags, 0, NumPrimaryControllerTypes);
|
||||||
|
memset(secondaryControllerFlags, 0, NumPrimaryControllerTypes);
|
||||||
|
}
|
||||||
glm::mat4 rigToSensorMatrix;
|
glm::mat4 rigToSensorMatrix;
|
||||||
AnimPose primaryControllerPoses[NumPrimaryControllerTypes]; // rig space
|
AnimPose primaryControllerPoses[NumPrimaryControllerTypes]; // rig space
|
||||||
uint8_t primaryControllerFlags[NumPrimaryControllerTypes];
|
uint8_t primaryControllerFlags[NumPrimaryControllerTypes];
|
||||||
|
@ -229,12 +233,13 @@ protected:
|
||||||
|
|
||||||
void updateHead(bool headEnabled, bool hipsEnabled, const AnimPose& headMatrix);
|
void updateHead(bool headEnabled, bool hipsEnabled, const AnimPose& headMatrix);
|
||||||
void updateHands(bool leftHandEnabled, bool rightHandEnabled, bool hipsEnabled, bool hipsEstimated,
|
void updateHands(bool leftHandEnabled, bool rightHandEnabled, bool hipsEnabled, bool hipsEstimated,
|
||||||
bool leftArmEnabled, bool rightArmEnabled, float dt,
|
bool leftArmEnabled, bool rightArmEnabled, bool headEnabled, float dt,
|
||||||
const AnimPose& leftHandPose, const AnimPose& rightHandPose,
|
const AnimPose& leftHandPose, const AnimPose& rightHandPose,
|
||||||
const FBXJointShapeInfo& hipsShapeInfo, const FBXJointShapeInfo& spineShapeInfo,
|
const FBXJointShapeInfo& hipsShapeInfo, const FBXJointShapeInfo& spineShapeInfo,
|
||||||
const FBXJointShapeInfo& spine1ShapeInfo, const FBXJointShapeInfo& spine2ShapeInfo,
|
const FBXJointShapeInfo& spine1ShapeInfo, const FBXJointShapeInfo& spine2ShapeInfo,
|
||||||
const glm::mat4& rigToSensorMatrix, const glm::mat4& sensorToRigMatrix);
|
const glm::mat4& rigToSensorMatrix, const glm::mat4& sensorToRigMatrix);
|
||||||
void updateFeet(bool leftFootEnabled, bool rightFootEnabled, const AnimPose& leftFootPose, const AnimPose& rightFootPose,
|
void updateFeet(bool leftFootEnabled, bool rightFootEnabled, bool headEnabled,
|
||||||
|
const AnimPose& leftFootPose, const AnimPose& rightFootPose,
|
||||||
const glm::mat4& rigToSensorMatrix, const glm::mat4& sensorToRigMatrix);
|
const glm::mat4& rigToSensorMatrix, const glm::mat4& sensorToRigMatrix);
|
||||||
|
|
||||||
void updateEyeJoint(int index, const glm::vec3& modelTranslation, const glm::quat& modelRotation, const glm::vec3& lookAt, const glm::vec3& saccade);
|
void updateEyeJoint(int index, const glm::vec3& modelTranslation, const glm::quat& modelRotation, const glm::vec3& lookAt, const glm::vec3& saccade);
|
||||||
|
|
|
@ -16,73 +16,13 @@
|
||||||
|
|
||||||
#include "Sound.h"
|
#include "Sound.h"
|
||||||
|
|
||||||
/// Scriptable interface for sound loading.
|
|
||||||
class SoundCache : public ResourceCache, public Dependency {
|
class SoundCache : public ResourceCache, public Dependency {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
SINGLETON_DEPENDENCY
|
SINGLETON_DEPENDENCY
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
// Properties are copied over from ResourceCache (see ResourceCache.h for reason).
|
|
||||||
|
|
||||||
/**jsdoc
|
|
||||||
* API to manage sound cache resources.
|
|
||||||
* @namespace SoundCache
|
|
||||||
*
|
|
||||||
* @hifi-interface
|
|
||||||
* @hifi-client-entity
|
|
||||||
* @hifi-server-entity
|
|
||||||
* @hifi-assignment-client
|
|
||||||
*
|
|
||||||
* @property {number} numTotal - Total number of total resources. <em>Read-only.</em>
|
|
||||||
* @property {number} numCached - Total number of cached resource. <em>Read-only.</em>
|
|
||||||
* @property {number} sizeTotal - Size in bytes of all resources. <em>Read-only.</em>
|
|
||||||
* @property {number} sizeCached - Size in bytes of all cached resources. <em>Read-only.</em>
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
// Functions are copied over from ResourceCache (see ResourceCache.h for reason).
|
|
||||||
|
|
||||||
/**jsdoc
|
|
||||||
* Get the list of all resource URLs.
|
|
||||||
* @function SoundCache.getResourceList
|
|
||||||
* @returns {string[]}
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**jsdoc
|
|
||||||
* @function SoundCache.dirty
|
|
||||||
* @returns {Signal}
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**jsdoc
|
|
||||||
* @function SoundCache.updateTotalSize
|
|
||||||
* @param {number} deltaSize
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**jsdoc
|
|
||||||
* Prefetches a resource.
|
|
||||||
* @function SoundCache.prefetch
|
|
||||||
* @param {string} url - URL of the resource to prefetch.
|
|
||||||
* @param {object} [extra=null]
|
|
||||||
* @returns {ResourceObject}
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**jsdoc
|
|
||||||
* Asynchronously loads a resource from the specified URL and returns it.
|
|
||||||
* @function SoundCache.getResource
|
|
||||||
* @param {string} url - URL of the resource to load.
|
|
||||||
* @param {string} [fallback=""] - Fallback URL if load of the desired URL fails.
|
|
||||||
* @param {} [extra=null]
|
|
||||||
* @returns {object}
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
/**jsdoc
|
|
||||||
* @function SoundCache.getSound
|
|
||||||
* @param {string} url
|
|
||||||
* @returns {SoundObject}
|
|
||||||
*/
|
|
||||||
Q_INVOKABLE SharedSoundPointer getSound(const QUrl& url);
|
Q_INVOKABLE SharedSoundPointer getSound(const QUrl& url);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual QSharedPointer<Resource> createResource(const QUrl& url, const QSharedPointer<Resource>& fallback,
|
virtual QSharedPointer<Resource> createResource(const QUrl& url, const QSharedPointer<Resource>& fallback,
|
||||||
const void* extra) override;
|
const void* extra) override;
|
||||||
|
|
20
libraries/audio/src/SoundCacheScriptingInterface.cpp
Normal file
20
libraries/audio/src/SoundCacheScriptingInterface.cpp
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
//
|
||||||
|
// SoundCacheScriptingInterface.cpp
|
||||||
|
// libraries/audio/src
|
||||||
|
//
|
||||||
|
// Created by David Rowe on 25 Jul 2018.
|
||||||
|
// Copyright 2018 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "SoundCacheScriptingInterface.h"
|
||||||
|
|
||||||
|
SoundCacheScriptingInterface::SoundCacheScriptingInterface() :
|
||||||
|
ScriptableResourceCache::ScriptableResourceCache(DependencyManager::get<SoundCache>())
|
||||||
|
{ }
|
||||||
|
|
||||||
|
SharedSoundPointer SoundCacheScriptingInterface::getSound(const QUrl& url) {
|
||||||
|
return DependencyManager::get<SoundCache>()->getSound(url);
|
||||||
|
}
|
58
libraries/audio/src/SoundCacheScriptingInterface.h
Normal file
58
libraries/audio/src/SoundCacheScriptingInterface.h
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
//
|
||||||
|
// SoundCacheScriptingInterface.h
|
||||||
|
// libraries/audio/src
|
||||||
|
//
|
||||||
|
// Created by David Rowe on 25 Jul 2018.
|
||||||
|
// Copyright 2018 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
//
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#ifndef hifi_SoundCacheScriptingInterface_h
|
||||||
|
#define hifi_SoundCacheScriptingInterface_h
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
|
||||||
|
#include <ResourceCache.h>
|
||||||
|
|
||||||
|
#include "SoundCache.h"
|
||||||
|
|
||||||
|
class SoundCacheScriptingInterface : public ScriptableResourceCache, public Dependency {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
// Properties are copied over from ResourceCache (see ResourceCache.h for reason).
|
||||||
|
|
||||||
|
/**jsdoc
|
||||||
|
* API to manage sound cache resources.
|
||||||
|
* @namespace SoundCache
|
||||||
|
*
|
||||||
|
* @hifi-interface
|
||||||
|
* @hifi-client-entity
|
||||||
|
* @hifi-server-entity
|
||||||
|
* @hifi-assignment-client
|
||||||
|
*
|
||||||
|
* @property {number} numTotal - Total number of total resources. <em>Read-only.</em>
|
||||||
|
* @property {number} numCached - Total number of cached resource. <em>Read-only.</em>
|
||||||
|
* @property {number} sizeTotal - Size in bytes of all resources. <em>Read-only.</em>
|
||||||
|
* @property {number} sizeCached - Size in bytes of all cached resources. <em>Read-only.</em>
|
||||||
|
*
|
||||||
|
* @borrows ResourceCache.getResourceList as getResourceList
|
||||||
|
* @borrows ResourceCache.updateTotalSize as updateTotalSize
|
||||||
|
* @borrows ResourceCache.prefetch as prefetch
|
||||||
|
* @borrows ResourceCache.dirty as dirty
|
||||||
|
*/
|
||||||
|
|
||||||
|
public:
|
||||||
|
SoundCacheScriptingInterface();
|
||||||
|
|
||||||
|
/**jsdoc
|
||||||
|
* @function SoundCache.getSound
|
||||||
|
* @param {string} url
|
||||||
|
* @returns {SoundObject}
|
||||||
|
*/
|
||||||
|
Q_INVOKABLE SharedSoundPointer getSound(const QUrl& url);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // hifi_SoundCacheScriptingInterface_h
|
|
@ -54,6 +54,17 @@ signals:
|
||||||
*/
|
*/
|
||||||
void displayModeChanged(bool isHMDMode);
|
void displayModeChanged(bool isHMDMode);
|
||||||
|
|
||||||
|
/**jsdoc
|
||||||
|
* Triggered when the <code>HMD.mounted</code> property value changes.
|
||||||
|
* @function HMD.mountedChanged
|
||||||
|
* @returns {Signal}
|
||||||
|
* @example <caption>Report when there's a change in the HMD being worn.</caption>
|
||||||
|
* HMD.mountedChanged.connect(function () {
|
||||||
|
* print("Mounted changed. HMD is mounted: " + HMD.mounted);
|
||||||
|
* });
|
||||||
|
*/
|
||||||
|
void mountedChanged();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
float _IPDScale{ 1.0 };
|
float _IPDScale{ 1.0 };
|
||||||
};
|
};
|
||||||
|
|
|
@ -21,6 +21,7 @@
|
||||||
#include "../OpenGLDisplayPlugin.h"
|
#include "../OpenGLDisplayPlugin.h"
|
||||||
|
|
||||||
class HmdDisplayPlugin : public OpenGLDisplayPlugin {
|
class HmdDisplayPlugin : public OpenGLDisplayPlugin {
|
||||||
|
Q_OBJECT
|
||||||
using Parent = OpenGLDisplayPlugin;
|
using Parent = OpenGLDisplayPlugin;
|
||||||
public:
|
public:
|
||||||
~HmdDisplayPlugin();
|
~HmdDisplayPlugin();
|
||||||
|
@ -45,6 +46,9 @@ public:
|
||||||
|
|
||||||
virtual bool onDisplayTextureReset() override { _clearPreviewFlag = true; return true; };
|
virtual bool onDisplayTextureReset() override { _clearPreviewFlag = true; return true; };
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void hmdMountedChanged();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual void hmdPresent() = 0;
|
virtual void hmdPresent() = 0;
|
||||||
virtual bool isHmdMounted() const = 0;
|
virtual bool isHmdMounted() const = 0;
|
||||||
|
|
|
@ -40,7 +40,6 @@
|
||||||
|
|
||||||
#include <PointerManager.h>
|
#include <PointerManager.h>
|
||||||
|
|
||||||
size_t std::hash<EntityItemID>::operator()(const EntityItemID& id) const { return qHash(id); }
|
|
||||||
std::function<bool()> EntityTreeRenderer::_entitiesShouldFadeFunction;
|
std::function<bool()> EntityTreeRenderer::_entitiesShouldFadeFunction;
|
||||||
|
|
||||||
QString resolveScriptURL(const QString& scriptUrl) {
|
QString resolveScriptURL(const QString& scriptUrl) {
|
||||||
|
|
|
@ -40,9 +40,6 @@ namespace render { namespace entities {
|
||||||
|
|
||||||
} }
|
} }
|
||||||
|
|
||||||
// Allow the use of std::unordered_map with QUuid keys
|
|
||||||
namespace std { template<> struct hash<EntityItemID> { size_t operator()(const EntityItemID& id) const; }; }
|
|
||||||
|
|
||||||
using EntityRenderer = render::entities::EntityRenderer;
|
using EntityRenderer = render::entities::EntityRenderer;
|
||||||
using EntityRendererPointer = render::entities::EntityRendererPointer;
|
using EntityRendererPointer = render::entities::EntityRendererPointer;
|
||||||
using EntityRendererWeakPointer = render::entities::EntityRendererWeakPointer;
|
using EntityRendererWeakPointer = render::entities::EntityRendererWeakPointer;
|
||||||
|
|
|
@ -69,3 +69,4 @@ QVector<EntityItemID> qVectorEntityItemIDFromScriptValue(const QScriptValue& arr
|
||||||
return newVector;
|
return newVector;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
size_t std::hash<EntityItemID>::operator()(const EntityItemID& id) const { return qHash(id); }
|
||||||
|
|
|
@ -45,4 +45,7 @@ QScriptValue EntityItemIDtoScriptValue(QScriptEngine* engine, const EntityItemID
|
||||||
void EntityItemIDfromScriptValue(const QScriptValue &object, EntityItemID& properties);
|
void EntityItemIDfromScriptValue(const QScriptValue &object, EntityItemID& properties);
|
||||||
QVector<EntityItemID> qVectorEntityItemIDFromScriptValue(const QScriptValue& array);
|
QVector<EntityItemID> qVectorEntityItemIDFromScriptValue(const QScriptValue& array);
|
||||||
|
|
||||||
|
// Allow the use of std::unordered_map with QUuid keys
|
||||||
|
namespace std { template<> struct hash<EntityItemID> { size_t operator()(const EntityItemID& id) const; }; }
|
||||||
|
|
||||||
#endif // hifi_EntityItemID_h
|
#endif // hifi_EntityItemID_h
|
||||||
|
|
|
@ -891,28 +891,30 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const {
|
||||||
* @property {string} textures="" - The URL of a JPG or PNG image file to display for each particle. If you want transparency,
|
* @property {string} textures="" - The URL of a JPG or PNG image file to display for each particle. If you want transparency,
|
||||||
* use PNG format.
|
* use PNG format.
|
||||||
* @property {number} particleRadius=0.025 - The radius of each particle at the middle of its life.
|
* @property {number} particleRadius=0.025 - The radius of each particle at the middle of its life.
|
||||||
* @property {number} radiusStart=NAN - The radius of each particle at the start of its life. If NAN, the
|
* @property {number} radiusStart=NaN - The radius of each particle at the start of its life. If <code>NaN</code>, the
|
||||||
* <code>particleRadius</code> value is used.
|
* <code>particleRadius</code> value is used.
|
||||||
* @property {number} radiusFinish=NAN - The radius of each particle at the end of its life. If NAN, the
|
* @property {number} radiusFinish=NaN - The radius of each particle at the end of its life. If <code>NaN</code>, the
|
||||||
* <code>particleRadius</code> value is used.
|
* <code>particleRadius</code> value is used.
|
||||||
* @property {number} radiusSpread=0 - The spread in radius that each particle is given. If <code>particleRadius == 0.5</code>
|
* @property {number} radiusSpread=0 - The spread in radius that each particle is given. If <code>particleRadius == 0.5</code>
|
||||||
* and <code>radiusSpread == 0.25</code>, each particle will have a radius in the range <code>0.25</code> – <code>0.75</code>.
|
* and <code>radiusSpread == 0.25</code>, each particle will have a radius in the range <code>0.25</code> –
|
||||||
|
* <code>0.75</code>.
|
||||||
* @property {Color} color=255,255,255 - The color of each particle at the middle of its life.
|
* @property {Color} color=255,255,255 - The color of each particle at the middle of its life.
|
||||||
* @property {Color} colorStart=NAN,NAN,NAN - The color of each particle at the start of its life. If any of the values are NAN, the
|
* @property {Color} colorStart={} - The color of each particle at the start of its life. If any of the component values are
|
||||||
* <code>color</code> value is used.
|
* undefined, the <code>color</code> value is used.
|
||||||
* @property {Color} colorFinish=NAN,NAN,NAN - The color of each particle at the end of its life. If any of the values are NAN, the
|
* @property {Color} colorFinish={} - The color of each particle at the end of its life. If any of the component values are
|
||||||
* <code>color</code> value is used.
|
* undefined, the <code>color</code> value is used.
|
||||||
* @property {Color} colorSpread=0,0,0 - The spread in color that each particle is given. If
|
* @property {Color} colorSpread=0,0,0 - The spread in color that each particle is given. If
|
||||||
* <code>color == {red: 100, green: 100, blue: 100}</code> and <code>colorSpread ==
|
* <code>color == {red: 100, green: 100, blue: 100}</code> and <code>colorSpread ==
|
||||||
* {red: 10, green: 25, blue: 50}</code>, each particle will have an acceleration in the range <code>{red: 90, green: 75, blue: 50}</code>
|
* {red: 10, green: 25, blue: 50}</code>, each particle will have a color in the range
|
||||||
* – <code>{red: 110, green: 125, blue: 150}</code>.
|
* <code>{red: 90, green: 75, blue: 50}</code> – <code>{red: 110, green: 125, blue: 150}</code>.
|
||||||
* @property {number} alpha=1 - The alpha of each particle at the middle of its life.
|
* @property {number} alpha=1 - The alpha of each particle at the middle of its life.
|
||||||
* @property {number} alphaStart=NAN - The alpha of each particle at the start of its life. If NAN, the
|
* @property {number} alphaStart=NaN - The alpha of each particle at the start of its life. If <code>NaN</code>, the
|
||||||
* <code>alpha</code> value is used.
|
* <code>alpha</code> value is used.
|
||||||
* @property {number} alphaFinish=NAN - The alpha of each particle at the end of its life. If NAN, the
|
* @property {number} alphaFinish=NaN - The alpha of each particle at the end of its life. If <code>NaN</code>, the
|
||||||
* <code>alpha</code> value is used.
|
* <code>alpha</code> value is used.
|
||||||
* @property {number} alphaSpread=0 - The spread in alpha that each particle is given. If <code>alpha == 0.5</code>
|
* @property {number} alphaSpread=0 - The spread in alpha that each particle is given. If <code>alpha == 0.5</code>
|
||||||
* and <code>alphaSpread == 0.25</code>, each particle will have an alpha in the range <code>0.25</code> – <code>0.75</code>.
|
* and <code>alphaSpread == 0.25</code>, each particle will have an alpha in the range <code>0.25</code> –
|
||||||
|
* <code>0.75</code>.
|
||||||
* @property {number} particleSpin=0 - The spin of each particle at the middle of its life. In the range <code>-2*PI</code> – <code>2*PI</code>.
|
* @property {number} particleSpin=0 - The spin of each particle at the middle of its life. In the range <code>-2*PI</code> – <code>2*PI</code>.
|
||||||
* @property {number} spinStart=NaN - The spin of each particle at the start of its life. In the range <code>-2*PI</code> – <code>2*PI</code>.
|
* @property {number} spinStart=NaN - The spin of each particle at the start of its life. In the range <code>-2*PI</code> – <code>2*PI</code>.
|
||||||
* If <code>NaN</code>, the <code>particleSpin</code> value is used.
|
* If <code>NaN</code>, the <code>particleSpin</code> value is used.
|
||||||
|
|
|
@ -140,58 +140,6 @@ class ModelCache : public ResourceCache, public Dependency {
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
// Properties are copied over from ResourceCache (see ResourceCache.h for reason).
|
|
||||||
|
|
||||||
/**jsdoc
|
|
||||||
* API to manage model cache resources.
|
|
||||||
* @namespace ModelCache
|
|
||||||
*
|
|
||||||
* @hifi-interface
|
|
||||||
* @hifi-client-entity
|
|
||||||
*
|
|
||||||
* @property {number} numTotal - Total number of total resources. <em>Read-only.</em>
|
|
||||||
* @property {number} numCached - Total number of cached resource. <em>Read-only.</em>
|
|
||||||
* @property {number} sizeTotal - Size in bytes of all resources. <em>Read-only.</em>
|
|
||||||
* @property {number} sizeCached - Size in bytes of all cached resources. <em>Read-only.</em>
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
// Functions are copied over from ResourceCache (see ResourceCache.h for reason).
|
|
||||||
|
|
||||||
/**jsdoc
|
|
||||||
* Get the list of all resource URLs.
|
|
||||||
* @function ModelCache.getResourceList
|
|
||||||
* @returns {string[]}
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**jsdoc
|
|
||||||
* @function ModelCache.dirty
|
|
||||||
* @returns {Signal}
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**jsdoc
|
|
||||||
* @function ModelCache.updateTotalSize
|
|
||||||
* @param {number} deltaSize
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**jsdoc
|
|
||||||
* Prefetches a resource.
|
|
||||||
* @function ModelCache.prefetch
|
|
||||||
* @param {string} url - URL of the resource to prefetch.
|
|
||||||
* @param {object} [extra=null]
|
|
||||||
* @returns {ResourceObject}
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**jsdoc
|
|
||||||
* Asynchronously loads a resource from the specified URL and returns it.
|
|
||||||
* @function ModelCache.getResource
|
|
||||||
* @param {string} url - URL of the resource to load.
|
|
||||||
* @param {string} [fallback=""] - Fallback URL if load of the desired URL fails.
|
|
||||||
* @param {} [extra=null]
|
|
||||||
* @returns {object}
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
GeometryResource::Pointer getGeometryResource(const QUrl& url,
|
GeometryResource::Pointer getGeometryResource(const QUrl& url,
|
||||||
const QVariantHash& mapping = QVariantHash(),
|
const QVariantHash& mapping = QVariantHash(),
|
||||||
const QUrl& textureBaseUrl = QUrl());
|
const QUrl& textureBaseUrl = QUrl());
|
||||||
|
|
|
@ -0,0 +1,16 @@
|
||||||
|
//
|
||||||
|
// ModelCacheScriptingInterface.cpp
|
||||||
|
// libraries/mmodel-networking/src/model-networking
|
||||||
|
//
|
||||||
|
// Created by David Rowe on 25 Jul 2018.
|
||||||
|
// Copyright 2018 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "ModelCacheScriptingInterface.h"
|
||||||
|
|
||||||
|
ModelCacheScriptingInterface::ModelCacheScriptingInterface() :
|
||||||
|
ScriptableResourceCache::ScriptableResourceCache(DependencyManager::get<ModelCache>())
|
||||||
|
{ }
|
|
@ -0,0 +1,49 @@
|
||||||
|
//
|
||||||
|
// ModelCacheScriptingInterface.h
|
||||||
|
// libraries/mmodel-networking/src/model-networking
|
||||||
|
//
|
||||||
|
// Created by David Rowe on 25 Jul 2018.
|
||||||
|
// Copyright 2018 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
//
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#ifndef hifi_ModelCacheScriptingInterface_h
|
||||||
|
#define hifi_ModelCacheScriptingInterface_h
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
|
||||||
|
#include <ResourceCache.h>
|
||||||
|
|
||||||
|
#include "ModelCache.h"
|
||||||
|
|
||||||
|
class ModelCacheScriptingInterface : public ScriptableResourceCache, public Dependency {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
// Properties are copied over from ResourceCache (see ResourceCache.h for reason).
|
||||||
|
|
||||||
|
/**jsdoc
|
||||||
|
* API to manage model cache resources.
|
||||||
|
* @namespace ModelCache
|
||||||
|
*
|
||||||
|
* @hifi-interface
|
||||||
|
* @hifi-client-entity
|
||||||
|
*
|
||||||
|
* @property {number} numTotal - Total number of total resources. <em>Read-only.</em>
|
||||||
|
* @property {number} numCached - Total number of cached resource. <em>Read-only.</em>
|
||||||
|
* @property {number} sizeTotal - Size in bytes of all resources. <em>Read-only.</em>
|
||||||
|
* @property {number} sizeCached - Size in bytes of all cached resources. <em>Read-only.</em>
|
||||||
|
*
|
||||||
|
* @borrows ResourceCache.getResourceList as getResourceList
|
||||||
|
* @borrows ResourceCache.updateTotalSize as updateTotalSize
|
||||||
|
* @borrows ResourceCache.prefetch as prefetch
|
||||||
|
* @borrows ResourceCache.dirty as dirty
|
||||||
|
*/
|
||||||
|
|
||||||
|
public:
|
||||||
|
ModelCacheScriptingInterface();
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // hifi_ModelCacheScriptingInterface_h
|
|
@ -156,58 +156,6 @@ class TextureCache : public ResourceCache, public Dependency {
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
// Properties are copied over from ResourceCache (see ResourceCache.h for reason).
|
|
||||||
|
|
||||||
/**jsdoc
|
|
||||||
* API to manage texture cache resources.
|
|
||||||
* @namespace TextureCache
|
|
||||||
*
|
|
||||||
* @hifi-interface
|
|
||||||
* @hifi-client-entity
|
|
||||||
*
|
|
||||||
* @property {number} numTotal - Total number of total resources. <em>Read-only.</em>
|
|
||||||
* @property {number} numCached - Total number of cached resource. <em>Read-only.</em>
|
|
||||||
* @property {number} sizeTotal - Size in bytes of all resources. <em>Read-only.</em>
|
|
||||||
* @property {number} sizeCached - Size in bytes of all cached resources. <em>Read-only.</em>
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
// Functions are copied over from ResourceCache (see ResourceCache.h for reason).
|
|
||||||
|
|
||||||
/**jsdoc
|
|
||||||
* Get the list of all resource URLs.
|
|
||||||
* @function TextureCache.getResourceList
|
|
||||||
* @returns {string[]}
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**jsdoc
|
|
||||||
* @function TextureCache.dirty
|
|
||||||
* @returns {Signal}
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**jsdoc
|
|
||||||
* @function TextureCache.updateTotalSize
|
|
||||||
* @param {number} deltaSize
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**jsdoc
|
|
||||||
* Prefetches a resource.
|
|
||||||
* @function TextureCache.prefetch
|
|
||||||
* @param {string} url - URL of the resource to prefetch.
|
|
||||||
* @param {object} [extra=null]
|
|
||||||
* @returns {ResourceObject}
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**jsdoc
|
|
||||||
* Asynchronously loads a resource from the specified URL and returns it.
|
|
||||||
* @function TextureCache.getResource
|
|
||||||
* @param {string} url - URL of the resource to load.
|
|
||||||
* @param {string} [fallback=""] - Fallback URL if load of the desired URL fails.
|
|
||||||
* @param {} [extra=null]
|
|
||||||
* @returns {object}
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
/// Returns the ID of the permutation/normal texture used for Perlin noise shader programs. This texture
|
/// Returns the ID of the permutation/normal texture used for Perlin noise shader programs. This texture
|
||||||
/// has two lines: the first, a set of random numbers in [0, 255] to be used as permutation offsets, and
|
/// has two lines: the first, a set of random numbers in [0, 255] to be used as permutation offsets, and
|
||||||
/// the second, a set of random unit vectors to be used as noise gradients.
|
/// the second, a set of random unit vectors to be used as noise gradients.
|
||||||
|
@ -248,21 +196,10 @@ public:
|
||||||
gpu::ContextPointer getGPUContext() const { return _gpuContext; }
|
gpu::ContextPointer getGPUContext() const { return _gpuContext; }
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
/**jsdoc
|
|
||||||
* @function TextureCache.spectatorCameraFramebufferReset
|
|
||||||
* @returns {Signal}
|
|
||||||
*/
|
|
||||||
void spectatorCameraFramebufferReset();
|
void spectatorCameraFramebufferReset();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
/**jsdoc
|
|
||||||
* @function TextureCache.prefetch
|
|
||||||
* @param {string} url
|
|
||||||
* @param {number} type
|
|
||||||
* @param {number} [maxNumPixels=67108864]
|
|
||||||
* @returns {ResourceObject}
|
|
||||||
*/
|
|
||||||
// Overload ResourceCache::prefetch to allow specifying texture type for loads
|
// Overload ResourceCache::prefetch to allow specifying texture type for loads
|
||||||
Q_INVOKABLE ScriptableResource* prefetch(const QUrl& url, int type, int maxNumPixels = ABSOLUTE_MAX_TEXTURE_NUM_PIXELS);
|
Q_INVOKABLE ScriptableResource* prefetch(const QUrl& url, int type, int maxNumPixels = ABSOLUTE_MAX_TEXTURE_NUM_PIXELS);
|
||||||
|
|
||||||
|
@ -273,6 +210,7 @@ private:
|
||||||
friend class ImageReader;
|
friend class ImageReader;
|
||||||
friend class NetworkTexture;
|
friend class NetworkTexture;
|
||||||
friend class DilatableNetworkTexture;
|
friend class DilatableNetworkTexture;
|
||||||
|
friend class TextureCacheScriptingInterface;
|
||||||
|
|
||||||
TextureCache();
|
TextureCache();
|
||||||
virtual ~TextureCache();
|
virtual ~TextureCache();
|
||||||
|
|
|
@ -0,0 +1,23 @@
|
||||||
|
//
|
||||||
|
// TextureCacheScriptingInterface.cpp
|
||||||
|
// libraries/mmodel-networking/src/model-networking
|
||||||
|
//
|
||||||
|
// Created by David Rowe on 25 Jul 2018.
|
||||||
|
// Copyright 2018 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "TextureCacheScriptingInterface.h"
|
||||||
|
|
||||||
|
TextureCacheScriptingInterface::TextureCacheScriptingInterface() :
|
||||||
|
ScriptableResourceCache::ScriptableResourceCache(DependencyManager::get<TextureCache>())
|
||||||
|
{
|
||||||
|
connect(DependencyManager::get<TextureCache>().data(), &TextureCache::spectatorCameraFramebufferReset,
|
||||||
|
this, &TextureCacheScriptingInterface::spectatorCameraFramebufferReset);
|
||||||
|
}
|
||||||
|
|
||||||
|
ScriptableResource* TextureCacheScriptingInterface::prefetch(const QUrl& url, int type, int maxNumPixels) {
|
||||||
|
return DependencyManager::get<TextureCache>()->prefetch(url, type, maxNumPixels);
|
||||||
|
}
|
|
@ -0,0 +1,65 @@
|
||||||
|
//
|
||||||
|
// TextureCacheScriptingInterface.h
|
||||||
|
// libraries/mmodel-networking/src/model-networking
|
||||||
|
//
|
||||||
|
// Created by David Rowe on 25 Jul 2018.
|
||||||
|
// Copyright 2018 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
//
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#ifndef hifi_TextureCacheScriptingInterface_h
|
||||||
|
#define hifi_TextureCacheScriptingInterface_h
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
|
||||||
|
#include <ResourceCache.h>
|
||||||
|
|
||||||
|
#include "TextureCache.h"
|
||||||
|
|
||||||
|
class TextureCacheScriptingInterface : public ScriptableResourceCache, public Dependency {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
// Properties are copied over from ResourceCache (see ResourceCache.h for reason).
|
||||||
|
|
||||||
|
/**jsdoc
|
||||||
|
* API to manage texture cache resources.
|
||||||
|
* @namespace TextureCache
|
||||||
|
*
|
||||||
|
* @hifi-interface
|
||||||
|
* @hifi-client-entity
|
||||||
|
*
|
||||||
|
* @property {number} numTotal - Total number of total resources. <em>Read-only.</em>
|
||||||
|
* @property {number} numCached - Total number of cached resource. <em>Read-only.</em>
|
||||||
|
* @property {number} sizeTotal - Size in bytes of all resources. <em>Read-only.</em>
|
||||||
|
* @property {number} sizeCached - Size in bytes of all cached resources. <em>Read-only.</em>
|
||||||
|
*
|
||||||
|
* @borrows ResourceCache.getResourceList as getResourceList
|
||||||
|
* @borrows ResourceCache.updateTotalSize as updateTotalSize
|
||||||
|
* @borrows ResourceCache.prefetch as prefetch
|
||||||
|
* @borrows ResourceCache.dirty as dirty
|
||||||
|
*/
|
||||||
|
|
||||||
|
public:
|
||||||
|
TextureCacheScriptingInterface();
|
||||||
|
|
||||||
|
/**jsdoc
|
||||||
|
* @function TextureCache.prefetch
|
||||||
|
* @param {string} url
|
||||||
|
* @param {number} type
|
||||||
|
* @param {number} [maxNumPixels=67108864]
|
||||||
|
* @returns {ResourceObject}
|
||||||
|
*/
|
||||||
|
Q_INVOKABLE ScriptableResource* prefetch(const QUrl& url, int type, int maxNumPixels = ABSOLUTE_MAX_TEXTURE_NUM_PIXELS);
|
||||||
|
|
||||||
|
signals:
|
||||||
|
/**jsdoc
|
||||||
|
* @function TextureCache.spectatorCameraFramebufferReset
|
||||||
|
* @returns {Signal}
|
||||||
|
*/
|
||||||
|
void spectatorCameraFramebufferReset();
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // hifi_TextureCacheScriptingInterface_h
|
|
@ -131,6 +131,24 @@ QSharedPointer<Resource> ResourceCacheSharedItems::getHighestPendingRequest() {
|
||||||
return highestResource;
|
return highestResource;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
ScriptableResourceCache::ScriptableResourceCache(QSharedPointer<ResourceCache> resourceCache) {
|
||||||
|
_resourceCache = resourceCache;
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariantList ScriptableResourceCache::getResourceList() {
|
||||||
|
return _resourceCache->getResourceList();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ScriptableResourceCache::updateTotalSize(const qint64& deltaSize) {
|
||||||
|
_resourceCache->updateTotalSize(deltaSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
ScriptableResource* ScriptableResourceCache::prefetch(const QUrl& url, void* extra) {
|
||||||
|
return _resourceCache->prefetch(url, extra);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
ScriptableResource::ScriptableResource(const QUrl& url) :
|
ScriptableResource::ScriptableResource(const QUrl& url) :
|
||||||
QObject(nullptr),
|
QObject(nullptr),
|
||||||
_url(url) { }
|
_url(url) { }
|
||||||
|
|
|
@ -124,9 +124,9 @@ public:
|
||||||
virtual ~ScriptableResource() = default;
|
virtual ~ScriptableResource() = default;
|
||||||
|
|
||||||
/**jsdoc
|
/**jsdoc
|
||||||
* Release this resource.
|
* Release this resource.
|
||||||
* @function ResourceObject#release
|
* @function ResourceObject#release
|
||||||
*/
|
*/
|
||||||
Q_INVOKABLE void release();
|
Q_INVOKABLE void release();
|
||||||
|
|
||||||
const QUrl& getURL() const { return _url; }
|
const QUrl& getURL() const { return _url; }
|
||||||
|
@ -186,15 +186,6 @@ Q_DECLARE_METATYPE(ScriptableResource*);
|
||||||
class ResourceCache : public QObject {
|
class ResourceCache : public QObject {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
// JSDoc 3.5.5 doesn't augment namespaces with @property or @function definitions.
|
|
||||||
// The ResourceCache properties and functions are copied to the different exposed cache classes.
|
|
||||||
|
|
||||||
/**jsdoc
|
|
||||||
* @property {number} numTotal - Total number of total resources. <em>Read-only.</em>
|
|
||||||
* @property {number} numCached - Total number of cached resource. <em>Read-only.</em>
|
|
||||||
* @property {number} sizeTotal - Size in bytes of all resources. <em>Read-only.</em>
|
|
||||||
* @property {number} sizeCached - Size in bytes of all cached resources. <em>Read-only.</em>
|
|
||||||
*/
|
|
||||||
Q_PROPERTY(size_t numTotal READ getNumTotalResources NOTIFY dirty)
|
Q_PROPERTY(size_t numTotal READ getNumTotalResources NOTIFY dirty)
|
||||||
Q_PROPERTY(size_t numCached READ getNumCachedResources NOTIFY dirty)
|
Q_PROPERTY(size_t numCached READ getNumCachedResources NOTIFY dirty)
|
||||||
Q_PROPERTY(size_t sizeTotal READ getSizeTotalResources NOTIFY dirty)
|
Q_PROPERTY(size_t sizeTotal READ getSizeTotalResources NOTIFY dirty)
|
||||||
|
@ -207,11 +198,6 @@ public:
|
||||||
size_t getNumCachedResources() const { return _numUnusedResources; }
|
size_t getNumCachedResources() const { return _numUnusedResources; }
|
||||||
size_t getSizeCachedResources() const { return _unusedResourcesSize; }
|
size_t getSizeCachedResources() const { return _unusedResourcesSize; }
|
||||||
|
|
||||||
/**jsdoc
|
|
||||||
* Get the list of all resource URLs.
|
|
||||||
* @function ResourceCache.getResourceList
|
|
||||||
* @returns {string[]}
|
|
||||||
*/
|
|
||||||
Q_INVOKABLE QVariantList getResourceList();
|
Q_INVOKABLE QVariantList getResourceList();
|
||||||
|
|
||||||
static void setRequestLimit(int limit);
|
static void setRequestLimit(int limit);
|
||||||
|
@ -237,40 +223,17 @@ public:
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
|
|
||||||
/**jsdoc
|
|
||||||
* @function ResourceCache.dirty
|
|
||||||
* @returns {Signal}
|
|
||||||
*/
|
|
||||||
void dirty();
|
void dirty();
|
||||||
|
|
||||||
protected slots:
|
protected slots:
|
||||||
|
|
||||||
/**jsdoc
|
|
||||||
* @function ResourceCache.updateTotalSize
|
|
||||||
* @param {number} deltaSize
|
|
||||||
*/
|
|
||||||
void updateTotalSize(const qint64& deltaSize);
|
void updateTotalSize(const qint64& deltaSize);
|
||||||
|
|
||||||
/**jsdoc
|
|
||||||
* Prefetches a resource.
|
|
||||||
* @function ResourceCache.prefetch
|
|
||||||
* @param {string} url - URL of the resource to prefetch.
|
|
||||||
* @param {object} [extra=null]
|
|
||||||
* @returns {ResourceObject}
|
|
||||||
*/
|
|
||||||
// Prefetches a resource to be held by the QScriptEngine.
|
// Prefetches a resource to be held by the QScriptEngine.
|
||||||
// Left as a protected member so subclasses can overload prefetch
|
// Left as a protected member so subclasses can overload prefetch
|
||||||
// and delegate to it (see TextureCache::prefetch(const QUrl&, int).
|
// and delegate to it (see TextureCache::prefetch(const QUrl&, int).
|
||||||
ScriptableResource* prefetch(const QUrl& url, void* extra);
|
ScriptableResource* prefetch(const QUrl& url, void* extra);
|
||||||
|
|
||||||
/**jsdoc
|
|
||||||
* Asynchronously loads a resource from the specified URL and returns it.
|
|
||||||
* @function ResourceCache.getResource
|
|
||||||
* @param {string} url - URL of the resource to load.
|
|
||||||
* @param {string} [fallback=""] - Fallback URL if load of the desired URL fails.
|
|
||||||
* @param {} [extra=null]
|
|
||||||
* @returns {object}
|
|
||||||
*/
|
|
||||||
// FIXME: The return type is not recognized by JavaScript.
|
// FIXME: The return type is not recognized by JavaScript.
|
||||||
/// Loads a resource from the specified URL and returns it.
|
/// Loads a resource from the specified URL and returns it.
|
||||||
/// If the caller is on a different thread than the ResourceCache,
|
/// If the caller is on a different thread than the ResourceCache,
|
||||||
|
@ -306,6 +269,7 @@ protected:
|
||||||
|
|
||||||
private:
|
private:
|
||||||
friend class Resource;
|
friend class Resource;
|
||||||
|
friend class ScriptableResourceCache;
|
||||||
|
|
||||||
void reserveUnusedResource(qint64 resourceSize);
|
void reserveUnusedResource(qint64 resourceSize);
|
||||||
void resetResourceCounters();
|
void resetResourceCounters();
|
||||||
|
@ -335,6 +299,66 @@ private:
|
||||||
QReadWriteLock _resourcesToBeGottenLock { QReadWriteLock::Recursive };
|
QReadWriteLock _resourcesToBeGottenLock { QReadWriteLock::Recursive };
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// Wrapper to expose resource caches to JS/QML
|
||||||
|
class ScriptableResourceCache : public QObject {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
// JSDoc 3.5.5 doesn't augment name spaces with @property definitions so the following properties JSDoc is copied to the
|
||||||
|
// different exposed cache classes.
|
||||||
|
|
||||||
|
/**jsdoc
|
||||||
|
* @property {number} numTotal - Total number of total resources. <em>Read-only.</em>
|
||||||
|
* @property {number} numCached - Total number of cached resource. <em>Read-only.</em>
|
||||||
|
* @property {number} sizeTotal - Size in bytes of all resources. <em>Read-only.</em>
|
||||||
|
* @property {number} sizeCached - Size in bytes of all cached resources. <em>Read-only.</em>
|
||||||
|
*/
|
||||||
|
Q_PROPERTY(size_t numTotal READ getNumTotalResources NOTIFY dirty)
|
||||||
|
Q_PROPERTY(size_t numCached READ getNumCachedResources NOTIFY dirty)
|
||||||
|
Q_PROPERTY(size_t sizeTotal READ getSizeTotalResources NOTIFY dirty)
|
||||||
|
Q_PROPERTY(size_t sizeCached READ getSizeCachedResources NOTIFY dirty)
|
||||||
|
|
||||||
|
public:
|
||||||
|
ScriptableResourceCache(QSharedPointer<ResourceCache> resourceCache);
|
||||||
|
|
||||||
|
/**jsdoc
|
||||||
|
* Get the list of all resource URLs.
|
||||||
|
* @function ResourceCache.getResourceList
|
||||||
|
* @returns {string[]}
|
||||||
|
*/
|
||||||
|
Q_INVOKABLE QVariantList getResourceList();
|
||||||
|
|
||||||
|
/**jsdoc
|
||||||
|
* @function ResourceCache.updateTotalSize
|
||||||
|
* @param {number} deltaSize
|
||||||
|
*/
|
||||||
|
Q_INVOKABLE void updateTotalSize(const qint64& deltaSize);
|
||||||
|
|
||||||
|
/**jsdoc
|
||||||
|
* Prefetches a resource.
|
||||||
|
* @function ResourceCache.prefetch
|
||||||
|
* @param {string} url - URL of the resource to prefetch.
|
||||||
|
* @param {object} [extra=null]
|
||||||
|
* @returns {ResourceObject}
|
||||||
|
*/
|
||||||
|
Q_INVOKABLE ScriptableResource* prefetch(const QUrl& url, void* extra = nullptr);
|
||||||
|
|
||||||
|
signals:
|
||||||
|
|
||||||
|
/**jsdoc
|
||||||
|
* @function ResourceCache.dirty
|
||||||
|
* @returns {Signal}
|
||||||
|
*/
|
||||||
|
void dirty();
|
||||||
|
|
||||||
|
private:
|
||||||
|
QSharedPointer<ResourceCache> _resourceCache;
|
||||||
|
|
||||||
|
size_t getNumTotalResources() const { return _resourceCache->getNumTotalResources(); }
|
||||||
|
size_t getSizeTotalResources() const { return _resourceCache->getSizeTotalResources(); }
|
||||||
|
size_t getNumCachedResources() const { return _resourceCache->getNumCachedResources(); }
|
||||||
|
size_t getSizeCachedResources() const { return _resourceCache->getSizeCachedResources(); }
|
||||||
|
};
|
||||||
|
|
||||||
/// Base class for resources.
|
/// Base class for resources.
|
||||||
class Resource : public QObject {
|
class Resource : public QObject {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
|
@ -219,6 +219,20 @@ ScriptEngine::ScriptEngine(Context context, const QString& scriptContents, const
|
||||||
}
|
}
|
||||||
logException(output);
|
logException(output);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (_type == Type::ENTITY_CLIENT || _type == Type::ENTITY_SERVER) {
|
||||||
|
QObject::connect(this, &ScriptEngine::update, this, [this]() {
|
||||||
|
// process pending entity script content
|
||||||
|
if (!_contentAvailableQueue.empty()) {
|
||||||
|
EntityScriptContentAvailableMap pending;
|
||||||
|
std::swap(_contentAvailableQueue, pending);
|
||||||
|
for (auto& pair : pending) {
|
||||||
|
auto& args = pair.second;
|
||||||
|
entityScriptContentAvailable(args.entityID, args.scriptOrURL, args.contents, args.isURL, args.success, args.status);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QString ScriptEngine::getContext() const {
|
QString ScriptEngine::getContext() const {
|
||||||
|
@ -2181,7 +2195,7 @@ void ScriptEngine::loadEntityScript(const EntityItemID& entityID, const QString&
|
||||||
qCDebug(scriptengine) << "loadEntityScript.contentAvailable" << status << QUrl(url).fileName() << entityID.toString();
|
qCDebug(scriptengine) << "loadEntityScript.contentAvailable" << status << QUrl(url).fileName() << entityID.toString();
|
||||||
#endif
|
#endif
|
||||||
if (!isStopping() && _entityScripts.contains(entityID)) {
|
if (!isStopping() && _entityScripts.contains(entityID)) {
|
||||||
entityScriptContentAvailable(entityID, url, contents, isURL, success, status);
|
_contentAvailableQueue[entityID] = { entityID, url, contents, isURL, success, status };
|
||||||
} else {
|
} else {
|
||||||
#ifdef DEBUG_ENTITY_STATES
|
#ifdef DEBUG_ENTITY_STATES
|
||||||
qCDebug(scriptengine) << "loadEntityScript.contentAvailable -- aborting";
|
qCDebug(scriptengine) << "loadEntityScript.contentAvailable -- aborting";
|
||||||
|
|
|
@ -12,6 +12,7 @@
|
||||||
#ifndef hifi_ScriptEngine_h
|
#ifndef hifi_ScriptEngine_h
|
||||||
#define hifi_ScriptEngine_h
|
#define hifi_ScriptEngine_h
|
||||||
|
|
||||||
|
#include <unordered_map>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include <QtCore/QObject>
|
#include <QtCore/QObject>
|
||||||
|
@ -71,6 +72,17 @@ public:
|
||||||
//bool forceRedownload;
|
//bool forceRedownload;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct EntityScriptContentAvailable {
|
||||||
|
EntityItemID entityID;
|
||||||
|
QString scriptOrURL;
|
||||||
|
QString contents;
|
||||||
|
bool isURL;
|
||||||
|
bool success;
|
||||||
|
QString status;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef std::unordered_map<EntityItemID, EntityScriptContentAvailable> EntityScriptContentAvailableMap;
|
||||||
|
|
||||||
typedef QList<CallbackData> CallbackList;
|
typedef QList<CallbackData> CallbackList;
|
||||||
typedef QHash<QString, CallbackList> RegisteredEventHandlers;
|
typedef QHash<QString, CallbackList> RegisteredEventHandlers;
|
||||||
|
|
||||||
|
@ -762,6 +774,7 @@ protected:
|
||||||
QHash<EntityItemID, EntityScriptDetails> _entityScripts;
|
QHash<EntityItemID, EntityScriptDetails> _entityScripts;
|
||||||
QHash<QString, EntityItemID> _occupiedScriptURLs;
|
QHash<QString, EntityItemID> _occupiedScriptURLs;
|
||||||
QList<DeferredLoadEntity> _deferredEntityLoads;
|
QList<DeferredLoadEntity> _deferredEntityLoads;
|
||||||
|
EntityScriptContentAvailableMap _contentAvailableQueue;
|
||||||
|
|
||||||
bool _isThreaded { false };
|
bool _isThreaded { false };
|
||||||
QScriptEngineDebugger* _debugger { nullptr };
|
QScriptEngineDebugger* _debugger { nullptr };
|
||||||
|
|
|
@ -36,6 +36,10 @@ bool OculusBaseDisplayPlugin::beginFrameRender(uint32_t frameIndex) {
|
||||||
if (ovr::reorientRequested(status)) {
|
if (ovr::reorientRequested(status)) {
|
||||||
emit resetSensorsRequested();
|
emit resetSensorsRequested();
|
||||||
}
|
}
|
||||||
|
if (ovr::hmdMounted(status) != _hmdMounted) {
|
||||||
|
_hmdMounted = !_hmdMounted;
|
||||||
|
emit hmdMountedChanged();
|
||||||
|
}
|
||||||
|
|
||||||
_currentRenderFrameInfo = FrameInfo();
|
_currentRenderFrameInfo = FrameInfo();
|
||||||
_currentRenderFrameInfo.sensorSampleTime = ovr_GetTimeInSeconds();
|
_currentRenderFrameInfo.sensorSampleTime = ovr_GetTimeInSeconds();
|
||||||
|
|
|
@ -44,4 +44,5 @@ protected:
|
||||||
ovrLayerEyeFov _sceneLayer;
|
ovrLayerEyeFov _sceneLayer;
|
||||||
ovrViewScaleDesc _viewScaleDesc;
|
ovrViewScaleDesc _viewScaleDesc;
|
||||||
// ovrLayerEyeFovDepth _depthLayer;
|
// ovrLayerEyeFovDepth _depthLayer;
|
||||||
|
bool _hmdMounted { false };
|
||||||
};
|
};
|
||||||
|
|
|
@ -699,7 +699,11 @@ void OpenVrDisplayPlugin::postPreview() {
|
||||||
_nextSimPoseData = nextSim;
|
_nextSimPoseData = nextSim;
|
||||||
});
|
});
|
||||||
_nextRenderPoseData = nextRender;
|
_nextRenderPoseData = nextRender;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isHmdMounted() != _hmdMounted) {
|
||||||
|
_hmdMounted = !_hmdMounted;
|
||||||
|
emit hmdMountedChanged();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -90,4 +90,6 @@ private:
|
||||||
friend class OpenVrSubmitThread;
|
friend class OpenVrSubmitThread;
|
||||||
|
|
||||||
bool _asyncReprojectionActive { false };
|
bool _asyncReprojectionActive { false };
|
||||||
|
|
||||||
|
bool _hmdMounted { false };
|
||||||
};
|
};
|
||||||
|
|
|
@ -66,11 +66,13 @@ Script.include("/~/system/libraries/utils.js");
|
||||||
|
|
||||||
this.sendPickData = function(controllerData) {
|
this.sendPickData = function(controllerData) {
|
||||||
if (controllerData.triggerClicks[this.hand]) {
|
if (controllerData.triggerClicks[this.hand]) {
|
||||||
|
var hand = this.hand === RIGHT_HAND ? Controller.Standard.RightHand : Controller.Standard.LeftHand;
|
||||||
if (!this.triggerClicked) {
|
if (!this.triggerClicked) {
|
||||||
this.selectedTarget = controllerData.rayPicks[this.hand];
|
this.selectedTarget = controllerData.rayPicks[this.hand];
|
||||||
if (!this.selectedTarget.intersects) {
|
if (!this.selectedTarget.intersects) {
|
||||||
Messages.sendLocalMessage("entityToolUpdates", JSON.stringify({
|
Messages.sendLocalMessage("entityToolUpdates", JSON.stringify({
|
||||||
method: "clearSelection"
|
method: "clearSelection",
|
||||||
|
hand: hand
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -78,13 +80,15 @@ Script.include("/~/system/libraries/utils.js");
|
||||||
if (!this.isTabletMaterialEntity(this.selectedTarget.objectID)) {
|
if (!this.isTabletMaterialEntity(this.selectedTarget.objectID)) {
|
||||||
Messages.sendLocalMessage("entityToolUpdates", JSON.stringify({
|
Messages.sendLocalMessage("entityToolUpdates", JSON.stringify({
|
||||||
method: "selectEntity",
|
method: "selectEntity",
|
||||||
entityID: this.selectedTarget.objectID
|
entityID: this.selectedTarget.objectID,
|
||||||
|
hand: hand
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
} else if (this.selectedTarget.type === Picks.INTERSECTED_OVERLAY) {
|
} else if (this.selectedTarget.type === Picks.INTERSECTED_OVERLAY) {
|
||||||
Messages.sendLocalMessage("entityToolUpdates", JSON.stringify({
|
Messages.sendLocalMessage("entityToolUpdates", JSON.stringify({
|
||||||
method: "selectOverlay",
|
method: "selectOverlay",
|
||||||
overlayID: this.selectedTarget.objectID
|
overlayID: this.selectedTarget.objectID,
|
||||||
|
hand: hand
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -316,10 +316,10 @@ var toolBar = (function () {
|
||||||
direction = Vec3.multiplyQbyV(direction, Vec3.UNIT_Z);
|
direction = Vec3.multiplyQbyV(direction, Vec3.UNIT_Z);
|
||||||
// Align entity with Avatar orientation.
|
// Align entity with Avatar orientation.
|
||||||
properties.rotation = MyAvatar.orientation;
|
properties.rotation = MyAvatar.orientation;
|
||||||
|
|
||||||
var PRE_ADJUST_ENTITY_TYPES = ["Box", "Sphere", "Shape", "Text", "Web", "Material"];
|
var PRE_ADJUST_ENTITY_TYPES = ["Box", "Sphere", "Shape", "Text", "Web", "Material"];
|
||||||
if (PRE_ADJUST_ENTITY_TYPES.indexOf(properties.type) !== -1) {
|
if (PRE_ADJUST_ENTITY_TYPES.indexOf(properties.type) !== -1) {
|
||||||
|
|
||||||
// Adjust position of entity per bounding box prior to creating it.
|
// Adjust position of entity per bounding box prior to creating it.
|
||||||
var registration = properties.registration;
|
var registration = properties.registration;
|
||||||
if (registration === undefined) {
|
if (registration === undefined) {
|
||||||
|
@ -352,7 +352,12 @@ var toolBar = (function () {
|
||||||
properties.userData = JSON.stringify({ grabbableKey: { grabbable: false } });
|
properties.userData = JSON.stringify({ grabbableKey: { grabbable: false } });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SelectionManager.saveProperties();
|
||||||
entityID = Entities.addEntity(properties);
|
entityID = Entities.addEntity(properties);
|
||||||
|
pushCommandForSelections([{
|
||||||
|
entityID: entityID,
|
||||||
|
properties: properties
|
||||||
|
}], [], true);
|
||||||
|
|
||||||
if (properties.type === "ParticleEffect") {
|
if (properties.type === "ParticleEffect") {
|
||||||
selectParticleEntity(entityID);
|
selectParticleEntity(entityID);
|
||||||
|
@ -963,13 +968,15 @@ function handleOverlaySelectionToolUpdates(channel, message, sender) {
|
||||||
var data = JSON.parse(message);
|
var data = JSON.parse(message);
|
||||||
|
|
||||||
if (data.method === "selectOverlay") {
|
if (data.method === "selectOverlay") {
|
||||||
if (wantDebug) {
|
if (!selectionDisplay.triggered() || selectionDisplay.triggeredHand === data.hand) {
|
||||||
print("setting selection to overlay " + data.overlayID);
|
if (wantDebug) {
|
||||||
}
|
print("setting selection to overlay " + data.overlayID);
|
||||||
var entity = entityIconOverlayManager.findEntity(data.overlayID);
|
}
|
||||||
|
var entity = entityIconOverlayManager.findEntity(data.overlayID);
|
||||||
|
|
||||||
if (entity !== null) {
|
if (entity !== null) {
|
||||||
selectionManager.setSelections([entity]);
|
selectionManager.setSelections([entity]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1590,7 +1597,7 @@ function deleteSelectedEntities() {
|
||||||
Entities.deleteEntity(entityID);
|
Entities.deleteEntity(entityID);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (savedProperties.length > 0) {
|
if (savedProperties.length > 0) {
|
||||||
SelectionManager.clearSelections();
|
SelectionManager.clearSelections();
|
||||||
pushCommandForSelections([], savedProperties);
|
pushCommandForSelections([], savedProperties);
|
||||||
|
@ -1880,12 +1887,14 @@ Controller.keyReleaseEvent.connect(keyReleaseEvent);
|
||||||
Controller.keyPressEvent.connect(keyPressEvent);
|
Controller.keyPressEvent.connect(keyPressEvent);
|
||||||
|
|
||||||
function recursiveAdd(newParentID, parentData) {
|
function recursiveAdd(newParentID, parentData) {
|
||||||
var children = parentData.children;
|
if (parentData.children !== undefined) {
|
||||||
for (var i = 0; i < children.length; i++) {
|
var children = parentData.children;
|
||||||
var childProperties = children[i].properties;
|
for (var i = 0; i < children.length; i++) {
|
||||||
childProperties.parentID = newParentID;
|
var childProperties = children[i].properties;
|
||||||
var newChildID = Entities.addEntity(childProperties);
|
childProperties.parentID = newParentID;
|
||||||
recursiveAdd(newChildID, children[i]);
|
var newChildID = Entities.addEntity(childProperties);
|
||||||
|
recursiveAdd(newChildID, children[i]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1895,16 +1904,22 @@ function recursiveAdd(newParentID, parentData) {
|
||||||
var DELETED_ENTITY_MAP = {};
|
var DELETED_ENTITY_MAP = {};
|
||||||
|
|
||||||
function applyEntityProperties(data) {
|
function applyEntityProperties(data) {
|
||||||
var properties = data.setProperties;
|
var editEntities = data.editEntities;
|
||||||
var selectedEntityIDs = [];
|
var selectedEntityIDs = [];
|
||||||
|
var selectEdits = data.createEntities.length == 0 || !data.selectCreated;
|
||||||
var i, entityID;
|
var i, entityID;
|
||||||
for (i = 0; i < properties.length; i++) {
|
for (i = 0; i < editEntities.length; i++) {
|
||||||
entityID = properties[i].entityID;
|
var entityID = editEntities[i].entityID;
|
||||||
if (DELETED_ENTITY_MAP[entityID] !== undefined) {
|
if (DELETED_ENTITY_MAP[entityID] !== undefined) {
|
||||||
entityID = DELETED_ENTITY_MAP[entityID];
|
entityID = DELETED_ENTITY_MAP[entityID];
|
||||||
}
|
}
|
||||||
Entities.editEntity(entityID, properties[i].properties);
|
var entityProperties = editEntities[i].properties;
|
||||||
selectedEntityIDs.push(entityID);
|
if (entityProperties !== null) {
|
||||||
|
Entities.editEntity(entityID, entityProperties);
|
||||||
|
}
|
||||||
|
if (selectEdits) {
|
||||||
|
selectedEntityIDs.push(entityID);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
for (i = 0; i < data.createEntities.length; i++) {
|
for (i = 0; i < data.createEntities.length; i++) {
|
||||||
entityID = data.createEntities[i].entityID;
|
entityID = data.createEntities[i].entityID;
|
||||||
|
@ -1933,31 +1948,39 @@ function applyEntityProperties(data) {
|
||||||
|
|
||||||
// For currently selected entities, push a command to the UndoStack that uses the current entity properties for the
|
// For currently selected entities, push a command to the UndoStack that uses the current entity properties for the
|
||||||
// redo command, and the saved properties for the undo command. Also, include create and delete entity data.
|
// redo command, and the saved properties for the undo command. Also, include create and delete entity data.
|
||||||
function pushCommandForSelections(createdEntityData, deletedEntityData) {
|
function pushCommandForSelections(createdEntityData, deletedEntityData, doNotSaveEditProperties) {
|
||||||
|
doNotSaveEditProperties = false;
|
||||||
var undoData = {
|
var undoData = {
|
||||||
setProperties: [],
|
editEntities: [],
|
||||||
createEntities: deletedEntityData || [],
|
createEntities: deletedEntityData || [],
|
||||||
deleteEntities: createdEntityData || [],
|
deleteEntities: createdEntityData || [],
|
||||||
selectCreated: true
|
selectCreated: true
|
||||||
};
|
};
|
||||||
var redoData = {
|
var redoData = {
|
||||||
setProperties: [],
|
editEntities: [],
|
||||||
createEntities: createdEntityData || [],
|
createEntities: createdEntityData || [],
|
||||||
deleteEntities: deletedEntityData || [],
|
deleteEntities: deletedEntityData || [],
|
||||||
selectCreated: false
|
selectCreated: true
|
||||||
};
|
};
|
||||||
for (var i = 0; i < SelectionManager.selections.length; i++) {
|
for (var i = 0; i < SelectionManager.selections.length; i++) {
|
||||||
var entityID = SelectionManager.selections[i];
|
var entityID = SelectionManager.selections[i];
|
||||||
var initialProperties = SelectionManager.savedProperties[entityID];
|
var initialProperties = SelectionManager.savedProperties[entityID];
|
||||||
var currentProperties = Entities.getEntityProperties(entityID);
|
var currentProperties = null;
|
||||||
if (!initialProperties) {
|
if (!initialProperties) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
undoData.setProperties.push({
|
|
||||||
|
if (doNotSaveEditProperties) {
|
||||||
|
initialProperties = null;
|
||||||
|
} else {
|
||||||
|
currentProperties = Entities.getEntityProperties(entityID);
|
||||||
|
}
|
||||||
|
|
||||||
|
undoData.editEntities.push({
|
||||||
entityID: entityID,
|
entityID: entityID,
|
||||||
properties: initialProperties
|
properties: initialProperties
|
||||||
});
|
});
|
||||||
redoData.setProperties.push({
|
redoData.editEntities.push({
|
||||||
entityID: entityID,
|
entityID: entityID,
|
||||||
properties: currentProperties
|
properties: currentProperties
|
||||||
});
|
});
|
||||||
|
@ -2229,7 +2252,7 @@ var PropertiesTool = function (opts) {
|
||||||
updateSelections(true);
|
updateSelections(true);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
createToolsWindow.webEventReceived.addListener(this, onWebEventReceived);
|
createToolsWindow.webEventReceived.addListener(this, onWebEventReceived);
|
||||||
|
|
||||||
webView.webEventReceived.connect(onWebEventReceived);
|
webView.webEventReceived.connect(onWebEventReceived);
|
||||||
|
|
|
@ -53,14 +53,18 @@ SelectionManager = (function() {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (messageParsed.method === "selectEntity") {
|
if (messageParsed.method === "selectEntity") {
|
||||||
if (wantDebug) {
|
if (!SelectionDisplay.triggered() || SelectionDisplay.triggeredHand === messageParsed.hand) {
|
||||||
print("setting selection to " + messageParsed.entityID);
|
if (wantDebug) {
|
||||||
|
print("setting selection to " + messageParsed.entityID);
|
||||||
|
}
|
||||||
|
that.setSelections([messageParsed.entityID]);
|
||||||
}
|
}
|
||||||
that.setSelections([messageParsed.entityID]);
|
|
||||||
} else if (messageParsed.method === "clearSelection") {
|
} else if (messageParsed.method === "clearSelection") {
|
||||||
that.clearSelections();
|
if (!SelectionDisplay.triggered() || SelectionDisplay.triggeredHand === messageParsed.hand) {
|
||||||
|
that.clearSelections();
|
||||||
|
}
|
||||||
} else if (messageParsed.method === "pointingAt") {
|
} else if (messageParsed.method === "pointingAt") {
|
||||||
if (messageParsed.rightHand) {
|
if (messageParsed.hand === Controller.Standard.RightHand) {
|
||||||
that.pointingAtDesktopWindowRight = messageParsed.desktopWindow;
|
that.pointingAtDesktopWindowRight = messageParsed.desktopWindow;
|
||||||
that.pointingAtTabletRight = messageParsed.tablet;
|
that.pointingAtTabletRight = messageParsed.tablet;
|
||||||
} else {
|
} else {
|
||||||
|
@ -194,11 +198,40 @@ SelectionManager = (function() {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Return true if the given entity with `properties` is being grabbed by an avatar.
|
||||||
|
// This is mostly a heuristic - there is no perfect way to know if an entity is being
|
||||||
|
// grabbed.
|
||||||
|
function nonDynamicEntityIsBeingGrabbedByAvatar(properties) {
|
||||||
|
if (properties.dynamic || Uuid.isNull(properties.parentID)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var avatar = AvatarList.getAvatar(properties.parentID);
|
||||||
|
if (Uuid.isNull(avatar.sessionUUID)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var grabJointNames = [
|
||||||
|
'RightHand', 'LeftHand',
|
||||||
|
'_CONTROLLER_RIGHTHAND', '_CONTROLLER_LEFTHAND',
|
||||||
|
'_CAMERA_RELATIVE_CONTROLLER_RIGHTHAND', '_CAMERA_RELATIVE_CONTROLLER_LEFTHAND'];
|
||||||
|
|
||||||
|
for (var i = 0; i < grabJointNames.length; ++i) {
|
||||||
|
if (avatar.getJointIndex(grabJointNames[i]) === properties.parentJointIndex) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
that.duplicateSelection = function() {
|
that.duplicateSelection = function() {
|
||||||
var entitiesToDuplicate = [];
|
var entitiesToDuplicate = [];
|
||||||
var duplicatedEntityIDs = [];
|
var duplicatedEntityIDs = [];
|
||||||
var duplicatedChildrenWithOldParents = [];
|
var duplicatedChildrenWithOldParents = [];
|
||||||
var originalEntityToNewEntityID = [];
|
var originalEntityToNewEntityID = [];
|
||||||
|
|
||||||
|
SelectionManager.saveProperties();
|
||||||
|
|
||||||
// build list of entities to duplicate by including any unselected children of selected parent entities
|
// build list of entities to duplicate by including any unselected children of selected parent entities
|
||||||
Object.keys(that.savedProperties).forEach(function(originalEntityID) {
|
Object.keys(that.savedProperties).forEach(function(originalEntityID) {
|
||||||
|
@ -216,7 +249,30 @@ SelectionManager = (function() {
|
||||||
properties = Entities.getEntityProperties(originalEntityID);
|
properties = Entities.getEntityProperties(originalEntityID);
|
||||||
}
|
}
|
||||||
if (!properties.locked && (!properties.clientOnly || properties.owningAvatarID === MyAvatar.sessionUUID)) {
|
if (!properties.locked && (!properties.clientOnly || properties.owningAvatarID === MyAvatar.sessionUUID)) {
|
||||||
|
if (nonDynamicEntityIsBeingGrabbedByAvatar(properties)) {
|
||||||
|
properties.parentID = null;
|
||||||
|
properties.parentJointIndex = null;
|
||||||
|
properties.localPosition = properties.position;
|
||||||
|
properties.localRotation = properties.rotation;
|
||||||
|
}
|
||||||
|
delete properties.actionData;
|
||||||
var newEntityID = Entities.addEntity(properties);
|
var newEntityID = Entities.addEntity(properties);
|
||||||
|
|
||||||
|
// Re-apply actions from the original entity
|
||||||
|
var actionIDs = Entities.getActionIDs(properties.id);
|
||||||
|
for (var i = 0; i < actionIDs.length; ++i) {
|
||||||
|
var actionID = actionIDs[i];
|
||||||
|
var actionArguments = Entities.getActionArguments(properties.id, actionID);
|
||||||
|
if (actionArguments) {
|
||||||
|
var type = actionArguments.type;
|
||||||
|
if (type == 'hold' || type == 'far-grab') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
delete actionArguments.ttl;
|
||||||
|
Entities.addAction(type, newEntityID, actionArguments);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
duplicatedEntityIDs.push({
|
duplicatedEntityIDs.push({
|
||||||
entityID: newEntityID,
|
entityID: newEntityID,
|
||||||
properties: properties
|
properties: properties
|
||||||
|
@ -255,7 +311,8 @@ SelectionManager = (function() {
|
||||||
that.worldPosition = null;
|
that.worldPosition = null;
|
||||||
that.worldRotation = null;
|
that.worldRotation = null;
|
||||||
} else if (that.selections.length === 1) {
|
} else if (that.selections.length === 1) {
|
||||||
properties = Entities.getEntityProperties(that.selections[0]);
|
properties = Entities.getEntityProperties(that.selections[0],
|
||||||
|
['dimensions', 'position', 'rotation', 'registrationPoint', 'boundingBox', 'type']);
|
||||||
that.localDimensions = properties.dimensions;
|
that.localDimensions = properties.dimensions;
|
||||||
that.localPosition = properties.position;
|
that.localPosition = properties.position;
|
||||||
that.localRotation = properties.rotation;
|
that.localRotation = properties.rotation;
|
||||||
|
@ -271,7 +328,7 @@ SelectionManager = (function() {
|
||||||
SelectionDisplay.setSpaceMode(SPACE_LOCAL);
|
SelectionDisplay.setSpaceMode(SPACE_LOCAL);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
properties = Entities.getEntityProperties(that.selections[0]);
|
properties = Entities.getEntityProperties(that.selections[0], ['type', 'boundingBox']);
|
||||||
|
|
||||||
that.entityType = properties.type;
|
that.entityType = properties.type;
|
||||||
|
|
||||||
|
@ -279,7 +336,7 @@ SelectionManager = (function() {
|
||||||
var tfl = properties.boundingBox.tfl;
|
var tfl = properties.boundingBox.tfl;
|
||||||
|
|
||||||
for (var i = 1; i < that.selections.length; i++) {
|
for (var i = 1; i < that.selections.length; i++) {
|
||||||
properties = Entities.getEntityProperties(that.selections[i]);
|
properties = Entities.getEntityProperties(that.selections[i], 'boundingBox');
|
||||||
var bb = properties.boundingBox;
|
var bb = properties.boundingBox;
|
||||||
brn.x = Math.min(bb.brn.x, brn.x);
|
brn.x = Math.min(bb.brn.x, brn.x);
|
||||||
brn.y = Math.min(bb.brn.y, brn.y);
|
brn.y = Math.min(bb.brn.y, brn.y);
|
||||||
|
@ -410,6 +467,8 @@ SelectionDisplay = (function() {
|
||||||
YAW: 1,
|
YAW: 1,
|
||||||
ROLL: 2
|
ROLL: 2
|
||||||
};
|
};
|
||||||
|
|
||||||
|
var NO_TRIGGER_HAND = -1;
|
||||||
|
|
||||||
var spaceMode = SPACE_LOCAL;
|
var spaceMode = SPACE_LOCAL;
|
||||||
var overlayNames = [];
|
var overlayNames = [];
|
||||||
|
@ -751,20 +810,17 @@ SelectionDisplay = (function() {
|
||||||
// But we dont' get mousePressEvents.
|
// But we dont' get mousePressEvents.
|
||||||
that.triggerMapping = Controller.newMapping(Script.resolvePath('') + '-click');
|
that.triggerMapping = Controller.newMapping(Script.resolvePath('') + '-click');
|
||||||
Script.scriptEnding.connect(that.triggerMapping.disable);
|
Script.scriptEnding.connect(that.triggerMapping.disable);
|
||||||
that.TRIGGER_GRAB_VALUE = 0.85; // From handControllerGrab/Pointer.js. Should refactor.
|
that.triggeredHand = NO_TRIGGER_HAND;
|
||||||
that.TRIGGER_ON_VALUE = 0.4;
|
that.triggered = function() {
|
||||||
that.TRIGGER_OFF_VALUE = 0.15;
|
return that.triggeredHand !== NO_TRIGGER_HAND;
|
||||||
that.triggered = false;
|
}
|
||||||
var activeHand = Controller.Standard.RightHand;
|
function makeClickHandler(hand) {
|
||||||
function makeTriggerHandler(hand) {
|
return function (clicked) {
|
||||||
return function (value) {
|
// Don't allow both hands to trigger at the same time
|
||||||
if (!that.triggered && (value > that.TRIGGER_GRAB_VALUE)) { // should we smooth?
|
if (that.triggered() && hand !== that.triggeredHand) {
|
||||||
that.triggered = true;
|
return;
|
||||||
if (activeHand !== hand) {
|
}
|
||||||
// No switching while the other is already triggered, so no need to release.
|
if (!that.triggered() && clicked) {
|
||||||
activeHand = (activeHand === Controller.Standard.RightHand) ?
|
|
||||||
Controller.Standard.LeftHand : Controller.Standard.RightHand;
|
|
||||||
}
|
|
||||||
var pointingAtDesktopWindow = (hand === Controller.Standard.RightHand &&
|
var pointingAtDesktopWindow = (hand === Controller.Standard.RightHand &&
|
||||||
SelectionManager.pointingAtDesktopWindowRight) ||
|
SelectionManager.pointingAtDesktopWindowRight) ||
|
||||||
(hand === Controller.Standard.LeftHand &&
|
(hand === Controller.Standard.LeftHand &&
|
||||||
|
@ -774,15 +830,16 @@ SelectionDisplay = (function() {
|
||||||
if (pointingAtDesktopWindow || pointingAtTablet) {
|
if (pointingAtDesktopWindow || pointingAtTablet) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
that.triggeredHand = hand;
|
||||||
that.mousePressEvent({});
|
that.mousePressEvent({});
|
||||||
} else if (that.triggered && (value < that.TRIGGER_OFF_VALUE)) {
|
} else if (that.triggered() && !clicked) {
|
||||||
that.triggered = false;
|
that.triggeredHand = NO_TRIGGER_HAND;
|
||||||
that.mouseReleaseEvent({});
|
that.mouseReleaseEvent({});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
that.triggerMapping.from(Controller.Standard.RT).peek().to(makeTriggerHandler(Controller.Standard.RightHand));
|
that.triggerMapping.from(Controller.Standard.RTClick).peek().to(makeClickHandler(Controller.Standard.RightHand));
|
||||||
that.triggerMapping.from(Controller.Standard.LT).peek().to(makeTriggerHandler(Controller.Standard.LeftHand));
|
that.triggerMapping.from(Controller.Standard.LTClick).peek().to(makeClickHandler(Controller.Standard.LeftHand));
|
||||||
|
|
||||||
// FUNCTION DEF(s): Intersection Check Helpers
|
// FUNCTION DEF(s): Intersection Check Helpers
|
||||||
function testRayIntersect(queryRay, overlayIncludes, overlayExcludes) {
|
function testRayIntersect(queryRay, overlayIncludes, overlayExcludes) {
|
||||||
|
@ -833,7 +890,7 @@ SelectionDisplay = (function() {
|
||||||
if (wantDebug) {
|
if (wantDebug) {
|
||||||
print("=============== eST::MousePressEvent BEG =======================");
|
print("=============== eST::MousePressEvent BEG =======================");
|
||||||
}
|
}
|
||||||
if (!event.isLeftButton && !that.triggered) {
|
if (!event.isLeftButton && !that.triggered()) {
|
||||||
// EARLY EXIT-(if another mouse button than left is pressed ignore it)
|
// EARLY EXIT-(if another mouse button than left is pressed ignore it)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -1079,9 +1136,9 @@ SelectionDisplay = (function() {
|
||||||
|
|
||||||
that.checkControllerMove = function() {
|
that.checkControllerMove = function() {
|
||||||
if (SelectionManager.hasSelection()) {
|
if (SelectionManager.hasSelection()) {
|
||||||
var controllerPose = getControllerWorldLocation(activeHand, true);
|
var controllerPose = getControllerWorldLocation(that.triggeredHand, true);
|
||||||
var hand = (activeHand === Controller.Standard.LeftHand) ? 0 : 1;
|
var hand = (that.triggeredHand === Controller.Standard.LeftHand) ? 0 : 1;
|
||||||
if (controllerPose.valid && lastControllerPoses[hand].valid && that.triggered) {
|
if (controllerPose.valid && lastControllerPoses[hand].valid && that.triggered()) {
|
||||||
if (!Vec3.equal(controllerPose.position, lastControllerPoses[hand].position) ||
|
if (!Vec3.equal(controllerPose.position, lastControllerPoses[hand].position) ||
|
||||||
!Vec3.equal(controllerPose.rotation, lastControllerPoses[hand].rotation)) {
|
!Vec3.equal(controllerPose.rotation, lastControllerPoses[hand].rotation)) {
|
||||||
that.mouseMoveEvent({});
|
that.mouseMoveEvent({});
|
||||||
|
@ -1092,8 +1149,8 @@ SelectionDisplay = (function() {
|
||||||
};
|
};
|
||||||
|
|
||||||
function controllerComputePickRay() {
|
function controllerComputePickRay() {
|
||||||
var controllerPose = getControllerWorldLocation(activeHand, true);
|
var controllerPose = getControllerWorldLocation(that.triggeredHand, true);
|
||||||
if (controllerPose.valid && that.triggered) {
|
if (controllerPose.valid && that.triggered()) {
|
||||||
var controllerPosition = controllerPose.translation;
|
var controllerPosition = controllerPose.translation;
|
||||||
// This gets point direction right, but if you want general quaternion it would be more complicated:
|
// This gets point direction right, but if you want general quaternion it would be more complicated:
|
||||||
var controllerDirection = Quat.getUp(controllerPose.rotation);
|
var controllerDirection = Quat.getUp(controllerPose.rotation);
|
||||||
|
@ -1716,6 +1773,20 @@ SelectionDisplay = (function() {
|
||||||
Vec3.print(" pickResult.intersection", pickResult.intersection);
|
Vec3.print(" pickResult.intersection", pickResult.intersection);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Duplicate entities if alt is pressed. This will make a
|
||||||
|
// copy of the selected entities and move the _original_ entities, not
|
||||||
|
// the new ones.
|
||||||
|
if (event.isAlt || doClone) {
|
||||||
|
duplicatedEntityIDs = SelectionManager.duplicateSelection();
|
||||||
|
var ids = [];
|
||||||
|
for (var i = 0; i < duplicatedEntityIDs.length; ++i) {
|
||||||
|
ids.push(duplicatedEntityIDs[i].entityID);
|
||||||
|
}
|
||||||
|
SelectionManager.setSelections(ids);
|
||||||
|
} else {
|
||||||
|
duplicatedEntityIDs = null;
|
||||||
|
}
|
||||||
|
|
||||||
SelectionManager.saveProperties();
|
SelectionManager.saveProperties();
|
||||||
that.resetPreviousHandleColor();
|
that.resetPreviousHandleColor();
|
||||||
|
|
||||||
|
@ -1745,15 +1816,6 @@ SelectionDisplay = (function() {
|
||||||
z: 0
|
z: 0
|
||||||
});
|
});
|
||||||
|
|
||||||
// Duplicate entities if alt is pressed. This will make a
|
|
||||||
// copy of the selected entities and move the _original_ entities, not
|
|
||||||
// the new ones.
|
|
||||||
if (event.isAlt || doClone) {
|
|
||||||
duplicatedEntityIDs = SelectionManager.duplicateSelection();
|
|
||||||
} else {
|
|
||||||
duplicatedEntityIDs = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
isConstrained = false;
|
isConstrained = false;
|
||||||
if (wantDebug) {
|
if (wantDebug) {
|
||||||
print("================== TRANSLATE_XZ(End) <- =======================");
|
print("================== TRANSLATE_XZ(End) <- =======================");
|
||||||
|
@ -1929,6 +1991,20 @@ SelectionDisplay = (function() {
|
||||||
addHandleTool(overlay, {
|
addHandleTool(overlay, {
|
||||||
mode: mode,
|
mode: mode,
|
||||||
onBegin: function(event, pickRay, pickResult) {
|
onBegin: function(event, pickRay, pickResult) {
|
||||||
|
// Duplicate entities if alt is pressed. This will make a
|
||||||
|
// copy of the selected entities and move the _original_ entities, not
|
||||||
|
// the new ones.
|
||||||
|
if (event.isAlt) {
|
||||||
|
duplicatedEntityIDs = SelectionManager.duplicateSelection();
|
||||||
|
var ids = [];
|
||||||
|
for (var i = 0; i < duplicatedEntityIDs.length; ++i) {
|
||||||
|
ids.push(duplicatedEntityIDs[i].entityID);
|
||||||
|
}
|
||||||
|
SelectionManager.setSelections(ids);
|
||||||
|
} else {
|
||||||
|
duplicatedEntityIDs = null;
|
||||||
|
}
|
||||||
|
|
||||||
var axisVector;
|
var axisVector;
|
||||||
if (direction === TRANSLATE_DIRECTION.X) {
|
if (direction === TRANSLATE_DIRECTION.X) {
|
||||||
axisVector = { x: 1, y: 0, z: 0 };
|
axisVector = { x: 1, y: 0, z: 0 };
|
||||||
|
@ -1955,15 +2031,6 @@ SelectionDisplay = (function() {
|
||||||
that.setHandleStretchVisible(false);
|
that.setHandleStretchVisible(false);
|
||||||
that.setHandleScaleCubeVisible(false);
|
that.setHandleScaleCubeVisible(false);
|
||||||
that.setHandleClonerVisible(false);
|
that.setHandleClonerVisible(false);
|
||||||
|
|
||||||
// Duplicate entities if alt is pressed. This will make a
|
|
||||||
// copy of the selected entities and move the _original_ entities, not
|
|
||||||
// the new ones.
|
|
||||||
if (event.isAlt) {
|
|
||||||
duplicatedEntityIDs = SelectionManager.duplicateSelection();
|
|
||||||
} else {
|
|
||||||
duplicatedEntityIDs = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
previousPickRay = pickRay;
|
previousPickRay = pickRay;
|
||||||
},
|
},
|
||||||
|
@ -2243,11 +2310,11 @@ SelectionDisplay = (function() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Are we using handControllers or Mouse - only relevant for 3D tools
|
// Are we using handControllers or Mouse - only relevant for 3D tools
|
||||||
var controllerPose = getControllerWorldLocation(activeHand, true);
|
var controllerPose = getControllerWorldLocation(that.triggeredHand, true);
|
||||||
var vector = null;
|
var vector = null;
|
||||||
var newPick = null;
|
var newPick = null;
|
||||||
if (HMD.isHMDAvailable() && HMD.isHandControllerAvailable() &&
|
if (HMD.isHMDAvailable() && HMD.isHandControllerAvailable() &&
|
||||||
controllerPose.valid && that.triggered && directionFor3DStretch) {
|
controllerPose.valid && that.triggered() && directionFor3DStretch) {
|
||||||
localDeltaPivot = deltaPivot3D;
|
localDeltaPivot = deltaPivot3D;
|
||||||
newPick = pickRay.origin;
|
newPick = pickRay.origin;
|
||||||
vector = Vec3.subtract(newPick, lastPick3D);
|
vector = Vec3.subtract(newPick, lastPick3D);
|
||||||
|
|
Loading…
Reference in a new issue