Merge pull request from highfidelity/rc-46

Beta Release 46 - Includes up to Developer Release 6801
This commit is contained in:
Chris Collins 2017-07-03 12:16:43 -07:00 committed by GitHub
commit 9264365328
153 changed files with 3789 additions and 1009 deletions
assignment-client
domain-server
ice-server
interface
libraries
animation/src
audio-client/src
avatars/src
controllers/src/controllers
entities-renderer/src
entities
gpu-gl/src/gpu/gl
gpu/src/gpu
model-networking/src/model-networking
model/src/model
networking/src
plugins/src/plugins
render-utils/src
render/src/render
script-engine/src

View file

@ -7,11 +7,13 @@ 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
networking animation recording shared script-engine embedded-webserver
controllers physics plugins
physics plugins
)
if (WIN32)

View file

@ -166,19 +166,6 @@ void AudioMixer::handleMuteEnvironmentPacket(QSharedPointer<ReceivedMessage> mes
}
}
DisplayPluginList getDisplayPlugins() {
DisplayPluginList result;
return result;
}
InputPluginList getInputPlugins() {
InputPluginList result;
return result;
}
// must be here to satisfy a reference in PluginManager::saveSettings()
void saveInputPluginSettings(const InputPluginList& plugins) {}
const std::pair<QString, CodecPluginPointer> AudioMixer::negotiateCodec(std::vector<QString> codecs) {
QString selectedCodecName;
CodecPluginPointer selectedCodec;

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);
}
@ -325,8 +325,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);

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

@ -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

@ -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

@ -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

@ -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>
@ -441,6 +439,11 @@ static const QString STATE_ADVANCED_MOVEMENT_CONTROLS = "AdvancedMovement";
static const QString STATE_GROUNDED = "Grounded";
static const QString STATE_NAV_FOCUSED = "NavigationFocused";
// Statically provided display and input plugins
extern DisplayPluginList getDisplayPlugins();
extern InputPluginList getInputPlugins();
extern void saveInputPluginSettings(const InputPluginList& plugins);
bool setupEssentials(int& argc, char** argv, bool runningMarkerExisted) {
const char** constArgv = const_cast<const char**>(argv);
@ -480,7 +483,12 @@ bool setupEssentials(int& argc, char** argv, bool runningMarkerExisted) {
Setting::init();
if (auto steamClient = PluginManager::getInstance()->getSteamClientPlugin()) {
// Tell the plugin manager about our statically linked plugins
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();
}
@ -714,9 +722,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>();
@ -903,11 +915,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);
@ -1174,7 +1181,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 {
@ -4082,7 +4095,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) {

View file

@ -95,6 +95,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)
@ -297,6 +298,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);
@ -688,5 +690,7 @@ private:
QUrl _avatarOverrideUrl;
bool _saveAvatarOverrideUrl { false };
QString _cacheDir;
};
#endif // hifi_Application_h

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

@ -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());

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

@ -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

@ -1509,6 +1509,7 @@ void AvatarData::processAvatarIdentity(const QByteArray& identityData, bool& ide
>> identity.attachmentData
>> identity.displayName
>> identity.sessionDisplayName
>> identity.isReplicated
>> identity.avatarEntityData;
// set the store identity sequence number to match the incoming identity
@ -1531,6 +1532,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;
@ -1561,7 +1567,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
@ -1576,6 +1582,7 @@ QByteArray AvatarData::identityByteArray() const {
<< _attachmentData
<< _displayName
<< getSessionDisplayNameForTransport() // depends on _sessionDisplayName
<< (_isReplicated || setIsReplicated)
<< _avatarEntityData;
});
@ -1971,7 +1978,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;
}
@ -2139,12 +2146,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

@ -531,6 +531,7 @@ public:
QVector<AttachmentData> attachmentData;
QString displayName;
QString sessionDisplayName;
bool isReplicated;
AvatarEntityMap avatarEntityData;
};
@ -539,7 +540,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; }
@ -629,6 +630,8 @@ public:
float getDensity() const { return _density; }
bool getIsReplicated() const { return _isReplicated; }
signals:
void displayNameChanged();
@ -665,6 +668,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

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

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)
//
// ATTACHMENT AND JOINT PROPERTIES
@ -95,6 +96,7 @@ public:
QUuid getSessionUUID() const;
QString getDisplayName() const;
QString getSessionDisplayName() const;
bool getIsReplicated() const;
//
// ATTACHMENT AND JOINT PROPERTIES

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

@ -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);
}
@ -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

@ -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

@ -114,7 +114,9 @@ GLBackend::CommandCall GLBackend::_commandCalls[Batch::NUM_COMMANDS] =
(&::gpu::gl::GLBackend::do_getQuery),
(&::gpu::gl::GLBackend::do_resetStages),
(&::gpu::gl::GLBackend::do_disableContextViewCorrection),
(&::gpu::gl::GLBackend::do_restoreContextViewCorrection),
(&::gpu::gl::GLBackend::do_disableContextStereo),
(&::gpu::gl::GLBackend::do_restoreContextStereo),
@ -178,6 +180,11 @@ void GLBackend::init() {
int swapInterval = wglGetSwapIntervalEXT();
qCDebug(gpugllogging, "V-Sync is %s\n", (swapInterval > 0 ? "ON" : "OFF"));
}*/
#endif
#if THREADED_TEXTURE_BUFFERING
// This has to happen on the main thread in order to give the thread
// pool a reasonable parent object
GLVariableAllocationSupport::TransferJob::startBufferingThread();
#endif
});
}
@ -369,6 +376,13 @@ void GLBackend::do_resetStages(const Batch& batch, size_t paramOffset) {
resetStages();
}
void GLBackend::do_disableContextViewCorrection(const Batch& batch, size_t paramOffset) {
_transform._viewCorrectionEnabled = false;
}
void GLBackend::do_restoreContextViewCorrection(const Batch& batch, size_t paramOffset) {
_transform._viewCorrectionEnabled = true;
}
void GLBackend::do_disableContextStereo(const Batch& batch, size_t paramOffset) {

View file

@ -143,6 +143,10 @@ public:
// Reset stages
virtual void do_resetStages(const Batch& batch, size_t paramOffset) final;
virtual void do_disableContextViewCorrection(const Batch& batch, size_t paramOffset) final;
virtual void do_restoreContextViewCorrection(const Batch& batch, size_t paramOffset) final;
virtual void do_disableContextStereo(const Batch& batch, size_t paramOffset) final;
virtual void do_restoreContextStereo(const Batch& batch, size_t paramOffset) final;
@ -333,6 +337,8 @@ protected:
bool _skybox { false };
Transform _view;
CameraCorrection _correction;
bool _viewCorrectionEnabled{ true };
Mat4 _projection;
Vec4i _viewport { 0, 0, 1, 1 };
@ -400,6 +406,7 @@ protected:
bool _invalidProgram { false };
BufferView _cameraCorrectionBuffer { gpu::BufferView(std::make_shared<gpu::Buffer>(sizeof(CameraCorrection), nullptr )) };
BufferView _cameraCorrectionBufferIdentity { gpu::BufferView(std::make_shared<gpu::Buffer>(sizeof(CameraCorrection), nullptr )) };
State::Data _stateCache{ State::DEFAULT };
State::Signature _stateSignatureCache { 0 };
@ -409,6 +416,8 @@ protected:
PipelineStageState() {
_cameraCorrectionBuffer.edit<CameraCorrection>() = CameraCorrection();
_cameraCorrectionBufferIdentity.edit<CameraCorrection>() = CameraCorrection();
_cameraCorrectionBufferIdentity._buffer->flush();
}
} _pipeline;

View file

@ -77,8 +77,14 @@ void GLBackend::do_setPipeline(const Batch& batch, size_t paramOffset) {
if (_pipeline._invalidProgram) {
glUseProgram(_pipeline._program);
if (_pipeline._cameraCorrectionLocation != -1) {
auto cameraCorrectionBuffer = syncGPUObject(*_pipeline._cameraCorrectionBuffer._buffer);
gl::GLBuffer* cameraCorrectionBuffer = nullptr;
if (_transform._viewCorrectionEnabled) {
cameraCorrectionBuffer = syncGPUObject(*_pipeline._cameraCorrectionBuffer._buffer);
} else {
cameraCorrectionBuffer = syncGPUObject(*_pipeline._cameraCorrectionBufferIdentity._buffer);
}
glBindBufferRange(GL_UNIFORM_BUFFER, _pipeline._cameraCorrectionLocation, cameraCorrectionBuffer->_id, 0, sizeof(CameraCorrection));
}
(void) CHECK_GL_ERROR();
_pipeline._invalidProgram = false;

View file

@ -102,7 +102,7 @@ void GLBackend::TransformStageState::preUpdate(size_t commandIndex, const Stereo
if (_invalidView) {
// Apply the correction
if (_viewIsCamera && _correction.correction != glm::mat4()) {
if (_viewIsCamera && (_viewCorrectionEnabled && _correction.correction != glm::mat4())) {
// FIXME should I switch to using the camera correction buffer in Transform.slf and leave this out?
Transform result;
_view.mult(result, _view, _correction.correction);

View file

@ -461,11 +461,6 @@ void GLVariableAllocationSupport::updateMemoryPressure() {
if (newState != _memoryPressureState) {
_memoryPressureState = newState;
#if THREADED_TEXTURE_BUFFERING
if (MemoryPressureState::Transfer == _memoryPressureState) {
TransferJob::startBufferingThread();
}
#endif
// Clear the existing queue
_transferQueue = WorkQueue();
_promoteQueue = WorkQueue();

View file

@ -390,6 +390,13 @@ void Batch::resetStages() {
ADD_COMMAND(resetStages);
}
void Batch::disableContextViewCorrection() {
ADD_COMMAND(disableContextViewCorrection);
}
void Batch::restoreContextViewCorrection() {
ADD_COMMAND(restoreContextViewCorrection);
}
void Batch::disableContextStereo() {
ADD_COMMAND(disableContextStereo);

View file

@ -217,6 +217,9 @@ public:
// Reset the stage caches and states
void resetStages();
void disableContextViewCorrection();
void restoreContextViewCorrection();
void disableContextStereo();
void restoreContextStereo();
@ -304,6 +307,9 @@ public:
COMMAND_resetStages,
COMMAND_disableContextViewCorrection,
COMMAND_restoreContextViewCorrection,
COMMAND_disableContextStereo,
COMMAND_restoreContextStereo,

View file

@ -1,44 +0,0 @@
//
// MeshFace.cpp
// libraries/model/src/model/
//
// Created by Seth Alves on 2017-3-23
// 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
//
#include <RegisteredMetaTypes.h>
#include "MeshFace.h"
QScriptValue meshFaceToScriptValue(QScriptEngine* engine, const MeshFace &meshFace) {
QScriptValue obj = engine->newObject();
obj.setProperty("vertices", qVectorIntToScriptValue(engine, meshFace.vertexIndices));
return obj;
}
void meshFaceFromScriptValue(const QScriptValue &object, MeshFace& meshFaceResult) {
qVectorIntFromScriptValue(object.property("vertices"), meshFaceResult.vertexIndices);
}
QScriptValue qVectorMeshFaceToScriptValue(QScriptEngine* engine, const QVector<MeshFace>& vector) {
QScriptValue array = engine->newArray();
for (int i = 0; i < vector.size(); i++) {
array.setProperty(i, meshFaceToScriptValue(engine, vector.at(i)));
}
return array;
}
void qVectorMeshFaceFromScriptValue(const QScriptValue& array, QVector<MeshFace>& result) {
int length = array.property("length").toInteger();
result.clear();
for (int i = 0; i < length; i++) {
MeshFace meshFace = MeshFace();
meshFaceFromScriptValue(array.property(i), meshFace);
result << meshFace;
}
}

View file

@ -1,43 +0,0 @@
//
// MeshFace.h
// libraries/model/src/model/
//
// Created by Seth Alves on 2017-3-23
// 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
//
#ifndef hifi_MeshFace_h
#define hifi_MeshFace_h
#include <QScriptEngine>
#include <QScriptValueIterator>
#include <QtScript/QScriptValue>
#include <model/Geometry.h>
using MeshPointer = std::shared_ptr<model::Mesh>;
class MeshFace {
public:
MeshFace() {}
~MeshFace() {}
QVector<uint32_t> vertexIndices;
// TODO -- material...
};
Q_DECLARE_METATYPE(MeshFace)
Q_DECLARE_METATYPE(QVector<MeshFace>)
QScriptValue meshFaceToScriptValue(QScriptEngine* engine, const MeshFace &meshFace);
void meshFaceFromScriptValue(const QScriptValue &object, MeshFace& meshFaceResult);
QScriptValue qVectorMeshFaceToScriptValue(QScriptEngine* engine, const QVector<MeshFace>& vector);
void qVectorMeshFaceFromScriptValue(const QScriptValue& array, QVector<MeshFace>& result);
#endif // hifi_MeshFace_h

View file

@ -1,48 +0,0 @@
//
// MeshProxy.cpp
// libraries/model/src/model/
//
// Created by Seth Alves on 2017-3-22.
// 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
//
#include "MeshProxy.h"
QScriptValue meshToScriptValue(QScriptEngine* engine, MeshProxy* const &in) {
return engine->newQObject(in, QScriptEngine::QtOwnership,
QScriptEngine::ExcludeDeleteLater | QScriptEngine::ExcludeChildObjects);
}
void meshFromScriptValue(const QScriptValue& value, MeshProxy* &out) {
out = qobject_cast<MeshProxy*>(value.toQObject());
}
QScriptValue meshesToScriptValue(QScriptEngine* engine, const MeshProxyList &in) {
// QScriptValueList result;
QScriptValue result = engine->newArray();
int i = 0;
foreach (MeshProxy* const meshProxy, in) {
result.setProperty(i++, meshToScriptValue(engine, meshProxy));
}
return result;
}
void meshesFromScriptValue(const QScriptValue& value, MeshProxyList &out) {
QScriptValueIterator itr(value);
qDebug() << "in meshesFromScriptValue, value.length =" << value.property("length").toInt32();
while(itr.hasNext()) {
itr.next();
MeshProxy* meshProxy = qscriptvalue_cast<MeshProxyList::value_type>(itr.value());
if (meshProxy) {
out.append(meshProxy);
} else {
qDebug() << "null meshProxy";
}
}
}

View file

@ -1,52 +0,0 @@
//
// MeshProxy.h
// libraries/model/src/model/
//
// Created by Seth Alves on 2017-1-27.
// 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
//
#ifndef hifi_MeshProxy_h
#define hifi_MeshProxy_h
#include <QScriptEngine>
#include <QScriptValueIterator>
#include <QtScript/QScriptValue>
#include <model/Geometry.h>
using MeshPointer = std::shared_ptr<model::Mesh>;
class MeshProxy : public QObject {
Q_OBJECT
public:
MeshProxy(MeshPointer mesh) : _mesh(mesh) {}
~MeshProxy() {}
MeshPointer getMeshPointer() const { return _mesh; }
Q_INVOKABLE int getNumVertices() const { return (int)_mesh->getNumVertices(); }
Q_INVOKABLE glm::vec3 getPos3(int index) const { return _mesh->getPos3(index); }
protected:
MeshPointer _mesh;
};
Q_DECLARE_METATYPE(MeshProxy*);
class MeshProxyList : public QList<MeshProxy*> {}; // typedef and using fight with the Qt macros/templates, do this instead
Q_DECLARE_METATYPE(MeshProxyList);
QScriptValue meshToScriptValue(QScriptEngine* engine, MeshProxy* const &in);
void meshFromScriptValue(const QScriptValue& value, MeshProxy* &out);
QScriptValue meshesToScriptValue(QScriptEngine* engine, const MeshProxyList &in);
void meshesFromScriptValue(const QScriptValue& value, MeshProxyList &out);
#endif // hifi_MeshProxy_h

View file

@ -0,0 +1,27 @@
//
// SimpleMeshProxy.cpp
// libraries/model-networking/src/model-networking/
//
// Created by Seth Alves on 2017-3-22.
// 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
//
#include "SimpleMeshProxy.h"
#include <model/Geometry.h>
MeshPointer SimpleMeshProxy::getMeshPointer() const {
return _mesh;
}
int SimpleMeshProxy::getNumVertices() const {
return (int)_mesh->getNumVertices();
}
glm::vec3 SimpleMeshProxy::getPos3(int index) const {
return _mesh->getPos3(index);
}

View file

@ -0,0 +1,36 @@
//
// SimpleMeshProxy.h
// libraries/model-networking/src/model-networking/
//
// Created by Seth Alves on 2017-1-27.
// 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
//
#ifndef hifi_SimpleMeshProxy_h
#define hifi_SimpleMeshProxy_h
#include <QScriptEngine>
#include <QScriptValueIterator>
#include <QtScript/QScriptValue>
#include <RegisteredMetaTypes.h>
class SimpleMeshProxy : public MeshProxy {
public:
SimpleMeshProxy(const MeshPointer& mesh) : _mesh(mesh) { }
MeshPointer getMeshPointer() const override;
int getNumVertices() const override;
glm::vec3 getPos3(int index) const override;
protected:
const MeshPointer _mesh;
};
#endif // hifi_SimpleMeshProxy_h

View file

@ -0,0 +1,19 @@
//
// Forward.h
// libraries/model/src/model
//
// Created by Bradley Austin Davis on 2017/06/21
// Copyright 2013-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
//
#ifndef hifi_model_Forward_h
#define hifi_model_Forward_h
namespace model {
class Mesh;
using MeshPointer = std::shared_ptr<Mesh>;
}
#endif

View file

@ -31,7 +31,7 @@
MessageID AssetClient::_currentID = 0;
AssetClient::AssetClient() {
AssetClient::AssetClient(const QString& cacheDir) : _cacheDir(cacheDir) {
setCustomDeleter([](Dependency* dependency){
static_cast<AssetClient*>(dependency)->deleteLater();
});
@ -55,14 +55,15 @@ void AssetClient::init() {
// Setup disk cache if not already
auto& networkAccessManager = NetworkAccessManager::getInstance();
if (!networkAccessManager.cache()) {
QString cachePath = QStandardPaths::writableLocation(QStandardPaths::DataLocation);
cachePath = !cachePath.isEmpty() ? cachePath : "interfaceCache";
if (_cacheDir.isEmpty()) {
QString cachePath = QStandardPaths::writableLocation(QStandardPaths::DataLocation);
_cacheDir = !cachePath.isEmpty() ? cachePath : "interfaceCache";
}
QNetworkDiskCache* cache = new QNetworkDiskCache();
cache->setMaximumCacheSize(MAXIMUM_CACHE_SIZE);
cache->setCacheDirectory(cachePath);
cache->setCacheDirectory(_cacheDir);
networkAccessManager.setCache(cache);
qInfo() << "ResourceManager disk cache setup at" << cachePath
qInfo() << "ResourceManager disk cache setup at" << _cacheDir
<< "(size:" << MAXIMUM_CACHE_SIZE / BYTES_PER_GIGABYTES << "GB)";
}
}

View file

@ -49,7 +49,7 @@ using ProgressCallback = std::function<void(qint64 totalReceived, qint64 total)>
class AssetClient : public QObject, public Dependency {
Q_OBJECT
public:
AssetClient();
AssetClient(const QString& cacheDir="");
Q_INVOKABLE GetMappingRequest* createGetMappingRequest(const AssetPath& path);
Q_INVOKABLE GetAllMappingsRequest* createGetAllMappingsRequest();
@ -109,6 +109,8 @@ private:
std::unordered_map<SharedNodePointer, std::unordered_map<MessageID, GetInfoCallback>> _pendingInfoRequests;
std::unordered_map<SharedNodePointer, std::unordered_map<MessageID, UploadResultCallback>> _pendingUploads;
QString _cacheDir;
friend class AssetRequest;
friend class AssetUpload;
friend class MappingRequest;

View file

@ -28,7 +28,7 @@
ResourceManager::ResourceManager() {
_thread.setObjectName("Resource Manager Thread");
auto assetClient = DependencyManager::set<AssetClient>();
auto assetClient = DependencyManager::set<AssetClient>(_cacheDir);
assetClient->moveToThread(&_thread);
QObject::connect(&_thread, &QThread::started, assetClient.data(), &AssetClient::init);
@ -160,3 +160,7 @@ bool ResourceManager::resourceExists(const QUrl& url) {
return false;
}
void ResourceManager::setCacheDir(const QString& cacheDir) {
// TODO: check for existence?
_cacheDir = cacheDir;
}

View file

@ -48,6 +48,9 @@ public:
// to return to the calling thread so that events can still be processed.
bool resourceExists(const QUrl& url);
// adjust where we persist the cache
void setCacheDir(const QString& cacheDir);
private:
QThread _thread;
@ -56,6 +59,7 @@ private:
PrefixMap _prefixMap;
QMutex _prefixMapLock;
QString _cacheDir;
};
#endif

View file

@ -69,7 +69,7 @@ PacketVersion versionForPacketType(PacketType packetType) {
case PacketType::AvatarData:
case PacketType::BulkAvatarData:
case PacketType::KillAvatar:
return static_cast<PacketVersion>(AvatarMixerPacketVersion::AvatarIdentitySequenceFront);
return static_cast<PacketVersion>(AvatarMixerPacketVersion::IsReplicatedInAvatarIdentity);
case PacketType::MessagesData:
return static_cast<PacketVersion>(MessageDataVersion::TextOrBinaryData);
case PacketType::ICEServerHeartbeat:

View file

@ -247,7 +247,8 @@ enum class AvatarMixerPacketVersion : PacketVersion {
IdentityPacketsIncludeUpdateTime,
AvatarIdentitySequenceId,
MannequinDefaultAvatar,
AvatarIdentitySequenceFront
AvatarIdentitySequenceFront,
IsReplicatedInAvatarIdentity
};
enum class DomainConnectRequestVersion : PacketVersion {

View file

@ -9,6 +9,7 @@
#include <vector>
#include <memory>
#include <functional>
enum class PluginType {
DISPLAY_PLUGIN,
@ -26,8 +27,12 @@ class PluginManager;
using DisplayPluginPointer = std::shared_ptr<DisplayPlugin>;
using DisplayPluginList = std::vector<DisplayPluginPointer>;
using DisplayPluginProvider = std::function<DisplayPluginList()>;
using InputPluginPointer = std::shared_ptr<InputPlugin>;
using InputPluginList = std::vector<InputPluginPointer>;
using InputPluginProvider = std::function<InputPluginList()>;
using CodecPluginPointer = std::shared_ptr<CodecPlugin>;
using CodecPluginList = std::vector<CodecPluginPointer>;
using CodecPluginProvider = std::function<CodecPluginList()>;
using SteamClientPluginPointer = std::shared_ptr<SteamClientPlugin>;
using InputPluginSettingsPersister = std::function<void(const InputPluginList&)>;

View file

@ -24,6 +24,22 @@
#include "PluginLogging.h"
void PluginManager::setDisplayPluginProvider(const DisplayPluginProvider& provider) {
_displayPluginProvider = provider;
}
void PluginManager::setInputPluginProvider(const InputPluginProvider& provider) {
_inputPluginProvider = provider;
}
void PluginManager::setCodecPluginProvider(const CodecPluginProvider& provider) {
_codecPluginProvider = provider;
}
void PluginManager::setInputPluginSettingsPersister(const InputPluginSettingsPersister& persister) {
_inputSettingsPersister = persister;
}
PluginManager* PluginManager::getInstance() {
static PluginManager _manager;
return &_manager;
@ -117,12 +133,12 @@ const LoaderList& getLoadedPlugins() {
PluginManager::PluginManager() {
}
extern CodecPluginList getCodecPlugins();
const CodecPluginList& PluginManager::getCodecPlugins() {
static CodecPluginList codecPlugins;
static std::once_flag once;
std::call_once(once, [&] {
codecPlugins = _codecPluginProvider();
// Now grab the dynamic plugins
for (auto loader : getLoadedPlugins()) {
CodecProvider* codecProvider = qobject_cast<CodecProvider*>(loader->instance());
@ -163,11 +179,6 @@ const SteamClientPluginPointer PluginManager::getSteamClientPlugin() {
#ifndef Q_OS_ANDROID
// TODO migrate to a DLL model where plugins are discovered and loaded at runtime by the PluginManager class
extern DisplayPluginList getDisplayPlugins();
extern InputPluginList getInputPlugins();
extern void saveInputPluginSettings(const InputPluginList& plugins);
static DisplayPluginList displayPlugins;
const DisplayPluginList& PluginManager::getDisplayPlugins() {
@ -183,7 +194,7 @@ const DisplayPluginList& PluginManager::getDisplayPlugins() {
std::call_once(once, [&] {
// Grab the built in plugins
displayPlugins = ::getDisplayPlugins();
displayPlugins = _displayPluginProvider();
// Now grab the dynamic plugins
@ -229,7 +240,7 @@ const InputPluginList& PluginManager::getInputPlugins() {
};
std::call_once(once, [&] {
inputPlugins = ::getInputPlugins();
inputPlugins = _inputPluginProvider();
// Now grab the dynamic plugins
for (auto loader : getLoadedPlugins()) {
@ -288,7 +299,7 @@ void PluginManager::disableInputs(const QStringList& inputs) {
}
void PluginManager::saveSettings() {
saveInputPluginSettings(getInputPlugins());
_inputSettingsPersister(getInputPlugins());
}
void PluginManager::shutdown() {

View file

@ -31,6 +31,17 @@ public:
void setContainer(PluginContainer* container) { _container = container; }
void shutdown();
// Application that have statically linked plugins can expose them to the plugin manager with these function
void setDisplayPluginProvider(const DisplayPluginProvider& provider);
void setInputPluginProvider(const InputPluginProvider& provider);
void setCodecPluginProvider(const CodecPluginProvider& provider);
void setInputPluginSettingsPersister(const InputPluginSettingsPersister& persister);
private:
DisplayPluginProvider _displayPluginProvider { []()->DisplayPluginList { return {}; } };
InputPluginProvider _inputPluginProvider { []()->InputPluginList { return {}; } };
CodecPluginProvider _codecPluginProvider { []()->CodecPluginList { return {}; } };
InputPluginSettingsPersister _inputSettingsPersister { [](const InputPluginList& list) {} };
PluginContainer* _container { nullptr };
};

View file

@ -39,7 +39,7 @@ void DeferredFrameTransform::update(RenderArgs* args) {
args->getViewFrustum().evalProjectionMatrix(frameTransformBuffer.projectionMono);
// Running in stero ?
bool isStereo = args->_context->isStereo();
bool isStereo = args->isStereo();
if (!isStereo) {
frameTransformBuffer.projection[0] = frameTransformBuffer.projectionMono;
frameTransformBuffer.stereoInfo = glm::vec4(0.0f, (float)args->_viewport.z, 0.0f, 0.0f);

View file

@ -867,10 +867,6 @@ bool Model::getRelativeDefaultJointTranslation(int jointIndex, glm::vec3& transl
return _rig.getRelativeDefaultJointTranslation(jointIndex, translationOut);
}
bool Model::getJointCombinedRotation(int jointIndex, glm::quat& rotation) const {
return _rig.getJointCombinedRotation(jointIndex, rotation, _rotation);
}
QStringList Model::getJointNames() const {
if (QThread::currentThread() != thread()) {
QStringList result;

View file

@ -177,7 +177,6 @@ public:
int getJointStateCount() const { return (int)_rig.getJointStateCount(); }
bool getJointPositionInWorldFrame(int jointIndex, glm::vec3& position) const;
bool getJointRotationInWorldFrame(int jointIndex, glm::quat& rotation) const;
bool getJointCombinedRotation(int jointIndex, glm::quat& rotation) const;
/// \param jointIndex index of joint in model structure
/// \param rotation[out] rotation of joint in model-frame

View file

@ -406,7 +406,7 @@ void Blit::run(const RenderContextPointer& renderContext, const gpu::Framebuffer
batch.setFramebuffer(blitFbo);
if (renderArgs->_renderMode == RenderArgs::MIRROR_RENDER_MODE) {
if (renderArgs->_context->isStereo()) {
if (renderArgs->isStereo()) {
gpu::Vec4i srcRectLeft;
srcRectLeft.z = width / 2;
srcRectLeft.w = height;

View file

@ -459,7 +459,7 @@ void SurfaceGeometryPass::run(const render::RenderContextPointer& renderContext,
auto diffuseVPipeline = _diffusePass.getBlurVPipeline();
auto diffuseHPipeline = _diffusePass.getBlurHPipeline();
_diffusePass.getParameters()->setWidthHeight(curvatureViewport.z, curvatureViewport.w, args->_context->isStereo());
_diffusePass.getParameters()->setWidthHeight(curvatureViewport.z, curvatureViewport.w, args->isStereo());
glm::ivec2 textureSize(curvatureTexture->getDimensions());
_diffusePass.getParameters()->setTexcoordTransform(gpu::Framebuffer::evalSubregionTexcoordTransformCoefficients(textureSize, curvatureViewport));
_diffusePass.getParameters()->setDepthPerspective(args->getViewFrustum().getProjection()[1][1]);

View file

@ -99,6 +99,8 @@ namespace render {
void pushViewFrustum(const ViewFrustum& viewFrustum) { _viewFrustums.push(viewFrustum); }
void popViewFrustum() { _viewFrustums.pop(); }
bool isStereo() const { return _displayMode != MONO; }
std::shared_ptr<gpu::Context> _context;
std::shared_ptr<gpu::Framebuffer> _blitFramebuffer;
std::shared_ptr<render::ShapePipeline> _pipeline;

View file

@ -217,7 +217,7 @@ void BlurGaussian::run(const RenderContextPointer& renderContext, const gpu::Fra
auto blurVPipeline = getBlurVPipeline();
auto blurHPipeline = getBlurHPipeline();
_parameters->setWidthHeight(args->_viewport.z, args->_viewport.w, args->_context->isStereo());
_parameters->setWidthHeight(args->_viewport.z, args->_viewport.w, args->isStereo());
glm::ivec2 textureSize(blurringResources.sourceTexture->getDimensions());
_parameters->setTexcoordTransform(gpu::Framebuffer::evalSubregionTexcoordTransformCoefficients(textureSize, args->_viewport));
@ -330,7 +330,7 @@ void BlurGaussianDepthAware::run(const RenderContextPointer& renderContext, cons
auto sourceViewport = args->_viewport;
_parameters->setWidthHeight(sourceViewport.z, sourceViewport.w, args->_context->isStereo());
_parameters->setWidthHeight(sourceViewport.z, sourceViewport.w, args->isStereo());
glm::ivec2 textureSize(blurringResources.sourceTexture->getDimensions());
_parameters->setTexcoordTransform(gpu::Framebuffer::evalSubregionTexcoordTransformCoefficients(textureSize, sourceViewport));
_parameters->setDepthPerspective(args->getViewFrustum().getProjection()[1][1]);

View file

@ -9,17 +9,17 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "ModelScriptingInterface.h"
#include <QScriptEngine>
#include <QScriptValueIterator>
#include <QtScript/QScriptValue>
#include <model-networking/MeshFace.h>
#include <model-networking/SimpleMeshProxy.h>
#include "ScriptEngine.h"
#include "ScriptEngineLogging.h"
#include "ModelScriptingInterface.h"
#include "OBJWriter.h"
ModelScriptingInterface::ModelScriptingInterface(QObject* parent) : QObject(parent) {
_modelScriptEngine = qobject_cast<ScriptEngine*>(parent);
_modelScriptEngine = qobject_cast<QScriptEngine*>(parent);
qScriptRegisterSequenceMetaType<QList<MeshProxy*>>(_modelScriptEngine);
qScriptRegisterMetaType(_modelScriptEngine, meshFaceToScriptValue, meshFaceFromScriptValue);
@ -118,7 +118,7 @@ QScriptValue ModelScriptingInterface::appendMeshes(MeshProxyList in) {
(gpu::Byte*) parts.data()), gpu::Element::PART_DRAWCALL));
MeshProxy* resultProxy = new MeshProxy(result);
MeshProxy* resultProxy = new SimpleMeshProxy(result);
return meshToScriptValue(_modelScriptEngine, resultProxy);
}
@ -134,7 +134,7 @@ QScriptValue ModelScriptingInterface::transformMesh(glm::mat4 transform, MeshPro
model::MeshPointer result = mesh->map([&](glm::vec3 position){ return glm::vec3(transform * glm::vec4(position, 1.0f)); },
[&](glm::vec3 normal){ return glm::vec3(transform * glm::vec4(normal, 0.0f)); },
[&](uint32_t index){ return index; });
MeshProxy* resultProxy = new MeshProxy(result);
MeshProxy* resultProxy = new SimpleMeshProxy(result);
return meshToScriptValue(_modelScriptEngine, resultProxy);
}
@ -188,6 +188,6 @@ QScriptValue ModelScriptingInterface::newMesh(const QVector<glm::vec3>& vertices
MeshProxy* meshProxy = new MeshProxy(mesh);
MeshProxy* meshProxy = new SimpleMeshProxy(mesh);
return meshToScriptValue(_modelScriptEngine, meshProxy);
}

View file

@ -9,19 +9,13 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#ifndef hifi_ModelScriptingInterface_h
#define hifi_ModelScriptingInterface_h
#include <QtCore/QObject>
#include <QScriptValue>
#include <OBJWriter.h>
#include <model/Geometry.h>
#include <model-networking/MeshProxy.h>
#include <model-networking/MeshFace.h>
using MeshPointer = std::shared_ptr<model::Mesh>;
class ScriptEngine;
#include <RegisteredMetaTypes.h>
class QScriptEngine;
class ModelScriptingInterface : public QObject {
Q_OBJECT
@ -37,7 +31,7 @@ public:
const QVector<MeshFace>& faces);
private:
ScriptEngine* _modelScriptEngine { nullptr };
QScriptEngine* _modelScriptEngine { nullptr };
};
#endif // hifi_ModelScriptingInterface_h

View file

@ -8,9 +8,17 @@
#include "RecordingScriptingInterface.h"
#include <QStandardPaths>
#include <QtCore/QThread>
#include <QtCore/QUrl>
#include <QtScript/QScriptValue>
#include <QtWidgets/QFileDialog>
#include <AssetClient.h>
#include <AssetUpload.h>
#include <BuildInfo.h>
#include <NumericalConstants.h>
#include <PathUtils.h>
#include <Transform.h>
#include <recording/Deck.h>
#include <recording/Recorder.h>
@ -18,13 +26,6 @@
#include <recording/Frame.h>
#include <recording/ClipCache.h>
#include <QtScript/QScriptValue>
#include <AssetClient.h>
#include <AssetUpload.h>
#include <QtCore/QUrl>
#include <QtWidgets/QFileDialog>
#include "ScriptEngineLogging.h"
using namespace recording;
@ -188,6 +189,14 @@ void RecordingScriptingInterface::stopRecording() {
_lastClip->seek(0);
}
QString RecordingScriptingInterface::getDefaultRecordingSaveDirectory() {
QString directory = PathUtils::getAppLocalDataPath() + "Avatar Recordings/";
if (!QDir(directory).exists()) {
QDir().mkdir(directory);
}
return directory;
}
void RecordingScriptingInterface::saveRecording(const QString& filename) {
if (QThread::currentThread() != thread()) {
QMetaObject::invokeMethod(this, "saveRecording", Qt::BlockingQueuedConnection,

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