From 44374732fa44d64dca310e91fd956b012b00f2aa Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Thu, 6 Mar 2014 19:30:24 -0800 Subject: [PATCH] Further joint bits. --- examples/crazylegs.js | 23 +++++++ interface/src/Application.cpp | 2 + interface/src/avatar/Avatar.cpp | 29 +++++++++ interface/src/avatar/Avatar.h | 4 ++ interface/src/avatar/MyAvatar.cpp | 14 +++++ interface/src/avatar/MyAvatar.h | 2 + interface/src/renderer/FBXReader.cpp | 8 +++ interface/src/renderer/FBXReader.h | 7 ++- interface/src/renderer/Model.cpp | 11 ++-- interface/src/renderer/Model.h | 2 +- libraries/avatars/src/AvatarData.cpp | 94 ++++++++++++++++++++++++++++ libraries/avatars/src/AvatarData.h | 16 +++++ 12 files changed, 204 insertions(+), 8 deletions(-) create mode 100644 examples/crazylegs.js diff --git a/examples/crazylegs.js b/examples/crazylegs.js new file mode 100644 index 0000000000..0f26088ae0 --- /dev/null +++ b/examples/crazylegs.js @@ -0,0 +1,23 @@ +// +// crazylegs.js +// hifi +// +// Created by Andrzej Kapolka on 3/6/14. +// Copyright (c) 2014 High Fidelity, Inc. All rights reserved. +// + +var FREQUENCY = 5.0; + +var AMPLITUDE = 45.0; + +var cumulativeTime = 0.0; + +Script.update.connect(function(deltaTime) { + cumulativeTime += deltaTime; + MyAvatar.setJointData("joint_R_hip", Quat.fromPitchYawRoll(0.0, 0.0, AMPLITUDE * Math.sin(cumulativeTime * FREQUENCY))); + MyAvatar.setJointData("joint_L_hip", Quat.fromPitchYawRoll(0.0, 0.0, -AMPLITUDE * Math.sin(cumulativeTime * FREQUENCY))); + MyAvatar.setJointData("joint_R_knee", Quat.fromPitchYawRoll(0.0, 0.0, + AMPLITUDE * (1.0 + Math.sin(cumulativeTime * FREQUENCY)))); + MyAvatar.setJointData("joint_L_knee", Quat.fromPitchYawRoll(0.0, 0.0, + AMPLITUDE * (1.0 - Math.sin(cumulativeTime * FREQUENCY)))); +}); diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 434aa0876f..d0f924ecf5 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -121,6 +121,8 @@ void messageHandler(QtMsgType type, const QMessageLogContext& context, const QSt QString& Application::resourcesPath() { #ifdef Q_OS_MAC static QString staticResourcePath = QCoreApplication::applicationDirPath() + "/../Resources/"; +#elif defined Q_OS_LINUX + static QString staticResourcePath = "resources/"; #else static QString staticResourcePath = QCoreApplication::applicationDirPath() + "/resources/"; #endif diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index 44dc83d8a6..2301822e28 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -581,6 +581,35 @@ bool Avatar::findParticleCollisions(const glm::vec3& particleCenter, float parti return collided; } +glm::quat Avatar::getJointRotation(int index) const { + if (QThread::currentThread() != thread()) { + return AvatarData::getJointRotation(index); + } + glm::quat rotation; + _skeletonModel.getJointState(index, rotation); + return rotation; +} + +int Avatar::getJointIndex(const QString& name) const { + if (QThread::currentThread() != thread()) { + int result; + QMetaObject::invokeMethod(const_cast(this), "getJointIndex", Qt::BlockingQueuedConnection, + Q_RETURN_ARG(int, result), Q_ARG(const QString&, name)); + return result; + } + return _skeletonModel.isActive() ? _skeletonModel.getGeometry()->getFBXGeometry().getJointIndex(name) : -1; +} + +QStringList Avatar::getJointNames() const { + if (QThread::currentThread() != thread()) { + QStringList result; + QMetaObject::invokeMethod(const_cast(this), "getJointNames", Qt::BlockingQueuedConnection, + Q_RETURN_ARG(QStringList, result)); + return result; + } + return _skeletonModel.isActive() ? _skeletonModel.getGeometry()->getFBXGeometry().getJointNames() : QStringList(); +} + void Avatar::setFaceModelURL(const QUrl& faceModelURL) { AvatarData::setFaceModelURL(faceModelURL); const QUrl DEFAULT_FACE_MODEL_URL = QUrl::fromLocalFile(Application::resourcesPath() + "meshes/defaultAvatar_head.fst"); diff --git a/interface/src/avatar/Avatar.h b/interface/src/avatar/Avatar.h index 9e0ec8100f..bba86828cb 100755 --- a/interface/src/avatar/Avatar.h +++ b/interface/src/avatar/Avatar.h @@ -122,6 +122,10 @@ public: virtual bool isMyAvatar() { return false; } + virtual glm::quat getJointRotation(int index) const; + virtual int getJointIndex(const QString& name) const; + virtual QStringList getJointNames() const; + virtual void setFaceModelURL(const QUrl& faceModelURL); virtual void setSkeletonModelURL(const QUrl& skeletonModelURL); virtual void setDisplayName(const QString& displayName); diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index fe18e066ed..7108e0d0cc 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -672,6 +672,20 @@ glm::vec3 MyAvatar::getUprightHeadPosition() const { return _position + getWorldAlignedOrientation() * glm::vec3(0.0f, getPelvisToHeadLength(), 0.0f); } +void MyAvatar::setJointData(int index, const glm::quat& rotation) { + Avatar::setJointData(index, rotation); + if (QThread::currentThread() == thread()) { + _skeletonModel.setJointState(index, true, rotation); + } +} + +void MyAvatar::clearJointData(int index) { + Avatar::clearJointData(index); + if (QThread::currentThread() == thread()) { + _skeletonModel.setJointState(index, false); + } +} + void MyAvatar::setFaceModelURL(const QUrl& faceModelURL) { Avatar::setFaceModelURL(faceModelURL); _billboardValid = false; diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index e9dfbd8bd0..f1448edb97 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -81,6 +81,8 @@ public: void updateLookAtTargetAvatar(); void clearLookAtTargetAvatar(); + virtual void setJointData(int index, const glm::quat& rotation); + virtual void clearJointData(int index); virtual void setFaceModelURL(const QUrl& faceModelURL); virtual void setSkeletonModelURL(const QUrl& skeletonModelURL); public slots: diff --git a/interface/src/renderer/FBXReader.cpp b/interface/src/renderer/FBXReader.cpp index 1ab362bb6d..c13cc7deab 100644 --- a/interface/src/renderer/FBXReader.cpp +++ b/interface/src/renderer/FBXReader.cpp @@ -46,6 +46,14 @@ void Extents::addPoint(const glm::vec3& point) { maximum = glm::max(maximum, point); } +QStringList FBXGeometry::getJointNames() const { + QStringList names; + foreach (const FBXJoint& joint, joints) { + names.append(joint.name); + } + return names; +} + static int fbxGeometryMetaTypeId = qRegisterMetaType(); template QVariant readBinaryArray(QDataStream& in) { diff --git a/interface/src/renderer/FBXReader.h b/interface/src/renderer/FBXReader.h index 3cd47e66b6..3bc80bbf2f 100644 --- a/interface/src/renderer/FBXReader.h +++ b/interface/src/renderer/FBXReader.h @@ -81,7 +81,7 @@ public: glm::quat inverseDefaultRotation; glm::quat inverseBindRotation; glm::mat4 bindTransform; - QString name; // temp field for debugging + QString name; glm::vec3 shapePosition; // in joint frame glm::quat shapeRotation; // in joint frame int shapeType; @@ -152,7 +152,7 @@ class FBXGeometry { public: QVector joints; - QHash jointIndices; + QHash jointIndices; ///< 1-based, so as to more easily detect missing indices QVector meshes; @@ -181,6 +181,9 @@ public: Extents staticExtents; QVector attachments; + + int getJointIndex(const QString& name) const { return jointIndices.value(name) - 1; } + QStringList getJointNames() const; }; Q_DECLARE_METATYPE(FBXGeometry) diff --git a/interface/src/renderer/Model.cpp b/interface/src/renderer/Model.cpp index afbad5cf63..c52e4d4603 100644 --- a/interface/src/renderer/Model.cpp +++ b/interface/src/renderer/Model.cpp @@ -379,7 +379,7 @@ Extents Model::getStaticExtents() const { } bool Model::getJointState(int index, glm::quat& rotation) const { - if (index >= _jointStates.size()) { + if (index == -1 || index >= _jointStates.size()) { return false; } rotation = _jointStates.at(index).rotation; @@ -391,7 +391,7 @@ bool Model::getJointState(int index, glm::quat& rotation) const { } void Model::setJointState(int index, bool valid, const glm::quat& rotation) { - if (index < _jointStates.size()) { + if (index != -1 && index < _jointStates.size()) { _jointStates[index].rotation = valid ? rotation : _geometry->getFBXGeometry().joints.at(index).rotation; } } @@ -896,9 +896,10 @@ QVector Model::updateGeometry(bool delayLoad) { newJointStates = createJointStates(newGeometry); for (QHash::const_iterator it = oldGeometry.jointIndices.constBegin(); it != oldGeometry.jointIndices.constEnd(); it++) { - int newIndex = newGeometry.jointIndices.value(it.key()); - if (newIndex != 0) { - newJointStates[newIndex - 1] = _jointStates.at(it.value() - 1); + int oldIndex = it.value() - 1; + int newIndex = newGeometry.getJointIndex(it.key()); + if (newIndex != -1) { + newJointStates[newIndex] = _jointStates.at(oldIndex); } } } diff --git a/interface/src/renderer/Model.h b/interface/src/renderer/Model.h index 6fc2ea5521..763c30ad39 100644 --- a/interface/src/renderer/Model.h +++ b/interface/src/renderer/Model.h @@ -89,7 +89,7 @@ public: bool getJointState(int index, glm::quat& rotation) const; /// Sets the joint state at the specified index. - void setJointState(int index, bool valid, const glm::quat& rotation); + void setJointState(int index, bool valid, const glm::quat& rotation = glm::quat()); /// Returns the index of the left hand joint, or -1 if not found. int getLeftHandJointIndex() const { return isActive() ? _geometry->getFBXGeometry().leftHandJointIndex : -1; } diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index 1de3988bdc..2483e0514b 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -11,6 +11,7 @@ #include #include +#include #include #include #include @@ -314,6 +315,99 @@ int AvatarData::parseData(const QByteArray& packet) { return sourceBuffer - startPosition; } +void AvatarData::setJointData(int index, const glm::quat& rotation) { + if (index == -1) { + return; + } + if (QThread::currentThread() != thread()) { + QMetaObject::invokeMethod(this, "setJointData", Q_ARG(int, index), Q_ARG(const glm::quat&, rotation)); + return; + } + if (_jointData.size() <= index) { + _jointData.resize(index + 1); + } + JointData& data = _jointData[index]; + data.valid = true; + data.rotation = rotation; +} + +void AvatarData::clearJointData(int index) { + if (index == -1) { + return; + } + if (QThread::currentThread() != thread()) { + QMetaObject::invokeMethod(this, "clearJointData", Q_ARG(int, index)); + return; + } + if (_jointData.size() <= index) { + _jointData.resize(index + 1); + } + _jointData[index].valid = false; +} + +bool AvatarData::isJointDataValid(int index) const { + if (index == -1) { + return false; + } + if (QThread::currentThread() != thread()) { + bool result; + QMetaObject::invokeMethod(const_cast(this), "isJointDataValid", Qt::BlockingQueuedConnection, + Q_RETURN_ARG(bool, result), Q_ARG(int, index)); + return result; + } + return index < _jointData.size() && _jointData.at(index).valid; +} + +glm::quat AvatarData::getJointRotation(int index) const { + if (index == -1) { + return glm::quat(); + } + if (QThread::currentThread() != thread()) { + glm::quat result; + QMetaObject::invokeMethod(const_cast(this), "getJointRotation", Qt::BlockingQueuedConnection, + Q_RETURN_ARG(glm::quat, result), Q_ARG(int, index)); + return result; + } + return index < _jointData.size() ? _jointData.at(index).rotation : glm::quat(); +} + +void AvatarData::setJointData(const QString& name, const glm::quat& rotation) { + if (QThread::currentThread() != thread()) { + QMetaObject::invokeMethod(this, "setJointData", Q_ARG(const QString&, name), + Q_ARG(const glm::quat&, rotation)); + return; + } + setJointData(getJointIndex(name), rotation); +} + +void AvatarData::clearJointData(const QString& name) { + if (QThread::currentThread() != thread()) { + QMetaObject::invokeMethod(this, "clearJointData", Q_ARG(const QString&, name)); + return; + } + clearJointData(getJointIndex(name)); +} + +bool AvatarData::isJointDataValid(const QString& name) const { + if (QThread::currentThread() != thread()) { + bool result; + QMetaObject::invokeMethod(const_cast(this), "isJointDataValid", Qt::BlockingQueuedConnection, + Q_RETURN_ARG(bool, result), Q_ARG(const QString&, name)); + return result; + } + return isJointDataValid(getJointIndex(name)); +} + +glm::quat AvatarData::getJointRotation(const QString& name) const { + if (QThread::currentThread() != thread()) { + glm::quat result; + QMetaObject::invokeMethod(const_cast(this), "getJointRotation", Qt::BlockingQueuedConnection, + Q_RETURN_ARG(glm::quat, result), Q_ARG(const QString&, name)); + return result; + } + return getJointRotation(getJointIndex(name)); +} + bool AvatarData::hasIdentityChangedAfterParsing(const QByteArray &packet) { QDataStream packetStream(packet); packetStream.skipRawData(numBytesForPacketHeader(packet)); diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index 966341824c..925540f899 100755 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -31,6 +31,7 @@ typedef unsigned long long quint64; #include #include +#include #include #include #include @@ -143,6 +144,21 @@ public: const QVector& getJointData() const { return _jointData; } void setJointData(const QVector& jointData) { _jointData = jointData; } + Q_INVOKABLE virtual void setJointData(int index, const glm::quat& rotation); + Q_INVOKABLE virtual void clearJointData(int index); + Q_INVOKABLE bool isJointDataValid(int index) const; + Q_INVOKABLE virtual glm::quat getJointRotation(int index) const; + + Q_INVOKABLE void setJointData(const QString& name, const glm::quat& rotation); + Q_INVOKABLE void clearJointData(const QString& name); + Q_INVOKABLE bool isJointDataValid(const QString& name) const; + Q_INVOKABLE glm::quat getJointRotation(const QString& name) const; + + /// Returns the index of the joint with the specified name, or -1 if not found/unknown. + Q_INVOKABLE virtual int getJointIndex(const QString& name) const { return -1; } + + Q_INVOKABLE virtual QStringList getJointNames() const { return QStringList(); } + // key state void setKeyState(KeyState s) { _keyState = s; } KeyState keyState() const { return _keyState; }