Merge remote-tracking branch 'upstream/master' into 21180-C++

This commit is contained in:
humbletim 2017-07-03 11:55:59 -04:00
commit 026e05f80c
241 changed files with 7355 additions and 2654 deletions

View file

@ -7,6 +7,8 @@ if (APPLE)
set_target_properties(${TARGET_NAME} PROPERTIES INSTALL_RPATH "@executable_path/../Frameworks")
endif ()
setup_memory_debugger()
# link in the shared libraries
link_hifi_libraries(
audio avatars octree gpu model fbx entities

View file

@ -62,7 +62,7 @@ Agent::Agent(ReceivedMessage& message) :
_entityEditSender.setPacketsPerSecond(DEFAULT_ENTITY_PPS_PER_SCRIPT);
DependencyManager::get<EntityScriptingInterface>()->setPacketSender(&_entityEditSender);
ResourceManager::init();
DependencyManager::set<ResourceManager>();
DependencyManager::registerInheritance<SpatialParentFinder, AssignmentParentFinder>();
@ -199,7 +199,7 @@ void Agent::requestScript() {
return;
}
auto request = ResourceManager::createResourceRequest(this, scriptURL);
auto request = DependencyManager::get<ResourceManager>()->createResourceRequest(this, scriptURL);
if (!request) {
qWarning() << "Could not create ResourceRequest for Agent script at" << scriptURL.toString();
@ -779,7 +779,7 @@ void Agent::aboutToFinish() {
// our entity tree is going to go away so tell that to the EntityScriptingInterface
DependencyManager::get<EntityScriptingInterface>()->setEntityTree(nullptr);
ResourceManager::cleanup();
DependencyManager::get<ResourceManager>()->cleanup();
// cleanup the AudioInjectorManager (and any still running injectors)
DependencyManager::destroy<AudioInjectorManager>();

View file

@ -9,8 +9,12 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include <QCommandLineParser>
#include <QThread>
#include "AssignmentClientApp.h"
#include <QtCore/QCommandLineParser>
#include <QtCore/QDir>
#include <QtCore/QStandardPaths>
#include <QtCore/QThread>
#include <LogHandler.h>
#include <SharedUtil.h>
@ -20,10 +24,6 @@
#include "Assignment.h"
#include "AssignmentClient.h"
#include "AssignmentClientMonitor.h"
#include "AssignmentClientApp.h"
#include <QtCore/QDir>
#include <QtCore/QStandardPaths>
AssignmentClientApp::AssignmentClientApp(int argc, char* argv[]) :
QCoreApplication(argc, argv)
@ -87,6 +87,9 @@ AssignmentClientApp::AssignmentClientApp(int argc, char* argv[]) :
const QCommandLineOption logDirectoryOption(ASSIGNMENT_LOG_DIRECTORY, "directory to store logs", "log-directory");
parser.addOption(logDirectoryOption);
const QCommandLineOption parentPIDOption(PARENT_PID_OPTION, "PID of the parent process", "parent-pid");
parser.addOption(parentPIDOption);
if (!parser.parse(QCoreApplication::arguments())) {
qCritical() << parser.errorText() << endl;
parser.showHelp();
@ -203,6 +206,16 @@ AssignmentClientApp::AssignmentClientApp(int argc, char* argv[]) :
}
}
if (parser.isSet(parentPIDOption)) {
bool ok = false;
int parentPID = parser.value(parentPIDOption).toInt(&ok);
if (ok) {
qDebug() << "Parent process PID is" << parentPID;
watchParentProcess(parentPID);
}
}
QThread::currentThread()->setObjectName("main thread");
DependencyManager::registerInheritance<LimitedNodeList, NodeList>();

View file

@ -131,7 +131,6 @@ void AssignmentClientMonitor::aboutToQuit() {
void AssignmentClientMonitor::spawnChildClient() {
QProcess* assignmentClient = new QProcess(this);
// unparse the parts of the command-line that the child cares about
QStringList _childArguments;
if (_assignmentPool != "") {
@ -160,6 +159,9 @@ void AssignmentClientMonitor::spawnChildClient() {
_childArguments.append("--" + ASSIGNMENT_CLIENT_MONITOR_PORT_OPTION);
_childArguments.append(QString::number(DependencyManager::get<NodeList>()->getLocalSockAddr().getPort()));
_childArguments.append("--" + PARENT_PID_OPTION);
_childArguments.append(QString::number(QCoreApplication::applicationPid()));
QString nowString, stdoutFilenameTemp, stderrFilenameTemp, stdoutPathTemp, stderrPathTemp;

View file

@ -497,13 +497,14 @@ float computeGain(const AvatarAudioStream& listeningNodeStream, const Positional
// avatar: apply fixed off-axis attenuation to make them quieter as they turn away
} else if (!isEcho && (streamToAdd.getType() == PositionalAudioStream::Microphone)) {
glm::vec3 rotatedListenerPosition = glm::inverse(streamToAdd.getOrientation()) * relativePosition;
float angleOfDelivery = glm::angle(glm::vec3(0.0f, 0.0f, -1.0f),
glm::normalize(rotatedListenerPosition));
// source directivity is based on angle of emission, in local coordinates
glm::vec3 direction = glm::normalize(rotatedListenerPosition);
float angleOfDelivery = fastAcosf(glm::clamp(-direction.z, -1.0f, 1.0f)); // UNIT_NEG_Z is "forward"
const float MAX_OFF_AXIS_ATTENUATION = 0.2f;
const float OFF_AXIS_ATTENUATION_STEP = (1 - MAX_OFF_AXIS_ATTENUATION) / 2.0f;
float offAxisCoefficient = MAX_OFF_AXIS_ATTENUATION +
(angleOfDelivery * (OFF_AXIS_ATTENUATION_STEP / PI_OVER_TWO));
float offAxisCoefficient = MAX_OFF_AXIS_ATTENUATION + (angleOfDelivery * (OFF_AXIS_ATTENUATION_STEP / PI_OVER_TWO));
gain *= offAxisCoefficient;
}
@ -545,7 +546,6 @@ float computeAzimuth(const AvatarAudioStream& listeningNodeStream, const Positio
const glm::vec3& relativePosition) {
glm::quat inverseOrientation = glm::inverse(listeningNodeStream.getOrientation());
// Compute sample delay for the two ears to create phase panning
glm::vec3 rotatedSourcePosition = inverseOrientation * relativePosition;
// project the rotated source position vector onto the XZ plane
@ -553,11 +553,16 @@ float computeAzimuth(const AvatarAudioStream& listeningNodeStream, const Positio
const float SOURCE_DISTANCE_THRESHOLD = 1e-30f;
if (glm::length2(rotatedSourcePosition) > SOURCE_DISTANCE_THRESHOLD) {
float rotatedSourcePositionLength2 = glm::length2(rotatedSourcePosition);
if (rotatedSourcePositionLength2 > SOURCE_DISTANCE_THRESHOLD) {
// produce an oriented angle about the y-axis
return glm::orientedAngle(glm::vec3(0.0f, 0.0f, -1.0f), glm::normalize(rotatedSourcePosition), glm::vec3(0.0f, -1.0f, 0.0f));
} else {
// there is no distance between listener and source - return no azimuth
return 0;
glm::vec3 direction = rotatedSourcePosition * (1.0f / fastSqrtf(rotatedSourcePositionLength2));
float angle = fastAcosf(glm::clamp(-direction.z, -1.0f, 1.0f)); // UNIT_NEG_Z is "forward"
return (direction.x < 0.0f) ? -angle : angle;
} else {
// no azimuth if they are in same spot
return 0.0f;
}
}

View file

@ -229,7 +229,7 @@ void AvatarMixer::start() {
auto start = usecTimestampNow();
nodeList->nestedEach([&](NodeList::const_iterator cbegin, NodeList::const_iterator cend) {
std::for_each(cbegin, cend, [&](const SharedNodePointer& node) {
if (node->getType() == NodeType::Agent && !node->isUpstream()) {
if (node->getType() == NodeType::Agent) {
manageIdentityData(node);
}
@ -285,6 +285,13 @@ void AvatarMixer::start() {
// is guaranteed to not be accessed by other thread
void AvatarMixer::manageIdentityData(const SharedNodePointer& node) {
AvatarMixerClientData* nodeData = reinterpret_cast<AvatarMixerClientData*>(node->getLinkedData());
// there is no need to manage identity data we haven't received yet
// so bail early if we've never received an identity packet for this avatar
if (!nodeData || !nodeData->getAvatar().hasProcessedFirstIdentity()) {
return;
}
bool sendIdentity = false;
if (nodeData && nodeData->getAvatarSessionDisplayNameMustChange()) {
AvatarData& avatar = nodeData->getAvatar();
@ -325,8 +332,8 @@ void AvatarMixer::manageIdentityData(const SharedNodePointer& node) {
sendIdentity = true;
}
}
if (sendIdentity) {
if (sendIdentity && !node->isUpstream()) {
sendIdentityPacket(nodeData, node); // Tell node whose name changed about its new session display name or avatar.
// since this packet includes a change to either the skeleton model URL or the display name
// it needs a new sequence number
nodeData->getAvatar().pushIdentitySequenceNumber();

View file

@ -81,7 +81,7 @@ int AvatarMixerSlave::sendIdentityPacket(const AvatarMixerClientData* nodeData,
int AvatarMixerSlave::sendReplicatedIdentityPacket(const Node& agentNode, const AvatarMixerClientData* nodeData, const Node& destinationNode) {
if (AvatarMixer::shouldReplicateTo(agentNode, destinationNode)) {
QByteArray individualData = nodeData->getConstAvatarData()->identityByteArray();
QByteArray individualData = nodeData->getConstAvatarData()->identityByteArray(true);
individualData.replace(0, NUM_BYTES_RFC4122_UUID, nodeData->getNodeID().toRfc4122()); // FIXME, this looks suspicious
auto identityPacket = NLPacketList::create(PacketType::ReplicatedAvatarIdentity, QByteArray(), true, true);
identityPacket->write(individualData);
@ -320,14 +320,18 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node)
++numOtherAvatars;
const AvatarMixerClientData* otherNodeData = reinterpret_cast<const AvatarMixerClientData*>(otherNode->getLinkedData());
const AvatarData* otherAvatar = otherNodeData->getConstAvatarData();
// If the time that the mixer sent AVATAR DATA about Avatar B to Avatar A is BEFORE OR EQUAL TO
// the time that Avatar B flagged an IDENTITY DATA change, send IDENTITY DATA about Avatar B to Avatar A.
if (nodeData->getLastBroadcastTime(otherNode->getUUID()) <= otherNodeData->getIdentityChangeTimestamp()) {
if (otherAvatar->hasProcessedFirstIdentity()
&& nodeData->getLastBroadcastTime(otherNode->getUUID()) <= otherNodeData->getIdentityChangeTimestamp()) {
identityBytesSent += sendIdentityPacket(otherNodeData, node);
// remember the last time we sent identity details about this other node to the receiver
nodeData->setLastBroadcastTime(otherNode->getUUID(), usecTimestampNow());
}
const AvatarData* otherAvatar = otherNodeData->getConstAvatarData();
glm::vec3 otherPosition = otherAvatar->getClientGlobalPosition();
// determine if avatar is in view, to determine how much data to include...
@ -400,9 +404,6 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node)
// set the last sent sequence number for this sender on the receiver
nodeData->setLastBroadcastSequenceNumber(otherNode->getUUID(),
otherNodeData->getLastReceivedSequenceNumber());
// remember the last time we sent details about this other node to the receiver
nodeData->setLastBroadcastTime(otherNode->getUUID(), start);
}
}

View file

@ -31,7 +31,7 @@ EntityServer::EntityServer(ReceivedMessage& message) :
OctreeServer(message),
_entitySimulation(NULL)
{
ResourceManager::init();
DependencyManager::set<ResourceManager>();
DependencyManager::set<ResourceCacheSharedItems>();
DependencyManager::set<ScriptCache>();

View file

@ -54,7 +54,7 @@ EntityScriptServer::EntityScriptServer(ReceivedMessage& message) : ThreadedAssig
DependencyManager::get<EntityScriptingInterface>()->setPacketSender(&_entityEditSender);
ResourceManager::init();
DependencyManager::set<ResourceManager>();
DependencyManager::registerInheritance<SpatialParentFinder, AssignmentParentFinder>();
@ -67,7 +67,6 @@ EntityScriptServer::EntityScriptServer(ReceivedMessage& message) : ThreadedAssig
DependencyManager::set<ScriptCache>();
DependencyManager::set<ScriptEngines>(ScriptEngine::ENTITY_SERVER_SCRIPT);
auto& packetReceiver = DependencyManager::get<NodeList>()->getPacketReceiver();
packetReceiver.registerListenerForTypes({ PacketType::OctreeStats, PacketType::EntityData, PacketType::EntityErase },
this, "handleOctreePacket");
@ -493,7 +492,7 @@ void EntityScriptServer::checkAndCallPreload(const EntityItemID& entityID, bool
if (entity && (reload || notRunning || details.scriptText != entity->getServerScripts())) {
QString scriptUrl = entity->getServerScripts();
if (!scriptUrl.isEmpty()) {
scriptUrl = ResourceManager::normalizeURL(scriptUrl);
scriptUrl = DependencyManager::get<ResourceManager>()->normalizeURL(scriptUrl);
qCDebug(entity_script_server) << "Loading entity server script" << scriptUrl << "for" << entityID;
_entitiesScriptEngine->loadEntityScript(entityID, scriptUrl, reload);
}
@ -551,7 +550,7 @@ void EntityScriptServer::aboutToFinish() {
// our entity tree is going to go away so tell that to the EntityScriptingInterface
DependencyManager::get<EntityScriptingInterface>()->setEntityTree(nullptr);
ResourceManager::cleanup();
DependencyManager::get<ResourceManager>()->cleanup();
// cleanup the AudioInjectorManager (and any still running injectors)
DependencyManager::destroy<AudioInjectorManager>();

View file

@ -0,0 +1,12 @@
#
# Created by David Rowe on 16 Jun 2017.
# Copyright 2017 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
#
macro(TARGET_LEAPMOTION)
target_include_directories(${TARGET_NAME} PRIVATE ${LEAPMOTION_INCLUDE_DIRS})
target_link_libraries(${TARGET_NAME} ${LEAPMOTION_LIBRARIES})
endmacro()

View file

@ -14,6 +14,8 @@ if (APPLE)
set_target_properties(${TARGET_NAME} PROPERTIES INSTALL_RPATH "@executable_path/../Frameworks")
endif ()
setup_memory_debugger()
# TODO: find a solution that will handle web file changes in resources on windows without a re-build.
# Currently the resources are only copied on post-build. If one is changed but the domain-server is not, they will
# not be re-copied. This is worked-around on OS X/UNIX by using a symlink.

View file

@ -171,7 +171,7 @@ void DomainMetadata::maybeUpdateUsers() {
if (linkedData) {
auto nodeData = static_cast<DomainServerNodeData*>(linkedData);
if (!nodeData->wasAssigned()) {
if (!nodeData->wasAssigned() && node->getType() == NodeType::Agent) {
++numConnected;
if (nodeData->getUsername().isEmpty()) {

View file

@ -221,6 +221,8 @@ void DomainServer::parseCommandLine() {
const QCommandLineOption masterConfigOption("master-config", "Deprecated config-file option");
parser.addOption(masterConfigOption);
const QCommandLineOption parentPIDOption(PARENT_PID_OPTION, "PID of the parent process", "parent-pid");
parser.addOption(parentPIDOption);
if (!parser.parse(QCoreApplication::arguments())) {
qWarning() << parser.errorText() << endl;
@ -249,6 +251,17 @@ void DomainServer::parseCommandLine() {
_overrideDomainID = true;
qDebug() << "domain-server ID is" << _overridingDomainID;
}
if (parser.isSet(parentPIDOption)) {
bool ok = false;
int parentPID = parser.value(parentPIDOption).toInt(&ok);
if (ok) {
qDebug() << "Parent process PID is" << parentPID;
watchParentProcess(parentPID);
}
}
}
DomainServer::~DomainServer() {

View file

@ -18,5 +18,7 @@ endif ()
include_directories(SYSTEM "${OPENSSL_INCLUDE_DIR}")
setup_memory_debugger()
# append OpenSSL to our list of libraries to link
target_link_libraries(${TARGET_NAME} ${OPENSSL_LIBRARIES})

View file

@ -4,6 +4,8 @@ project(${TARGET_NAME})
# set a default root dir for each of our optional externals if it was not passed
set(OPTIONAL_EXTERNALS "LeapMotion")
setup_memory_debugger()
foreach(EXTERNAL ${OPTIONAL_EXTERNALS})
string(TOUPPER ${EXTERNAL} ${EXTERNAL}_UPPERCASE)
if (NOT ${${EXTERNAL}_UPPERCASE}_ROOT_DIR)

View file

@ -10,7 +10,7 @@ Interface has been tested with SDK versions:
1. Copy the LeapSDK folders from the LeapDeveloperKit installation directory (Lib, Include) into the interface/externals/leapmotion folder.
This readme.txt should be there as well.
The files neeeded in the folders are:
The files needed in the folders are:
include/
- Leap.h
@ -21,8 +21,8 @@ Interface has been tested with SDK versions:
x86/
- Leap.dll
- Leap.lib
- mscvcp120.dll (optional if you already have the Msdev 2012 SDK redistriuable installed)
- mscvcr120.dll (optional if you already have the Msdev 2012 SDK redistriuable installed)
- mscvcp120.dll (optional if you already have the Msdev 2012 SDK redistributable installed)
- mscvcr120.dll (optional if you already have the Msdev 2012 SDK redistributable installed)
- lipLeap.dylib
libc++/
-libLeap.dylib
@ -30,4 +30,4 @@ Interface has been tested with SDK versions:
You may optionally choose to copy the SDK folders to a location outside the repository (so you can re-use with different checkouts and different projects).
If so our CMake find module expects you to set the ENV variable 'HIFI_LIB_DIR' to a directory containing a subfolder 'leapmotion' that contains the 2 folders mentioned above (Include, Lib).
2. Clear your build directory, run cmake and build, and you should be all set.
2. Clear your build directory, run cmake and build, and you should be all set.

View file

@ -68,7 +68,10 @@
"typeVar": "rightHandType",
"weightVar": "rightHandWeight",
"weight": 1.0,
"flexCoefficients": [1, 0.5, 0.5, 0.2, 0.01, 0.005, 0.001, 0.0, 0.0]
"flexCoefficients": [1, 0.5, 0.5, 0.2, 0.01, 0.005, 0.001, 0.0, 0.0],
"poleVectorEnabledVar": "rightHandPoleVectorEnabled",
"poleReferenceVectorVar": "rightHandPoleReferenceVector",
"poleVectorVar": "rightHandPoleVector"
},
{
"jointName": "LeftHand",
@ -77,7 +80,10 @@
"typeVar": "leftHandType",
"weightVar": "leftHandWeight",
"weight": 1.0,
"flexCoefficients": [1, 0.5, 0.5, 0.2, 0.01, 0.005, 0.001, 0.0, 0.0]
"flexCoefficients": [1, 0.5, 0.5, 0.2, 0.01, 0.005, 0.001, 0.0, 0.0],
"poleVectorEnabledVar": "leftHandPoleVectorEnabled",
"poleReferenceVectorVar": "leftHandPoleReferenceVector",
"poleVectorVar": "leftHandPoleVector"
},
{
"jointName": "RightFoot",
@ -86,7 +92,10 @@
"typeVar": "rightFootType",
"weightVar": "rightFootWeight",
"weight": 1.0,
"flexCoefficients": [1, 0.45, 0.45]
"flexCoefficients": [1, 0.45, 0.45],
"poleVectorEnabledVar": "rightFootPoleVectorEnabled",
"poleReferenceVectorVar": "rightFootPoleReferenceVector",
"poleVectorVar": "rightFootPoleVector"
},
{
"jointName": "LeftFoot",
@ -95,7 +104,10 @@
"typeVar": "leftFootType",
"weightVar": "leftFootWeight",
"weight": 1.0,
"flexCoefficients": [1, 0.45, 0.45]
"flexCoefficients": [1, 0.45, 0.45],
"poleVectorEnabledVar": "leftFootPoleVectorEnabled",
"poleReferenceVectorVar": "leftFootPoleReferenceVector",
"poleVectorVar": "leftFootPoleVector"
},
{
"jointName": "Spine2",
@ -138,28 +150,19 @@
"children": []
},
{
"id": "hipsManipulatorOverlay",
"id": "defaultPoseOverlay",
"type": "overlay",
"data": {
"alpha": 0.0,
"boneSet": "hipsOnly"
"alphaVar": "defaultPoseOverlayAlpha",
"boneSet": "fullBody",
"boneSetVar": "defaultPoseOverlayBoneSet"
},
"children": [
{
"id": "hipsManipulator",
"type": "manipulator",
"id": "defaultPose",
"type": "defaultPose",
"data": {
"alpha": 0.0,
"alphaVar": "hipsManipulatorAlpha",
"joints": [
{
"jointName": "Hips",
"rotationType": "absolute",
"translationType": "absolute",
"rotationVar": "hipsManipulatorRotation",
"translationVar": "hipsManipulatorPosition"
}
]
},
"children": []
},

View file

@ -0,0 +1,48 @@
{
"name": "Leap Motion to Standard",
"channels": [
{ "from": "LeapMotion.LeftHand", "to": "Standard.LeftHand" },
{ "from": "LeapMotion.LeftHandThumb1", "to": "Standard.LeftHandThumb1"},
{ "from": "LeapMotion.LeftHandThumb2", "to": "Standard.LeftHandThumb2"},
{ "from": "LeapMotion.LeftHandThumb3", "to": "Standard.LeftHandThumb3"},
{ "from": "LeapMotion.LeftHandThumb4", "to": "Standard.LeftHandThumb4"},
{ "from": "LeapMotion.LeftHandIndex1", "to": "Standard.LeftHandIndex1"},
{ "from": "LeapMotion.LeftHandIndex2", "to": "Standard.LeftHandIndex2"},
{ "from": "LeapMotion.LeftHandIndex3", "to": "Standard.LeftHandIndex3"},
{ "from": "LeapMotion.LeftHandIndex4", "to": "Standard.LeftHandIndex4"},
{ "from": "LeapMotion.LeftHandMiddle1", "to": "Standard.LeftHandMiddle1"},
{ "from": "LeapMotion.LeftHandMiddle2", "to": "Standard.LeftHandMiddle2"},
{ "from": "LeapMotion.LeftHandMiddle3", "to": "Standard.LeftHandMiddle3"},
{ "from": "LeapMotion.LeftHandMiddle4", "to": "Standard.LeftHandMiddle4"},
{ "from": "LeapMotion.LeftHandRing1", "to": "Standard.LeftHandRing1"},
{ "from": "LeapMotion.LeftHandRing2", "to": "Standard.LeftHandRing2"},
{ "from": "LeapMotion.LeftHandRing3", "to": "Standard.LeftHandRing3"},
{ "from": "LeapMotion.LeftHandRing4", "to": "Standard.LeftHandRing4"},
{ "from": "LeapMotion.LeftHandPinky1", "to": "Standard.LeftHandPinky1"},
{ "from": "LeapMotion.LeftHandPinky2", "to": "Standard.LeftHandPinky2"},
{ "from": "LeapMotion.LeftHandPinky3", "to": "Standard.LeftHandPinky3"},
{ "from": "LeapMotion.LeftHandPinky4", "to": "Standard.LeftHandPinky4"},
{ "from": "LeapMotion.RightHand", "to": "Standard.RightHand" },
{ "from": "LeapMotion.RightHandThumb1", "to": "Standard.RightHandThumb1"},
{ "from": "LeapMotion.RightHandThumb2", "to": "Standard.RightHandThumb2"},
{ "from": "LeapMotion.RightHandThumb3", "to": "Standard.RightHandThumb3"},
{ "from": "LeapMotion.RightHandThumb4", "to": "Standard.RightHandThumb4"},
{ "from": "LeapMotion.RightHandIndex1", "to": "Standard.RightHandIndex1"},
{ "from": "LeapMotion.RightHandIndex2", "to": "Standard.RightHandIndex2"},
{ "from": "LeapMotion.RightHandIndex3", "to": "Standard.RightHandIndex3"},
{ "from": "LeapMotion.RightHandIndex4", "to": "Standard.RightHandIndex4"},
{ "from": "LeapMotion.RightHandMiddle1", "to": "Standard.RightHandMiddle1"},
{ "from": "LeapMotion.RightHandMiddle2", "to": "Standard.RightHandMiddle2"},
{ "from": "LeapMotion.RightHandMiddle3", "to": "Standard.RightHandMiddle3"},
{ "from": "LeapMotion.RightHandMiddle4", "to": "Standard.RightHandMiddle4"},
{ "from": "LeapMotion.RightHandRing1", "to": "Standard.RightHandRing1"},
{ "from": "LeapMotion.RightHandRing2", "to": "Standard.RightHandRing2"},
{ "from": "LeapMotion.RightHandRing3", "to": "Standard.RightHandRing3"},
{ "from": "LeapMotion.RightHandRing4", "to": "Standard.RightHandRing4"},
{ "from": "LeapMotion.RightHandPinky1", "to": "Standard.RightHandPinky1"},
{ "from": "LeapMotion.RightHandPinky2", "to": "Standard.RightHandPinky2"},
{ "from": "LeapMotion.RightHandPinky3", "to": "Standard.RightHandPinky3"},
{ "from": "LeapMotion.RightHandPinky4", "to": "Standard.RightHandPinky4"}
]
}

View file

@ -58,7 +58,48 @@
{ "from": "Standard.RT", "to": "Actions.RightHandClick" },
{ "from": "Standard.LeftHand", "to": "Actions.LeftHand" },
{ "from": "Standard.LeftHandThumb1", "to": "Actions.LeftHandThumb1"},
{ "from": "Standard.LeftHandThumb2", "to": "Actions.LeftHandThumb2"},
{ "from": "Standard.LeftHandThumb3", "to": "Actions.LeftHandThumb3"},
{ "from": "Standard.LeftHandThumb4", "to": "Actions.LeftHandThumb4"},
{ "from": "Standard.LeftHandIndex1", "to": "Actions.LeftHandIndex1"},
{ "from": "Standard.LeftHandIndex2", "to": "Actions.LeftHandIndex2"},
{ "from": "Standard.LeftHandIndex3", "to": "Actions.LeftHandIndex3"},
{ "from": "Standard.LeftHandIndex4", "to": "Actions.LeftHandIndex4"},
{ "from": "Standard.LeftHandMiddle1", "to": "Actions.LeftHandMiddle1"},
{ "from": "Standard.LeftHandMiddle2", "to": "Actions.LeftHandMiddle2"},
{ "from": "Standard.LeftHandMiddle3", "to": "Actions.LeftHandMiddle3"},
{ "from": "Standard.LeftHandMiddle4", "to": "Actions.LeftHandMiddle4"},
{ "from": "Standard.LeftHandRing1", "to": "Actions.LeftHandRing1"},
{ "from": "Standard.LeftHandRing2", "to": "Actions.LeftHandRing2"},
{ "from": "Standard.LeftHandRing3", "to": "Actions.LeftHandRing3"},
{ "from": "Standard.LeftHandRing4", "to": "Actions.LeftHandRing4"},
{ "from": "Standard.LeftHandPinky1", "to": "Actions.LeftHandPinky1"},
{ "from": "Standard.LeftHandPinky2", "to": "Actions.LeftHandPinky2"},
{ "from": "Standard.LeftHandPinky3", "to": "Actions.LeftHandPinky3"},
{ "from": "Standard.LeftHandPinky4", "to": "Actions.LeftHandPinky4"},
{ "from": "Standard.RightHand", "to": "Actions.RightHand" },
{ "from": "Standard.RightHandThumb1", "to": "Actions.RightHandThumb1"},
{ "from": "Standard.RightHandThumb2", "to": "Actions.RightHandThumb2"},
{ "from": "Standard.RightHandThumb3", "to": "Actions.RightHandThumb3"},
{ "from": "Standard.RightHandThumb4", "to": "Actions.RightHandThumb4"},
{ "from": "Standard.RightHandIndex1", "to": "Actions.RightHandIndex1"},
{ "from": "Standard.RightHandIndex2", "to": "Actions.RightHandIndex2"},
{ "from": "Standard.RightHandIndex3", "to": "Actions.RightHandIndex3"},
{ "from": "Standard.RightHandIndex4", "to": "Actions.RightHandIndex4"},
{ "from": "Standard.RightHandMiddle1", "to": "Actions.RightHandMiddle1"},
{ "from": "Standard.RightHandMiddle2", "to": "Actions.RightHandMiddle2"},
{ "from": "Standard.RightHandMiddle3", "to": "Actions.RightHandMiddle3"},
{ "from": "Standard.RightHandMiddle4", "to": "Actions.RightHandMiddle4"},
{ "from": "Standard.RightHandRing1", "to": "Actions.RightHandRing1"},
{ "from": "Standard.RightHandRing2", "to": "Actions.RightHandRing2"},
{ "from": "Standard.RightHandRing3", "to": "Actions.RightHandRing3"},
{ "from": "Standard.RightHandRing4", "to": "Actions.RightHandRing4"},
{ "from": "Standard.RightHandPinky1", "to": "Actions.RightHandPinky1"},
{ "from": "Standard.RightHandPinky2", "to": "Actions.RightHandPinky2"},
{ "from": "Standard.RightHandPinky3", "to": "Actions.RightHandPinky3"},
{ "from": "Standard.RightHandPinky4", "to": "Actions.RightHandPinky4"},
{ "from": "Standard.LeftFoot", "to": "Actions.LeftFoot" },
{ "from": "Standard.RightFoot", "to": "Actions.RightFoot" },

View file

@ -217,7 +217,7 @@ FocusScope {
anchors.leftMargin: hifi.dimensions.textPadding
anchors.verticalCenter: parent.verticalCenter
id: popupText
text: listView.model[index] ? listView.model[index] : (listView.model.get(index).text ? listView.model.get(index).text : "")
text: listView.model[index] ? listView.model[index] : (listView.model.get && listView.model.get(index).text ? listView.model.get(index).text : "")
size: hifi.fontSizes.textFieldInput
color: hifi.colors.baseGray
}

View file

@ -34,6 +34,8 @@ ModalWindow {
HifiConstants { id: hifi }
property var filesModel: ListModel { }
Settings {
category: "FileDialog"
property alias width: root.width
@ -253,7 +255,9 @@ ModalWindow {
}
currentSelectionUrl = helper.pathToUrl(fileTableView.model.get(row).filePath);
currentSelectionIsFolder = fileTableView.model.isFolder(row);
currentSelectionIsFolder = fileTableView.model !== filesModel ?
fileTableView.model.isFolder(row) :
fileTableModel.isFolder(row);
if (root.selectDirectory || !currentSelectionIsFolder) {
currentSelection.text = capitalizeDrive(helper.urlToPath(currentSelectionUrl));
} else {
@ -331,7 +335,12 @@ ModalWindow {
}
}
ListModel {
Component {
id: filesModelBuilder
ListModel { }
}
QtObject {
id: fileTableModel
// FolderListModel has a couple of problems:
@ -383,7 +392,11 @@ ModalWindow {
if (row === -1) {
return false;
}
return get(row).fileIsDir;
return filesModel.get(row).fileIsDir;
}
function get(row) {
return filesModel.get(row)
}
function update() {
@ -401,7 +414,7 @@ ModalWindow {
rows = 0,
i;
clear();
var newFilesModel = filesModelBuilder.createObject(root);
comparisonFunction = sortOrder === Qt.AscendingOrder
? function(a, b) { return a < b; }
@ -423,7 +436,7 @@ ModalWindow {
while (lower < upper) {
middle = Math.floor((lower + upper) / 2);
var lessThan;
if (comparisonFunction(sortValue, get(middle)[sortField])) {
if (comparisonFunction(sortValue, newFilesModel.get(middle)[sortField])) {
lessThan = true;
upper = middle;
} else {
@ -432,7 +445,7 @@ ModalWindow {
}
}
insert(lower, {
newFilesModel.insert(lower, {
fileName: fileName,
fileModified: (fileIsDir ? new Date(0) : model.getItem(i, "fileModified")),
fileSize: model.getItem(i, "fileSize"),
@ -443,6 +456,7 @@ ModalWindow {
rows++;
}
filesModel = newFilesModel;
d.clearSelection();
}
@ -469,7 +483,7 @@ ModalWindow {
sortIndicatorOrder: Qt.AscendingOrder
sortIndicatorVisible: true
model: fileTableModel
model: filesModel
function updateSort() {
model.sortOrder = sortIndicatorOrder;
@ -561,11 +575,12 @@ ModalWindow {
}
function navigateToCurrentRow() {
var currentModel = fileTableView.model !== filesModel ? fileTableView.model : fileTableModel
var row = fileTableView.currentRow
var isFolder = model.isFolder(row);
var file = model.get(row).filePath;
var isFolder = currentModel.isFolder(row);
var file = currentModel.get(row).filePath;
if (isFolder) {
fileTableView.model.folder = helper.pathToUrl(file);
currentModel.folder = helper.pathToUrl(file);
} else {
okAction.trigger();
}
@ -580,7 +595,8 @@ ModalWindow {
var newPrefix = prefix + event.text.toLowerCase();
var matchedIndex = -1;
for (var i = 0; i < model.count; ++i) {
var name = model.get(i).fileName.toLowerCase();
var name = model !== filesModel ? model.get(i).fileName.toLowerCase() :
filesModel.get(i).fileName.toLowerCase();
if (0 === name.indexOf(newPrefix)) {
matchedIndex = i;
break;

View file

@ -25,11 +25,14 @@ import "fileDialog"
//FIXME implement shortcuts for favorite location
TabletModalWindow {
id: root
anchors.fill: parent
width: parent.width
height: parent.height
HifiConstants { id: hifi }
property var filesModel: ListModel { }
Settings {
category: "FileDialog"
property alias width: root.width
@ -250,7 +253,9 @@ TabletModalWindow {
}
currentSelectionUrl = helper.pathToUrl(fileTableView.model.get(row).filePath);
currentSelectionIsFolder = fileTableView.model.isFolder(row);
currentSelectionIsFolder = fileTableView.model !== filesModel ?
fileTableView.model.isFolder(row) :
fileTableModel.isFolder(row);
if (root.selectDirectory || !currentSelectionIsFolder) {
currentSelection.text = capitalizeDrive(helper.urlToPath(currentSelectionUrl));
} else {
@ -288,7 +293,7 @@ TabletModalWindow {
}
onFolderChanged: {
fileTableModel.update(); // Update once the data from the folder change is available.
fileTableModel.update()
}
function getItem(index, field) {
@ -328,7 +333,12 @@ TabletModalWindow {
}
}
ListModel {
Component {
id: filesModelBuilder
ListModel { }
}
QtObject {
id: fileTableModel
// FolderListModel has a couple of problems:
@ -359,17 +369,16 @@ TabletModalWindow {
}
onFolderChanged: {
if (folder === rootFolder) {
model = driveListModel;
helper.monitorDirectory("");
update();
} else {
var needsUpdate = model === driveListModel && folder === folderListModel.folder;
model = folderListModel;
folderListModel.folder = folder;
helper.monitorDirectory(helper.urlToPath(folder));
if (needsUpdate) {
update();
}
@ -380,7 +389,11 @@ TabletModalWindow {
if (row === -1) {
return false;
}
return get(row).fileIsDir;
return filesModel.get(row).fileIsDir;
}
function get(row) {
return filesModel.get(row)
}
function update() {
@ -398,7 +411,7 @@ TabletModalWindow {
rows = 0,
i;
clear();
var newFilesModel = filesModelBuilder.createObject(root);
comparisonFunction = sortOrder === Qt.AscendingOrder
? function(a, b) { return a < b; }
@ -420,7 +433,7 @@ TabletModalWindow {
while (lower < upper) {
middle = Math.floor((lower + upper) / 2);
var lessThan;
if (comparisonFunction(sortValue, get(middle)[sortField])) {
if (comparisonFunction(sortValue, newFilesModel.get(middle)[sortField])) {
lessThan = true;
upper = middle;
} else {
@ -429,7 +442,7 @@ TabletModalWindow {
}
}
insert(lower, {
newFilesModel.insert(lower, {
fileName: fileName,
fileModified: (fileIsDir ? new Date(0) : model.getItem(i, "fileModified")),
fileSize: model.getItem(i, "fileSize"),
@ -440,6 +453,7 @@ TabletModalWindow {
rows++;
}
filesModel = newFilesModel;
d.clearSelection();
}
@ -467,7 +481,7 @@ TabletModalWindow {
sortIndicatorOrder: Qt.AscendingOrder
sortIndicatorVisible: true
model: fileTableModel
model: filesModel
function updateSort() {
model.sortOrder = sortIndicatorOrder;
@ -559,11 +573,12 @@ TabletModalWindow {
}
function navigateToCurrentRow() {
var currentModel = fileTableView.model !== filesModel ? fileTableView.model : fileTableModel
var row = fileTableView.currentRow
var isFolder = model.isFolder(row);
var file = model.get(row).filePath;
var isFolder = currentModel.isFolder(row);
var file = currentModel.get(row).filePath;
if (isFolder) {
fileTableView.model.folder = helper.pathToUrl(file);
currentModel.folder = helper.pathToUrl(file);
} else {
okAction.trigger();
}
@ -578,7 +593,8 @@ TabletModalWindow {
var newPrefix = prefix + event.text.toLowerCase();
var matchedIndex = -1;
for (var i = 0; i < model.count; ++i) {
var name = model.get(i).fileName.toLowerCase();
var name = model !== filesModel ? model.get(i).fileName.toLowerCase() :
filesModel.get(i).fileName.toLowerCase();
if (0 === name.indexOf(newPrefix)) {
matchedIndex = i;
break;

View file

@ -43,6 +43,7 @@ Item {
property bool selected: false
property bool isAdmin: false
property bool isPresent: true
property bool isReplicated: false
property string placeName: ""
property string profilePicBorderColor: (connectionStatus == "connection" ? hifi.colors.indigoAccent : (connectionStatus == "friend" ? hifi.colors.greenHighlight : "transparent"))
property alias avImage: avatarImage

View file

@ -473,6 +473,7 @@ Rectangle {
visible: !isCheckBox && !isButton && !isAvgAudio;
uuid: model ? model.sessionId : "";
selected: styleData.selected;
isReplicated: model.isReplicated;
isAdmin: model && model.admin;
isPresent: model && model.isPresent;
// Size
@ -553,6 +554,7 @@ Rectangle {
id: actionButton;
color: 2; // Red
visible: isButton;
enabled: !nameCard.isReplicated;
anchors.centerIn: parent;
width: 32;
height: 32;

View file

@ -17,7 +17,7 @@ PreferencesDialog {
id: root
objectName: "GeneralPreferencesDialog"
title: "General Settings"
showCategories: ["UI", "Snapshots", "Scripts", "Privacy", "Octree", "HMD", "Sixense Controllers", "Perception Neuron", "Kinect"]
showCategories: ["UI", "Snapshots", "Scripts", "Privacy", "Octree", "HMD", "Sixense Controllers", "Perception Neuron", "Kinect", "Leap Motion"]
property var settings: Settings {
category: root.objectName
property alias x: root.x

View file

@ -32,6 +32,6 @@ StackView {
TabletPreferencesDialog {
id: root
objectName: "TabletGeneralPreferences"
showCategories: ["UI", "Snapshots", "Scripts", "Privacy", "Octree", "HMD", "Sixense Controllers", "Perception Neuron", "Kinect", "Vive Pucks Configuration"]
showCategories: ["UI", "Snapshots", "Scripts", "Privacy", "Octree", "HMD", "Sixense Controllers", "Perception Neuron", "Kinect", "Vive Pucks Configuration", "Leap Motion"]
}
}

View file

@ -23,11 +23,13 @@ import "../../../windows"
import "../../../dialogs/fileDialog"
//FIXME implement shortcuts for favorite location
Item {
Rectangle {
id: root
anchors.top: parent.top
anchors.top: parent ? parent.top : undefined
HifiConstants { id: hifi }
color: hifi.colors.baseGray;
Settings {
category: "FileDialog"
property alias width: root.width

View file

@ -37,8 +37,6 @@
#include <QtQml/QQmlEngine>
#include <QtQuick/QQuickWindow>
#include <QtWebEngineWidgets/QWebEngineProfile>
#include <QtWidgets/QDesktopWidget>
#include <QtWidgets/QMessageBox>
@ -147,7 +145,6 @@
#include "avatar/MyHead.h"
#include "CrashHandler.h"
#include "devices/DdeFaceTracker.h"
#include "devices/Leapmotion.h"
#include "DiscoverabilityManager.h"
#include "GLCanvas.h"
#include "InterfaceDynamicFactory.h"
@ -486,11 +483,11 @@ bool setupEssentials(int& argc, char** argv, bool runningMarkerExisted) {
Setting::init();
// Tell the plugin manager about our statically linked plugins
PluginManager::setInputPluginProvider([] { return getInputPlugins(); });
PluginManager::setDisplayPluginProvider([] { return getDisplayPlugins(); });
PluginManager::setInputPluginSettingsPersister([](const InputPluginList& plugins) { saveInputPluginSettings(plugins); });
if (auto steamClient = PluginManager::getInstance()->getSteamClientPlugin()) {
auto pluginManager = PluginManager::getInstance();
pluginManager->setInputPluginProvider([] { return getInputPlugins(); });
pluginManager->setDisplayPluginProvider([] { return getDisplayPlugins(); });
pluginManager->setInputPluginSettingsPersister([](const InputPluginList& plugins) { saveInputPluginSettings(plugins); });
if (auto steamClient = pluginManager->getSteamClientPlugin()) {
steamClient->init();
}
@ -584,6 +581,7 @@ bool setupEssentials(int& argc, char** argv, bool runningMarkerExisted) {
DependencyManager::set<LocationBookmarks>();
DependencyManager::set<Snapshot>();
DependencyManager::set<CloseEventSender>();
DependencyManager::set<ResourceManager>();
return previousSessionCrashed;
}
@ -723,9 +721,13 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
updateHeartbeat();
// setup a timer for domain-server check ins
QTimer* domainCheckInTimer = new QTimer(nodeList.data());
QTimer* domainCheckInTimer = new QTimer(this);
connect(domainCheckInTimer, &QTimer::timeout, nodeList.data(), &NodeList::sendDomainServerCheckIn);
domainCheckInTimer->start(DOMAIN_SERVER_CHECK_IN_MSECS);
connect(this, &QCoreApplication::aboutToQuit, [domainCheckInTimer] {
domainCheckInTimer->stop();
domainCheckInTimer->deleteLater();
});
auto audioIO = DependencyManager::get<AudioClient>();
@ -774,7 +776,6 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
connect(this, &Application::activeDisplayPluginChanged,
reinterpret_cast<scripting::Audio*>(audioScriptingInterface.data()), &scripting::Audio::onContextChanged);
ResourceManager::init();
// Make sure we don't time out during slow operations at startup
updateHeartbeat();
@ -913,11 +914,6 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
_saveAvatarOverrideUrl = true;
}
QString defaultScriptsLocation = getCmdOption(argc, constArgv, "--scripts");
if (!defaultScriptsLocation.isEmpty()) {
PathUtils::defaultScriptsLocation(defaultScriptsLocation);
}
_glWidget = new GLCanvas();
getApplicationCompositor().setRenderingWidget(_glWidget);
_window->setCentralWidget(_glWidget);
@ -1178,7 +1174,13 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
// do this as late as possible so that all required subsystems are initialized
// If we've overridden the default scripts location, just load default scripts
// otherwise, load 'em all
if (!defaultScriptsLocation.isEmpty()) {
// we just want to see if --scripts was set, we've already parsed it and done
// the change in PathUtils. Rather than pass that in the constructor, lets just
// look (this could be debated)
QString scriptsSwitch = QString("--").append(SCRIPTS_SWITCH);
QDir defaultScriptsLocation(getCmdOption(argc, constArgv, scriptsSwitch.toStdString().c_str()));
if (!defaultScriptsLocation.exists()) {
scriptEngines->loadDefaultScripts();
scriptEngines->defaultScriptsLocationOverridden(true);
} else {
@ -1879,13 +1881,11 @@ Application::~Application() {
DependencyManager::destroy<SoundCache>();
DependencyManager::destroy<OctreeStatsProvider>();
ResourceManager::cleanup();
DependencyManager::get<ResourceManager>()->cleanup();
// remove the NodeList from the DependencyManager
DependencyManager::destroy<NodeList>();
Leapmotion::destroy();
if (auto steamClient = PluginManager::getInstance()->getSteamClientPlugin()) {
steamClient->shutdown();
}
@ -4055,8 +4055,6 @@ void Application::init() {
qCDebug(interfaceapp) << "Loaded settings";
Leapmotion::init();
// fire off an immediate domain-server check in now that settings are loaded
DependencyManager::get<NodeList>()->sendDomainServerCheckIn();
@ -4095,7 +4093,10 @@ void Application::init() {
EntityTreePointer tree = getEntities()->getTree();
if (auto entity = tree->findEntityByEntityItemID(id)) {
auto sound = DependencyManager::get<SoundCache>()->getSound(newURL);
entity->setCollisionSound(sound);
auto renderable = entity->getRenderableInterface();
if (renderable) {
renderable->setCollisionSound(sound);
}
}
}, Qt::QueuedConnection);
connect(getMyAvatar().get(), &MyAvatar::newCollisionSoundURL, this, [this](QUrl newURL) {
@ -4196,7 +4197,7 @@ void Application::updateMyAvatarLookAtPosition() {
lookAtSpot = transformPoint(worldHeadMat, glm::vec3(0.0f, 0.0f, TREE_SCALE));
} else {
lookAtSpot = myAvatar->getHead()->getEyePosition() +
(myAvatar->getHead()->getFinalOrientationInWorldFrame() * glm::vec3(0.0f, 0.0f, TREE_SCALE));
(myAvatar->getHead()->getFinalOrientationInWorldFrame() * glm::vec3(0.0f, 0.0f, -TREE_SCALE));
}
}
@ -4520,7 +4521,6 @@ void Application::update(float deltaTime) {
{
PerformanceTimer perfTimer("devices");
DeviceTracker::updateAll();
FaceTracker* tracker = getSelectedFaceTracker();
if (tracker && Menu::getInstance()->isOptionChecked(MenuOption::MuteFaceTracking) != tracker->isMuted()) {
@ -4589,8 +4589,6 @@ void Application::update(float deltaTime) {
keyboardMousePlugin->pluginUpdate(deltaTime, calibrationData);
}
_controllerScriptingInterface->updateInputControllers();
// Transfer the user inputs to the driveKeys
// FIXME can we drop drive keys and just have the avatar read the action states directly?
myAvatar->clearDriveKeys();
@ -4615,6 +4613,31 @@ void Application::update(float deltaTime) {
auto avatarToSensorMatrix = worldToSensorMatrix * myAvatarMatrix;
myAvatar->setHandControllerPosesInSensorFrame(leftHandPose.transform(avatarToSensorMatrix), rightHandPose.transform(avatarToSensorMatrix));
// If have previously done finger poses or there are new valid finger poses, update finger pose values. This so that if
// fingers are not being controlled, finger joints are not updated in MySkeletonModel.
// Assumption: Finger poses are either all present and valid or not present at all; thus can test just one joint.
MyAvatar::FingerPosesMap leftHandFingerPoses;
if (myAvatar->getLeftHandFingerControllerPosesInSensorFrame().size() > 0
|| userInputMapper->getPoseState(controller::Action::LEFT_HAND_THUMB1).isValid()) {
for (int i = (int)controller::Action::LEFT_HAND_THUMB1; i <= (int)controller::Action::LEFT_HAND_PINKY4; i++) {
leftHandFingerPoses[i] = {
userInputMapper->getPoseState((controller::Action)i).transform(avatarToSensorMatrix),
userInputMapper->getActionName((controller::Action)i)
};
}
}
MyAvatar::FingerPosesMap rightHandFingerPoses;
if (myAvatar->getRightHandFingerControllerPosesInSensorFrame().size() > 0
|| userInputMapper->getPoseState(controller::Action::RIGHT_HAND_THUMB1).isValid()) {
for (int i = (int)controller::Action::RIGHT_HAND_THUMB1; i <= (int)controller::Action::RIGHT_HAND_PINKY4; i++) {
rightHandFingerPoses[i] = {
userInputMapper->getPoseState((controller::Action)i).transform(avatarToSensorMatrix),
userInputMapper->getActionName((controller::Action)i)
};
}
}
myAvatar->setFingerControllerPosesInSensorFrame(leftHandFingerPoses, rightHandFingerPoses);
controller::Pose leftFootPose = userInputMapper->getPoseState(controller::Action::LEFT_FOOT);
controller::Pose rightFootPose = userInputMapper->getPoseState(controller::Action::RIGHT_FOOT);
myAvatar->setFootControllerPosesInSensorFrame(leftFootPose.transform(avatarToSensorMatrix), rightFootPose.transform(avatarToSensorMatrix));
@ -5941,7 +5964,7 @@ void Application::addAssetToWorldFromURL(QString url) {
addAssetToWorldInfo(filename, "Downloading model file " + filename + ".");
auto request = ResourceManager::createResourceRequest(nullptr, QUrl(url));
auto request = DependencyManager::get<ResourceManager>()->createResourceRequest(nullptr, QUrl(url));
connect(request, &ResourceRequest::finished, this, &Application::addAssetToWorldFromURLRequestFinished);
request->send();
}

View file

@ -96,6 +96,7 @@ static const UINT UWM_SHOW_APPLICATION =
#endif
static const QString RUNNING_MARKER_FILENAME = "Interface.running";
static const QString SCRIPTS_SWITCH = "scripts";
class Application;
#if defined(qApp)
@ -298,6 +299,7 @@ public:
void setAvatarOverrideUrl(const QUrl& url, bool save);
QUrl getAvatarOverrideUrl() { return _avatarOverrideUrl; }
bool getSaveAvatarOverrideUrl() { return _saveAvatarOverrideUrl; }
void setCacheOverrideDir(const QString& dirName) { _cacheDir = dirName; }
signals:
void svoImportRequested(const QString& url);
@ -689,5 +691,7 @@ private:
QUrl _avatarOverrideUrl;
bool _saveAvatarOverrideUrl { false };
QString _cacheDir;
};
#endif // hifi_Application_h

View file

@ -14,6 +14,7 @@
#include <QMessageBox>
#include <QStandardPaths>
#include <QQmlContext>
#include <QList>
#include <Application.h>
#include <OffscreenUi.h>
@ -24,6 +25,8 @@
#include "AvatarBookmarks.h"
#include "InterfaceLogging.h"
#include "QVariantGLM.h"
#include <QtQuick/QQuickWindow>
AvatarBookmarks::AvatarBookmarks() {
@ -58,16 +61,48 @@ void AvatarBookmarks::setupMenus(Menu* menubar, MenuWrapper* menu) {
_deleteBookmarksAction = menubar->addActionToQMenuAndActionHash(menu, MenuOption::DeleteAvatarBookmark);
QObject::connect(_deleteBookmarksAction, SIGNAL(triggered()), this, SLOT(deleteBookmark()), Qt::QueuedConnection);
Bookmarks::setupMenus(menubar, menu);
for (auto it = _bookmarks.begin(); it != _bookmarks.end(); ++it) {
addBookmarkToMenu(menubar, it.key(), it.value());
}
Bookmarks::sortActions(menubar, _bookmarksMenu);
}
void AvatarBookmarks::changeToBookmarkedAvatar() {
QAction* action = qobject_cast<QAction*>(sender());
const QString& address = action->data().toString();
auto myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar();
myAvatar->useFullAvatarURL(address);
if (action->data().type() == QVariant::String) {
// TODO: Phase this out eventually.
// Legacy avatar bookmark.
myAvatar->useFullAvatarURL(action->data().toString());
qCDebug(interfaceapp) << " Using Legacy V1 Avatar Bookmark ";
} else {
const QMap<QString, QVariant> bookmark = action->data().toMap();
// Not magic value. This is the current made version, and if it changes this interpreter should be updated to
// handle the new one separately.
// This is where the avatar bookmark entry is parsed. If adding new Value, make sure to have backward compatability with previous
if (bookmark.value(ENTRY_VERSION) == 3) {
const QString& avatarUrl = bookmark.value(ENTRY_AVATAR_URL, "").toString();
myAvatar->useFullAvatarURL(avatarUrl);
qCDebug(interfaceapp) << "Avatar On " << avatarUrl;
const QList<QVariant>& attachments = bookmark.value(ENTRY_AVATAR_ATTACHMENTS, QList<QVariant>()).toList();
qCDebug(interfaceapp) << "Attach " << attachments;
myAvatar->setAttachmentsVariant(attachments);
const float& qScale = bookmark.value(ENTRY_AVATAR_SCALE, 1.0f).toFloat();
myAvatar->setAvatarScale(qScale);
} else {
qCDebug(interfaceapp) << " Bookmark entry does not match client version, make sure client has a handler for the new AvatarBookmark";
}
}
}
void AvatarBookmarks::addBookmark() {
@ -83,13 +118,23 @@ void AvatarBookmarks::addBookmark() {
}
auto myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar();
const QString& bookmarkAddress = myAvatar->getSkeletonModelURL().toString();
Bookmarks::addBookmarkToFile(bookmarkName, bookmarkAddress);
const QString& avatarUrl = myAvatar->getSkeletonModelURL().toString();
const QVariant& avatarScale = myAvatar->getAvatarScale();
// If Avatar attachments ever change, this is where to update them, when saving remember to also append to AVATAR_BOOKMARK_VERSION
QVariantMap *bookmark = new QVariantMap;
bookmark->insert(ENTRY_VERSION, AVATAR_BOOKMARK_VERSION);
bookmark->insert(ENTRY_AVATAR_URL, avatarUrl);
bookmark->insert(ENTRY_AVATAR_SCALE, avatarScale);
bookmark->insert(ENTRY_AVATAR_ATTACHMENTS, myAvatar->getAttachmentsVariant());
Bookmarks::addBookmarkToFile(bookmarkName, *bookmark);
}
void AvatarBookmarks::addBookmarkToMenu(Menu* menubar, const QString& name, const QString& address) {
void AvatarBookmarks::addBookmarkToMenu(Menu* menubar, const QString& name, const QVariant& bookmark) {
QAction* changeAction = _bookmarksMenu->newAction();
changeAction->setData(address);
changeAction->setData(bookmark);
connect(changeAction, SIGNAL(triggered()), this, SLOT(changeToBookmarkedAvatar()));
if (!_isMenuSorted) {
menubar->addActionToQMenuAndActionHash(_bookmarksMenu, changeAction, name, 0, QAction::NoRole);

View file

@ -21,18 +21,23 @@ class AvatarBookmarks: public Bookmarks, public Dependency {
public:
AvatarBookmarks();
void setupMenus(Menu* menubar, MenuWrapper* menu) override;
public slots:
void addBookmark();
protected:
void addBookmarkToMenu(Menu* menubar, const QString& name, const QString& address) override;
void readFromFile();
void addBookmarkToMenu(Menu* menubar, const QString& name, const QVariant& bookmark) override;
void readFromFile() override;
private:
const QString AVATARBOOKMARKS_FILENAME = "avatarbookmarks.json";
const QString ENTRY_AVATAR_URL = "avatarUrl";
const QString ENTRY_AVATAR_ATTACHMENTS = "attachments";
const QString ENTRY_AVATAR_SCALE = "avatarScale";
const QString ENTRY_VERSION = "version";
const int AVATAR_BOOKMARK_VERSION = 3;
private slots:
void changeToBookmarkedAvatar();

View file

@ -28,19 +28,6 @@ Bookmarks::Bookmarks() :
_isMenuSorted(false)
{
}
void Bookmarks::setupMenus(Menu* menubar, MenuWrapper* menu) {
// Enable/Disable menus as needed
enableMenuItems(_bookmarks.count() > 0);
// Load Bookmarks
for (auto it = _bookmarks.begin(); it != _bookmarks.end(); ++it) {
QString bookmarkName = it.key();
QString bookmarkAddress = it.value().toString();
addBookmarkToMenu(menubar, bookmarkName, bookmarkAddress);
}
}
void Bookmarks::deleteBookmark() {
QStringList bookmarkList;
QList<QAction*> menuItems = _bookmarksMenu->actions();
@ -67,7 +54,7 @@ void Bookmarks::deleteBookmark() {
}
}
void Bookmarks::addBookmarkToFile(const QString& bookmarkName, const QString& bookmarkAddress) {
void Bookmarks::addBookmarkToFile(const QString& bookmarkName, const QVariant& bookmark) {
Menu* menubar = Menu::getInstance();
if (contains(bookmarkName)) {
auto offscreenUi = DependencyManager::get<OffscreenUi>();
@ -75,7 +62,6 @@ void Bookmarks::addBookmarkToFile(const QString& bookmarkName, const QString& bo
"The bookmark name you entered already exists in your list.",
QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes);
duplicateBookmarkMessage->setProperty("informativeText", "Would you like to overwrite it?");
auto result = offscreenUi->waitForMessageBoxResult(duplicateBookmarkMessage);
if (result != QMessageBox::Yes) {
return;
@ -83,19 +69,20 @@ void Bookmarks::addBookmarkToFile(const QString& bookmarkName, const QString& bo
removeBookmarkFromMenu(menubar, bookmarkName);
}
addBookmarkToMenu(menubar, bookmarkName, bookmarkAddress);
insert(bookmarkName, bookmarkAddress); // Overwrites any item with the same bookmarkName.
addBookmarkToMenu(menubar, bookmarkName, bookmark);
insert(bookmarkName, bookmark); // Overwrites any item with the same bookmarkName.
enableMenuItems(true);
}
void Bookmarks::insert(const QString& name, const QString& address) {
_bookmarks.insert(name, address);
void Bookmarks::insert(const QString& name, const QVariant& bookmark) {
_bookmarks.insert(name, bookmark);
if (contains(name)) {
qCDebug(interfaceapp) << "Added bookmark:" << name << "," << address;
qCDebug(interfaceapp) << "Added bookmark:" << name;
persistToFile();
} else {
qWarning() << "Couldn't add bookmark: " << name << "," << address;
}
else {
qWarning() << "Couldn't add bookmark: " << name;
}
}

View file

@ -27,18 +27,20 @@ class Bookmarks: public QObject {
public:
Bookmarks();
virtual void setupMenus(Menu* menubar, MenuWrapper* menu);
virtual void setupMenus(Menu* menubar, MenuWrapper* menu) = 0;
QString addressForBookmark(const QString& name) const;
protected:
virtual void addBookmarkToFile(const QString& bookmarkName, const QString& bookmarkAddress);
virtual void addBookmarkToMenu(Menu* menubar, const QString& name, const QString& address) = 0;
void addBookmarkToFile(const QString& bookmarkName, const QVariant& bookmark);
virtual void addBookmarkToMenu(Menu* menubar, const QString& name, const QVariant& bookmark) = 0;
void enableMenuItems(bool enabled);
void readFromFile();
void insert(const QString& name, const QString& address); // Overwrites any existing entry with same name.
virtual void readFromFile();
void insert(const QString& name, const QVariant& address); // Overwrites any existing entry with same name.
void sortActions(Menu* menubar, MenuWrapper* menu);
int getMenuItemLocation(QList<QAction*> actions, const QString& name) const;
bool contains(const QString& name) const;
QVariantMap _bookmarks; // { name: url, ... }
QPointer<MenuWrapper> _bookmarksMenu;
QPointer<QAction> _deleteBookmarksAction;
@ -50,7 +52,6 @@ protected slots:
private:
void remove(const QString& name);
bool contains(const QString& name) const;
static bool sortOrder(QAction* a, QAction* b);
void persistToFile();

View file

@ -41,13 +41,25 @@ void LocationBookmarks::setupMenus(Menu* menubar, MenuWrapper* menu) {
_deleteBookmarksAction = menubar->addActionToQMenuAndActionHash(menu, MenuOption::DeleteBookmark);
QObject::connect(_deleteBookmarksAction, SIGNAL(triggered()), this, SLOT(deleteBookmark()), Qt::QueuedConnection);
Bookmarks::setupMenus(menubar, menu);
// Legacy Location to Bookmark.
// Enable/Disable menus as needed
enableMenuItems(_bookmarks.count() > 0);
// Load Bookmarks
for (auto it = _bookmarks.begin(); it != _bookmarks.end(); ++it) {
QString bookmarkName = it.key();
QString bookmarkAddress = it.value().toString();
addBookmarkToMenu(menubar, bookmarkName, bookmarkAddress);
}
Bookmarks::sortActions(menubar, _bookmarksMenu);
}
void LocationBookmarks::setHomeLocation() {
auto addressManager = DependencyManager::get<AddressManager>();
QString bookmarkAddress = addressManager->currentAddress().toString();
Bookmarks::addBookmarkToFile(HOME_BOOKMARK, bookmarkAddress);
}
@ -74,7 +86,7 @@ void LocationBookmarks::addBookmark() {
Bookmarks::addBookmarkToFile(bookmarkName, bookmarkAddress);
}
void LocationBookmarks::addBookmarkToMenu(Menu* menubar, const QString& name, const QString& address) {
void LocationBookmarks::addBookmarkToMenu(Menu* menubar, const QString& name, const QVariant& address) {
QAction* teleportAction = _bookmarksMenu->newAction();
teleportAction->setData(address);
connect(teleportAction, SIGNAL(triggered()), this, SLOT(teleportToBookmark()));
@ -85,4 +97,4 @@ void LocationBookmarks::addBookmarkToMenu(Menu* menubar, const QString& name, co
menubar->addActionToQMenuAndActionHash(_bookmarksMenu, teleportAction, name, 0, QAction::NoRole);
Bookmarks::sortActions(menubar, _bookmarksMenu);
}
}
}

View file

@ -29,7 +29,7 @@ public slots:
void addBookmark();
protected:
void addBookmarkToMenu(Menu* menubar, const QString& name, const QString& address) override;
void addBookmarkToMenu(Menu* menubar, const QString& name, const QVariant& address) override;
private:
const QString LOCATIONBOOKMARKS_FILENAME = "bookmarks.json";

View file

@ -571,9 +571,6 @@ Menu::Menu() {
addCheckableActionToQMenuAndActionHash(handOptionsMenu, MenuOption::DisplayHandTargets, 0, false,
avatar.get(), SLOT(setEnableDebugDrawHandControllers(bool)));
MenuWrapper* leapOptionsMenu = handOptionsMenu->addMenu("Leap Motion");
addCheckableActionToQMenuAndActionHash(leapOptionsMenu, MenuOption::LeapMotionOnHMD, 0, false);
// Developer > Entities >>>
MenuWrapper* entitiesOptionsMenu = developerMenu->addMenu("Entities");

View file

@ -114,7 +114,6 @@ namespace MenuOption {
const QString IncreaseAvatarSize = "Increase Avatar Size";
const QString IndependentMode = "Independent Mode";
const QString ActionMotorControl = "Enable Default Motor Control";
const QString LeapMotionOnHMD = "Leap Motion on HMD";
const QString LoadScript = "Open and Run Script File...";
const QString LoadScriptURL = "Open and Run Script from URL...";
const QString LodTools = "LOD Tools";

View file

@ -79,6 +79,7 @@ public:
gpu::doInBatch(args->_context, [&](gpu::Batch& batch) {
batch.disableContextStereo();
batch.disableContextViewCorrection();
});
auto srcViewFrustum = args->getViewFrustum();
@ -112,6 +113,7 @@ public:
gpu::doInBatch(args->_context, [&](gpu::Batch& batch) {
batch.restoreContextStereo();
batch.restoreContextViewCorrection();
});
}
};

View file

@ -106,7 +106,8 @@ void ATPAssetMigrator::loadEntityServerFile() {
jsonValue = entityObject;
} else if (wantsToMigrateResource(migrationURL)) {
auto request = ResourceManager::createResourceRequest(this, migrationURL);
auto request =
DependencyManager::get<ResourceManager>()->createResourceRequest(this, migrationURL);
if (request) {
qCDebug(asset_migrator) << "Requesting" << migrationURL << "for ATP asset migration";

View file

@ -1474,6 +1474,19 @@ controller::Pose MyAvatar::getRightHandControllerPoseInAvatarFrame() const {
return getRightHandControllerPoseInWorldFrame().transform(invAvatarMatrix);
}
void MyAvatar::setFingerControllerPosesInSensorFrame(const FingerPosesMap& left, const FingerPosesMap& right) {
_leftHandFingerPosesInSensorFramceCache.set(left);
_rightHandFingerPosesInSensorFramceCache.set(right);
}
MyAvatar::FingerPosesMap MyAvatar::getLeftHandFingerControllerPosesInSensorFrame() const {
return _leftHandFingerPosesInSensorFramceCache.get();
}
MyAvatar::FingerPosesMap MyAvatar::getRightHandFingerControllerPosesInSensorFrame() const {
return _rightHandFingerPosesInSensorFramceCache.get();
}
void MyAvatar::setFootControllerPosesInSensorFrame(const controller::Pose& left, const controller::Pose& right) {
_leftFootControllerPoseInSensorFrameCache.set(left);
_rightFootControllerPoseInSensorFrameCache.set(right);
@ -2544,6 +2557,21 @@ bool MyAvatar::getFlyingEnabled() {
return _enableFlying;
}
// Public interface for targetscale
float MyAvatar::getAvatarScale() {
return getTargetScale();
}
void MyAvatar::setAvatarScale(float val) {
if (QThread::currentThread() != thread()) {
QMetaObject::invokeMethod(this, "setAvatarScale", Q_ARG(float, val));
return;
}
setTargetScale(val);
}
void MyAvatar::setCollisionsEnabled(bool enabled) {
if (QThread::currentThread() != thread()) {

View file

@ -370,7 +370,7 @@ public:
float getDriveKey(DriveKeys key) const;
Q_INVOKABLE float getRawDriveKey(DriveKeys key) const;
void relayDriveKeysToCharacterController();
Q_INVOKABLE void disableDriveKey(DriveKeys key);
Q_INVOKABLE void enableDriveKey(DriveKeys key);
Q_INVOKABLE bool isDriveKeyDisabled(DriveKeys key) const;
@ -479,6 +479,11 @@ public:
controller::Pose getLeftHandControllerPoseInAvatarFrame() const;
controller::Pose getRightHandControllerPoseInAvatarFrame() const;
typedef std::map<int, std::pair<controller::Pose, QString>> FingerPosesMap;
void setFingerControllerPosesInSensorFrame(const FingerPosesMap& left, const FingerPosesMap& right);
FingerPosesMap getLeftHandFingerControllerPosesInSensorFrame() const;
FingerPosesMap getRightHandFingerControllerPosesInSensorFrame() const;
void setFootControllerPosesInSensorFrame(const controller::Pose& left, const controller::Pose& right);
controller::Pose getLeftFootControllerPoseInSensorFrame() const;
controller::Pose getRightFootControllerPoseInSensorFrame() const;
@ -517,6 +522,9 @@ public:
Q_INVOKABLE void setFlyingEnabled(bool enabled);
Q_INVOKABLE bool getFlyingEnabled();
Q_INVOKABLE float getAvatarScale();
Q_INVOKABLE void setAvatarScale(float scale);
Q_INVOKABLE void setCollisionsEnabled(bool enabled);
Q_INVOKABLE bool getCollisionsEnabled();
Q_INVOKABLE void setCharacterControllerEnabled(bool enabled); // deprecated
@ -793,13 +801,15 @@ private:
// These are stored in SENSOR frame
ThreadSafeValueCache<controller::Pose> _leftHandControllerPoseInSensorFrameCache { controller::Pose() };
ThreadSafeValueCache<controller::Pose> _rightHandControllerPoseInSensorFrameCache { controller::Pose() };
ThreadSafeValueCache<controller::Pose> _leftFootControllerPoseInSensorFrameCache{ controller::Pose() };
ThreadSafeValueCache<controller::Pose> _rightFootControllerPoseInSensorFrameCache{ controller::Pose() };
ThreadSafeValueCache<controller::Pose> _hipsControllerPoseInSensorFrameCache{ controller::Pose() };
ThreadSafeValueCache<controller::Pose> _spine2ControllerPoseInSensorFrameCache{ controller::Pose() };
ThreadSafeValueCache<controller::Pose> _headControllerPoseInSensorFrameCache{ controller::Pose() };
ThreadSafeValueCache<controller::Pose> _leftArmControllerPoseInSensorFrameCache{ controller::Pose() };
ThreadSafeValueCache<controller::Pose> _rightArmControllerPoseInSensorFrameCache{ controller::Pose() };
ThreadSafeValueCache<FingerPosesMap> _leftHandFingerPosesInSensorFramceCache { };
ThreadSafeValueCache<FingerPosesMap> _rightHandFingerPosesInSensorFramceCache { };
ThreadSafeValueCache<controller::Pose> _leftFootControllerPoseInSensorFrameCache { controller::Pose() };
ThreadSafeValueCache<controller::Pose> _rightFootControllerPoseInSensorFrameCache { controller::Pose() };
ThreadSafeValueCache<controller::Pose> _hipsControllerPoseInSensorFrameCache { controller::Pose() };
ThreadSafeValueCache<controller::Pose> _spine2ControllerPoseInSensorFrameCache { controller::Pose() };
ThreadSafeValueCache<controller::Pose> _headControllerPoseInSensorFrameCache { controller::Pose() };
ThreadSafeValueCache<controller::Pose> _leftArmControllerPoseInSensorFrameCache { controller::Pose() };
ThreadSafeValueCache<controller::Pose> _rightArmControllerPoseInSensorFrameCache { controller::Pose() };
bool _hmdLeanRecenterEnabled = true;
AnimPose _prePhysicsRoomPose;

View file

@ -47,110 +47,113 @@ void MySkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) {
MyAvatar* myAvatar = static_cast<MyAvatar*>(_owningAvatar);
Rig::HeadParameters headParams;
Rig::ControllerParameters params;
AnimPose avatarToRigPose(glm::vec3(1.0f), Quaternions::Y_180, glm::vec3(0.0f));
// input action is the highest priority source for head orientation.
auto avatarHeadPose = myAvatar->getHeadControllerPoseInAvatarFrame();
if (avatarHeadPose.isValid()) {
glm::mat4 rigHeadMat = Matrices::Y_180 *
createMatFromQuatAndPos(avatarHeadPose.getRotation(), avatarHeadPose.getTranslation());
headParams.rigHeadPosition = extractTranslation(rigHeadMat);
headParams.rigHeadOrientation = glmExtractRotation(rigHeadMat);
headParams.headEnabled = true;
AnimPose pose(avatarHeadPose.getRotation(), avatarHeadPose.getTranslation());
params.controllerPoses[Rig::ControllerType_Head] = avatarToRigPose * pose;
params.controllerActiveFlags[Rig::ControllerType_Head] = true;
} else {
// even though full head IK is disabled, the rig still needs the head orientation to rotate the head up and
// down in desktop mode.
// preMult 180 is necessary to convert from avatar to rig coordinates.
// postMult 180 is necessary to convert head from -z forward to z forward.
headParams.rigHeadOrientation = Quaternions::Y_180 * head->getFinalOrientationInLocalFrame() * Quaternions::Y_180;
headParams.headEnabled = false;
glm::quat headRot = Quaternions::Y_180 * head->getFinalOrientationInLocalFrame() * Quaternions::Y_180;
params.controllerPoses[Rig::ControllerType_Head] = AnimPose(glm::vec3(1.0f), headRot, glm::vec3(0.0f));
params.controllerActiveFlags[Rig::ControllerType_Head] = false;
}
auto avatarHipsPose = myAvatar->getHipsControllerPoseInAvatarFrame();
if (avatarHipsPose.isValid()) {
glm::mat4 rigHipsMat = Matrices::Y_180 * createMatFromQuatAndPos(avatarHipsPose.getRotation(), avatarHipsPose.getTranslation());
headParams.hipsMatrix = rigHipsMat;
headParams.hipsEnabled = true;
AnimPose pose(avatarHipsPose.getRotation(), avatarHipsPose.getTranslation());
params.controllerPoses[Rig::ControllerType_Hips] = avatarToRigPose * pose;
params.controllerActiveFlags[Rig::ControllerType_Hips] = true;
} else {
headParams.hipsEnabled = false;
params.controllerPoses[Rig::ControllerType_Hips] = AnimPose::identity;
params.controllerActiveFlags[Rig::ControllerType_Hips] = false;
}
auto avatarSpine2Pose = myAvatar->getSpine2ControllerPoseInAvatarFrame();
if (avatarSpine2Pose.isValid()) {
glm::mat4 rigSpine2Mat = Matrices::Y_180 * createMatFromQuatAndPos(avatarSpine2Pose.getRotation(), avatarSpine2Pose.getTranslation());
headParams.spine2Matrix = rigSpine2Mat;
headParams.spine2Enabled = true;
AnimPose pose(avatarSpine2Pose.getRotation(), avatarSpine2Pose.getTranslation());
params.controllerPoses[Rig::ControllerType_Spine2] = avatarToRigPose * pose;
params.controllerActiveFlags[Rig::ControllerType_Spine2] = true;
} else {
headParams.spine2Enabled = false;
params.controllerPoses[Rig::ControllerType_Spine2] = AnimPose::identity;
params.controllerActiveFlags[Rig::ControllerType_Spine2] = false;
}
auto avatarRightArmPose = myAvatar->getRightArmControllerPoseInAvatarFrame();
if (avatarRightArmPose.isValid()) {
glm::mat4 rightArmMat = Matrices::Y_180 * createMatFromQuatAndPos(avatarRightArmPose.getRotation(), avatarRightArmPose.getTranslation());
headParams.rightArmPosition = extractTranslation(rightArmMat);
headParams.rightArmRotation = glmExtractRotation(rightArmMat);
headParams.rightArmEnabled = true;
AnimPose pose(avatarRightArmPose.getRotation(), avatarRightArmPose.getTranslation());
params.controllerPoses[Rig::ControllerType_RightArm] = avatarToRigPose * pose;
params.controllerActiveFlags[Rig::ControllerType_RightArm] = true;
} else {
headParams.rightArmEnabled = false;
params.controllerPoses[Rig::ControllerType_RightArm] = AnimPose::identity;
params.controllerActiveFlags[Rig::ControllerType_RightArm] = false;
}
auto avatarLeftArmPose = myAvatar->getLeftArmControllerPoseInAvatarFrame();
if (avatarLeftArmPose.isValid()) {
glm::mat4 leftArmMat = Matrices::Y_180 * createMatFromQuatAndPos(avatarLeftArmPose.getRotation(), avatarLeftArmPose.getTranslation());
headParams.leftArmPosition = extractTranslation(leftArmMat);
headParams.leftArmRotation = glmExtractRotation(leftArmMat);
headParams.leftArmEnabled = true;
AnimPose pose(avatarLeftArmPose.getRotation(), avatarLeftArmPose.getTranslation());
params.controllerPoses[Rig::ControllerType_LeftArm] = avatarToRigPose * pose;
params.controllerActiveFlags[Rig::ControllerType_LeftArm] = true;
} else {
headParams.leftArmEnabled = false;
params.controllerPoses[Rig::ControllerType_LeftArm] = AnimPose::identity;
params.controllerActiveFlags[Rig::ControllerType_LeftArm] = false;
}
headParams.isTalking = head->getTimeWithoutTalking() <= 1.5f;
_rig.updateFromHeadParameters(headParams, deltaTime);
Rig::HandAndFeetParameters handAndFeetParams;
auto leftPose = myAvatar->getLeftHandControllerPoseInAvatarFrame();
if (leftPose.isValid()) {
handAndFeetParams.isLeftEnabled = true;
handAndFeetParams.leftPosition = Quaternions::Y_180 * leftPose.getTranslation();
handAndFeetParams.leftOrientation = Quaternions::Y_180 * leftPose.getRotation();
auto avatarLeftHandPose = myAvatar->getLeftHandControllerPoseInAvatarFrame();
if (avatarLeftHandPose.isValid()) {
AnimPose pose(avatarLeftHandPose.getRotation(), avatarLeftHandPose.getTranslation());
params.controllerPoses[Rig::ControllerType_LeftHand] = avatarToRigPose * pose;
params.controllerActiveFlags[Rig::ControllerType_LeftHand] = true;
} else {
handAndFeetParams.isLeftEnabled = false;
params.controllerPoses[Rig::ControllerType_LeftHand] = AnimPose::identity;
params.controllerActiveFlags[Rig::ControllerType_LeftHand] = false;
}
auto rightPose = myAvatar->getRightHandControllerPoseInAvatarFrame();
if (rightPose.isValid()) {
handAndFeetParams.isRightEnabled = true;
handAndFeetParams.rightPosition = Quaternions::Y_180 * rightPose.getTranslation();
handAndFeetParams.rightOrientation = Quaternions::Y_180 * rightPose.getRotation();
auto avatarRightHandPose = myAvatar->getRightHandControllerPoseInAvatarFrame();
if (avatarRightHandPose.isValid()) {
AnimPose pose(avatarRightHandPose.getRotation(), avatarRightHandPose.getTranslation());
params.controllerPoses[Rig::ControllerType_RightHand] = avatarToRigPose * pose;
params.controllerActiveFlags[Rig::ControllerType_RightHand] = true;
} else {
handAndFeetParams.isRightEnabled = false;
params.controllerPoses[Rig::ControllerType_RightHand] = AnimPose::identity;
params.controllerActiveFlags[Rig::ControllerType_RightHand] = false;
}
auto leftFootPose = myAvatar->getLeftFootControllerPoseInAvatarFrame();
if (leftFootPose.isValid()) {
handAndFeetParams.isLeftFootEnabled = true;
handAndFeetParams.leftFootPosition = Quaternions::Y_180 * leftFootPose.getTranslation();
handAndFeetParams.leftFootOrientation = Quaternions::Y_180 * leftFootPose.getRotation();
auto avatarLeftFootPose = myAvatar->getLeftFootControllerPoseInAvatarFrame();
if (avatarLeftFootPose.isValid()) {
AnimPose pose(avatarLeftFootPose.getRotation(), avatarLeftFootPose.getTranslation());
params.controllerPoses[Rig::ControllerType_LeftFoot] = avatarToRigPose * pose;
params.controllerActiveFlags[Rig::ControllerType_LeftFoot] = true;
} else {
handAndFeetParams.isLeftFootEnabled = false;
params.controllerPoses[Rig::ControllerType_LeftFoot] = AnimPose::identity;
params.controllerActiveFlags[Rig::ControllerType_LeftFoot] = false;
}
auto rightFootPose = myAvatar->getRightFootControllerPoseInAvatarFrame();
if (rightFootPose.isValid()) {
handAndFeetParams.isRightFootEnabled = true;
handAndFeetParams.rightFootPosition = Quaternions::Y_180 * rightFootPose.getTranslation();
handAndFeetParams.rightFootOrientation = Quaternions::Y_180 * rightFootPose.getRotation();
auto avatarRightFootPose = myAvatar->getRightFootControllerPoseInAvatarFrame();
if (avatarRightFootPose.isValid()) {
AnimPose pose(avatarRightFootPose.getRotation(), avatarRightFootPose.getTranslation());
params.controllerPoses[Rig::ControllerType_RightFoot] = avatarToRigPose * pose;
params.controllerActiveFlags[Rig::ControllerType_RightFoot] = true;
} else {
handAndFeetParams.isRightFootEnabled = false;
params.controllerPoses[Rig::ControllerType_RightFoot] = AnimPose::identity;
params.controllerActiveFlags[Rig::ControllerType_RightFoot] = false;
}
handAndFeetParams.bodyCapsuleRadius = myAvatar->getCharacterController()->getCapsuleRadius();
handAndFeetParams.bodyCapsuleHalfHeight = myAvatar->getCharacterController()->getCapsuleHalfHeight();
handAndFeetParams.bodyCapsuleLocalOffset = myAvatar->getCharacterController()->getCapsuleLocalOffset();
params.bodyCapsuleRadius = myAvatar->getCharacterController()->getCapsuleRadius();
params.bodyCapsuleHalfHeight = myAvatar->getCharacterController()->getCapsuleHalfHeight();
params.bodyCapsuleLocalOffset = myAvatar->getCharacterController()->getCapsuleLocalOffset();
_rig.updateFromHandAndFeetParameters(handAndFeetParams, deltaTime);
params.isTalking = head->getTimeWithoutTalking() <= 1.5f;
_rig.updateFromControllerParameters(params, deltaTime);
Rig::CharacterControllerState ccState = convertCharacterControllerState(myAvatar->getCharacterController()->getState());
@ -171,5 +174,50 @@ void MySkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) {
eyeParams.rightEyeJointIndex = geometry.rightEyeJointIndex;
_rig.updateFromEyeParameters(eyeParams);
updateFingers(myAvatar->getLeftHandFingerControllerPosesInSensorFrame());
updateFingers(myAvatar->getRightHandFingerControllerPosesInSensorFrame());
}
void MySkeletonModel::updateFingers(const MyAvatar::FingerPosesMap& fingerPoses) {
// Assumes that finger poses are kept in order in the poses map.
if (fingerPoses.size() == 0) {
return;
}
auto posesMapItr = fingerPoses.begin();
bool isLeftHand = posesMapItr->first < (int)controller::Action::RIGHT_HAND_THUMB1;
MyAvatar* myAvatar = static_cast<MyAvatar*>(_owningAvatar);
auto handPose = isLeftHand
? myAvatar->getLeftHandControllerPoseInSensorFrame()
: myAvatar->getRightHandControllerPoseInSensorFrame();
auto handJointRotation = handPose.getRotation();
bool isHandValid = handPose.isValid();
bool isFingerValid = false;
glm::quat previousJointRotation;
while (posesMapItr != fingerPoses.end()) {
auto jointName = posesMapItr->second.second;
if (isHandValid && jointName.right(1) == "1") {
isFingerValid = posesMapItr->second.first.isValid();
previousJointRotation = handJointRotation;
}
if (isHandValid && isFingerValid) {
auto thisJointRotation = posesMapItr->second.first.getRotation();
const float CONTROLLER_PRIORITY = 2.0f;
_rig.setJointRotation(_rig.indexOfJoint(jointName), true, glm::inverse(previousJointRotation) * thisJointRotation,
CONTROLLER_PRIORITY);
previousJointRotation = thisJointRotation;
} else {
_rig.clearJointAnimationPriority(_rig.indexOfJoint(jointName));
}
posesMapItr++;
}
}

View file

@ -10,6 +10,7 @@
#define hifi_MySkeletonModel_h
#include <avatars-renderer/SkeletonModel.h>
#include "MyAvatar.h"
/// A skeleton loaded from a model.
class MySkeletonModel : public SkeletonModel {
@ -21,6 +22,9 @@ private:
public:
MySkeletonModel(Avatar* owningAvatar, QObject* parent = nullptr);
void updateRig(float deltaTime, glm::mat4 parentTransform) override;
private:
void updateFingers(const MyAvatar::FingerPosesMap& fingerPoses);
};
#endif // hifi_MySkeletonModel_h

View file

@ -1,246 +0,0 @@
//
// Created by Sam Cake on 6/2/2014
// Copyright 2014 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "Leapmotion.h"
#include <NumericalConstants.h>
#include "Menu.h"
const int PALMROOT_NUM_JOINTS = 3;
const int FINGER_NUM_JOINTS = 4;
const int HAND_NUM_JOINTS = FINGER_NUM_JOINTS*5+PALMROOT_NUM_JOINTS;
const DeviceTracker::Name Leapmotion::NAME = "Leapmotion";
// find the index of a joint from
// the side: true = right
// the finger & the bone:
// finger in [0..4] : bone in [0..3] a finger phalange
// [-1] up the hand branch : bone in [0..2] <=> [ hand, forearm, arm]
MotionTracker::Index evalJointIndex(bool isRightSide, int finger, int bone) {
MotionTracker::Index offset = 1 // start after root
+ (int(isRightSide) * HAND_NUM_JOINTS) // then offset for side
+ PALMROOT_NUM_JOINTS; // then add the arm/forearm/hand chain
if (finger >= 0) {
// from there go down in the correct finger and bone
return offset + (finger * FINGER_NUM_JOINTS) + bone;
} else {
// or go back up for the correct root bone
return offset - 1 - bone;
}
}
// static
void Leapmotion::init() {
DeviceTracker* device = DeviceTracker::getDevice(NAME);
if (!device) {
// create a new Leapmotion and register it
Leapmotion* leap = new Leapmotion();
DeviceTracker::registerDevice(NAME, leap);
}
}
// static
void Leapmotion::destroy() {
DeviceTracker::destroyDevice(NAME);
}
// static
Leapmotion* Leapmotion::getInstance() {
DeviceTracker* device = DeviceTracker::getDevice(NAME);
if (!device) {
// create a new Leapmotion and register it
device = new Leapmotion();
DeviceTracker::registerDevice(NAME, device);
}
return dynamic_cast< Leapmotion* > (device);
}
Leapmotion::Leapmotion() :
MotionTracker(),
_active(false)
{
// Create the Leapmotion joint hierarchy
std::vector< Semantic > sides;
sides.push_back("joint_L_");
sides.push_back("joint_R_");
std::vector< Semantic > rootBones;
rootBones.push_back("elbow");
rootBones.push_back("wrist");
rootBones.push_back("hand");
std::vector< Semantic > fingers;
fingers.push_back("thumb");
fingers.push_back("index");
fingers.push_back("middle");
fingers.push_back("ring");
fingers.push_back("pinky");
std::vector< Semantic > fingerBones;
fingerBones.push_back("1");
fingerBones.push_back("2");
fingerBones.push_back("3");
fingerBones.push_back("4");
std::vector< Index > palms;
for (unsigned int s = 0; s < sides.size(); s++) {
Index rootJoint = 0;
for (unsigned int rb = 0; rb < rootBones.size(); rb++) {
rootJoint = addJoint(sides[s] + rootBones[rb], rootJoint);
}
// capture the hand index for debug
palms.push_back(rootJoint);
for (unsigned int f = 0; f < fingers.size(); f++) {
for (unsigned int b = 0; b < fingerBones.size(); b++) {
rootJoint = addJoint(sides[s] + fingers[f] + fingerBones[b], rootJoint);
}
}
}
#ifdef HAVE_LEAPMOTION
if (Menu::getInstance()->isOptionChecked(MenuOption::LeapMotionOnHMD)) {
_controller.setPolicyFlags(Leap::Controller::POLICY_OPTIMIZE_HMD);
}
#endif
}
Leapmotion::~Leapmotion() {
}
#ifdef HAVE_LEAPMOTION
glm::quat quatFromLeapBase(float sideSign, const Leap::Matrix& basis) {
// fix the handness to right and always...
glm::vec3 xAxis = glm::normalize(sideSign * glm::vec3(basis.xBasis.x, basis.xBasis.y, basis.xBasis.z));
glm::vec3 yAxis = glm::normalize(glm::vec3(basis.yBasis.x, basis.yBasis.y, basis.yBasis.z));
glm::vec3 zAxis = glm::normalize(glm::vec3(basis.zBasis.x, basis.zBasis.y, basis.zBasis.z));
xAxis = glm::normalize(glm::cross(yAxis, zAxis));
glm::quat orientation = (glm::quat_cast(glm::mat3(xAxis, yAxis, zAxis)));
return orientation;
}
glm::vec3 vec3FromLeapVector(const Leap::Vector& vec) {
return glm::vec3(vec.x * METERS_PER_MILLIMETER, vec.y * METERS_PER_MILLIMETER, vec.z * METERS_PER_MILLIMETER);
}
#endif
void Leapmotion::update() {
#ifdef HAVE_LEAPMOTION
bool wasActive = _active;
_active = _controller.isConnected();
if (_active || wasActive) {
// Go through all the joints and increment their counter since last update.
// Increment all counters once after controller first becomes inactive so that each joint reports itself as inactive.
// TODO C++11 for (auto jointIt = _jointsArray.begin(); jointIt != _jointsArray.end(); jointIt++) {
for (JointTracker::Vector::iterator jointIt = _jointsArray.begin(); jointIt != _jointsArray.end(); jointIt++) {
(*jointIt).tickNewFrame();
}
}
if (!_active) {
return;
}
// Get the most recent frame and report some basic information
const Leap::Frame frame = _controller.frame();
static int64_t lastFrameID = -1;
int64_t newFrameID = frame.id();
// If too soon then exit
if (lastFrameID >= newFrameID)
return;
glm::vec3 delta(0.0f);
glm::quat handOri;
if (!frame.hands().isEmpty()) {
for (int handNum = 0; handNum < frame.hands().count(); handNum++) {
const Leap::Hand hand = frame.hands()[handNum];
int side = (hand.isRight() ? 1 : -1);
JointTracker* parentJointTracker = _jointsArray.data();
int rootBranchIndex = -1;
Leap::Arm arm = hand.arm();
if (arm.isValid()) {
glm::quat ori = quatFromLeapBase(float(side), arm.basis());
glm::vec3 pos = vec3FromLeapVector(arm.elbowPosition());
JointTracker* elbow = editJointTracker(evalJointIndex((side > 0), rootBranchIndex, 2)); // 2 is the index of the elbow joint
elbow->editAbsFrame().setTranslation(pos);
elbow->editAbsFrame().setRotation(ori);
elbow->updateLocFromAbsTransform(parentJointTracker);
elbow->activeFrame();
parentJointTracker = elbow;
pos = vec3FromLeapVector(arm.wristPosition());
JointTracker* wrist = editJointTracker(evalJointIndex((side > 0), rootBranchIndex, 1)); // 1 is the index of the wrist joint
wrist->editAbsFrame().setTranslation(pos);
wrist->editAbsFrame().setRotation(ori);
wrist->updateLocFromAbsTransform(parentJointTracker);
wrist->activeFrame();
parentJointTracker = wrist;
}
JointTracker* palmJoint = NULL;
{
glm::vec3 pos = vec3FromLeapVector(hand.palmPosition());
glm::quat ori = quatFromLeapBase(float(side), hand.basis());
palmJoint = editJointTracker(evalJointIndex((side > 0), rootBranchIndex, 0)); // 0 is the index of the palm joint
palmJoint->editAbsFrame().setTranslation(pos);
palmJoint->editAbsFrame().setRotation(ori);
palmJoint->updateLocFromAbsTransform(parentJointTracker);
palmJoint->activeFrame();
}
// Check if the hand has any fingers
const Leap::FingerList fingers = hand.fingers();
if (!fingers.isEmpty()) {
// For every fingers in the list
for (int i = 0; i < fingers.count(); ++i) {
// Reset the parent joint to the palmJoint for every finger traversal
parentJointTracker = palmJoint;
// surprisingly, Leap::Finger::Type start at 0 for thumb a until 4 for the pinky
Index fingerIndex = evalJointIndex((side > 0), int(fingers[i].type()), 0);
// let's update the finger's joints
for (int b = 0; b < FINGER_NUM_JOINTS; b++) {
Leap::Bone::Type type = Leap::Bone::Type(b + Leap::Bone::TYPE_METACARPAL);
Leap::Bone bone = fingers[i].bone(type);
JointTracker* ljointTracker = editJointTracker(fingerIndex + b);
if (bone.isValid()) {
Leap::Vector bp = bone.nextJoint();
ljointTracker->editAbsFrame().setTranslation(vec3FromLeapVector(bp));
ljointTracker->editAbsFrame().setRotation(quatFromLeapBase(float(side), bone.basis()));
ljointTracker->updateLocFromAbsTransform(parentJointTracker);
ljointTracker->activeFrame();
}
parentJointTracker = ljointTracker;
}
}
}
}
}
lastFrameID = newFrameID;
#endif
}

View file

@ -1,48 +0,0 @@
//
// Created by Sam Cake on 6/2/2014
// Copyright 2014 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#ifndef hifi_Leapmotion_h
#define hifi_Leapmotion_h
#include <QDateTime>
#include <trackers/MotionTracker.h>
#ifdef HAVE_LEAPMOTION
#include <Leap.h>
#endif
/// Handles interaction with the Leapmotion skeleton tracking suit.
class Leapmotion : public MotionTracker {
public:
static const Name NAME;
static void init();
static void destroy();
/// Leapmotion MotionTracker factory
static Leapmotion* getInstance();
bool isActive() const { return _active; }
virtual void update() override;
protected:
Leapmotion();
virtual ~Leapmotion();
private:
#ifdef HAVE_LEAPMOTION
Leap::Listener _listener;
Leap::Controller _controller;
#endif
bool _active;
};
#endif // hifi_Leapmotion_h

View file

@ -23,6 +23,7 @@
#include <gl/OpenGLVersionChecker.h>
#include <SandboxUtils.h>
#include <SharedUtil.h>
#include <NetworkAccessManager.h>
#include "AddressManager.h"
#include "Application.h"
@ -71,15 +72,19 @@ int main(int argc, const char* argv[]) {
QCommandLineOption runServerOption("runServer", "Whether to run the server");
QCommandLineOption serverContentPathOption("serverContentPath", "Where to find server content", "serverContentPath");
QCommandLineOption allowMultipleInstancesOption("allowMultipleInstances", "Allow multiple instances to run");
QCommandLineOption overrideAppLocalDataPathOption("cache", "set test cache <dir>", "dir");
QCommandLineOption overrideScriptsPathOption(SCRIPTS_SWITCH, "set scripts <path>", "path");
parser.addOption(urlOption);
parser.addOption(noUpdaterOption);
parser.addOption(checkMinSpecOption);
parser.addOption(runServerOption);
parser.addOption(serverContentPathOption);
parser.addOption(overrideAppLocalDataPathOption);
parser.addOption(overrideScriptsPathOption);
parser.addOption(allowMultipleInstancesOption);
parser.parse(arguments);
const QString& applicationName = getInterfaceSharedMemoryName();
bool instanceMightBeRunning = true;
#ifdef Q_OS_WIN
@ -96,6 +101,29 @@ int main(int argc, const char* argv[]) {
if (allowMultipleInstances) {
instanceMightBeRunning = false;
}
// this needs to be done here in main, as the mechanism for setting the
// scripts directory appears not to work. See the bug report
// https://highfidelity.fogbugz.com/f/cases/5759/Issues-changing-scripts-directory-in-ScriptsEngine
if (parser.isSet(overrideScriptsPathOption)) {
QDir scriptsPath(parser.value(overrideScriptsPathOption));
if (scriptsPath.exists()) {
PathUtils::defaultScriptsLocation(scriptsPath.path());
}
}
if (parser.isSet(overrideAppLocalDataPathOption)) {
// get dir to use for cache
QString cacheDir = parser.value(overrideAppLocalDataPathOption);
if (!cacheDir.isEmpty()) {
// tell everyone to use the right cache location
//
// this handles data8 and prepared
DependencyManager::get<ResourceManager>()->setCacheDir(cacheDir);
// this does the ktx_cache
PathUtils::getAppLocalDataPath(cacheDir);
}
}
if (instanceMightBeRunning) {
// Try to connect and send message to existing interface instance
@ -179,7 +207,7 @@ int main(int argc, const char* argv[]) {
QString openvrDllPath = appPath + "/plugins/openvr.dll";
HMODULE openvrDll;
CHECKMINSPECPROC checkMinSpecPtr;
if ((openvrDll = LoadLibrary(openvrDllPath.toLocal8Bit().data())) &&
if ((openvrDll = LoadLibrary(openvrDllPath.toLocal8Bit().data())) &&
(checkMinSpecPtr = (CHECKMINSPECPROC)GetProcAddress(openvrDll, "CheckMinSpec"))) {
if (!checkMinSpecPtr()) {
return -1;

View file

@ -17,7 +17,6 @@
#include <plugins/PluginManager.h>
#include "Application.h"
#include <trackers/MotionTracker.h>
void ControllerScriptingInterface::handleMetaEvent(HFMetaEvent* event) {
if (event->type() == HFActionEvent::startType()) {
@ -97,86 +96,6 @@ QVariant ControllerScriptingInterface::getRecommendedOverlayRect() const {
return qRectToVariant(rect);
}
controller::InputController* ControllerScriptingInterface::createInputController(const QString& deviceName, const QString& tracker) {
// This is where we retrieve the Device Tracker category and then the sub tracker within it
auto icIt = _inputControllers.find(0);
if (icIt != _inputControllers.end()) {
return (*icIt).second.get();
}
// Look for device
DeviceTracker::ID deviceID = DeviceTracker::getDeviceID(deviceName.toStdString());
if (deviceID < 0) {
deviceID = 0;
}
// TODO in this current implementation, we just pick the device assuming there is one (normally the Leapmotion)
// in the near future we need to change that to a real mapping between the devices and the deviceName
// ALso we need to expand the spec so we can fall back on the "default" controller per categories
if (deviceID >= 0) {
// TODO here again the assumption it's the LeapMotion and so it's a MOtionTracker, this would need to be changed to support different types of devices
MotionTracker* motionTracker = dynamic_cast< MotionTracker* > (DeviceTracker::getDevice(deviceID));
if (motionTracker) {
MotionTracker::Index trackerID = motionTracker->findJointIndex(tracker.toStdString());
if (trackerID >= 0) {
controller::InputController::Pointer inputController = std::make_shared<InputController>(deviceID, trackerID, this);
controller::InputController::Key key = inputController->getKey();
_inputControllers.insert(InputControllerMap::value_type(key, inputController));
return inputController.get();
}
}
}
return nullptr;
}
void ControllerScriptingInterface::releaseInputController(controller::InputController* input) {
_inputControllers.erase(input->getKey());
}
void ControllerScriptingInterface::updateInputControllers() {
for (auto it = _inputControllers.begin(); it != _inputControllers.end(); it++) {
(*it).second->update();
}
}
InputController::InputController(int deviceTrackerId, int subTrackerId, QObject* parent) :
_deviceTrackerId(deviceTrackerId),
_subTrackerId(subTrackerId),
_isActive(false)
{
}
void InputController::update() {
_isActive = false;
// TODO for now the InputController is only supporting a JointTracker from a MotionTracker
MotionTracker* motionTracker = dynamic_cast< MotionTracker*> (DeviceTracker::getDevice(_deviceTrackerId));
if (motionTracker) {
if ((int)_subTrackerId < motionTracker->numJointTrackers()) {
const MotionTracker::JointTracker* joint = motionTracker->getJointTracker(_subTrackerId);
if (joint->isActive()) {
joint->getAbsFrame().getTranslation(_eventCache.absTranslation);
joint->getAbsFrame().getRotation(_eventCache.absRotation);
joint->getLocFrame().getTranslation(_eventCache.locTranslation);
joint->getLocFrame().getRotation(_eventCache.locRotation);
_isActive = true;
//emit spatialEvent(_eventCache);
}
}
}
}
const unsigned int INPUTCONTROLLER_KEY_DEVICE_OFFSET = 16;
const unsigned int INPUTCONTROLLER_KEY_DEVICE_MASK = 16;
InputController::Key InputController::getKey() const {
return (((_deviceTrackerId & INPUTCONTROLLER_KEY_DEVICE_MASK) << INPUTCONTROLLER_KEY_DEVICE_OFFSET) | _subTrackerId);
}
void ControllerScriptingInterface::emitKeyPressEvent(QKeyEvent* event) { emit keyPressEvent(KeyEvent(*event)); }
void ControllerScriptingInterface::emitKeyReleaseEvent(QKeyEvent* event) { emit keyReleaseEvent(KeyEvent(*event)); }

View file

@ -25,38 +25,6 @@
#include <WheelEvent.h>
class ScriptEngine;
class PalmData;
class InputController : public controller::InputController {
Q_OBJECT
public:
InputController(int deviceTrackerId, int subTrackerId, QObject* parent = NULL);
virtual void update() override;
virtual Key getKey() const override;
public slots:
virtual bool isActive() const override { return _isActive; }
virtual glm::vec3 getAbsTranslation() const override { return _eventCache.absTranslation; }
virtual glm::quat getAbsRotation() const override { return _eventCache.absRotation; }
virtual glm::vec3 getLocTranslation() const override { return _eventCache.locTranslation; }
virtual glm::quat getLocRotation() const override { return _eventCache.locRotation; }
private:
int _deviceTrackerId;
uint _subTrackerId;
// cache for the spatial
SpatialEvent _eventCache;
bool _isActive;
signals:
};
/// handles scripting of input controller commands from JS
class ControllerScriptingInterface : public controller::ScriptingInterface {
Q_OBJECT
@ -86,8 +54,6 @@ public:
bool isJoystickCaptured(int joystickIndex) const;
bool areEntityClicksCaptured() const;
void updateInputControllers();
public slots:
virtual void captureKeyEvents(const KeyEvent& event);
@ -102,10 +68,6 @@ public slots:
virtual glm::vec2 getViewportDimensions() const;
virtual QVariant getRecommendedOverlayRect() const;
/// Factory to create an InputController
virtual controller::InputController* createInputController(const QString& deviceName, const QString& tracker);
virtual void releaseInputController(controller::InputController* input);
signals:
void keyPressEvent(const KeyEvent& event);
void keyReleaseEvent(const KeyEvent& event);
@ -135,8 +97,6 @@ private:
bool _captureEntityClicks;
using InputKey = controller::InputController::Key;
using InputControllerMap = std::map<InputKey, controller::InputController::Pointer>;
InputControllerMap _inputControllers;
};
const int NUMBER_OF_SPATIALCONTROLS_PER_PALM = 2; // the hand and the tip

View file

@ -62,7 +62,7 @@ bool TestScriptingInterface::loadTestScene(QString scene) {
static const QString TEST_SCRIPTS_ROOT = TEST_ROOT + "scripts/";
static const QString TEST_SCENES_ROOT = TEST_ROOT + "scenes/";
return DependencyManager::get<OffscreenUi>()->returnFromUiThread([scene]()->QVariant {
ResourceManager::setUrlPrefixOverride("atp:/", TEST_BINARY_ROOT + scene + ".atp/");
DependencyManager::get<ResourceManager>()->setUrlPrefixOverride("atp:/", TEST_BINARY_ROOT + scene + ".atp/");
auto tree = qApp->getEntities()->getTree();
auto treeIsClient = tree->getIsClient();
// Force the tree to accept the load regardless of permissions

View file

@ -0,0 +1,34 @@
//
// AnimDefaultPose.cpp
//
// Created by Anthony J. Thibault on 6/26/17.
// Copyright (c) 2017 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 "AnimDefaultPose.h"
AnimDefaultPose::AnimDefaultPose(const QString& id) :
AnimNode(AnimNode::Type::DefaultPose, id)
{
}
AnimDefaultPose::~AnimDefaultPose() {
}
const AnimPoseVec& AnimDefaultPose::evaluate(const AnimVariantMap& animVars, const AnimContext& context, float dt, Triggers& triggersOut) {
if (_skeleton) {
_poses = _skeleton->getRelativeDefaultPoses();
} else {
_poses.clear();
}
return _poses;
}
const AnimPoseVec& AnimDefaultPose::getPosesInternal() const {
return _poses;
}

View file

@ -0,0 +1,36 @@
//
// AnimDefaultPose.h
//
// Created by Anthony J. Thibault on 6/26/17.
// Copyright (c) 2017 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_AnimDefaultPose_h
#define hifi_AnimDefaultPose_h
#include <string>
#include "AnimNode.h"
// Always returns the default pose of the current skeleton.
class AnimDefaultPose : public AnimNode {
public:
AnimDefaultPose(const QString& id);
virtual ~AnimDefaultPose() override;
virtual const AnimPoseVec& evaluate(const AnimVariantMap& animVars, const AnimContext& context, float dt, Triggers& triggersOut) override;
protected:
// for AnimDebugDraw rendering
virtual const AnimPoseVec& getPosesInternal() const override;
AnimPoseVec _poses;
// no copies
AnimDefaultPose(const AnimDefaultPose&) = delete;
AnimDefaultPose& operator=(const AnimDefaultPose&) = delete;
};
#endif // hifi_AnimDefaultPose_h

View file

@ -23,13 +23,40 @@
#include "CubicHermiteSpline.h"
#include "AnimUtil.h"
static void lookupJointChainInfo(AnimInverseKinematics::JointChainInfo* jointChainInfos, size_t numJointChainInfos,
int indexA, int indexB,
AnimInverseKinematics::JointChainInfo** jointChainInfoA,
AnimInverseKinematics::JointChainInfo** jointChainInfoB) {
*jointChainInfoA = nullptr;
*jointChainInfoB = nullptr;
for (size_t i = 0; i < numJointChainInfos; i++) {
if (jointChainInfos[i].jointIndex == indexA) {
*jointChainInfoA = jointChainInfos + i;
}
if (jointChainInfos[i].jointIndex == indexB) {
*jointChainInfoB = jointChainInfos + i;
}
if (*jointChainInfoA && *jointChainInfoB) {
break;
}
}
}
static float easeOutExpo(float t) {
return 1.0f - powf(2, -10.0f * t);
}
AnimInverseKinematics::IKTargetVar::IKTargetVar(const QString& jointNameIn, const QString& positionVarIn, const QString& rotationVarIn,
const QString& typeVarIn, const QString& weightVarIn, float weightIn, const std::vector<float>& flexCoefficientsIn) :
const QString& typeVarIn, const QString& weightVarIn, float weightIn, const std::vector<float>& flexCoefficientsIn,
const QString& poleVectorEnabledVarIn, const QString& poleReferenceVectorVarIn, const QString& poleVectorVarIn) :
jointName(jointNameIn),
positionVar(positionVarIn),
rotationVar(rotationVarIn),
typeVar(typeVarIn),
weightVar(weightVarIn),
poleVectorEnabledVar(poleVectorEnabledVarIn),
poleReferenceVectorVar(poleReferenceVectorVarIn),
poleVectorVar(poleVectorVarIn),
weight(weightIn),
numFlexCoefficients(flexCoefficientsIn.size()),
jointIndex(-1)
@ -46,6 +73,9 @@ AnimInverseKinematics::IKTargetVar::IKTargetVar(const IKTargetVar& orig) :
rotationVar(orig.rotationVar),
typeVar(orig.typeVar),
weightVar(orig.weightVar),
poleVectorEnabledVar(orig.poleVectorEnabledVar),
poleReferenceVectorVar(orig.poleReferenceVectorVar),
poleVectorVar(orig.poleVectorVar),
weight(orig.weight),
numFlexCoefficients(orig.numFlexCoefficients),
jointIndex(orig.jointIndex)
@ -99,8 +129,9 @@ void AnimInverseKinematics::computeAbsolutePoses(AnimPoseVec& absolutePoses) con
}
void AnimInverseKinematics::setTargetVars(const QString& jointName, const QString& positionVar, const QString& rotationVar,
const QString& typeVar, const QString& weightVar, float weight, const std::vector<float>& flexCoefficients) {
IKTargetVar targetVar(jointName, positionVar, rotationVar, typeVar, weightVar, weight, flexCoefficients);
const QString& typeVar, const QString& weightVar, float weight, const std::vector<float>& flexCoefficients,
const QString& poleVectorEnabledVar, const QString& poleReferenceVectorVar, const QString& poleVectorVar) {
IKTargetVar targetVar(jointName, positionVar, rotationVar, typeVar, weightVar, weight, flexCoefficients, poleVectorEnabledVar, poleReferenceVectorVar, poleVectorVar);
// if there are dups, last one wins.
bool found = false;
@ -138,9 +169,9 @@ void AnimInverseKinematics::computeTargets(const AnimVariantMap& animVars, std::
IKTarget target;
target.setType(animVars.lookup(targetVar.typeVar, (int)IKTarget::Type::RotationAndPosition));
if (target.getType() != IKTarget::Type::Unknown) {
AnimPose defaultPose = _skeleton->getAbsolutePose(targetVar.jointIndex, underPoses);
glm::quat rotation = animVars.lookupRigToGeometry(targetVar.rotationVar, defaultPose.rot());
glm::vec3 translation = animVars.lookupRigToGeometry(targetVar.positionVar, defaultPose.trans());
AnimPose absPose = _skeleton->getAbsolutePose(targetVar.jointIndex, underPoses);
glm::quat rotation = animVars.lookupRigToGeometry(targetVar.rotationVar, absPose.rot());
glm::vec3 translation = animVars.lookupRigToGeometry(targetVar.positionVar, absPose.trans());
float weight = animVars.lookup(targetVar.weightVar, targetVar.weight);
target.setPose(rotation, translation);
@ -148,6 +179,15 @@ void AnimInverseKinematics::computeTargets(const AnimVariantMap& animVars, std::
target.setWeight(weight);
target.setFlexCoefficients(targetVar.numFlexCoefficients, targetVar.flexCoefficients);
bool poleVectorEnabled = animVars.lookup(targetVar.poleVectorEnabledVar, false);
target.setPoleVectorEnabled(poleVectorEnabled);
glm::vec3 poleVector = animVars.lookupRigToGeometryVector(targetVar.poleVectorVar, Vectors::UNIT_Z);
target.setPoleVector(glm::normalize(poleVector));
glm::vec3 poleReferenceVector = animVars.lookupRigToGeometryVector(targetVar.poleReferenceVectorVar, Vectors::UNIT_Z);
target.setPoleReferenceVector(glm::normalize(poleReferenceVector));
targets.push_back(target);
if (targetVar.jointIndex > _maxTargetIndex) {
@ -298,7 +338,8 @@ void AnimInverseKinematics::solveTargetWithCCD(const AnimContext& context, const
// the tip's parent-relative as we proceed up the chain
glm::quat tipParentOrientation = absolutePoses[pivotIndex].rot();
std::map<int, DebugJoint> debugJointMap;
const size_t MAX_CHAIN_DEPTH = 30;
JointChainInfo jointChainInfos[MAX_CHAIN_DEPTH];
// NOTE: if this code is removed, the head will remain rigid, causing the spine/hips to thrust forward backward
// as the head is nodded.
@ -326,15 +367,8 @@ void AnimInverseKinematics::solveTargetWithCCD(const AnimContext& context, const
}
}
// store the relative rotation change in the accumulator
_rotationAccumulators[tipIndex].add(tipRelativeRotation, target.getWeight());
glm::vec3 tipRelativeTranslation = _relativePoses[target.getIndex()].trans();
_translationAccumulators[tipIndex].add(tipRelativeTranslation);
if (debug) {
debugJointMap[tipIndex] = DebugJoint(tipRelativeRotation, tipRelativeTranslation, constrained);
}
jointChainInfos[chainDepth] = { tipRelativeRotation, tipRelativeTranslation, target.getWeight(), tipIndex, constrained };
}
// cache tip absolute position
@ -344,6 +378,9 @@ void AnimInverseKinematics::solveTargetWithCCD(const AnimContext& context, const
// descend toward root, pivoting each joint to get tip closer to target position
while (pivotIndex != _hipsIndex && pivotsParentIndex != -1) {
assert(chainDepth < MAX_CHAIN_DEPTH);
// compute the two lines that should be aligned
glm::vec3 jointPosition = absolutePoses[pivotIndex].trans();
glm::vec3 leverArm = tipPosition - jointPosition;
@ -357,6 +394,7 @@ void AnimInverseKinematics::solveTargetWithCCD(const AnimContext& context, const
const float MIN_AXIS_LENGTH = 1.0e-4f;
RotationConstraint* constraint = getConstraint(pivotIndex);
// only allow swing on lowerSpine if there is a hips IK target.
if (_hipsTargetIndex < 0 && constraint && constraint->isLowerSpine() && tipIndex != _headIndex) {
// for these types of targets we only allow twist at the lower-spine
@ -382,6 +420,7 @@ void AnimInverseKinematics::solveTargetWithCCD(const AnimContext& context, const
float cosAngle = glm::clamp(glm::dot(leverArm, targetLine) / (glm::length(leverArm) * glm::length(targetLine)), -1.0f, 1.0f);
float angle = acosf(cosAngle);
const float MIN_ADJUSTMENT_ANGLE = 1.0e-4f;
if (angle > MIN_ADJUSTMENT_ANGLE) {
// reduce angle by a flexCoefficient
angle *= target.getFlexCoefficient(chainDepth);
@ -440,15 +479,8 @@ void AnimInverseKinematics::solveTargetWithCCD(const AnimContext& context, const
}
}
// store the relative rotation change in the accumulator
_rotationAccumulators[pivotIndex].add(newRot, target.getWeight());
glm::vec3 newTrans = _relativePoses[pivotIndex].trans();
_translationAccumulators[pivotIndex].add(newTrans);
if (debug) {
debugJointMap[pivotIndex] = DebugJoint(newRot, newTrans, constrained);
}
jointChainInfos[chainDepth] = { newRot, newTrans, target.getWeight(), pivotIndex, constrained };
// keep track of tip's new transform as we descend towards root
tipPosition = jointPosition + deltaRotation * (tipPosition - jointPosition);
@ -461,8 +493,127 @@ void AnimInverseKinematics::solveTargetWithCCD(const AnimContext& context, const
chainDepth++;
}
if (target.getPoleVectorEnabled()) {
int topJointIndex = target.getIndex();
int midJointIndex = _skeleton->getParentIndex(topJointIndex);
if (midJointIndex != -1) {
int baseJointIndex = _skeleton->getParentIndex(midJointIndex);
if (baseJointIndex != -1) {
int baseParentJointIndex = _skeleton->getParentIndex(baseJointIndex);
AnimPose topPose, midPose, basePose;
int topChainIndex = -1, baseChainIndex = -1;
AnimPose postAbsPoses[MAX_CHAIN_DEPTH];
AnimPose accum = absolutePoses[_hipsIndex];
AnimPose baseParentPose = absolutePoses[_hipsIndex];
for (int i = (int)chainDepth - 1; i >= 0; i--) {
accum = accum * AnimPose(glm::vec3(1.0f), jointChainInfos[i].relRot, jointChainInfos[i].relTrans);
postAbsPoses[i] = accum;
if (jointChainInfos[i].jointIndex == topJointIndex) {
topChainIndex = i;
topPose = accum;
}
if (jointChainInfos[i].jointIndex == midJointIndex) {
midPose = accum;
}
if (jointChainInfos[i].jointIndex == baseJointIndex) {
baseChainIndex = i;
basePose = accum;
}
if (jointChainInfos[i].jointIndex == baseParentJointIndex) {
baseParentPose = accum;
}
}
glm::quat poleRot = Quaternions::IDENTITY;
glm::vec3 d = basePose.trans() - topPose.trans();
float dLen = glm::length(d);
if (dLen > EPSILON) {
glm::vec3 dUnit = d / dLen;
glm::vec3 e = midPose.xformVector(target.getPoleReferenceVector());
glm::vec3 eProj = e - glm::dot(e, dUnit) * dUnit;
float eProjLen = glm::length(eProj);
const float MIN_EPROJ_LEN = 0.5f;
if (eProjLen < MIN_EPROJ_LEN) {
glm::vec3 midPoint = topPose.trans() + d * 0.5f;
e = midPose.trans() - midPoint;
eProj = e - glm::dot(e, dUnit) * dUnit;
eProjLen = glm::length(eProj);
}
glm::vec3 p = target.getPoleVector();
glm::vec3 pProj = p - glm::dot(p, dUnit) * dUnit;
float pProjLen = glm::length(pProj);
if (eProjLen > EPSILON && pProjLen > EPSILON) {
// as pProjLen become orthognal to d, reduce the amount of rotation.
float magnitude = easeOutExpo(pProjLen);
float dot = glm::clamp(glm::dot(eProj / eProjLen, pProj / pProjLen), 0.0f, 1.0f);
float theta = acosf(dot);
glm::vec3 cross = glm::cross(eProj, pProj);
const float MIN_ADJUSTMENT_ANGLE = 0.001745f; // 0.1 degree
if (theta > MIN_ADJUSTMENT_ANGLE) {
glm::vec3 axis = dUnit;
if (glm::dot(cross, dUnit) < 0) {
axis = -dUnit;
}
poleRot = glm::angleAxis(magnitude * theta, axis);
}
}
}
if (debug) {
const vec4 RED(1.0f, 0.0f, 0.0f, 1.0f);
const vec4 GREEN(0.0f, 1.0f, 0.0f, 1.0f);
const vec4 BLUE(0.0f, 0.0f, 1.0f, 1.0f);
const vec4 YELLOW(1.0f, 1.0f, 0.0f, 1.0f);
const vec4 WHITE(1.0f, 1.0f, 1.0f, 1.0f);
AnimPose geomToWorldPose = AnimPose(context.getRigToWorldMatrix() * context.getGeometryToRigMatrix());
glm::vec3 dUnit = d / dLen;
glm::vec3 e = midPose.xformVector(target.getPoleReferenceVector());
glm::vec3 eProj = e - glm::dot(e, dUnit) * dUnit;
float eProjLen = glm::length(eProj);
const float MIN_EPROJ_LEN = 0.5f;
if (eProjLen < MIN_EPROJ_LEN) {
glm::vec3 midPoint = topPose.trans() + d * 0.5f;
e = midPose.trans() - midPoint;
eProj = e - glm::dot(e, dUnit) * dUnit;
eProjLen = glm::length(eProj);
}
glm::vec3 p = target.getPoleVector();
const float PROJ_VECTOR_LEN = 10.0f;
const float POLE_VECTOR_LEN = 100.0f;
glm::vec3 midPoint = (basePose.trans() + topPose.trans()) * 0.5f;
DebugDraw::getInstance().drawRay(geomToWorldPose.xformPoint(basePose.trans()),
geomToWorldPose.xformPoint(topPose.trans()),
YELLOW);
DebugDraw::getInstance().drawRay(geomToWorldPose.xformPoint(midPoint),
geomToWorldPose.xformPoint(midPoint + PROJ_VECTOR_LEN * glm::normalize(e)),
RED);
DebugDraw::getInstance().drawRay(geomToWorldPose.xformPoint(midPoint),
geomToWorldPose.xformPoint(midPoint + POLE_VECTOR_LEN * glm::normalize(p)),
BLUE);
}
glm::quat newBaseRelRot = glm::inverse(baseParentPose.rot()) * poleRot * basePose.rot();
jointChainInfos[baseChainIndex].relRot = newBaseRelRot;
glm::quat newTopRelRot = glm::inverse(midPose.rot()) * glm::inverse(poleRot) * topPose.rot();
jointChainInfos[topChainIndex].relRot = newTopRelRot;
}
}
}
for (size_t i = 0; i < chainDepth; i++) {
_rotationAccumulators[jointChainInfos[i].jointIndex].add(jointChainInfos[i].relRot, jointChainInfos[i].weight);
_translationAccumulators[jointChainInfos[i].jointIndex].add(jointChainInfos[i].relTrans, jointChainInfos[i].weight);
}
if (debug) {
debugDrawIKChain(debugJointMap, context);
debugDrawIKChain(jointChainInfos, chainDepth, context);
}
}
@ -548,7 +699,8 @@ const std::vector<AnimInverseKinematics::SplineJointInfo>* AnimInverseKinematics
void AnimInverseKinematics::solveTargetWithSpline(const AnimContext& context, const IKTarget& target, const AnimPoseVec& absolutePoses, bool debug) {
std::map<int, DebugJoint> debugJointMap;
const size_t MAX_CHAIN_DEPTH = 30;
JointChainInfo jointChainInfos[MAX_CHAIN_DEPTH];
const int baseIndex = _hipsIndex;
@ -608,7 +760,6 @@ void AnimInverseKinematics::solveTargetWithSpline(const AnimContext& context, co
::blend(1, &absolutePoses[splineJointInfo.jointIndex], &desiredAbsPose, target.getFlexCoefficient(i), &flexedAbsPose);
AnimPose relPose = parentAbsPose.inverse() * flexedAbsPose;
_rotationAccumulators[splineJointInfo.jointIndex].add(relPose.rot(), target.getWeight());
bool constrained = false;
if (splineJointInfo.jointIndex != _hipsIndex) {
@ -632,18 +783,19 @@ void AnimInverseKinematics::solveTargetWithSpline(const AnimContext& context, co
}
}
_translationAccumulators[splineJointInfo.jointIndex].add(relPose.trans(), target.getWeight());
if (debug) {
debugJointMap[splineJointInfo.jointIndex] = DebugJoint(relPose.rot(), relPose.trans(), constrained);
}
jointChainInfos[i] = { relPose.rot(), relPose.trans(), target.getWeight(), splineJointInfo.jointIndex, constrained };
parentAbsPose = flexedAbsPose;
}
}
for (size_t i = 0; i < splineJointInfoVec->size(); i++) {
_rotationAccumulators[jointChainInfos[i].jointIndex].add(jointChainInfos[i].relRot, jointChainInfos[i].weight);
_translationAccumulators[jointChainInfos[i].jointIndex].add(jointChainInfos[i].relTrans, jointChainInfos[i].weight);
}
if (debug) {
debugDrawIKChain(debugJointMap, context);
debugDrawIKChain(jointChainInfos, splineJointInfoVec->size(), context);
}
}
@ -654,6 +806,7 @@ const AnimPoseVec& AnimInverseKinematics::evaluate(const AnimVariantMap& animVar
return _relativePoses;
}
//virtual
const AnimPoseVec& AnimInverseKinematics::overlay(const AnimVariantMap& animVars, const AnimContext& context, float dt, Triggers& triggersOut, const AnimPoseVec& underPoses) {
// allows solutionSource to be overridden by an animVar
@ -717,9 +870,9 @@ const AnimPoseVec& AnimInverseKinematics::overlay(const AnimVariantMap& animVars
float scaleFactor = ((offsetLength - MIN_HIPS_OFFSET_LENGTH) / offsetLength);
glm::vec3 hipsOffset = scaleFactor * _hipsOffset;
if (_hipsParentIndex == -1) {
_relativePoses[_hipsIndex].trans() = underPoses[_hipsIndex].trans() + hipsOffset;
_relativePoses[_hipsIndex].trans() = _relativePoses[_hipsIndex].trans() + hipsOffset;
} else {
auto absHipsPose = _skeleton->getAbsolutePose(_hipsIndex, underPoses);
auto absHipsPose = _skeleton->getAbsolutePose(_hipsIndex, _relativePoses);
absHipsPose.trans() += hipsOffset;
_relativePoses[_hipsIndex] = _skeleton->getAbsolutePose(_hipsParentIndex, _relativePoses).inverse() * absHipsPose;
}
@ -767,6 +920,7 @@ const AnimPoseVec& AnimInverseKinematics::overlay(const AnimVariantMap& animVars
{
PROFILE_RANGE_EX(simulation_animation, "ik/ccd", 0xffff00ff, 0);
preconditionRelativePosesToAvoidLimbLock(context, targets);
solve(context, targets);
}
@ -921,7 +1075,7 @@ void AnimInverseKinematics::initConstraints() {
// y |
// | |
// | O---O---O RightUpLeg
// z | | Hips2 |
// z | | Hips |
// \ | | |
// \| | |
// x -----+ O O RightLeg
@ -966,7 +1120,9 @@ void AnimInverseKinematics::initConstraints() {
if (0 == baseName.compare("Arm", Qt::CaseSensitive)) {
SwingTwistConstraint* stConstraint = new SwingTwistConstraint();
stConstraint->setReferenceRotation(_defaultRelativePoses[i].rot());
stConstraint->setTwistLimits(-PI / 2.0f, PI / 2.0f);
//stConstraint->setTwistLimits(-PI / 2.0f, PI / 2.0f);
const float TWIST_LIMIT = 5.0f * PI / 8.0f;
stConstraint->setTwistLimits(-TWIST_LIMIT, TWIST_LIMIT);
/* KEEP THIS CODE for future experimentation
// these directions are approximate swing limits in root-frame
@ -992,7 +1148,7 @@ void AnimInverseKinematics::initConstraints() {
// simple cone
std::vector<float> minDots;
const float MAX_HAND_SWING = PI / 2.0f;
const float MAX_HAND_SWING = 5.0f * PI / 8.0f;
minDots.push_back(cosf(MAX_HAND_SWING));
stConstraint->setSwingLimits(minDots);
@ -1000,7 +1156,7 @@ void AnimInverseKinematics::initConstraints() {
} else if (0 == baseName.compare("UpLeg", Qt::CaseSensitive)) {
SwingTwistConstraint* stConstraint = new SwingTwistConstraint();
stConstraint->setReferenceRotation(_defaultRelativePoses[i].rot());
stConstraint->setTwistLimits(-PI / 4.0f, PI / 4.0f);
stConstraint->setTwistLimits(-PI / 2.0f, PI / 2.0f);
std::vector<glm::vec3> swungDirections;
float deltaTheta = PI / 4.0f;
@ -1142,7 +1298,7 @@ void AnimInverseKinematics::initConstraints() {
// we determine the max/min angles by rotating the swing limit lines from parent- to child-frame
// then measure the angles to swing the yAxis into alignment
glm::vec3 hingeAxis = - mirror * Vectors::UNIT_Z;
const float MIN_ELBOW_ANGLE = 0.05f;
const float MIN_ELBOW_ANGLE = 0.0f;
const float MAX_ELBOW_ANGLE = 11.0f * PI / 12.0f;
glm::quat invReferenceRotation = glm::inverse(referenceRotation);
glm::vec3 minSwingAxis = invReferenceRotation * glm::angleAxis(MIN_ELBOW_ANGLE, hingeAxis) * Vectors::UNIT_Y;
@ -1173,8 +1329,8 @@ void AnimInverseKinematics::initConstraints() {
// we determine the max/min angles by rotating the swing limit lines from parent- to child-frame
// then measure the angles to swing the yAxis into alignment
const float MIN_KNEE_ANGLE = 0.097f; // ~5 deg
const float MAX_KNEE_ANGLE = 7.0f * PI / 8.0f;
const float MIN_KNEE_ANGLE = 0.0f;
const float MAX_KNEE_ANGLE = 7.0f * PI / 8.0f; // 157.5 deg
glm::quat invReferenceRotation = glm::inverse(referenceRotation);
glm::vec3 minSwingAxis = invReferenceRotation * glm::angleAxis(MIN_KNEE_ANGLE, hingeAxis) * Vectors::UNIT_Y;
glm::vec3 maxSwingAxis = invReferenceRotation * glm::angleAxis(MAX_KNEE_ANGLE, hingeAxis) * Vectors::UNIT_Y;
@ -1308,6 +1464,7 @@ void AnimInverseKinematics::debugDrawRelativePoses(const AnimContext& context) c
// convert relative poses to absolute
_skeleton->convertRelativePosesToAbsolute(poses);
mat4 geomToWorldMatrix = context.getRigToWorldMatrix() * context.getGeometryToRigMatrix();
const vec4 RED(1.0f, 0.0f, 0.0f, 1.0f);
@ -1338,13 +1495,14 @@ void AnimInverseKinematics::debugDrawRelativePoses(const AnimContext& context) c
}
}
void AnimInverseKinematics::debugDrawIKChain(std::map<int, DebugJoint>& debugJointMap, const AnimContext& context) const {
void AnimInverseKinematics::debugDrawIKChain(JointChainInfo* jointChainInfos, size_t numJointChainInfos, const AnimContext& context) const {
AnimPoseVec poses = _relativePoses;
// copy debug joint rotations into the relative poses
for (auto& debugJoint : debugJointMap) {
poses[debugJoint.first].rot() = debugJoint.second.relRot;
poses[debugJoint.first].trans() = debugJoint.second.relTrans;
for (size_t i = 0; i < numJointChainInfos; i++) {
const JointChainInfo& info = jointChainInfos[i];
poses[info.jointIndex].rot() = info.relRot;
poses[info.jointIndex].trans() = info.relTrans;
}
// convert relative poses to absolute
@ -1360,11 +1518,11 @@ void AnimInverseKinematics::debugDrawIKChain(std::map<int, DebugJoint>& debugJoi
// draw each pose
for (int i = 0; i < (int)poses.size(); i++) {
// only draw joints that are actually in debugJointMap, or their parents
auto iter = debugJointMap.find(i);
auto parentIter = debugJointMap.find(_skeleton->getParentIndex(i));
if (iter != debugJointMap.end() || parentIter != debugJointMap.end()) {
int parentIndex = _skeleton->getParentIndex(i);
JointChainInfo* jointInfo = nullptr;
JointChainInfo* parentJointInfo = nullptr;
lookupJointChainInfo(jointChainInfos, numJointChainInfos, i, parentIndex, &jointInfo, &parentJointInfo);
if (jointInfo && parentJointInfo) {
// transform local axes into world space.
auto pose = poses[i];
@ -1377,13 +1535,12 @@ void AnimInverseKinematics::debugDrawIKChain(std::map<int, DebugJoint>& debugJoi
DebugDraw::getInstance().drawRay(pos, pos + AXIS_LENGTH * zAxis, BLUE);
// draw line to parent
int parentIndex = _skeleton->getParentIndex(i);
if (parentIndex != -1) {
glm::vec3 parentPos = transformPoint(geomToWorldMatrix, poses[parentIndex].trans());
glm::vec4 color = GRAY;
// draw constrained joints with a RED link to their parent.
if (parentIter != debugJointMap.end() && parentIter->second.constrained) {
if (parentJointInfo->constrained) {
color = RED;
}
DebugDraw::getInstance().drawRay(pos, parentPos, color);
@ -1486,7 +1643,7 @@ void AnimInverseKinematics::debugDrawConstraints(const AnimContext& context) con
glm::vec3 worldSwungAxis = transformVectorFast(geomToWorldMatrix, parentAbsRot * refRot * swungAxis);
glm::vec3 swingTip = pos + SWING_LENGTH * worldSwungAxis;
float prevPhi = acos(swingTwistConstraint->getMinDots()[j]);
float prevPhi = acosf(swingTwistConstraint->getMinDots()[j]);
float prevTheta = theta - D_THETA;
glm::vec3 prevSwungAxis = sphericalToCartesian(prevPhi, prevTheta - PI_2);
glm::vec3 prevWorldSwungAxis = transformVectorFast(geomToWorldMatrix, parentAbsRot * refRot * prevSwungAxis);
@ -1521,6 +1678,50 @@ void AnimInverseKinematics::blendToPoses(const AnimPoseVec& targetPoses, const A
}
}
void AnimInverseKinematics::preconditionRelativePosesToAvoidLimbLock(const AnimContext& context, const std::vector<IKTarget>& targets) {
const int NUM_LIMBS = 4;
std::pair<int, int> limbs[NUM_LIMBS] = {
{_skeleton->nameToJointIndex("LeftHand"), _skeleton->nameToJointIndex("LeftArm")},
{_skeleton->nameToJointIndex("RightHand"), _skeleton->nameToJointIndex("RightArm")},
{_skeleton->nameToJointIndex("LeftFoot"), _skeleton->nameToJointIndex("LeftUpLeg")},
{_skeleton->nameToJointIndex("RightFoot"), _skeleton->nameToJointIndex("RightUpLeg")}
};
const float MIN_AXIS_LENGTH = 1.0e-4f;
for (auto& target : targets) {
if (target.getIndex() != -1) {
for (int i = 0; i < NUM_LIMBS; i++) {
if (limbs[i].first == target.getIndex()) {
int tipIndex = limbs[i].first;
int baseIndex = limbs[i].second;
// TODO: as an optimization, these poses can be computed in one pass down the chain, instead of three.
AnimPose tipPose = _skeleton->getAbsolutePose(tipIndex, _relativePoses);
AnimPose basePose = _skeleton->getAbsolutePose(baseIndex, _relativePoses);
AnimPose baseParentPose = _skeleton->getAbsolutePose(_skeleton->getParentIndex(baseIndex), _relativePoses);
// to help reduce limb locking, and to help the CCD solver converge faster
// rotate the limbs leverArm over the targetLine.
glm::vec3 targetLine = target.getTranslation() - basePose.trans();
glm::vec3 leverArm = tipPose.trans() - basePose.trans();
glm::vec3 axis = glm::cross(leverArm, targetLine);
float axisLength = glm::length(axis);
if (axisLength > MIN_AXIS_LENGTH) {
// compute angle of rotation that brings tip to target
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 newBaseRotation = glm::angleAxis(angle, axis) * basePose.rot();
// convert base rotation into relative space of base.
_relativePoses[baseIndex].rot() = glm::inverse(baseParentPose.rot()) * newBaseRotation;
}
}
}
}
}
}
void AnimInverseKinematics::initRelativePosesFromSolutionSource(SolutionSource solutionSource, const AnimPoseVec& underPoses) {
const float RELAX_BLEND_FACTOR = (1.0f / 16.0f);
const float COPY_BLEND_FACTOR = 1.0f;
@ -1531,6 +1732,10 @@ void AnimInverseKinematics::initRelativePosesFromSolutionSource(SolutionSource s
break;
case SolutionSource::RelaxToLimitCenterPoses:
blendToPoses(_limitCenterPoses, underPoses, RELAX_BLEND_FACTOR);
// special case for hips: copy over hips pose whether or not IK is enabled.
if (_hipsIndex >= 0 && _hipsIndex < (int)_relativePoses.size()) {
_relativePoses[_hipsIndex] = _limitCenterPoses[_hipsIndex];
}
break;
case SolutionSource::PreviousSolution:
// do nothing... _relativePoses is already the previous solution
@ -1540,7 +1745,7 @@ void AnimInverseKinematics::initRelativePosesFromSolutionSource(SolutionSource s
break;
case SolutionSource::LimitCenterPoses:
// essentially copy limitCenterPoses over to _relativePoses.
blendToPoses(_limitCenterPoses, underPoses, COPY_BLEND_FACTOR);
blendToPoses(underPoses, _limitCenterPoses, COPY_BLEND_FACTOR);
break;
}
}

View file

@ -26,6 +26,14 @@ class RotationConstraint;
class AnimInverseKinematics : public AnimNode {
public:
struct JointChainInfo {
glm::quat relRot;
glm::vec3 relTrans;
float weight;
int jointIndex;
bool constrained;
};
explicit AnimInverseKinematics(const QString& id);
virtual ~AnimInverseKinematics() override;
@ -34,7 +42,8 @@ public:
void computeAbsolutePoses(AnimPoseVec& absolutePoses) const;
void setTargetVars(const QString& jointName, const QString& positionVar, const QString& rotationVar,
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);
virtual const AnimPoseVec& evaluate(const AnimVariantMap& animVars, const AnimContext& context, float dt, AnimNode::Triggers& triggersOut) override;
virtual const AnimPoseVec& overlay(const AnimVariantMap& animVars, const AnimContext& context, float dt, Triggers& triggersOut, const AnimPoseVec& underPoses) override;
@ -67,19 +76,13 @@ protected:
void solveTargetWithCCD(const AnimContext& context, const IKTarget& target, const AnimPoseVec& absolutePoses, bool debug);
void solveTargetWithSpline(const AnimContext& context, const IKTarget& target, const AnimPoseVec& absolutePoses, bool debug);
virtual void setSkeletonInternal(AnimSkeleton::ConstPointer skeleton) override;
struct DebugJoint {
DebugJoint() : relRot(), constrained(false) {}
DebugJoint(const glm::quat& relRotIn, const glm::vec3& relTransIn, bool constrainedIn) : relRot(relRotIn), relTrans(relTransIn), constrained(constrainedIn) {}
glm::quat relRot;
glm::vec3 relTrans;
bool constrained;
};
void debugDrawIKChain(std::map<int, DebugJoint>& debugJointMap, const AnimContext& context) const;
void debugDrawIKChain(JointChainInfo* jointChainInfos, size_t numJointChainInfos, const AnimContext& context) const;
void debugDrawRelativePoses(const AnimContext& context) const;
void debugDrawConstraints(const AnimContext& context) const;
void debugDrawSpineSplines(const AnimContext& context, const std::vector<IKTarget>& targets) const;
void initRelativePosesFromSolutionSource(SolutionSource solutionSource, const AnimPoseVec& underPose);
void blendToPoses(const AnimPoseVec& targetPoses, const AnimPoseVec& underPose, float blendFactor);
void preconditionRelativePosesToAvoidLimbLock(const AnimContext& context, const std::vector<IKTarget>& targets);
// used to pre-compute information about each joint influeced by a spline IK target.
struct SplineJointInfo {
@ -107,7 +110,8 @@ protected:
enum FlexCoefficients { MAX_FLEX_COEFFICIENTS = 10 };
struct IKTargetVar {
IKTargetVar(const QString& jointNameIn, const QString& positionVarIn, const QString& rotationVarIn,
const QString& typeVarIn, const QString& weightVarIn, float weightIn, const std::vector<float>& flexCoefficientsIn);
const QString& typeVarIn, const QString& weightVarIn, float weightIn, const std::vector<float>& flexCoefficientsIn,
const QString& poleVectorEnabledVar, const QString& poleReferenceVectorVar, const QString& poleVectorVar);
IKTargetVar(const IKTargetVar& orig);
QString jointName;
@ -115,6 +119,9 @@ protected:
QString rotationVar;
QString typeVar;
QString weightVar;
QString poleVectorEnabledVar;
QString poleReferenceVectorVar;
QString poleVectorVar;
float weight;
float flexCoefficients[MAX_FLEX_COEFFICIENTS];
size_t numFlexCoefficients;

View file

@ -44,6 +44,7 @@ public:
StateMachine,
Manipulator,
InverseKinematics,
DefaultPose,
NumTypes
};
using Pointer = std::shared_ptr<AnimNode>;

View file

@ -23,6 +23,7 @@
#include "AnimStateMachine.h"
#include "AnimManipulator.h"
#include "AnimInverseKinematics.h"
#include "AnimDefaultPose.h"
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);
@ -35,6 +36,7 @@ static AnimNode::Pointer loadOverlayNode(const QJsonObject& jsonObj, const QStri
static AnimNode::Pointer loadStateMachineNode(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 loadDefaultPoseNode(const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl);
// called after children have been loaded
// returns node on success, nullptr on failure.
@ -50,6 +52,7 @@ static const char* animNodeTypeToString(AnimNode::Type type) {
case AnimNode::Type::StateMachine: return "stateMachine";
case AnimNode::Type::Manipulator: return "manipulator";
case AnimNode::Type::InverseKinematics: return "inverseKinematics";
case AnimNode::Type::DefaultPose: return "defaultPose";
case AnimNode::Type::NumTypes: return nullptr;
};
return nullptr;
@ -109,6 +112,7 @@ static NodeLoaderFunc animNodeTypeToLoaderFunc(AnimNode::Type type) {
case AnimNode::Type::StateMachine: return loadStateMachineNode;
case AnimNode::Type::Manipulator: return loadManipulatorNode;
case AnimNode::Type::InverseKinematics: return loadInverseKinematicsNode;
case AnimNode::Type::DefaultPose: return loadDefaultPoseNode;
case AnimNode::Type::NumTypes: return nullptr;
};
return nullptr;
@ -123,6 +127,7 @@ static NodeProcessFunc animNodeTypeToProcessFunc(AnimNode::Type type) {
case AnimNode::Type::StateMachine: return processStateMachineNode;
case AnimNode::Type::Manipulator: return processDoNothing;
case AnimNode::Type::InverseKinematics: return processDoNothing;
case AnimNode::Type::DefaultPose: return processDoNothing;
case AnimNode::Type::NumTypes: return nullptr;
};
return nullptr;
@ -347,7 +352,8 @@ static const char* boneSetStrings[AnimOverlay::NumBoneSets] = {
"empty",
"leftHand",
"rightHand",
"hipsOnly"
"hipsOnly",
"bothFeet"
};
static AnimOverlay::BoneSet stringToBoneSetEnum(const QString& str) {
@ -479,6 +485,9 @@ AnimNode::Pointer loadInverseKinematicsNode(const QJsonObject& jsonObj, const QS
READ_OPTIONAL_STRING(typeVar, targetObj);
READ_OPTIONAL_STRING(weightVar, targetObj);
READ_OPTIONAL_FLOAT(weight, targetObj, 1.0f);
READ_OPTIONAL_STRING(poleVectorEnabledVar, targetObj);
READ_OPTIONAL_STRING(poleReferenceVectorVar, targetObj);
READ_OPTIONAL_STRING(poleVectorVar, targetObj);
auto flexCoefficientsValue = targetObj.value("flexCoefficients");
if (!flexCoefficientsValue.isArray()) {
@ -491,7 +500,7 @@ AnimNode::Pointer loadInverseKinematicsNode(const QJsonObject& jsonObj, const QS
flexCoefficients.push_back((float)value.toDouble());
}
node->setTargetVars(jointName, positionVar, rotationVar, typeVar, weightVar, weight, flexCoefficients);
node->setTargetVars(jointName, positionVar, rotationVar, typeVar, weightVar, weight, flexCoefficients, poleVectorEnabledVar, poleReferenceVectorVar, poleVectorVar);
};
READ_OPTIONAL_STRING(solutionSource, jsonObj);
@ -514,6 +523,11 @@ AnimNode::Pointer loadInverseKinematicsNode(const QJsonObject& jsonObj, const QS
return node;
}
static AnimNode::Pointer loadDefaultPoseNode(const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl) {
auto node = std::make_shared<AnimDefaultPose>(id);
return node;
}
void buildChildMap(std::map<QString, int>& map, AnimNode::Pointer node) {
for (int i = 0; i < (int)node->getChildCount(); ++i) {
map.insert(std::pair<QString, int>(node->getChild(i)->getID(), i));

View file

@ -35,6 +35,7 @@ void AnimOverlay::buildBoneSet(BoneSet boneSet) {
case LeftHandBoneSet: buildLeftHandBoneSet(); break;
case RightHandBoneSet: buildRightHandBoneSet(); break;
case HipsOnlyBoneSet: buildHipsOnlyBoneSet(); break;
case BothFeetBoneSet: buildBothFeetBoneSet(); break;
default:
case EmptyBoneSet: buildEmptyBoneSet(); break;
}
@ -196,6 +197,20 @@ void AnimOverlay::buildHipsOnlyBoneSet() {
_boneSetVec[hipsJoint] = 1.0f;
}
void AnimOverlay::buildBothFeetBoneSet() {
assert(_skeleton);
buildEmptyBoneSet();
int rightFoot = _skeleton->nameToJointIndex("RightFoot");
for_each_child_joint(_skeleton, rightFoot, [&](int i) {
_boneSetVec[i] = 1.0f;
});
int leftFoot = _skeleton->nameToJointIndex("LeftFoot");
for_each_child_joint(_skeleton, leftFoot, [&](int i) {
_boneSetVec[i] = 1.0f;
});
}
// for AnimDebugDraw rendering
const AnimPoseVec& AnimOverlay::getPosesInternal() const {
return _poses;

View file

@ -38,6 +38,7 @@ public:
LeftHandBoneSet,
RightHandBoneSet,
HipsOnlyBoneSet,
BothFeetBoneSet,
NumBoneSets
};
@ -77,6 +78,7 @@ public:
void buildLeftHandBoneSet();
void buildRightHandBoneSet();
void buildHipsOnlyBoneSet();
void buildBothFeetBoneSet();
// no copies
AnimOverlay(const AnimOverlay&) = delete;

View file

@ -21,6 +21,8 @@ class AnimPose {
public:
AnimPose() {}
explicit AnimPose(const glm::mat4& mat);
explicit AnimPose(const glm::quat& rotIn) : _scale(1.0f), _rot(rotIn), _trans(0.0f) {}
AnimPose(const glm::quat& rotIn, const glm::vec3& transIn) : _scale(1.0f), _rot(rotIn), _trans(transIn) {}
AnimPose(const glm::vec3& scaleIn, const glm::quat& rotIn, const glm::vec3& transIn) : _scale(scaleIn), _rot(rotIn), _trans(transIn) {}
static const AnimPose identity;

View file

@ -76,11 +76,11 @@ const QString& AnimSkeleton::getJointName(int jointIndex) const {
return _joints[jointIndex].name;
}
AnimPose AnimSkeleton::getAbsolutePose(int jointIndex, const AnimPoseVec& poses) const {
if (jointIndex < 0 || jointIndex >= (int)poses.size() || jointIndex >= _jointsSize) {
AnimPose AnimSkeleton::getAbsolutePose(int jointIndex, const AnimPoseVec& relativePoses) const {
if (jointIndex < 0 || jointIndex >= (int)relativePoses.size() || jointIndex >= _jointsSize) {
return AnimPose::identity;
} else {
return getAbsolutePose(_joints[jointIndex].parentIndex, poses) * poses[jointIndex];
return getAbsolutePose(_joints[jointIndex].parentIndex, relativePoses) * relativePoses[jointIndex];
}
}

View file

@ -50,7 +50,7 @@ public:
int getParentIndex(int jointIndex) const;
AnimPose getAbsolutePose(int jointIndex, const AnimPoseVec& poses) const;
AnimPose getAbsolutePose(int jointIndex, const AnimPoseVec& relativePoses) const;
void convertRelativePosesToAbsolute(AnimPoseVec& poses) const;
void convertAbsolutePosesToRelative(AnimPoseVec& poses) const;

View file

@ -144,38 +144,3 @@ void Animation::animationParseError(int error, QString str) {
finishedLoading(false);
}
AnimationDetails::AnimationDetails() :
role(), url(), fps(0.0f), priority(0.0f), loop(false), hold(false),
startAutomatically(false), firstFrame(0.0f), lastFrame(0.0f), running(false), currentFrame(0.0f)
{
}
AnimationDetails::AnimationDetails(QString role, QUrl url, float fps, float priority, bool loop,
bool hold, bool startAutomatically, float firstFrame, float lastFrame, bool running, float currentFrame) :
role(role), url(url), fps(fps), priority(priority), loop(loop), hold(hold),
startAutomatically(startAutomatically), firstFrame(firstFrame), lastFrame(lastFrame),
running(running), currentFrame(currentFrame)
{
}
QScriptValue animationDetailsToScriptValue(QScriptEngine* engine, const AnimationDetails& details) {
QScriptValue obj = engine->newObject();
obj.setProperty("role", details.role);
obj.setProperty("url", details.url.toString());
obj.setProperty("fps", details.fps);
obj.setProperty("priority", details.priority);
obj.setProperty("loop", details.loop);
obj.setProperty("hold", details.hold);
obj.setProperty("startAutomatically", details.startAutomatically);
obj.setProperty("firstFrame", details.firstFrame);
obj.setProperty("lastFrame", details.lastFrame);
obj.setProperty("running", details.running);
obj.setProperty("currentFrame", details.currentFrame);
return obj;
}
void animationDetailsFromScriptValue(const QScriptValue& object, AnimationDetails& details) {
// nothing for now...
}

View file

@ -107,26 +107,5 @@ private:
QByteArray _data;
};
class AnimationDetails {
public:
AnimationDetails();
AnimationDetails(QString role, QUrl url, float fps, float priority, bool loop,
bool hold, bool startAutomatically, float firstFrame, float lastFrame, bool running, float currentFrame);
QString role;
QUrl url;
float fps;
float priority;
bool loop;
bool hold;
bool startAutomatically;
float firstFrame;
float lastFrame;
bool running;
float currentFrame;
};
Q_DECLARE_METATYPE(AnimationDetails);
QScriptValue animationDetailsToScriptValue(QScriptEngine* engine, const AnimationDetails& event);
void animationDetailsFromScriptValue(const QScriptValue& object, AnimationDetails& event);
#endif // hifi_AnimationCache_h

View file

@ -30,10 +30,16 @@ public:
const glm::vec3& getTranslation() const { return _pose.trans(); }
const glm::quat& getRotation() const { return _pose.rot(); }
const AnimPose& getPose() const { return _pose; }
glm::vec3 getPoleVector() const { return _poleVector; }
glm::vec3 getPoleReferenceVector() const { return _poleReferenceVector; }
bool getPoleVectorEnabled() const { return _poleVectorEnabled; }
int getIndex() const { return _index; }
Type getType() const { return _type; }
void setPose(const glm::quat& rotation, const glm::vec3& translation);
void setPoleVector(const glm::vec3& poleVector) { _poleVector = poleVector; }
void setPoleReferenceVector(const glm::vec3& poleReferenceVector) { _poleReferenceVector = poleReferenceVector; }
void setPoleVectorEnabled(bool poleVectorEnabled) { _poleVectorEnabled = poleVectorEnabled; }
void setIndex(int index) { _index = index; }
void setType(int);
void setFlexCoefficients(size_t numFlexCoefficientsIn, const float* flexCoefficientsIn);
@ -46,8 +52,11 @@ public:
private:
AnimPose _pose;
int _index{-1};
Type _type{Type::RotationAndPosition};
glm::vec3 _poleVector;
glm::vec3 _poleReferenceVector;
bool _poleVectorEnabled { false };
int _index { -1 };
Type _type { Type::RotationAndPosition };
float _weight;
float _flexCoefficients[MAX_FLEX_COEFFICIENTS];
size_t _numFlexCoefficients;

View file

@ -27,6 +27,7 @@
#include "AnimationLogging.h"
#include "AnimClip.h"
#include "AnimInverseKinematics.h"
#include "AnimOverlay.h"
#include "AnimSkeleton.h"
#include "AnimUtil.h"
#include "IKTarget.h"
@ -479,12 +480,6 @@ bool Rig::getAbsoluteJointPoseInRigFrame(int jointIndex, AnimPose& returnPose) c
}
}
bool Rig::getJointCombinedRotation(int jointIndex, glm::quat& result, const glm::quat& rotation) const {
// AJT: TODO: used by attachments
ASSERT(false);
return false;
}
void Rig::calcAnimAlpha(float speed, const std::vector<float>& referenceSpeeds, float* alphaOut) const {
ASSERT(referenceSpeeds.size() > 0);
@ -950,6 +945,7 @@ void Rig::updateAnimations(float deltaTime, const glm::mat4& rootTransform, cons
// evaluate the animation
AnimNode::Triggers triggersOut;
_internalPoseSet._relativePoses = _animNode->evaluate(_animVars, context, deltaTime, triggersOut);
if ((int)_internalPoseSet._relativePoses.size() != _animSkeleton->getNumJoints()) {
// animations haven't fully loaded yet.
@ -1015,46 +1011,6 @@ glm::quat Rig::getJointDefaultRotationInParentFrame(int jointIndex) {
return glm::quat();
}
void Rig::updateFromHeadParameters(const HeadParameters& params, float dt) {
updateHeadAnimVars(params);
_animVars.set("isTalking", params.isTalking);
_animVars.set("notIsTalking", !params.isTalking);
if (params.hipsEnabled) {
_animVars.set("solutionSource", (int)AnimInverseKinematics::SolutionSource::RelaxToLimitCenterPoses);
_animVars.set("hipsType", (int)IKTarget::Type::RotationAndPosition);
_animVars.set("hipsPosition", extractTranslation(params.hipsMatrix));
_animVars.set("hipsRotation", glmExtractRotation(params.hipsMatrix));
} else {
_animVars.set("solutionSource", (int)AnimInverseKinematics::SolutionSource::RelaxToUnderPoses);
_animVars.set("hipsType", (int)IKTarget::Type::Unknown);
}
if (params.hipsEnabled && params.spine2Enabled) {
_animVars.set("spine2Type", (int)IKTarget::Type::Spline);
_animVars.set("spine2Position", extractTranslation(params.spine2Matrix));
_animVars.set("spine2Rotation", glmExtractRotation(params.spine2Matrix));
} else {
_animVars.set("spine2Type", (int)IKTarget::Type::Unknown);
}
if (params.leftArmEnabled) {
_animVars.set("leftArmType", (int)IKTarget::Type::RotationAndPosition);
_animVars.set("leftArmPosition", params.leftArmPosition);
_animVars.set("leftArmRotation", params.leftArmRotation);
} else {
_animVars.set("leftArmType", (int)IKTarget::Type::Unknown);
}
if (params.rightArmEnabled) {
_animVars.set("rightArmType", (int)IKTarget::Type::RotationAndPosition);
_animVars.set("rightArmPosition", params.rightArmPosition);
_animVars.set("rightArmRotation", params.rightArmRotation);
} else {
_animVars.set("rightArmType", (int)IKTarget::Type::Unknown);
}
}
void Rig::updateFromEyeParameters(const EyeParameters& params) {
updateEyeJoint(params.leftEyeJointIndex, params.modelTranslation, params.modelRotation, params.eyeLookAt, params.eyeSaccade);
@ -1086,12 +1042,12 @@ void Rig::computeHeadFromHMD(const AnimPose& hmdPose, glm::vec3& headPositionOut
headOrientationOut = hmdOrientation;
}
void Rig::updateHeadAnimVars(const HeadParameters& params) {
void Rig::updateHead(bool headEnabled, bool hipsEnabled, const AnimPose& headPose) {
if (_animSkeleton) {
if (params.headEnabled) {
_animVars.set("headPosition", params.rigHeadPosition);
_animVars.set("headRotation", params.rigHeadOrientation);
if (params.hipsEnabled) {
if (headEnabled) {
_animVars.set("headPosition", headPose.trans());
_animVars.set("headRotation", headPose.rot());
if (hipsEnabled) {
// Since there is an explicit hips ik target, switch the head to use the more flexible Spline IK chain type.
// this will allow the spine to compress/expand and bend more natrually, ensuring that it can reach the head target position.
_animVars.set("headType", (int)IKTarget::Type::Spline);
@ -1104,12 +1060,271 @@ void Rig::updateHeadAnimVars(const HeadParameters& params) {
}
} else {
_animVars.unset("headPosition");
_animVars.set("headRotation", params.rigHeadOrientation);
_animVars.set("headRotation", headPose.rot());
_animVars.set("headType", (int)IKTarget::Type::RotationOnly);
}
}
}
void Rig::updateHands(bool leftHandEnabled, bool rightHandEnabled, bool hipsEnabled, bool leftArmEnabled, bool rightArmEnabled, float dt,
const AnimPose& leftHandPose, const AnimPose& rightHandPose,
float bodyCapsuleRadius, float bodyCapsuleHalfHeight, const glm::vec3& bodyCapsuleLocalOffset) {
// Use this capsule to represent the avatar body.
int hipsIndex = indexOfJoint("Hips");
glm::vec3 hipsTrans;
if (hipsIndex >= 0) {
hipsTrans = _internalPoseSet._absolutePoses[hipsIndex].trans();
}
const glm::vec3 bodyCapsuleCenter = hipsTrans - bodyCapsuleLocalOffset;
const glm::vec3 bodyCapsuleStart = bodyCapsuleCenter - glm::vec3(0, bodyCapsuleHalfHeight, 0);
const glm::vec3 bodyCapsuleEnd = bodyCapsuleCenter + glm::vec3(0, bodyCapsuleHalfHeight, 0);
const float HAND_RADIUS = 0.05f;
const float RELAX_DURATION = 0.6f;
const float CONTROL_DURATION = 0.4f;
const bool TO_CONTROLLED = true;
const bool FROM_CONTROLLED = false;
const bool LEFT_HAND = true;
const bool RIGHT_HAND = false;
const float ELBOW_POLE_VECTOR_BLEND_FACTOR = 0.95f;
if (leftHandEnabled) {
if (!_isLeftHandControlled) {
_leftHandControlTimeRemaining = CONTROL_DURATION;
_isLeftHandControlled = true;
}
glm::vec3 handPosition = leftHandPose.trans();
glm::quat handRotation = leftHandPose.rot();
if (_leftHandControlTimeRemaining > 0.0f) {
// Move hand from non-controlled position to controlled position.
_leftHandControlTimeRemaining = std::max(_leftHandControlTimeRemaining - dt, 0.0f);
AnimPose handPose(Vectors::ONE, handRotation, handPosition);
if (transitionHandPose(_leftHandControlTimeRemaining, CONTROL_DURATION, handPose,
LEFT_HAND, TO_CONTROLLED, handPose)) {
handPosition = handPose.trans();
handRotation = handPose.rot();
}
}
if (!hipsEnabled) {
// prevent the hand IK targets from intersecting the body capsule
glm::vec3 displacement;
if (findSphereCapsulePenetration(handPosition, HAND_RADIUS, bodyCapsuleStart, bodyCapsuleEnd, bodyCapsuleRadius, displacement)) {
handPosition -= displacement;
}
}
_animVars.set("leftHandPosition", handPosition);
_animVars.set("leftHandRotation", handRotation);
_animVars.set("leftHandType", (int)IKTarget::Type::RotationAndPosition);
_lastLeftHandControlledPose = AnimPose(Vectors::ONE, handRotation, handPosition);
_isLeftHandControlled = true;
// compute pole vector
int handJointIndex = _animSkeleton->nameToJointIndex("LeftHand");
int armJointIndex = _animSkeleton->nameToJointIndex("LeftArm");
int elbowJointIndex = _animSkeleton->nameToJointIndex("LeftForeArm");
if (!leftArmEnabled && elbowJointIndex >= 0 && armJointIndex >= 0 && elbowJointIndex >= 0) {
glm::vec3 poleVector = calculateElbowPoleVector(handJointIndex, elbowJointIndex, armJointIndex, hipsIndex, true);
// smooth toward desired pole vector from previous pole vector... to reduce jitter
if (!_prevLeftHandPoleVectorValid) {
_prevLeftHandPoleVectorValid = true;
_prevLeftHandPoleVector = poleVector;
}
glm::quat deltaRot = rotationBetween(_prevLeftHandPoleVector, poleVector);
glm::quat smoothDeltaRot = safeMix(deltaRot, Quaternions::IDENTITY, ELBOW_POLE_VECTOR_BLEND_FACTOR);
_prevLeftHandPoleVector = smoothDeltaRot * _prevLeftHandPoleVector;
_animVars.set("leftHandPoleVectorEnabled", true);
_animVars.set("leftHandPoleReferenceVector", Vectors::UNIT_X);
_animVars.set("leftHandPoleVector", _prevLeftHandPoleVector);
} else {
_prevLeftHandPoleVectorValid = false;
_animVars.set("leftHandPoleVectorEnabled", false);
}
} else {
_prevLeftHandPoleVectorValid = false;
_animVars.set("leftHandPoleVectorEnabled", false);
if (_isLeftHandControlled) {
_leftHandRelaxTimeRemaining = RELAX_DURATION;
_isLeftHandControlled = false;
}
if (_leftHandRelaxTimeRemaining > 0.0f) {
// Move hand from controlled position to non-controlled position.
_leftHandRelaxTimeRemaining = std::max(_leftHandRelaxTimeRemaining - dt, 0.0f);
AnimPose handPose;
if (transitionHandPose(_leftHandRelaxTimeRemaining, RELAX_DURATION, _lastLeftHandControlledPose,
LEFT_HAND, FROM_CONTROLLED, handPose)) {
_animVars.set("leftHandPosition", handPose.trans());
_animVars.set("leftHandRotation", handPose.rot());
_animVars.set("leftHandType", (int)IKTarget::Type::RotationAndPosition);
}
} else {
_animVars.unset("leftHandPosition");
_animVars.unset("leftHandRotation");
_animVars.set("leftHandType", (int)IKTarget::Type::HipsRelativeRotationAndPosition);
}
}
if (rightHandEnabled) {
if (!_isRightHandControlled) {
_rightHandControlTimeRemaining = CONTROL_DURATION;
_isRightHandControlled = true;
}
glm::vec3 handPosition = rightHandPose.trans();
glm::quat handRotation = rightHandPose.rot();
if (_rightHandControlTimeRemaining > 0.0f) {
// Move hand from non-controlled position to controlled position.
_rightHandControlTimeRemaining = std::max(_rightHandControlTimeRemaining - dt, 0.0f);
AnimPose handPose(Vectors::ONE, handRotation, handPosition);
if (transitionHandPose(_rightHandControlTimeRemaining, CONTROL_DURATION, handPose, RIGHT_HAND, TO_CONTROLLED, handPose)) {
handPosition = handPose.trans();
handRotation = handPose.rot();
}
}
if (!hipsEnabled) {
// prevent the hand IK targets from intersecting the body capsule
glm::vec3 displacement;
if (findSphereCapsulePenetration(handPosition, HAND_RADIUS, bodyCapsuleStart, bodyCapsuleEnd, bodyCapsuleRadius, displacement)) {
handPosition -= displacement;
}
}
_animVars.set("rightHandPosition", handPosition);
_animVars.set("rightHandRotation", handRotation);
_animVars.set("rightHandType", (int)IKTarget::Type::RotationAndPosition);
_lastRightHandControlledPose = AnimPose(Vectors::ONE, handRotation, handPosition);
_isRightHandControlled = true;
// compute pole vector
int handJointIndex = _animSkeleton->nameToJointIndex("RightHand");
int armJointIndex = _animSkeleton->nameToJointIndex("RightArm");
int elbowJointIndex = _animSkeleton->nameToJointIndex("RightForeArm");
if (!rightArmEnabled && elbowJointIndex >= 0 && armJointIndex >= 0 && elbowJointIndex >= 0) {
glm::vec3 poleVector = calculateElbowPoleVector(handJointIndex, elbowJointIndex, armJointIndex, hipsIndex, false);
// smooth toward desired pole vector from previous pole vector... to reduce jitter
if (!_prevRightHandPoleVectorValid) {
_prevRightHandPoleVectorValid = true;
_prevRightHandPoleVector = poleVector;
}
glm::quat deltaRot = rotationBetween(_prevRightHandPoleVector, poleVector);
glm::quat smoothDeltaRot = safeMix(deltaRot, Quaternions::IDENTITY, ELBOW_POLE_VECTOR_BLEND_FACTOR);
_prevRightHandPoleVector = smoothDeltaRot * _prevRightHandPoleVector;
_animVars.set("rightHandPoleVectorEnabled", true);
_animVars.set("rightHandPoleReferenceVector", -Vectors::UNIT_X);
_animVars.set("rightHandPoleVector", _prevRightHandPoleVector);
} else {
_prevRightHandPoleVectorValid = false;
_animVars.set("rightHandPoleVectorEnabled", false);
}
} else {
_prevRightHandPoleVectorValid = false;
_animVars.set("rightHandPoleVectorEnabled", false);
if (_isRightHandControlled) {
_rightHandRelaxTimeRemaining = RELAX_DURATION;
_isRightHandControlled = false;
}
if (_rightHandRelaxTimeRemaining > 0.0f) {
// Move hand from controlled position to non-controlled position.
_rightHandRelaxTimeRemaining = std::max(_rightHandRelaxTimeRemaining - dt, 0.0f);
AnimPose handPose;
if (transitionHandPose(_rightHandRelaxTimeRemaining, RELAX_DURATION, _lastRightHandControlledPose, RIGHT_HAND, FROM_CONTROLLED, handPose)) {
_animVars.set("rightHandPosition", handPose.trans());
_animVars.set("rightHandRotation", handPose.rot());
_animVars.set("rightHandType", (int)IKTarget::Type::RotationAndPosition);
}
} else {
_animVars.unset("rightHandPosition");
_animVars.unset("rightHandRotation");
_animVars.set("rightHandType", (int)IKTarget::Type::HipsRelativeRotationAndPosition);
}
}
}
void Rig::updateFeet(bool leftFootEnabled, bool rightFootEnabled, const AnimPose& leftFootPose, const AnimPose& rightFootPose) {
const float KNEE_POLE_VECTOR_BLEND_FACTOR = 0.95f;
int hipsIndex = indexOfJoint("Hips");
if (leftFootEnabled) {
_animVars.set("leftFootPosition", leftFootPose.trans());
_animVars.set("leftFootRotation", leftFootPose.rot());
_animVars.set("leftFootType", (int)IKTarget::Type::RotationAndPosition);
int footJointIndex = _animSkeleton->nameToJointIndex("LeftFoot");
int kneeJointIndex = _animSkeleton->nameToJointIndex("LeftLeg");
int upLegJointIndex = _animSkeleton->nameToJointIndex("LeftUpLeg");
glm::vec3 poleVector = calculateKneePoleVector(footJointIndex, kneeJointIndex, upLegJointIndex, hipsIndex, leftFootPose);
// smooth toward desired pole vector from previous pole vector... to reduce jitter
if (!_prevLeftFootPoleVectorValid) {
_prevLeftFootPoleVectorValid = true;
_prevLeftFootPoleVector = poleVector;
}
glm::quat deltaRot = rotationBetween(_prevLeftFootPoleVector, poleVector);
glm::quat smoothDeltaRot = safeMix(deltaRot, Quaternions::IDENTITY, KNEE_POLE_VECTOR_BLEND_FACTOR);
_prevLeftFootPoleVector = smoothDeltaRot * _prevLeftFootPoleVector;
_animVars.set("leftFootPoleVectorEnabled", true);
_animVars.set("leftFootPoleReferenceVector", Vectors::UNIT_Z);
_animVars.set("leftFootPoleVector", _prevLeftFootPoleVector);
} else {
_animVars.unset("leftFootPosition");
_animVars.unset("leftFootRotation");
_animVars.set("leftFootType", (int)IKTarget::Type::RotationAndPosition);
_animVars.set("leftFootPoleVectorEnabled", false);
_prevLeftFootPoleVectorValid = false;
}
if (rightFootEnabled) {
_animVars.set("rightFootPosition", rightFootPose.trans());
_animVars.set("rightFootRotation", rightFootPose.rot());
_animVars.set("rightFootType", (int)IKTarget::Type::RotationAndPosition);
int footJointIndex = _animSkeleton->nameToJointIndex("RightFoot");
int kneeJointIndex = _animSkeleton->nameToJointIndex("RightLeg");
int upLegJointIndex = _animSkeleton->nameToJointIndex("RightUpLeg");
glm::vec3 poleVector = calculateKneePoleVector(footJointIndex, kneeJointIndex, upLegJointIndex, hipsIndex, rightFootPose);
// smooth toward desired pole vector from previous pole vector... to reduce jitter
if (!_prevRightFootPoleVectorValid) {
_prevRightFootPoleVectorValid = true;
_prevRightFootPoleVector = poleVector;
}
glm::quat deltaRot = rotationBetween(_prevRightFootPoleVector, poleVector);
glm::quat smoothDeltaRot = safeMix(deltaRot, Quaternions::IDENTITY, KNEE_POLE_VECTOR_BLEND_FACTOR);
_prevRightFootPoleVector = smoothDeltaRot * _prevRightFootPoleVector;
_animVars.set("rightFootPoleVectorEnabled", true);
_animVars.set("rightFootPoleReferenceVector", Vectors::UNIT_Z);
_animVars.set("rightFootPoleVector", _prevRightFootPoleVector);
} else {
_animVars.unset("rightFootPosition");
_animVars.unset("rightFootRotation");
_animVars.set("rightFootPoleVectorEnabled", false);
_animVars.set("rightFootType", (int)IKTarget::Type::RotationAndPosition);
}
}
void Rig::updateEyeJoint(int index, const glm::vec3& modelTranslation, const glm::quat& modelRotation, const glm::vec3& lookAtSpot, const glm::vec3& saccade) {
// TODO: does not properly handle avatar scale.
@ -1145,162 +1360,153 @@ void Rig::updateEyeJoint(int index, const glm::vec3& modelTranslation, const glm
}
}
void Rig::updateFromHandAndFeetParameters(const HandAndFeetParameters& params, float dt) {
if (_animSkeleton && _animNode) {
const float HAND_RADIUS = 0.05f;
int hipsIndex = indexOfJoint("Hips");
glm::vec3 hipsTrans;
if (hipsIndex >= 0) {
hipsTrans = _internalPoseSet._absolutePoses[hipsIndex].trans();
}
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));
}
// Use this capsule to represent the avatar body.
const float bodyCapsuleRadius = params.bodyCapsuleRadius;
const glm::vec3 bodyCapsuleCenter = hipsTrans - params.bodyCapsuleLocalOffset;
const glm::vec3 bodyCapsuleStart = bodyCapsuleCenter - glm::vec3(0, params.bodyCapsuleHalfHeight, 0);
const glm::vec3 bodyCapsuleEnd = bodyCapsuleCenter + glm::vec3(0, params.bodyCapsuleHalfHeight, 0);
glm::vec3 Rig::calculateElbowPoleVector(int handIndex, int elbowIndex, int armIndex, int hipsIndex, bool isLeft) const {
AnimPose hipsPose = _externalPoseSet._absolutePoses[hipsIndex];
AnimPose handPose = _externalPoseSet._absolutePoses[handIndex];
AnimPose elbowPose = _externalPoseSet._absolutePoses[elbowIndex];
AnimPose armPose = _externalPoseSet._absolutePoses[armIndex];
// TODO: add isHipsEnabled
bool bodySensorTrackingEnabled = params.isLeftFootEnabled || params.isRightFootEnabled;
// ray from hand to arm.
glm::vec3 d = glm::normalize(handPose.trans() - armPose.trans());
const float RELAX_DURATION = 0.6f;
const float CONTROL_DURATION = 0.4f;
const bool TO_CONTROLLED = true;
const bool FROM_CONTROLLED = false;
const bool LEFT_HAND = true;
const bool RIGHT_HAND = false;
float sign = isLeft ? 1.0f : -1.0f;
if (params.isLeftEnabled) {
if (!_isLeftHandControlled) {
_leftHandControlTimeRemaining = CONTROL_DURATION;
_isLeftHandControlled = true;
}
// form a plane normal to the hips x-axis.
glm::vec3 n = hipsPose.rot() * Vectors::UNIT_X;
glm::vec3 y = hipsPose.rot() * Vectors::UNIT_Y;
glm::vec3 handPosition = params.leftPosition;
glm::quat handRotation = params.leftOrientation;
// project d onto this plane
glm::vec3 dProj = d - glm::dot(d, n) * n;
if (_leftHandControlTimeRemaining > 0.0f) {
// Move hand from non-controlled position to controlled position.
_leftHandControlTimeRemaining = std::max(_leftHandControlTimeRemaining - dt, 0.0f);
AnimPose handPose(Vectors::ONE, handRotation, handPosition);
if (transitionHandPose(_leftHandControlTimeRemaining, CONTROL_DURATION, handPose, LEFT_HAND, TO_CONTROLLED,
handPose)) {
handPosition = handPose.trans();
handRotation = handPose.rot();
}
}
// give dProj a bit of offset away from the body.
float avatarScale = extractUniformScale(_modelOffset);
const float LATERAL_OFFSET = 1.0f * avatarScale;
const float VERTICAL_OFFSET = -0.333f * avatarScale;
glm::vec3 dProjWithOffset = dProj + sign * LATERAL_OFFSET * n + y * VERTICAL_OFFSET;
if (!bodySensorTrackingEnabled) {
// prevent the hand IK targets from intersecting the body capsule
glm::vec3 displacement;
if (findSphereCapsulePenetration(handPosition, HAND_RADIUS, bodyCapsuleStart, bodyCapsuleEnd, bodyCapsuleRadius, displacement)) {
handPosition -= displacement;
}
}
// rotate dProj by 90 degrees to get the poleVector.
glm::vec3 poleVector = glm::angleAxis(PI / 2.0f, n) * dProjWithOffset;
_animVars.set("leftHandPosition", handPosition);
_animVars.set("leftHandRotation", handRotation);
_animVars.set("leftHandType", (int)IKTarget::Type::RotationAndPosition);
// blend the wrist oreintation into the pole vector to reduce the painfully bent wrist problem.
glm::quat elbowToHandDelta = handPose.rot() * glm::inverse(elbowPose.rot());
const float WRIST_POLE_ADJUST_FACTOR = 0.5f;
glm::quat poleAdjust = quatLerp(Quaternions::IDENTITY, elbowToHandDelta, WRIST_POLE_ADJUST_FACTOR);
_lastLeftHandControlledPose = AnimPose(Vectors::ONE, handRotation, handPosition);
} else {
if (_isLeftHandControlled) {
_leftHandRelaxTimeRemaining = RELAX_DURATION;
_isLeftHandControlled = false;
}
return glm::normalize(poleAdjust * poleVector);
}
if (_leftHandRelaxTimeRemaining > 0.0f) {
// Move hand from controlled position to non-controlled position.
_leftHandRelaxTimeRemaining = std::max(_leftHandRelaxTimeRemaining - dt, 0.0f);
AnimPose handPose;
if (transitionHandPose(_leftHandRelaxTimeRemaining, RELAX_DURATION, _lastLeftHandControlledPose, LEFT_HAND,
FROM_CONTROLLED, handPose)) {
_animVars.set("leftHandPosition", handPose.trans());
_animVars.set("leftHandRotation", handPose.rot());
_animVars.set("leftHandType", (int)IKTarget::Type::RotationAndPosition);
}
} else {
_animVars.unset("leftHandPosition");
_animVars.unset("leftHandRotation");
_animVars.set("leftHandType", (int)IKTarget::Type::HipsRelativeRotationAndPosition);
}
}
glm::vec3 Rig::calculateKneePoleVector(int footJointIndex, int kneeIndex, int upLegIndex, int hipsIndex, const AnimPose& targetFootPose) const {
if (params.isRightEnabled) {
if (!_isRightHandControlled) {
_rightHandControlTimeRemaining = CONTROL_DURATION;
_isRightHandControlled = true;
}
AnimPose hipsPose = _externalPoseSet._absolutePoses[hipsIndex];
AnimPose footPose = targetFootPose;
AnimPose kneePose = _externalPoseSet._absolutePoses[kneeIndex];
AnimPose upLegPose = _externalPoseSet._absolutePoses[upLegIndex];
glm::vec3 handPosition = params.rightPosition;
glm::quat handRotation = params.rightOrientation;
// ray from foot to upLeg
glm::vec3 d = glm::normalize(footPose.trans() - upLegPose.trans());
if (_rightHandControlTimeRemaining > 0.0f) {
// Move hand from non-controlled position to controlled position.
_rightHandControlTimeRemaining = std::max(_rightHandControlTimeRemaining - dt, 0.0f);
AnimPose handPose(Vectors::ONE, handRotation, handPosition);
if (transitionHandPose(_rightHandControlTimeRemaining, CONTROL_DURATION, handPose, RIGHT_HAND, TO_CONTROLLED,
handPose)) {
handPosition = handPose.trans();
handRotation = handPose.rot();
}
}
// form a plane normal to the hips x-axis
glm::vec3 n = hipsPose.rot() * Vectors::UNIT_X;
if (!bodySensorTrackingEnabled) {
// prevent the hand IK targets from intersecting the body capsule
glm::vec3 displacement;
if (findSphereCapsulePenetration(handPosition, HAND_RADIUS, bodyCapsuleStart, bodyCapsuleEnd, bodyCapsuleRadius, displacement)) {
handPosition -= displacement;
}
}
// project d onto this plane
glm::vec3 dProj = d - glm::dot(d, n) * n;
_animVars.set("rightHandPosition", handPosition);
_animVars.set("rightHandRotation", handRotation);
_animVars.set("rightHandType", (int)IKTarget::Type::RotationAndPosition);
// rotate dProj by 90 degrees to get the poleVector.
glm::vec3 poleVector = glm::angleAxis(-PI / 2.0f, n) * dProj;
_lastRightHandControlledPose = AnimPose(Vectors::ONE, handRotation, handPosition);
} else {
if (_isRightHandControlled) {
_rightHandRelaxTimeRemaining = RELAX_DURATION;
_isRightHandControlled = false;
}
// 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);
if (_rightHandRelaxTimeRemaining > 0.0f) {
// Move hand from controlled position to non-controlled position.
_rightHandRelaxTimeRemaining = std::max(_rightHandRelaxTimeRemaining - dt, 0.0f);
AnimPose handPose;
if (transitionHandPose(_rightHandRelaxTimeRemaining, RELAX_DURATION, _lastRightHandControlledPose, RIGHT_HAND,
FROM_CONTROLLED, handPose)) {
_animVars.set("rightHandPosition", handPose.trans());
_animVars.set("rightHandRotation", handPose.rot());
_animVars.set("rightHandType", (int)IKTarget::Type::RotationAndPosition);
}
} else {
_animVars.unset("rightHandPosition");
_animVars.unset("rightHandRotation");
_animVars.set("rightHandType", (int)IKTarget::Type::HipsRelativeRotationAndPosition);
}
}
return glm::normalize(poleAdjust * poleVector);
}
if (params.isLeftFootEnabled) {
_animVars.set("leftFootPosition", params.leftFootPosition);
_animVars.set("leftFootRotation", params.leftFootOrientation);
_animVars.set("leftFootType", (int)IKTarget::Type::RotationAndPosition);
} else {
_animVars.unset("leftFootPosition");
_animVars.unset("leftFootRotation");
_animVars.set("leftFootType", (int)IKTarget::Type::RotationAndPosition);
}
void Rig::updateFromControllerParameters(const ControllerParameters& params, float dt) {
if (!_animSkeleton || !_animNode) {
return;
}
if (params.isRightFootEnabled) {
_animVars.set("rightFootPosition", params.rightFootPosition);
_animVars.set("rightFootRotation", params.rightFootOrientation);
_animVars.set("rightFootType", (int)IKTarget::Type::RotationAndPosition);
} else {
_animVars.unset("rightFootPosition");
_animVars.unset("rightFootRotation");
_animVars.set("rightFootType", (int)IKTarget::Type::RotationAndPosition);
}
_animVars.set("isTalking", params.isTalking);
_animVars.set("notIsTalking", !params.isTalking);
bool headEnabled = params.controllerActiveFlags[ControllerType_Head];
bool leftHandEnabled = params.controllerActiveFlags[ControllerType_LeftHand];
bool rightHandEnabled = params.controllerActiveFlags[ControllerType_RightHand];
bool hipsEnabled = params.controllerActiveFlags[ControllerType_Hips];
bool leftFootEnabled = params.controllerActiveFlags[ControllerType_LeftFoot];
bool rightFootEnabled = params.controllerActiveFlags[ControllerType_RightFoot];
bool leftArmEnabled = params.controllerActiveFlags[ControllerType_LeftArm];
bool rightArmEnabled = params.controllerActiveFlags[ControllerType_RightArm];
bool spine2Enabled = params.controllerActiveFlags[ControllerType_Spine2];
updateHead(headEnabled, hipsEnabled, params.controllerPoses[ControllerType_Head]);
updateHands(leftHandEnabled, rightHandEnabled, hipsEnabled, leftArmEnabled, rightArmEnabled, dt,
params.controllerPoses[ControllerType_LeftHand], params.controllerPoses[ControllerType_RightHand],
params.bodyCapsuleRadius, params.bodyCapsuleHalfHeight, params.bodyCapsuleLocalOffset);
updateFeet(leftFootEnabled, rightFootEnabled,
params.controllerPoses[ControllerType_LeftFoot], params.controllerPoses[ControllerType_RightFoot]);
// if the hips or the feet are being controlled.
if (hipsEnabled || rightFootEnabled || leftFootEnabled) {
// for more predictable IK solve from the center of the joint limits, not from the underpose
_animVars.set("solutionSource", (int)AnimInverseKinematics::SolutionSource::RelaxToLimitCenterPoses);
// replace the feet animation with the default pose, this is to prevent unexpected toe wiggling.
_animVars.set("defaultPoseOverlayAlpha", 1.0f);
_animVars.set("defaultPoseOverlayBoneSet", (int)AnimOverlay::BothFeetBoneSet);
} else {
// augment the IK with the underPose.
_animVars.set("solutionSource", (int)AnimInverseKinematics::SolutionSource::RelaxToUnderPoses);
// feet should follow source animation
_animVars.unset("defaultPoseOverlayAlpha");
_animVars.unset("defaultPoseOverlayBoneSet");
}
if (hipsEnabled) {
_animVars.set("hipsType", (int)IKTarget::Type::RotationAndPosition);
_animVars.set("hipsPosition", params.controllerPoses[ControllerType_Hips].trans());
_animVars.set("hipsRotation", params.controllerPoses[ControllerType_Hips].rot());
} else {
_animVars.set("hipsType", (int)IKTarget::Type::Unknown);
}
if (hipsEnabled && spine2Enabled) {
_animVars.set("spine2Type", (int)IKTarget::Type::Spline);
_animVars.set("spine2Position", params.controllerPoses[ControllerType_Spine2].trans());
_animVars.set("spine2Rotation", params.controllerPoses[ControllerType_Spine2].rot());
} else {
_animVars.set("spine2Type", (int)IKTarget::Type::Unknown);
}
if (leftArmEnabled) {
_animVars.set("leftArmType", (int)IKTarget::Type::RotationAndPosition);
_animVars.set("leftArmPosition", params.controllerPoses[ControllerType_LeftArm].trans());
_animVars.set("leftArmRotation", params.controllerPoses[ControllerType_LeftArm].rot());
} else {
_animVars.set("leftArmType", (int)IKTarget::Type::Unknown);
}
if (rightArmEnabled) {
_animVars.set("rightArmType", (int)IKTarget::Type::RotationAndPosition);
_animVars.set("rightArmPosition", params.controllerPoses[ControllerType_RightArm].trans());
_animVars.set("rightArmRotation", params.controllerPoses[ControllerType_RightArm].rot());
} else {
_animVars.set("rightArmType", (int)IKTarget::Type::Unknown);
}
}
@ -1486,22 +1692,18 @@ void Rig::computeAvatarBoundingCapsule(
AnimInverseKinematics ikNode("boundingShape");
ikNode.setSkeleton(_animSkeleton);
ikNode.setTargetVars("LeftHand",
"leftHandPosition",
"leftHandRotation",
"leftHandType", "leftHandWeight", 1.0f, {});
ikNode.setTargetVars("RightHand",
"rightHandPosition",
"rightHandRotation",
"rightHandType", "rightHandWeight", 1.0f, {});
ikNode.setTargetVars("LeftFoot",
"leftFootPosition",
"leftFootRotation",
"leftFootType", "leftFootWeight", 1.0f, {});
ikNode.setTargetVars("RightFoot",
"rightFootPosition",
"rightFootRotation",
"rightFootType", "rightFootWeight", 1.0f, {});
ikNode.setTargetVars("LeftHand", "leftHandPosition", "leftHandRotation",
"leftHandType", "leftHandWeight", 1.0f, {},
QString(), QString(), QString());
ikNode.setTargetVars("RightHand", "rightHandPosition", "rightHandRotation",
"rightHandType", "rightHandWeight", 1.0f, {},
QString(), QString(), QString());
ikNode.setTargetVars("LeftFoot", "leftFootPosition", "leftFootRotation",
"leftFootType", "leftFootWeight", 1.0f, {},
QString(), QString(), QString());
ikNode.setTargetVars("RightFoot", "rightFootPosition", "rightFootRotation",
"rightFootType", "rightFootWeight", 1.0f, {},
QString(), QString(), QString());
AnimPose geometryToRig = _modelOffset * _geometryOffset;

View file

@ -41,21 +41,26 @@ public:
bool useNames;
};
struct HeadParameters {
glm::mat4 hipsMatrix = glm::mat4(); // rig space
glm::mat4 spine2Matrix = glm::mat4(); // rig space
glm::quat rigHeadOrientation = glm::quat(); // rig space (-z forward)
glm::vec3 rigHeadPosition = glm::vec3(); // rig space
glm::vec3 rightArmPosition = glm::vec3(); // rig space
glm::quat rightArmRotation = glm::quat(); // rig space
glm::vec3 leftArmPosition = glm::vec3(); // rig space
glm::quat leftArmRotation = glm::quat(); // rig space
bool hipsEnabled = false;
bool headEnabled = false;
bool spine2Enabled = false;
bool leftArmEnabled = false;
bool rightArmEnabled = false;
bool isTalking = false;
enum ControllerType {
ControllerType_Head = 0,
ControllerType_LeftHand,
ControllerType_RightHand,
ControllerType_Hips,
ControllerType_LeftFoot,
ControllerType_RightFoot,
ControllerType_LeftArm,
ControllerType_RightArm,
ControllerType_Spine2,
NumControllerTypes
};
struct ControllerParameters {
AnimPose controllerPoses[NumControllerTypes]; // rig space
bool controllerActiveFlags[NumControllerTypes];
bool isTalking;
float bodyCapsuleRadius;
float bodyCapsuleHalfHeight;
glm::vec3 bodyCapsuleLocalOffset;
};
struct EyeParameters {
@ -67,25 +72,6 @@ public:
int rightEyeJointIndex = -1;
};
struct HandAndFeetParameters {
bool isLeftEnabled;
bool isRightEnabled;
float bodyCapsuleRadius;
float bodyCapsuleHalfHeight;
glm::vec3 bodyCapsuleLocalOffset;
glm::vec3 leftPosition = glm::vec3(); // rig space
glm::quat leftOrientation = glm::quat(); // rig space (z forward)
glm::vec3 rightPosition = glm::vec3(); // rig space
glm::quat rightOrientation = glm::quat(); // rig space (z forward)
bool isLeftFootEnabled;
bool isRightFootEnabled;
glm::vec3 leftFootPosition = glm::vec3(); // rig space
glm::quat leftFootOrientation = glm::quat(); // rig space (z forward)
glm::vec3 rightFootPosition = glm::vec3(); // rig space
glm::quat rightFootOrientation = glm::quat(); // rig space (z forward)
};
enum class CharacterControllerState {
Ground = 0,
Takeoff,
@ -153,9 +139,6 @@ public:
bool getAbsoluteJointTranslationInRigFrame(int jointIndex, glm::vec3& translation) const;
bool getAbsoluteJointPoseInRigFrame(int jointIndex, AnimPose& returnPose) const;
// legacy
bool getJointCombinedRotation(int jointIndex, glm::quat& result, const glm::quat& rotation) const;
// rig space
glm::mat4 getJointTransform(int jointIndex) const;
@ -195,9 +178,8 @@ public:
// legacy
void clearJointStatePriorities();
void updateFromHeadParameters(const HeadParameters& params, float dt);
void updateFromControllerParameters(const ControllerParameters& params, float dt);
void updateFromEyeParameters(const EyeParameters& params);
void updateFromHandAndFeetParameters(const HandAndFeetParameters& params, float dt);
void initAnimGraph(const QUrl& url);
@ -247,11 +229,18 @@ protected:
void applyOverridePoses();
void buildAbsoluteRigPoses(const AnimPoseVec& relativePoses, AnimPoseVec& absolutePosesOut);
void updateHeadAnimVars(const HeadParameters& params);
void updateHead(bool headEnabled, bool hipsEnabled, const AnimPose& headMatrix);
void updateHands(bool leftHandEnabled, bool rightHandEnabled, bool hipsEnabled, bool leftArmEnabled, bool rightArmEnabled, float dt,
const AnimPose& leftHandPose, const AnimPose& rightHandPose,
float bodyCapsuleRadius, float bodyCapsuleHalfHeight, const glm::vec3& bodyCapsuleLocalOffset);
void updateFeet(bool leftFootEnabled, bool rightFootEnabled, const AnimPose& leftFootPose, const AnimPose& rightFootPose);
void updateEyeJoint(int index, const glm::vec3& modelTranslation, const glm::quat& modelRotation, const glm::vec3& lookAt, const glm::vec3& saccade);
void calcAnimAlpha(float speed, const std::vector<float>& referenceSpeeds, float* alphaOut) const;
glm::vec3 calculateElbowPoleVector(int handIndex, int elbowIndex, int armIndex, int hipsIndex, bool isLeft) const;
glm::vec3 calculateKneePoleVector(int footJointIndex, int kneeJoint, int upLegIndex, int hipsIndex, const AnimPose& targetFootPose) const;
AnimPose _modelOffset; // model to rig space
AnimPose _geometryOffset; // geometry to model space (includes unit offset & fst offsets)
AnimPose _invGeometryOffset;
@ -347,13 +336,12 @@ protected:
bool _enableDebugDrawIKConstraints { false };
bool _enableDebugDrawIKChains { false };
private:
QMap<int, StateHandler> _stateHandlers;
int _nextStateHandlerId { 0 };
QMutex _stateMutex;
bool transitionHandPose(float deltaTime, float totalDuration, AnimPose& controlledHandPose, bool isLeftHand,
bool isToControlled, AnimPose& returnHandPose);
bool transitionHandPose(float deltaTime, float totalDuration, AnimPose& controlledHandPose, bool isLeftHand,
bool isToControlled, AnimPose& returnHandPose);
bool _isLeftHandControlled { false };
bool _isRightHandControlled { false };
@ -363,6 +351,18 @@ private:
float _rightHandRelaxTimeRemaining { 0.0f };
AnimPose _lastLeftHandControlledPose;
AnimPose _lastRightHandControlledPose;
glm::vec3 _prevRightFootPoleVector { Vectors::UNIT_Z };
bool _prevRightFootPoleVectorValid { false };
glm::vec3 _prevLeftFootPoleVector { Vectors::UNIT_Z };
bool _prevLeftFootPoleVectorValid { false };
glm::vec3 _prevRightHandPoleVector { -Vectors::UNIT_Z };
bool _prevRightHandPoleVectorValid { false };
glm::vec3 _prevLeftHandPoleVector { -Vectors::UNIT_Z };
bool _prevLeftHandPoleVectorValid { false };
};
#endif /* defined(__hifi__Rig__) */

View file

@ -48,6 +48,7 @@
#include "AudioClientLogging.h"
#include "AudioLogging.h"
#include "AudioHelpers.h"
#include "AudioClient.h"
@ -1688,23 +1689,24 @@ int AudioClient::calculateNumberOfFrameSamples(int numBytes) const {
}
float AudioClient::azimuthForSource(const glm::vec3& relativePosition) {
// copied from AudioMixer, more or less
glm::quat inverseOrientation = glm::inverse(_orientationGetter());
// compute sample delay for the 2 ears to create phase panning
glm::vec3 rotatedSourcePosition = inverseOrientation * relativePosition;
// project the rotated source position vector onto x-y plane
// project the rotated source position vector onto the XZ plane
rotatedSourcePosition.y = 0.0f;
static const float SOURCE_DISTANCE_THRESHOLD = 1e-30f;
if (glm::length2(rotatedSourcePosition) > SOURCE_DISTANCE_THRESHOLD) {
float rotatedSourcePositionLength2 = glm::length2(rotatedSourcePosition);
if (rotatedSourcePositionLength2 > SOURCE_DISTANCE_THRESHOLD) {
// produce an oriented angle about the y-axis
return glm::orientedAngle(glm::vec3(0.0f, 0.0f, -1.0f), glm::normalize(rotatedSourcePosition), glm::vec3(0.0f, -1.0f, 0.0f));
} else {
glm::vec3 direction = rotatedSourcePosition * (1.0f / fastSqrtf(rotatedSourcePositionLength2));
float angle = fastAcosf(glm::clamp(-direction.z, -1.0f, 1.0f)); // UNIT_NEG_Z is "forward"
return (direction.x < 0.0f) ? -angle : angle;
} else {
// no azimuth if they are in same spot
return 0.0f;
}

View file

@ -1511,6 +1511,7 @@ void AvatarData::processAvatarIdentity(const QByteArray& identityData, bool& ide
>> identity.attachmentData
>> identity.displayName
>> identity.sessionDisplayName
>> identity.isReplicated
>> identity.avatarEntityData
>> identity.lookAtSnappingEnabled
;
@ -1535,6 +1536,11 @@ void AvatarData::processAvatarIdentity(const QByteArray& identityData, bool& ide
}
maybeUpdateSessionDisplayNameFromTransport(identity.sessionDisplayName);
if (identity.isReplicated != _isReplicated) {
_isReplicated = identity.isReplicated;
identityChanged = true;
}
if (identity.attachmentData != _attachmentData) {
setAttachmentData(identity.attachmentData);
identityChanged = true;
@ -1570,7 +1576,7 @@ void AvatarData::processAvatarIdentity(const QByteArray& identityData, bool& ide
}
}
QByteArray AvatarData::identityByteArray() const {
QByteArray AvatarData::identityByteArray(bool setIsReplicated) const {
QByteArray identityData;
QDataStream identityStream(&identityData, QIODevice::Append);
const QUrl& urlToSend = cannonicalSkeletonModelURL(emptyURL); // depends on _skeletonModelURL
@ -1585,6 +1591,7 @@ QByteArray AvatarData::identityByteArray() const {
<< _attachmentData
<< _displayName
<< getSessionDisplayNameForTransport() // depends on _sessionDisplayName
<< (_isReplicated || setIsReplicated)
<< _avatarEntityData
<< _lookAtSnappingEnabled
;
@ -1982,7 +1989,7 @@ JointData jointDataFromJsonValue(const QJsonValue& json) {
result.rotation = quatFromJsonValue(array[0]);
result.rotationSet = true;
result.translation = vec3FromJsonValue(array[1]);
result.translationSet = false;
result.translationSet = true;
}
return result;
}
@ -2150,12 +2157,9 @@ void AvatarData::fromJson(const QJsonObject& json, bool useFrameSkeleton) {
QVector<JointData> jointArray;
QJsonArray jointArrayJson = json[JSON_AVATAR_JOINT_ARRAY].toArray();
jointArray.reserve(jointArrayJson.size());
int i = 0;
for (const auto& jointJson : jointArrayJson) {
auto joint = jointDataFromJsonValue(jointJson);
jointArray.push_back(joint);
setJointData(i, joint.rotation, joint.translation);
i++;
}
setRawJointData(jointArray);
}

View file

@ -533,6 +533,7 @@ public:
QVector<AttachmentData> attachmentData;
QString displayName;
QString sessionDisplayName;
bool isReplicated;
AvatarEntityMap avatarEntityData;
bool lookAtSnappingEnabled;
};
@ -542,7 +543,7 @@ public:
void processAvatarIdentity(const QByteArray& identityData, bool& identityChanged,
bool& displayNameChanged, bool& skeletonModelUrlChanged);
QByteArray identityByteArray() const;
QByteArray identityByteArray(bool setIsReplicated = false) const;
const QUrl& getSkeletonModelURL() const { return _skeletonModelURL; }
const QString& getDisplayName() const { return _displayName; }
@ -630,9 +631,12 @@ public:
void markIdentityDataChanged() { _identityDataChanged = true; }
void pushIdentitySequenceNumber() { ++_identitySequenceNumber; };
bool hasProcessedFirstIdentity() const { return _hasProcessedFirstIdentity; }
float getDensity() const { return _density; }
bool getIsReplicated() const { return _isReplicated; }
signals:
void displayNameChanged();
void lookAtSnappingChanged(bool enabled);
@ -670,6 +674,10 @@ protected:
bool hasParent() const { return !getParentID().isNull(); }
bool hasFaceTracker() const { return _headData ? _headData->_isFaceTrackerConnected : false; }
// isReplicated will be true on downstream Avatar Mixers and their clients, but false on the upstream "master"
// Audio Mixer that the replicated avatar is connected to.
bool _isReplicated{ false };
glm::vec3 _handPosition;
virtual const QString& getSessionDisplayNameForTransport() const { return _sessionDisplayName; }
virtual void maybeUpdateSessionDisplayNameFromTransport(const QString& sessionDisplayName) { } // No-op in AvatarMixer

View file

@ -153,6 +153,15 @@ QString ScriptAvatarData::getSessionDisplayName() const {
return QString();
}
}
bool ScriptAvatarData::getIsReplicated() const {
if (AvatarSharedPointer sharedAvatarData = _avatarData.lock()) {
return sharedAvatarData->getIsReplicated();
} else {
return false;
}
}
bool ScriptAvatarData::getLookAtSnappingEnabled() const {
if (AvatarSharedPointer sharedAvatarData = _avatarData.lock()) {
return sharedAvatarData->getLookAtSnappingEnabled();

View file

@ -45,6 +45,7 @@ class ScriptAvatarData : public QObject {
Q_PROPERTY(QUuid sessionUUID READ getSessionUUID)
Q_PROPERTY(QString displayName READ getDisplayName NOTIFY displayNameChanged)
Q_PROPERTY(QString sessionDisplayName READ getSessionDisplayName)
Q_PROPERTY(bool isReplicated READ getIsReplicated)
Q_PROPERTY(bool lookAtSnappingEnabled READ getLookAtSnappingEnabled NOTIFY lookAtSnappingChanged)
//
@ -96,6 +97,7 @@ public:
QUuid getSessionUUID() const;
QString getDisplayName() const;
QString getSessionDisplayName() const;
bool getIsReplicated() const;
bool getLookAtSnappingEnabled() const;
//

View file

@ -59,6 +59,48 @@ namespace controller {
makePosePair(Action::SPINE2, "Spine2"),
makePosePair(Action::HEAD, "Head"),
makePosePair(Action::LEFT_HAND_THUMB1, "LeftHandThumb1"),
makePosePair(Action::LEFT_HAND_THUMB2, "LeftHandThumb2"),
makePosePair(Action::LEFT_HAND_THUMB3, "LeftHandThumb3"),
makePosePair(Action::LEFT_HAND_THUMB4, "LeftHandThumb4"),
makePosePair(Action::LEFT_HAND_INDEX1, "LeftHandIndex1"),
makePosePair(Action::LEFT_HAND_INDEX2, "LeftHandIndex2"),
makePosePair(Action::LEFT_HAND_INDEX3, "LeftHandIndex3"),
makePosePair(Action::LEFT_HAND_INDEX4, "LeftHandIndex4"),
makePosePair(Action::LEFT_HAND_MIDDLE1, "LeftHandMiddle1"),
makePosePair(Action::LEFT_HAND_MIDDLE2, "LeftHandMiddle2"),
makePosePair(Action::LEFT_HAND_MIDDLE3, "LeftHandMiddle3"),
makePosePair(Action::LEFT_HAND_MIDDLE4, "LeftHandMiddle4"),
makePosePair(Action::LEFT_HAND_RING1, "LeftHandRing1"),
makePosePair(Action::LEFT_HAND_RING2, "LeftHandRing2"),
makePosePair(Action::LEFT_HAND_RING3, "LeftHandRing3"),
makePosePair(Action::LEFT_HAND_RING4, "LeftHandRing4"),
makePosePair(Action::LEFT_HAND_PINKY1, "LeftHandPinky1"),
makePosePair(Action::LEFT_HAND_PINKY2, "LeftHandPinky2"),
makePosePair(Action::LEFT_HAND_PINKY3, "LeftHandPinky3"),
makePosePair(Action::LEFT_HAND_PINKY4, "LeftHandPinky4"),
makePosePair(Action::RIGHT_HAND_THUMB1, "RightHandThumb1"),
makePosePair(Action::RIGHT_HAND_THUMB2, "RightHandThumb2"),
makePosePair(Action::RIGHT_HAND_THUMB3, "RightHandThumb3"),
makePosePair(Action::RIGHT_HAND_THUMB4, "RightHandThumb4"),
makePosePair(Action::RIGHT_HAND_INDEX1, "RightHandIndex1"),
makePosePair(Action::RIGHT_HAND_INDEX2, "RightHandIndex2"),
makePosePair(Action::RIGHT_HAND_INDEX3, "RightHandIndex3"),
makePosePair(Action::RIGHT_HAND_INDEX4, "RightHandIndex4"),
makePosePair(Action::RIGHT_HAND_MIDDLE1, "RightHandMiddle1"),
makePosePair(Action::RIGHT_HAND_MIDDLE2, "RightHandMiddle2"),
makePosePair(Action::RIGHT_HAND_MIDDLE3, "RightHandMiddle3"),
makePosePair(Action::RIGHT_HAND_MIDDLE4, "RightHandMiddle4"),
makePosePair(Action::RIGHT_HAND_RING1, "RightHandRing1"),
makePosePair(Action::RIGHT_HAND_RING2, "RightHandRing2"),
makePosePair(Action::RIGHT_HAND_RING3, "RightHandRing3"),
makePosePair(Action::RIGHT_HAND_RING4, "RightHandRing4"),
makePosePair(Action::RIGHT_HAND_PINKY1, "RightHandPinky1"),
makePosePair(Action::RIGHT_HAND_PINKY2, "RightHandPinky2"),
makePosePair(Action::RIGHT_HAND_PINKY3, "RightHandPinky3"),
makePosePair(Action::RIGHT_HAND_PINKY4, "RightHandPinky4"),
makeButtonPair(Action::LEFT_HAND_CLICK, "LeftHandClick"),
makeButtonPair(Action::RIGHT_HAND_CLICK, "RightHandClick"),

View file

@ -104,6 +104,47 @@ enum class Action {
LEFT_ARM,
RIGHT_ARM,
LEFT_HAND_THUMB1,
LEFT_HAND_THUMB2,
LEFT_HAND_THUMB3,
LEFT_HAND_THUMB4,
LEFT_HAND_INDEX1,
LEFT_HAND_INDEX2,
LEFT_HAND_INDEX3,
LEFT_HAND_INDEX4,
LEFT_HAND_MIDDLE1,
LEFT_HAND_MIDDLE2,
LEFT_HAND_MIDDLE3,
LEFT_HAND_MIDDLE4,
LEFT_HAND_RING1,
LEFT_HAND_RING2,
LEFT_HAND_RING3,
LEFT_HAND_RING4,
LEFT_HAND_PINKY1,
LEFT_HAND_PINKY2,
LEFT_HAND_PINKY3,
LEFT_HAND_PINKY4,
RIGHT_HAND_THUMB1,
RIGHT_HAND_THUMB2,
RIGHT_HAND_THUMB3,
RIGHT_HAND_THUMB4,
RIGHT_HAND_INDEX1,
RIGHT_HAND_INDEX2,
RIGHT_HAND_INDEX3,
RIGHT_HAND_INDEX4,
RIGHT_HAND_MIDDLE1,
RIGHT_HAND_MIDDLE2,
RIGHT_HAND_MIDDLE3,
RIGHT_HAND_MIDDLE4,
RIGHT_HAND_RING1,
RIGHT_HAND_RING2,
RIGHT_HAND_RING3,
RIGHT_HAND_RING4,
RIGHT_HAND_PINKY1,
RIGHT_HAND_PINKY2,
RIGHT_HAND_PINKY3,
RIGHT_HAND_PINKY4,
NUM_ACTIONS,
};

View file

@ -9,9 +9,14 @@
#include "Input.h"
namespace controller {
const Input Input::INVALID_INPUT = Input(0x7fffffff);
const Input Input::INVALID_INPUT = invalidInput();
const uint16_t Input::INVALID_DEVICE = Input::INVALID_INPUT.device;
const uint16_t Input::INVALID_CHANNEL = Input::INVALID_INPUT.channel;
const uint16_t Input::INVALID_TYPE = Input::INVALID_INPUT.type;
const Input& Input::invalidInput() {
static const Input INVALID_INPUT = Input(0x7fffffff);
return INVALID_INPUT;
}
}

View file

@ -83,6 +83,8 @@ struct Input {
using NamedPair = QPair<Input, QString>;
using NamedVector = QVector<NamedPair>;
static const Input& invalidInput();
};
}

View file

@ -101,7 +101,47 @@ Input::NamedVector StandardController::getAvailableInputs() const {
// Poses
makePair(LEFT_HAND, "LeftHand"),
makePair(LEFT_HAND_THUMB1, "LeftHandThumb1"),
makePair(LEFT_HAND_THUMB2, "LeftHandThumb2"),
makePair(LEFT_HAND_THUMB3, "LeftHandThumb3"),
makePair(LEFT_HAND_THUMB4, "LeftHandThumb4"),
makePair(LEFT_HAND_INDEX1, "LeftHandIndex1"),
makePair(LEFT_HAND_INDEX2, "LeftHandIndex2"),
makePair(LEFT_HAND_INDEX3, "LeftHandIndex3"),
makePair(LEFT_HAND_INDEX4, "LeftHandIndex4"),
makePair(LEFT_HAND_MIDDLE1, "LeftHandMiddle1"),
makePair(LEFT_HAND_MIDDLE2, "LeftHandMiddle2"),
makePair(LEFT_HAND_MIDDLE3, "LeftHandMiddle3"),
makePair(LEFT_HAND_MIDDLE4, "LeftHandMiddle4"),
makePair(LEFT_HAND_RING1, "LeftHandRing1"),
makePair(LEFT_HAND_RING2, "LeftHandRing2"),
makePair(LEFT_HAND_RING3, "LeftHandRing3"),
makePair(LEFT_HAND_RING4, "LeftHandRing4"),
makePair(LEFT_HAND_PINKY1, "LeftHandPinky1"),
makePair(LEFT_HAND_PINKY2, "LeftHandPinky2"),
makePair(LEFT_HAND_PINKY3, "LeftHandPinky3"),
makePair(LEFT_HAND_PINKY4, "LeftHandPinky4"),
makePair(RIGHT_HAND, "RightHand"),
makePair(RIGHT_HAND_THUMB1, "RightHandThumb1"),
makePair(RIGHT_HAND_THUMB2, "RightHandThumb2"),
makePair(RIGHT_HAND_THUMB3, "RightHandThumb3"),
makePair(RIGHT_HAND_THUMB4, "RightHandThumb4"),
makePair(RIGHT_HAND_INDEX1, "RightHandIndex1"),
makePair(RIGHT_HAND_INDEX2, "RightHandIndex2"),
makePair(RIGHT_HAND_INDEX3, "RightHandIndex3"),
makePair(RIGHT_HAND_INDEX4, "RightHandIndex4"),
makePair(RIGHT_HAND_MIDDLE1, "RightHandMiddle1"),
makePair(RIGHT_HAND_MIDDLE2, "RightHandMiddle2"),
makePair(RIGHT_HAND_MIDDLE3, "RightHandMiddle3"),
makePair(RIGHT_HAND_MIDDLE4, "RightHandMiddle4"),
makePair(RIGHT_HAND_RING1, "RightHandRing1"),
makePair(RIGHT_HAND_RING2, "RightHandRing2"),
makePair(RIGHT_HAND_RING3, "RightHandRing3"),
makePair(RIGHT_HAND_RING4, "RightHandRing4"),
makePair(RIGHT_HAND_PINKY1, "RightHandPinky1"),
makePair(RIGHT_HAND_PINKY2, "RightHandPinky2"),
makePair(RIGHT_HAND_PINKY3, "RightHandPinky3"),
makePair(RIGHT_HAND_PINKY4, "RightHandPinky4"),
makePair(LEFT_FOOT, "LeftFoot"),
makePair(RIGHT_FOOT, "RightFoot"),
makePair(RIGHT_ARM, "RightArm"),

View file

@ -47,8 +47,8 @@
namespace controller {
const uint16_t UserInputMapper::STANDARD_DEVICE = 0;
const uint16_t UserInputMapper::ACTIONS_DEVICE = Input::INVALID_DEVICE - 0x00FF;
const uint16_t UserInputMapper::STATE_DEVICE = Input::INVALID_DEVICE - 0x0100;
const uint16_t UserInputMapper::ACTIONS_DEVICE = Input::invalidInput().device - 0x00FF;
const uint16_t UserInputMapper::STATE_DEVICE = Input::invalidInput().device - 0x0100;
}
// Default contruct allocate the poutput size with the current hardcoded action channels

View file

@ -163,7 +163,6 @@ void EntityTreeRenderer::reloadEntityScripts() {
void EntityTreeRenderer::init() {
OctreeProcessor::init();
EntityTreePointer entityTree = std::static_pointer_cast<EntityTree>(_tree);
entityTree->setFBXService(this);
if (_wantScripts) {
resetEntitiesScriptEngine();
@ -188,7 +187,6 @@ void EntityTreeRenderer::shutdown() {
void EntityTreeRenderer::setTree(OctreePointer newTree) {
OctreeProcessor::setTree(newTree);
std::static_pointer_cast<EntityTree>(_tree)->setFBXService(this);
}
void EntityTreeRenderer::update() {
@ -373,31 +371,6 @@ bool EntityTreeRenderer::applyLayeredZones() {
return true;
}
const FBXGeometry* EntityTreeRenderer::getGeometryForEntity(EntityItemPointer entityItem) {
const FBXGeometry* result = NULL;
if (entityItem->getType() == EntityTypes::Model) {
std::shared_ptr<RenderableModelEntityItem> modelEntityItem =
std::dynamic_pointer_cast<RenderableModelEntityItem>(entityItem);
assert(modelEntityItem); // we need this!!!
ModelPointer model = modelEntityItem->getModel(getSharedFromThis());
if (model && model->isLoaded()) {
result = &model->getFBXGeometry();
}
}
return result;
}
ModelPointer EntityTreeRenderer::getModelForEntityItem(EntityItemPointer entityItem) {
ModelPointer result = nullptr;
if (entityItem->getType() == EntityTypes::Model) {
std::shared_ptr<RenderableModelEntityItem> modelEntityItem =
std::dynamic_pointer_cast<RenderableModelEntityItem>(entityItem);
result = modelEntityItem->getModel(getSharedFromThis());
}
return result;
}
void EntityTreeRenderer::processEraseMessage(ReceivedMessage& message, const SharedNodePointer& sourceNode) {
std::static_pointer_cast<EntityTree>(_tree)->processEraseMessage(message, sourceNode);
}
@ -880,7 +853,7 @@ void EntityTreeRenderer::checkAndCallPreload(const EntityItemID& entityID, bool
entity->scriptHasUnloaded();
}
if (shouldLoad) {
scriptUrl = ResourceManager::normalizeURL(scriptUrl);
scriptUrl = DependencyManager::get<ResourceManager>()->normalizeURL(scriptUrl);
_entitiesScriptEngine->loadEntityScript(entityID, scriptUrl, reload);
entity->scriptHasPreloaded();
}
@ -889,7 +862,12 @@ void EntityTreeRenderer::checkAndCallPreload(const EntityItemID& entityID, bool
void EntityTreeRenderer::playEntityCollisionSound(EntityItemPointer entity, const Collision& collision) {
assert((bool)entity);
SharedSoundPointer collisionSound = entity->getCollisionSound();
auto renderable = entity->getRenderableInterface();
if (!renderable) {
return;
}
SharedSoundPointer collisionSound = renderable->getCollisionSound();
if (!collisionSound) {
return;
}

View file

@ -39,7 +39,7 @@ using ModelWeakPointer = std::weak_ptr<Model>;
using CalculateEntityLoadingPriority = std::function<float(const EntityItem& item)>;
// Generic client side Octree renderer class.
class EntityTreeRenderer : public OctreeProcessor, public EntityItemFBXService, public Dependency {
class EntityTreeRenderer : public OctreeProcessor, public Dependency {
Q_OBJECT
public:
EntityTreeRenderer(bool wantScripts, AbstractViewStateInterface* viewState,
@ -68,9 +68,6 @@ public:
virtual void init() override;
virtual const FBXGeometry* getGeometryForEntity(EntityItemPointer entityItem) override;
virtual ModelPointer getModelForEntityItem(EntityItemPointer entityItem) override;
/// clears the tree
virtual void clear() override;

View file

@ -14,6 +14,7 @@
#include <render/Scene.h>
#include <EntityItem.h>
#include <Sound.h>
#include "AbstractViewStateInterface.h"
#include "EntitiesRendererLogging.h"
@ -40,7 +41,11 @@ public:
virtual void render(RenderArgs* args) {};
virtual bool addToScene(const EntityItemPointer& self, const render::ScenePointer& scene, render::Transaction& transaction) = 0;
virtual void removeFromScene(const EntityItemPointer& self, const render::ScenePointer& scene, render::Transaction& transaction) = 0;
const SharedSoundPointer& getCollisionSound() { return _collisionSound; }
void setCollisionSound(const SharedSoundPointer& sound) { _collisionSound = sound; }
virtual RenderableEntityInterface* getRenderableInterface() { return nullptr; }
private:
SharedSoundPointer _collisionSound;
};
class RenderableEntityItemProxy {

View file

@ -69,11 +69,9 @@ void RenderableModelEntityItem::setModelURL(const QString& url) {
void RenderableModelEntityItem::loader() {
_needsModelReload = true;
auto renderer = DependencyManager::get<EntityTreeRenderer>();
assert(renderer);
{
PerformanceTimer perfTimer("getModel");
getModel(renderer);
getModel();
}
}
@ -390,8 +388,7 @@ void RenderableModelEntityItem::render(RenderArgs* args) {
if (!_model || _needsModelReload) {
// TODO: this getModel() appears to be about 3% of model render time. We should optimize
PerformanceTimer perfTimer("getModel");
auto renderer = qSharedPointerCast<EntityTreeRenderer>(args->_renderData);
getModel(renderer);
getModel();
// Remap textures immediately after loading to avoid flicker
remapTextures();
@ -483,7 +480,7 @@ void RenderableModelEntityItem::render(RenderArgs* args) {
auto& currentURL = getParsedModelURL();
if (currentURL != _model->getURL()) {
// Defer setting the url to the render thread
getModel(_myRenderer);
getModel();
}
}
}
@ -492,16 +489,11 @@ ModelPointer RenderableModelEntityItem::getModelNotSafe() {
return _model;
}
ModelPointer RenderableModelEntityItem::getModel(QSharedPointer<EntityTreeRenderer> renderer) {
if (!renderer) {
return nullptr;
}
ModelPointer RenderableModelEntityItem::getModel() {
// make sure our renderer is setup
if (!_myRenderer) {
_myRenderer = renderer;
_myRenderer = DependencyManager::get<EntityTreeRenderer>();
}
assert(_myRenderer == renderer); // you should only ever render on one renderer
if (!_myRenderer || QThread::currentThread() != _myRenderer->thread()) {
return _model;
@ -513,7 +505,7 @@ ModelPointer RenderableModelEntityItem::getModel(QSharedPointer<EntityTreeRender
if (!getModelURL().isEmpty()) {
// If we don't have a model, allocate one *immediately*
if (!_model) {
_model = _myRenderer->allocateModel(getModelURL(), renderer->getEntityLoadingPriority(*this), this);
_model = _myRenderer->allocateModel(getModelURL(), _myRenderer->getEntityLoadingPriority(*this), this);
_needsInitialSimulation = true;
// If we need to change URLs, update it *after rendering* (to avoid access violations)
} else if (QUrl(getModelURL()) != _model->getURL()) {
@ -587,6 +579,17 @@ EntityItemProperties RenderableModelEntityItem::getProperties(EntityPropertyFlag
properties.setRenderInfoHasTransparent(_model->getRenderInfoHasTransparent());
}
if (_model && _model->isLoaded()) {
// TODO: improve naturalDimensions in the future,
// for now we've added this hack for setting natural dimensions of models
Extents meshExtents = _model->getFBXGeometry().getUnscaledMeshExtents();
properties.setNaturalDimensions(meshExtents.maximum - meshExtents.minimum);
properties.calculateNaturalPosition(meshExtents.minimum, meshExtents.maximum);
}
return properties;
}
@ -1255,3 +1258,27 @@ QStringList RenderableModelEntityItem::getJointNames() const {
}
return result;
}
void RenderableModelEntityItem::mapJoints(const QStringList& modelJointNames) {
// if we don't have animation, or we're already joint mapped then bail early
if (!hasAnimation() || jointsMapped()) {
return;
}
if (!_animation || _animation->getURL().toString() != getAnimationURL()) {
_animation = DependencyManager::get<AnimationCache>()->getAnimation(getAnimationURL());
}
if (_animation && _animation->isLoaded()) {
QStringList animationJointNames = _animation->getJointNames();
if (modelJointNames.size() > 0 && animationJointNames.size() > 0) {
_jointMapping.resize(modelJointNames.size());
for (int i = 0; i < modelJointNames.size(); i++) {
_jointMapping[i] = animationJointNames.indexOf(modelJointNames[i]);
}
_jointMappingCompleted = true;
_jointMappingURL = _animationProperties.getURL();
}
}
}

View file

@ -16,6 +16,7 @@
#include <QStringList>
#include <ModelEntityItem.h>
#include <AnimationCache.h>
class Model;
class EntityTreeRenderer;
@ -53,7 +54,7 @@ public:
bool& keepSearching, OctreeElementPointer& element, float& distance,
BoxFace& face, glm::vec3& surfaceNormal,
void** intersectedObject, bool precisionPicking) const override;
ModelPointer getModel(QSharedPointer<EntityTreeRenderer> renderer);
ModelPointer getModel();
ModelPointer getModelNotSafe();
virtual bool needsToCallUpdate() const override;
@ -106,6 +107,15 @@ public:
// Transparency is handled in ModelMeshPartPayload
bool isTransparent() override { return false; }
void mapJoints(const QStringList& modelJointNames);
bool jointsMapped() const {
return _jointMappingURL == getAnimationURL() && _jointMappingCompleted;
}
AnimationPointer getAnimation() const {
return _animation;
}
private:
QVariantMap parseTexturesToMap(QString textures);
void remapTextures();
@ -131,6 +141,12 @@ private:
bool _needsJointSimulation { false };
bool _showCollisionGeometry { false };
const void* _collisionMeshKey { nullptr };
// used on client side
bool _jointMappingCompleted { false };
QVector<int> _jointMapping; // domain is index into model-joints, range is index into animation-joints
QString _jointMappingURL;
AnimationPointer _animation;
};
#endif // hifi_RenderableModelEntityItem_h

View file

@ -9,11 +9,15 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "RenderablePolyVoxEntityItem.h"
#include <math.h>
#include <QObject>
#include <QByteArray>
#include <QtConcurrent/QtConcurrentRun>
#include <glm/gtx/transform.hpp>
#include <model-networking/SimpleMeshProxy.h>
#include "ModelScriptingInterface.h"
#if defined(__GNUC__) && !defined(__clang__)
@ -52,7 +56,6 @@
#include "EntityTreeRenderer.h"
#include "polyvox_vert.h"
#include "polyvox_frag.h"
#include "RenderablePolyVoxEntityItem.h"
#include "EntityEditPacketSender.h"
#include "PhysicalEntitySimulation.h"
@ -1626,6 +1629,7 @@ void RenderablePolyVoxEntityItem::locationChanged(bool tellPhysics) {
scene->enqueueTransaction(transaction);
}
bool RenderablePolyVoxEntityItem::getMeshes(MeshProxyList& result) {
if (!updateDependents()) {
return false;
@ -1645,7 +1649,7 @@ bool RenderablePolyVoxEntityItem::getMeshes(MeshProxyList& result) {
} else {
success = true;
// the mesh will be in voxel-space. transform it into object-space
meshProxy = new MeshProxy(
meshProxy = new SimpleMeshProxy(
_mesh->map([=](glm::vec3 position){ return glm::vec3(transform * glm::vec4(position, 1.0f)); },
[=](glm::vec3 normal){ return glm::normalize(glm::vec3(transform * glm::vec4(normal, 0.0f))); },
[&](uint32_t index){ return index; }));

View file

@ -12,17 +12,19 @@
#ifndef hifi_RenderablePolyVoxEntityItem_h
#define hifi_RenderablePolyVoxEntityItem_h
#include <QSemaphore>
#include <atomic>
#include <QSemaphore>
#include <PolyVoxCore/SimpleVolume.h>
#include <PolyVoxCore/Raycast.h>
#include <gpu/Context.h>
#include <model/Forward.h>
#include <TextureCache.h>
#include <PolyVoxEntityItem.h>
#include "PolyVoxEntityItem.h"
#include "RenderableEntityItem.h"
#include "gpu/Context.h"
class PolyVoxPayload {
public:

View file

@ -1,9 +1,3 @@
set(TARGET_NAME entities)
setup_hifi_library(Network Script)
link_hifi_libraries(avatars shared audio octree model model-networking fbx networking animation)
include_hifi_library_headers(networking)
include_hifi_library_headers(gpu)
target_bullet()
include_hifi_library_headers(render)
link_hifi_libraries(shared networking octree avatars)

View file

@ -9,12 +9,12 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "AnimationPropertyGroup.h"
#include <QJsonDocument>
#include <OctreePacketData.h>
#include <AnimationLoop.h>
#include "AnimationPropertyGroup.h"
#include "EntityItemProperties.h"
#include "EntityItemPropertiesMacros.h"

View file

@ -19,7 +19,7 @@
#include <QtScript/QScriptEngine>
#include "AnimationLoop.h"
#include <shared/types/AnimationLoop.h> // for Animation, AnimationCache, and AnimationPointer classes
#include "EntityItemPropertiesMacros.h"
#include "PropertyGroup.h"

View file

@ -132,7 +132,7 @@ void EntityEditFilters::addFilter(EntityItemID entityID, QString filterURL) {
_filterDataMap.insert(entityID, filterData);
_lock.unlock();
auto scriptRequest = ResourceManager::createResourceRequest(this, scriptURL);
auto scriptRequest = DependencyManager::get<ResourceManager>()->createResourceRequest(this, scriptURL);
if (!scriptRequest) {
qWarning() << "Could not create ResourceRequest for Entity Edit filter script at" << scriptURL.toString();
scriptRequestFinished(entityID);

View file

@ -23,8 +23,8 @@
#include <PhysicsHelpers.h>
#include <RegisteredMetaTypes.h>
#include <SharedUtil.h> // usecTimestampNow()
#include <SoundCache.h>
#include <LogHandler.h>
#include <Extents.h>
#include "EntityScriptingInterface.h"
#include "EntitiesLogging.h"
@ -988,21 +988,6 @@ void EntityItem::setCollisionSoundURL(const QString& value) {
}
}
SharedSoundPointer EntityItem::getCollisionSound() {
SharedSoundPointer result;
withReadLock([&] {
result = _collisionSound;
});
if (!result) {
result = DependencyManager::get<SoundCache>()->getSound(_collisionSoundURL);
withWriteLock([&] {
_collisionSound = result;
});
}
return result;
}
void EntityItem::simulate(const quint64& now) {
if (getLastSimulated() == 0) {
setLastSimulated(now);
@ -2650,12 +2635,6 @@ QString EntityItem::getCollisionSoundURL() const {
return result;
}
void EntityItem::setCollisionSound(SharedSoundPointer sound) {
withWriteLock([&] {
_collisionSound = sound;
});
}
glm::vec3 EntityItem::getRegistrationPoint() const {
glm::vec3 result;
withReadLock([&] {

View file

@ -19,14 +19,13 @@
#include <QtGui/QWindow>
#include <AnimationCache.h> // for Animation, AnimationCache, and AnimationPointer classes
#include <shared/types/AnimationLoop.h> // for Animation, AnimationCache, and AnimationPointer classes
#include <Octree.h> // for EncodeBitstreamParams class
#include <OctreeElement.h> // for OctreeElement::AppendState
#include <OctreePacketData.h>
#include <PhysicsCollisionGroups.h>
#include <ShapeInfo.h>
#include <Transform.h>
#include <Sound.h>
#include <SpatiallyNestable.h>
#include <Interpolate.h>
@ -260,9 +259,6 @@ public:
QString getCollisionSoundURL() const;
void setCollisionSoundURL(const QString& value);
SharedSoundPointer getCollisionSound();
void setCollisionSound(SharedSoundPointer sound);
glm::vec3 getRegistrationPoint() const; /// registration point as ratio of entity
/// registration point as ratio of entity
@ -526,7 +522,6 @@ protected:
quint64 _loadedScriptTimestamp { ENTITY_ITEM_DEFAULT_SCRIPT_TIMESTAMP + 1 };
QString _collisionSoundURL;
SharedSoundPointer _collisionSound;
glm::vec3 _registrationPoint;
float _angularDamping;
bool _visible;

View file

@ -9,6 +9,7 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "EntityItemID.h"
#include <QtCore/QObject>
#include <QDebug>
@ -17,7 +18,6 @@
#include <UUID.h>
#include "RegisteredMetaTypes.h"
#include "EntityItemID.h"
int entityItemIDTypeID = qRegisterMetaType<EntityItemID>();

View file

@ -17,6 +17,7 @@
#include <ByteCountCoding.h>
#include <GLMHelpers.h>
#include <RegisteredMetaTypes.h>
#include <Extents.h>
#include "EntitiesLogging.h"
#include "EntityItem.h"

View file

@ -15,6 +15,7 @@
#include <stdint.h>
#include <glm/glm.hpp>
#include <glm/gtx/component_wise.hpp>
#include <QtScript/QScriptEngine>
#include <QtCore/QObject>

View file

@ -21,7 +21,6 @@
#include <VariantMapToScriptValue.h>
#include <SharedUtil.h>
#include <SpatialParentFinder.h>
#include <model-networking/MeshProxy.h>
#include "EntitiesLogging.h"
#include "EntityDynamicFactoryInterface.h"
@ -298,18 +297,6 @@ EntityItemProperties EntityScriptingInterface::getEntityProperties(QUuid identit
}
results = entity->getProperties(desiredProperties);
// TODO: improve naturalDimensions in the future,
// for now we've added this hack for setting natural dimensions of models
if (entity->getType() == EntityTypes::Model) {
const FBXGeometry* geometry = _entityTree->getGeometryForEntity(entity);
if (geometry) {
Extents meshExtents = geometry->getUnscaledMeshExtents();
results.setNaturalDimensions(meshExtents.maximum - meshExtents.minimum);
results.calculateNaturalPosition(meshExtents.minimum, meshExtents.maximum);
}
}
}
});
}

View file

@ -9,11 +9,15 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include <PerfStat.h>
#include <QDateTime>
#include "EntityTree.h"
#include <QtCore/QDateTime>
#include <QtCore/QQueue>
#include <QtScript/QScriptEngine>
#include "EntityTree.h"
#include <PerfStat.h>
#include <Extents.h>
#include "EntitySimulation.h"
#include "VariantMapToScriptValue.h"
@ -55,9 +59,7 @@ public:
EntityTree::EntityTree(bool shouldReaverage) :
Octree(shouldReaverage),
_fbxService(NULL),
_simulation(NULL)
Octree(shouldReaverage)
{
resetClientEditStats();
}

View file

@ -41,13 +41,6 @@ public:
virtual void entityCreated(const EntityItem& newEntity, const SharedNodePointer& senderNode) = 0;
};
class EntityItemFBXService {
public:
virtual const FBXGeometry* getGeometryForEntity(EntityItemPointer entityItem) = 0;
virtual ModelPointer getModelForEntityItem(EntityItemPointer entityItem) = 0;
};
class SendEntitiesOperationArgs {
public:
glm::vec3 root;
@ -189,15 +182,6 @@ public:
int processEraseMessage(ReceivedMessage& message, const SharedNodePointer& sourceNode);
int processEraseMessageDetails(const QByteArray& buffer, const SharedNodePointer& sourceNode);
EntityItemFBXService* getFBXService() const { return _fbxService; }
void setFBXService(EntityItemFBXService* service) { _fbxService = service; }
const FBXGeometry* getGeometryForEntity(EntityItemPointer entityItem) {
return _fbxService ? _fbxService->getGeometryForEntity(entityItem) : NULL;
}
ModelPointer getModelForEntityItem(EntityItemPointer entityItem) {
return _fbxService ? _fbxService->getModelForEntityItem(entityItem) : NULL;
}
EntityTreeElementPointer getContainingElement(const EntityItemID& entityItemID) /*const*/;
void setContainingElement(const EntityItemID& entityItemID, EntityTreeElementPointer element);
void debugDumpMap();
@ -325,8 +309,6 @@ protected:
_deletedEntityItemIDs << id;
}
EntityItemFBXService* _fbxService;
mutable QReadWriteLock _entityToElementLock;
QHash<EntityItemID, EntityTreeElementPointer> _entityToElementMap;

View file

@ -9,17 +9,18 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "EntityTreeElement.h"
#include <glm/gtx/transform.hpp>
#include <FBXReader.h>
#include <GeometryUtil.h>
#include <OctreeUtils.h>
#include <Extents.h>
#include "EntitiesLogging.h"
#include "EntityNodeData.h"
#include "EntityItemProperties.h"
#include "EntityTree.h"
#include "EntityTreeElement.h"
#include "EntityTypes.h"
EntityTreeElement::EntityTreeElement(unsigned char* octalCode) : OctreeElement() {

View file

@ -9,12 +9,11 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "KeyLightPropertyGroup.h"
#include <QJsonDocument>
#include <OctreePacketData.h>
#include <AnimationLoop.h>
#include "KeyLightPropertyGroup.h"
#include "EntityItemProperties.h"
#include "EntityItemPropertiesMacros.h"

View file

@ -37,7 +37,6 @@ ModelEntityItem::ModelEntityItem(const EntityItemID& entityItemID) : EntityItem(
_animationLoop.setResetOnRunning(false);
_type = EntityTypes::Model;
_jointMappingCompleted = false;
_lastKnownCurrentFrame = -1;
_color[0] = _color[1] = _color[2] = 0;
}
@ -204,30 +203,6 @@ void ModelEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBit
}
void ModelEntityItem::mapJoints(const QStringList& modelJointNames) {
// if we don't have animation, or we're already joint mapped then bail early
if (!hasAnimation() || jointsMapped()) {
return;
}
if (!_animation || _animation->getURL().toString() != getAnimationURL()) {
_animation = DependencyManager::get<AnimationCache>()->getAnimation(getAnimationURL());
}
if (_animation && _animation->isLoaded()) {
QStringList animationJointNames = _animation->getJointNames();
if (modelJointNames.size() > 0 && animationJointNames.size() > 0) {
_jointMapping.resize(modelJointNames.size());
for (int i = 0; i < modelJointNames.size(); i++) {
_jointMapping[i] = animationJointNames.indexOf(modelJointNames[i]);
}
_jointMappingCompleted = true;
_jointMappingURL = _animationProperties.getURL();
}
}
}
bool ModelEntityItem::isAnimatingSomething() const {
return getAnimationIsPlaying() &&
getAnimationFPS() != 0.0f &&

View file

@ -12,8 +12,6 @@
#ifndef hifi_ModelEntityItem_h
#define hifi_ModelEntityItem_h
#include <AnimationLoop.h>
#include "EntityItem.h"
#include "AnimationPropertyGroup.h"
@ -103,10 +101,7 @@ public:
void setAnimationLastFrame(float lastFrame) { _animationLoop.setLastFrame(lastFrame); }
float getAnimationLastFrame() const { return _animationLoop.getLastFrame(); }
void mapJoints(const QStringList& modelJointNames);
bool jointsMapped() const { return _jointMappingURL == getAnimationURL() && _jointMappingCompleted; }
AnimationPointer getAnimation() const { return _animation; }
bool getAnimationIsPlaying() const { return _animationLoop.getRunning(); }
float getAnimationCurrentFrame() const { return _animationLoop.getCurrentFrame(); }
float getAnimationFPS() const { return _animationLoop.getFPS(); }
@ -158,7 +153,6 @@ protected:
QUrl _parsedModelURL;
QString _compoundShapeURL;
AnimationPointer _animation;
AnimationPropertyGroup _animationProperties;
AnimationLoop _animationLoop;
@ -166,11 +160,6 @@ protected:
QString _textures;
ShapeType _shapeType = SHAPE_TYPE_NONE;
// used on client side
bool _jointMappingCompleted;
QVector<int> _jointMapping; // domain is index into model-joints, range is index into animation-joints
QString _jointMappingURL;
};
#endif // hifi_ModelEntityItem_h

View file

@ -202,7 +202,7 @@ bool OBJReader::isValidTexture(const QByteArray &filename) {
}
QUrl candidateUrl = _url.resolved(QUrl(filename));
return ResourceManager::resourceExists(candidateUrl);
return DependencyManager::get<ResourceManager>()->resourceExists(candidateUrl);
}
void OBJReader::parseMaterialLibrary(QIODevice* device) {
@ -267,7 +267,7 @@ void OBJReader::parseMaterialLibrary(QIODevice* device) {
}
std::tuple<bool, QByteArray> requestData(QUrl& url) {
auto request = ResourceManager::createResourceRequest(nullptr, url);
auto request = DependencyManager::get<ResourceManager>()->createResourceRequest(nullptr, url);
if (!request) {
return std::make_tuple(false, QByteArray());

Some files were not shown because too many files have changed in this diff Show more