mirror of
https://github.com/overte-org/overte.git
synced 2025-04-20 18:23:54 +02:00
Merge branch 'master' of https://github.com/worklist/hifi into fix_mirror_mode
This commit is contained in:
commit
46eadb5fb7
29 changed files with 1206 additions and 534 deletions
|
@ -8,7 +8,7 @@
|
|||
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
const int MAX_CLUSTERS = 32;
|
||||
const int MAX_CLUSTERS = 64;
|
||||
const int INDICES_PER_VERTEX = 4;
|
||||
|
||||
uniform mat4 clusterMatrices[MAX_CLUSTERS];
|
|
@ -1375,7 +1375,8 @@ void Application::processAvatarURLsMessage(unsigned char* packetData, size_t dat
|
|||
QMetaObject::invokeMethod(avatar->getVoxels(), "setVoxelURL", Q_ARG(QUrl, voxelURL));
|
||||
|
||||
// use this timing to as the data-server for an updated mesh for this avatar (if we have UUID)
|
||||
DataServerClient::getValueForKeyAndUUID(DataServerKey::FaceMeshURL, avatar->getUUID());
|
||||
DataServerClient::getValuesForKeysAndUUID(QStringList() << DataServerKey::FaceMeshURL << DataServerKey::SkeletonURL,
|
||||
avatar->getUUID());
|
||||
}
|
||||
|
||||
void Application::processAvatarFaceVideoMessage(unsigned char* packetData, size_t dataBytes) {
|
||||
|
@ -1383,7 +1384,7 @@ void Application::processAvatarFaceVideoMessage(unsigned char* packetData, size_
|
|||
if (!avatar) {
|
||||
return;
|
||||
}
|
||||
avatar->getHead().getFace().processVideoMessage(packetData, dataBytes);
|
||||
avatar->getHead().getVideoFace().processVideoMessage(packetData, dataBytes);
|
||||
}
|
||||
|
||||
void Application::checkBandwidthMeterClick() {
|
||||
|
@ -1690,6 +1691,7 @@ void Application::init() {
|
|||
if (!_profile.getUsername().isEmpty()) {
|
||||
// we have a username for this avatar, ask the data-server for the mesh URL for this avatar
|
||||
DataServerClient::getClientValueForKey(DataServerKey::FaceMeshURL);
|
||||
DataServerClient::getClientValueForKey(DataServerKey::SkeletonURL);
|
||||
}
|
||||
|
||||
// Set up VoxelSystem after loading preferences so we can get the desired max voxel count
|
||||
|
|
|
@ -131,8 +131,8 @@ void DataServerClient::processSendFromDataServer(unsigned char* packetData, int
|
|||
if (keyList[i] == DataServerKey::FaceMeshURL) {
|
||||
|
||||
if (userUUID.isNull() || userUUID == Application::getInstance()->getProfile()->getUUID()) {
|
||||
qDebug("Changing user's face model URL to %s\n", valueList[0].toLocal8Bit().constData());
|
||||
Application::getInstance()->getProfile()->setFaceModelURL(QUrl(valueList[0]));
|
||||
qDebug("Changing user's face model URL to %s\n", valueList[i].toLocal8Bit().constData());
|
||||
Application::getInstance()->getProfile()->setFaceModelURL(QUrl(valueList[i]));
|
||||
} else {
|
||||
// mesh URL for a UUID, find avatar in our list
|
||||
NodeList* nodeList = NodeList::getInstance();
|
||||
|
@ -141,9 +141,27 @@ void DataServerClient::processSendFromDataServer(unsigned char* packetData, int
|
|||
Avatar* avatar = (Avatar *) node->getLinkedData();
|
||||
|
||||
if (avatar->getUUID() == userUUID) {
|
||||
QMetaObject::invokeMethod(&avatar->getHead().getBlendFace(),
|
||||
"setModelURL",
|
||||
Q_ARG(QUrl, QUrl(valueList[0])));
|
||||
QMetaObject::invokeMethod(&avatar->getHead().getFaceModel(),
|
||||
"setURL", Q_ARG(QUrl, QUrl(valueList[i])));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (keyList[i] == DataServerKey::SkeletonURL) {
|
||||
|
||||
if (userUUID.isNull() || userUUID == Application::getInstance()->getProfile()->getUUID()) {
|
||||
qDebug("Changing user's skeleton URL to %s\n", valueList[i].toLocal8Bit().constData());
|
||||
Application::getInstance()->getProfile()->setSkeletonModelURL(QUrl(valueList[i]));
|
||||
} else {
|
||||
// skeleton URL for a UUID, find avatar in our list
|
||||
NodeList* nodeList = NodeList::getInstance();
|
||||
for (NodeList::iterator node = nodeList->begin(); node != nodeList->end(); node++) {
|
||||
if (node->getLinkedData() != NULL && node->getType() == NODE_TYPE_AGENT) {
|
||||
Avatar* avatar = (Avatar *) node->getLinkedData();
|
||||
|
||||
if (avatar->getUUID() == userUUID) {
|
||||
QMetaObject::invokeMethod(&avatar->getSkeletonModel(), "setURL",
|
||||
Q_ARG(QUrl, QUrl(valueList[i])));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -169,7 +187,7 @@ void DataServerClient::processSendFromDataServer(unsigned char* packetData, int
|
|||
|
||||
} else if (keyList[i] == DataServerKey::UUID) {
|
||||
// this is the user's UUID - set it on the profile
|
||||
Application::getInstance()->getProfile()->setUUID(valueList[0]);
|
||||
Application::getInstance()->getProfile()->setUUID(valueList[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -38,6 +38,7 @@ private:
|
|||
namespace DataServerKey {
|
||||
const QString Domain = "domain";
|
||||
const QString FaceMeshURL = "mesh";
|
||||
const QString SkeletonURL = "skeleton";
|
||||
const QString Position = "position";
|
||||
const QString UUID = "uuid";
|
||||
}
|
||||
|
|
|
@ -309,7 +309,7 @@ Menu::Menu() :
|
|||
addActionToQMenuAndActionHash(avatarOptionsMenu,
|
||||
MenuOption::FaceMode,
|
||||
0,
|
||||
&appInstance->getAvatar()->getHead().getFace(),
|
||||
&appInstance->getAvatar()->getHead().getVideoFace(),
|
||||
SLOT(cycleRenderMode()));
|
||||
|
||||
addCheckableActionToQMenuAndActionHash(avatarOptionsMenu, MenuOption::LookAtVectors, 0, true);
|
||||
|
@ -811,6 +811,11 @@ void Menu::editPreferences() {
|
|||
faceURLEdit->setMinimumWidth(QLINE_MINIMUM_WIDTH);
|
||||
form->addRow("Face URL:", faceURLEdit);
|
||||
|
||||
QString skeletonURLString = applicationInstance->getProfile()->getSkeletonModelURL().toString();
|
||||
QLineEdit* skeletonURLEdit = new QLineEdit(skeletonURLString);
|
||||
skeletonURLEdit->setMinimumWidth(QLINE_MINIMUM_WIDTH);
|
||||
form->addRow("Skeleton URL:", skeletonURLEdit);
|
||||
|
||||
QSlider* pupilDilation = new QSlider(Qt::Horizontal);
|
||||
pupilDilation->setValue(applicationInstance->getAvatar()->getHead().getPupilDilation() * pupilDilation->maximum());
|
||||
form->addRow("Pupil Dilation:", pupilDilation);
|
||||
|
@ -863,6 +868,17 @@ void Menu::editPreferences() {
|
|||
faceModelURL.toString().toLocal8Bit().constData());
|
||||
}
|
||||
|
||||
QUrl skeletonModelURL(skeletonURLEdit->text());
|
||||
|
||||
if (skeletonModelURL.toString() != skeletonURLString) {
|
||||
// change the skeletonModelURL in the profile, it will also update this user's Body
|
||||
applicationInstance->getProfile()->setSkeletonModelURL(skeletonModelURL);
|
||||
|
||||
// send the new skeleton model URL to the data-server (if we have a client UUID)
|
||||
DataServerClient::putValueForKey(DataServerKey::SkeletonURL,
|
||||
skeletonModelURL.toString().toLocal8Bit().constData());
|
||||
}
|
||||
|
||||
QUrl avatarVoxelURL(avatarURL->text());
|
||||
applicationInstance->getAvatar()->getVoxels()->setVoxelURL(avatarVoxelURL);
|
||||
|
||||
|
|
|
@ -158,6 +158,66 @@ glm::quat safeMix(const glm::quat& q1, const glm::quat& q2, float proportion) {
|
|||
return glm::normalize(glm::quat(s0 * q1.w + s1 * ow, s0 * q1.x + s1 * ox, s0 * q1.y + s1 * oy, s0 * q1.z + s1 * oz));
|
||||
}
|
||||
|
||||
glm::vec3 extractTranslation(const glm::mat4& matrix) {
|
||||
return glm::vec3(matrix[3][0], matrix[3][1], matrix[3][2]);
|
||||
}
|
||||
|
||||
glm::quat extractRotation(const glm::mat4& matrix, bool assumeOrthogonal) {
|
||||
// uses the iterative polar decomposition algorithm described by Ken Shoemake at
|
||||
// http://www.cs.wisc.edu/graphics/Courses/838-s2002/Papers/polar-decomp.pdf
|
||||
// code adapted from Clyde, https://github.com/threerings/clyde/blob/master/src/main/java/com/threerings/math/Matrix4f.java
|
||||
|
||||
// start with the contents of the upper 3x3 portion of the matrix
|
||||
glm::mat3 upper = glm::mat3(matrix);
|
||||
if (!assumeOrthogonal) {
|
||||
for (int i = 0; i < 10; i++) {
|
||||
// store the results of the previous iteration
|
||||
glm::mat3 previous = upper;
|
||||
|
||||
// compute average of the matrix with its inverse transpose
|
||||
float sd00 = previous[1][1] * previous[2][2] - previous[2][1] * previous[1][2];
|
||||
float sd10 = previous[0][1] * previous[2][2] - previous[2][1] * previous[0][2];
|
||||
float sd20 = previous[0][1] * previous[1][2] - previous[1][1] * previous[0][2];
|
||||
float det = previous[0][0] * sd00 + previous[2][0] * sd20 - previous[1][0] * sd10;
|
||||
if (fabs(det) == 0.0f) {
|
||||
// determinant is zero; matrix is not invertible
|
||||
break;
|
||||
}
|
||||
float hrdet = 0.5f / det;
|
||||
upper[0][0] = +sd00 * hrdet + previous[0][0] * 0.5f;
|
||||
upper[1][0] = -sd10 * hrdet + previous[1][0] * 0.5f;
|
||||
upper[2][0] = +sd20 * hrdet + previous[2][0] * 0.5f;
|
||||
|
||||
upper[0][1] = -(previous[1][0] * previous[2][2] - previous[2][0] * previous[1][2]) * hrdet + previous[0][1] * 0.5f;
|
||||
upper[1][1] = +(previous[0][0] * previous[2][2] - previous[2][0] * previous[0][2]) * hrdet + previous[1][1] * 0.5f;
|
||||
upper[2][1] = -(previous[0][0] * previous[1][2] - previous[1][0] * previous[0][2]) * hrdet + previous[2][1] * 0.5f;
|
||||
|
||||
upper[0][2] = +(previous[1][0] * previous[2][1] - previous[2][0] * previous[1][1]) * hrdet + previous[0][2] * 0.5f;
|
||||
upper[1][2] = -(previous[0][0] * previous[2][1] - previous[2][0] * previous[0][1]) * hrdet + previous[1][2] * 0.5f;
|
||||
upper[2][2] = +(previous[0][0] * previous[1][1] - previous[1][0] * previous[0][1]) * hrdet + previous[2][2] * 0.5f;
|
||||
|
||||
// compute the difference; if it's small enough, we're done
|
||||
glm::mat3 diff = upper - previous;
|
||||
if (diff[0][0] * diff[0][0] + diff[1][0] * diff[1][0] + diff[2][0] * diff[2][0] + diff[0][1] * diff[0][1] +
|
||||
diff[1][1] * diff[1][1] + diff[2][1] * diff[2][1] + diff[0][2] * diff[0][2] + diff[1][2] * diff[1][2] +
|
||||
diff[2][2] * diff[2][2] < EPSILON) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// now that we have a nice orthogonal matrix, we can extract the rotation quaternion
|
||||
// using the method described in http://en.wikipedia.org/wiki/Rotation_matrix#Conversions
|
||||
float x2 = fabs(1.0f + upper[0][0] - upper[1][1] - upper[2][2]);
|
||||
float y2 = fabs(1.0f - upper[0][0] + upper[1][1] - upper[2][2]);
|
||||
float z2 = fabs(1.0f - upper[0][0] - upper[1][1] + upper[2][2]);
|
||||
float w2 = fabs(1.0f + upper[0][0] + upper[1][1] + upper[2][2]);
|
||||
return glm::normalize(glm::quat(0.5f * sqrtf(w2),
|
||||
0.5f * sqrtf(x2) * (upper[1][2] >= upper[2][1] ? 1.0f : -1.0f),
|
||||
0.5f * sqrtf(y2) * (upper[2][0] >= upper[0][2] ? 1.0f : -1.0f),
|
||||
0.5f * sqrtf(z2) * (upper[0][1] >= upper[1][0] ? 1.0f : -1.0f)));
|
||||
}
|
||||
|
||||
// Draw a 3D vector floating in space
|
||||
void drawVector(glm::vec3 * vector) {
|
||||
glDisable(GL_LIGHTING);
|
||||
|
|
|
@ -55,6 +55,10 @@ glm::vec3 safeEulerAngles(const glm::quat& q);
|
|||
|
||||
glm::quat safeMix(const glm::quat& q1, const glm::quat& q2, float alpha);
|
||||
|
||||
glm::vec3 extractTranslation(const glm::mat4& matrix);
|
||||
|
||||
glm::quat extractRotation(const glm::mat4& matrix, bool assumeOrthogonal = false);
|
||||
|
||||
double diffclock(timeval *clock1,timeval *clock2);
|
||||
|
||||
void renderGroundPlaneGrid(float size, float impact);
|
||||
|
|
|
@ -79,6 +79,7 @@ Avatar::Avatar(Node* owningNode) :
|
|||
AvatarData(owningNode),
|
||||
_head(this),
|
||||
_hand(this),
|
||||
_skeletonModel(this),
|
||||
_ballSpringsInitialized(false),
|
||||
_bodyYawDelta(0.0f),
|
||||
_movedHandOffset(0.0f, 0.0f, 0.0f),
|
||||
|
@ -260,6 +261,7 @@ Avatar::~Avatar() {
|
|||
void Avatar::init() {
|
||||
_head.init();
|
||||
_hand.init();
|
||||
_skeletonModel.init();
|
||||
_voxels.init();
|
||||
_initialized = true;
|
||||
}
|
||||
|
@ -410,8 +412,13 @@ void Avatar::simulate(float deltaTime, Transmitter* transmitter) {
|
|||
}
|
||||
}
|
||||
|
||||
_skeletonModel.simulate(deltaTime);
|
||||
_head.setBodyRotation(glm::vec3(_bodyPitch, _bodyYaw, _bodyRoll));
|
||||
_head.setPosition(_bodyBall[ BODY_BALL_HEAD_BASE ].position);
|
||||
glm::vec3 headPosition;
|
||||
if (!_skeletonModel.getHeadPosition(headPosition)) {
|
||||
headPosition = _bodyBall[BODY_BALL_HEAD_BASE].position;
|
||||
}
|
||||
_head.setPosition(headPosition);
|
||||
_head.setSkinColor(glm::vec3(SKIN_COLOR[0], SKIN_COLOR[1], SKIN_COLOR[2]));
|
||||
_head.simulate(deltaTime, false);
|
||||
_hand.simulate(deltaTime, false);
|
||||
|
@ -742,21 +749,16 @@ float Avatar::getBallRenderAlpha(int ball, bool lookingInMirror) const {
|
|||
|
||||
void Avatar::renderBody(bool lookingInMirror, bool renderAvatarBalls) {
|
||||
|
||||
if (_head.getFace().isFullFrame()) {
|
||||
if (_head.getVideoFace().isFullFrame()) {
|
||||
// Render the full-frame video
|
||||
float alpha = getBallRenderAlpha(BODY_BALL_HEAD_BASE, lookingInMirror);
|
||||
if (alpha > 0.0f) {
|
||||
_head.getFace().render(1.0f);
|
||||
_head.getVideoFace().render(1.0f);
|
||||
}
|
||||
} else if (renderAvatarBalls || !_voxels.getVoxelURL().isValid()) {
|
||||
} else if (renderAvatarBalls || !(_voxels.getVoxelURL().isValid() || _skeletonModel.isActive())) {
|
||||
// Render the body as balls and cones
|
||||
glm::vec3 skinColor(SKIN_COLOR[0], SKIN_COLOR[1], SKIN_COLOR[2]);
|
||||
glm::vec3 darkSkinColor(DARK_SKIN_COLOR[0], DARK_SKIN_COLOR[1], DARK_SKIN_COLOR[2]);
|
||||
if (_head.getBlendFace().isActive()) {
|
||||
skinColor = glm::vec3(_head.getBlendFace().computeAverageColor());
|
||||
const float SKIN_DARKENING = 0.9f;
|
||||
darkSkinColor = skinColor * SKIN_DARKENING;
|
||||
}
|
||||
glm::vec3 skinColor, darkSkinColor;
|
||||
getSkinColors(skinColor, darkSkinColor);
|
||||
for (int b = 0; b < NUM_AVATAR_BODY_BALLS; b++) {
|
||||
float alpha = getBallRenderAlpha(b, lookingInMirror);
|
||||
|
||||
|
@ -778,7 +780,7 @@ void Avatar::renderBody(bool lookingInMirror, bool renderAvatarBalls) {
|
|||
skinColor.g - _bodyBall[b].touchForce * 0.2f,
|
||||
skinColor.b - _bodyBall[b].touchForce * 0.1f);
|
||||
|
||||
if (b == BODY_BALL_NECK_BASE && _head.getBlendFace().isActive()) {
|
||||
if (b == BODY_BALL_NECK_BASE && _head.getFaceModel().isActive()) {
|
||||
continue; // don't render the neck if we have a face model
|
||||
}
|
||||
|
||||
|
@ -813,13 +815,25 @@ void Avatar::renderBody(bool lookingInMirror, bool renderAvatarBalls) {
|
|||
// Render the body's voxels and head
|
||||
float alpha = getBallRenderAlpha(BODY_BALL_HEAD_BASE, lookingInMirror);
|
||||
if (alpha > 0.0f) {
|
||||
_voxels.render(false);
|
||||
if (!_skeletonModel.render(alpha)) {
|
||||
_voxels.render(false);
|
||||
}
|
||||
_head.render(alpha, false);
|
||||
}
|
||||
}
|
||||
_hand.render(lookingInMirror);
|
||||
}
|
||||
|
||||
void Avatar::getSkinColors(glm::vec3& lighter, glm::vec3& darker) {
|
||||
lighter = glm::vec3(SKIN_COLOR[0], SKIN_COLOR[1], SKIN_COLOR[2]);
|
||||
darker = glm::vec3(DARK_SKIN_COLOR[0], DARK_SKIN_COLOR[1], DARK_SKIN_COLOR[2]);
|
||||
if (_head.getFaceModel().isActive()) {
|
||||
lighter = glm::vec3(_head.getFaceModel().computeAverageColor());
|
||||
const float SKIN_DARKENING = 0.9f;
|
||||
darker = lighter * SKIN_DARKENING;
|
||||
}
|
||||
}
|
||||
|
||||
void Avatar::getBodyBallTransform(AvatarJointID jointID, glm::vec3& position, glm::quat& rotation) const {
|
||||
position = _bodyBall[jointID].position;
|
||||
rotation = _bodyBall[jointID].rotation;
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
#include "Head.h"
|
||||
#include "InterfaceConfig.h"
|
||||
#include "Skeleton.h"
|
||||
#include "SkeletonModel.h"
|
||||
#include "world.h"
|
||||
#include "devices/SerialInterface.h"
|
||||
#include "devices/Transmitter.h"
|
||||
|
@ -146,16 +147,19 @@ public:
|
|||
//getters
|
||||
bool isInitialized() const { return _initialized; }
|
||||
const Skeleton& getSkeleton() const { return _skeleton; }
|
||||
SkeletonModel& getSkeletonModel() { return _skeletonModel; }
|
||||
float getHeadYawRate() const { return _head.yawRate; }
|
||||
const glm::vec3& getHeadJointPosition() const { return _skeleton.joint[ AVATAR_JOINT_HEAD_BASE ].position; }
|
||||
float getScale() const { return _scale; }
|
||||
const glm::vec3& getVelocity() const { return _velocity; }
|
||||
Head& getHead() {return _head; }
|
||||
Hand& getHand() {return _hand; }
|
||||
Head& getHead() { return _head; }
|
||||
Hand& getHand() { return _hand; }
|
||||
glm::quat getOrientation() const;
|
||||
glm::quat getWorldAlignedOrientation() const;
|
||||
AvatarVoxelSystem* getVoxels() { return &_voxels; }
|
||||
|
||||
void getSkinColors(glm::vec3& lighter, glm::vec3& darker);
|
||||
|
||||
// Get the position/rotation of a single body ball
|
||||
void getBodyBallTransform(AvatarJointID jointID, glm::vec3& position, glm::quat& rotation) const;
|
||||
|
||||
|
@ -198,6 +202,7 @@ protected:
|
|||
Head _head;
|
||||
Hand _hand;
|
||||
Skeleton _skeleton;
|
||||
SkeletonModel _skeletonModel;
|
||||
bool _ballSpringsInitialized;
|
||||
float _bodyYawDelta;
|
||||
glm::vec3 _movedHandOffset;
|
||||
|
|
|
@ -1,92 +0,0 @@
|
|||
//
|
||||
// BlendFace.h
|
||||
// interface
|
||||
//
|
||||
// Created by Andrzej Kapolka on 9/16/13.
|
||||
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef __interface__BlendFace__
|
||||
#define __interface__BlendFace__
|
||||
|
||||
#include <QObject>
|
||||
#include <QUrl>
|
||||
|
||||
#include "InterfaceConfig.h"
|
||||
#include "renderer/GeometryCache.h"
|
||||
#include "renderer/ProgramObject.h"
|
||||
#include "renderer/TextureCache.h"
|
||||
|
||||
class QNetworkReply;
|
||||
|
||||
class Head;
|
||||
|
||||
/// A face formed from a linear mix of blendshapes according to a set of coefficients.
|
||||
class BlendFace : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
|
||||
BlendFace(Head* owningHead);
|
||||
~BlendFace();
|
||||
|
||||
bool isActive() const { return _geometry && _geometry->isLoaded(); }
|
||||
|
||||
void init();
|
||||
void reset();
|
||||
void simulate(float deltaTime);
|
||||
bool render(float alpha);
|
||||
|
||||
Q_INVOKABLE void setModelURL(const QUrl& url);
|
||||
const QUrl& getModelURL() const { return _modelURL; }
|
||||
|
||||
/// Retrieve the positions of up to two eye meshes.
|
||||
/// \param upright if true, retrieve the locations of the eyes in the upright position
|
||||
/// \return whether or not both eye meshes were found
|
||||
bool getEyePositions(glm::vec3& firstEyePosition, glm::vec3& secondEyePosition, bool upright = false) const;
|
||||
|
||||
/// Returns the average color of all meshes in the geometry.
|
||||
glm::vec4 computeAverageColor() const;
|
||||
|
||||
private:
|
||||
|
||||
void deleteGeometry();
|
||||
|
||||
Head* _owningHead;
|
||||
|
||||
QUrl _modelURL;
|
||||
|
||||
QSharedPointer<NetworkGeometry> _geometry;
|
||||
|
||||
class JointState {
|
||||
public:
|
||||
glm::quat rotation;
|
||||
glm::mat4 transform;
|
||||
};
|
||||
|
||||
QVector<JointState> _jointStates;
|
||||
|
||||
class MeshState {
|
||||
public:
|
||||
QVector<glm::mat4> clusterMatrices;
|
||||
QVector<glm::vec3> worldSpaceVertices;
|
||||
QVector<glm::vec3> vertexVelocities;
|
||||
QVector<glm::vec3> worldSpaceNormals;
|
||||
};
|
||||
|
||||
QVector<MeshState> _meshStates;
|
||||
QVector<GLuint> _blendedVertexBufferIDs;
|
||||
QVector<QVector<QSharedPointer<Texture> > > _dilatedTextures;
|
||||
bool _resetStates;
|
||||
|
||||
QVector<glm::vec3> _blendedVertices;
|
||||
QVector<glm::vec3> _blendedNormals;
|
||||
|
||||
static ProgramObject _program;
|
||||
static ProgramObject _skinProgram;
|
||||
static int _clusterMatricesLocation;
|
||||
static int _clusterIndicesLocation;
|
||||
static int _clusterWeightsLocation;
|
||||
};
|
||||
|
||||
#endif /* defined(__interface__BlendFace__) */
|
66
interface/src/avatar/FaceModel.cpp
Normal file
66
interface/src/avatar/FaceModel.cpp
Normal file
|
@ -0,0 +1,66 @@
|
|||
//
|
||||
// FaceModel.cpp
|
||||
// interface
|
||||
//
|
||||
// Created by Andrzej Kapolka on 9/16/13.
|
||||
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#include <glm/gtx/transform.hpp>
|
||||
|
||||
#include "Avatar.h"
|
||||
#include "FaceModel.h"
|
||||
#include "Head.h"
|
||||
|
||||
FaceModel::FaceModel(Head* owningHead) :
|
||||
_owningHead(owningHead)
|
||||
{
|
||||
}
|
||||
|
||||
void FaceModel::simulate(float deltaTime) {
|
||||
if (!isActive()) {
|
||||
return;
|
||||
}
|
||||
|
||||
Avatar* owningAvatar = static_cast<Avatar*>(_owningHead->_owningAvatar);
|
||||
glm::vec3 neckPosition;
|
||||
if (!owningAvatar->getSkeletonModel().getNeckPosition(neckPosition)) {
|
||||
neckPosition = owningAvatar->getSkeleton().joint[AVATAR_JOINT_NECK_BASE].position;
|
||||
}
|
||||
setTranslation(neckPosition);
|
||||
glm::quat neckRotation;
|
||||
if (!owningAvatar->getSkeletonModel().getNeckRotation(neckRotation)) {
|
||||
neckRotation = owningAvatar->getSkeleton().joint[AVATAR_JOINT_NECK_BASE].absoluteRotation *
|
||||
glm::angleAxis(180.0f, 0.0f, 1.0f, 0.0f);
|
||||
}
|
||||
setRotation(neckRotation);
|
||||
const float MODEL_SCALE = 0.0006f;
|
||||
setScale(glm::vec3(1.0f, 1.0f, 1.0f) * _owningHead->getScale() * MODEL_SCALE);
|
||||
const glm::vec3 MODEL_TRANSLATION(0.0f, -60.0f, 40.0f); // temporary fudge factor
|
||||
setOffset(MODEL_TRANSLATION - _geometry->getFBXGeometry().neckPivot);
|
||||
|
||||
setPupilDilation(_owningHead->getPupilDilation());
|
||||
setBlendshapeCoefficients(_owningHead->getBlendshapeCoefficients());
|
||||
|
||||
Model::simulate(deltaTime);
|
||||
}
|
||||
|
||||
void FaceModel::maybeUpdateNeckRotation(const JointState& parentState, const FBXJoint& joint, JointState& state) {
|
||||
// get the rotation axes in joint space and use them to adjust the rotation
|
||||
glm::mat3 axes = glm::mat3_cast(_rotation);
|
||||
glm::mat3 inverse = glm::mat3(glm::inverse(parentState.transform *
|
||||
joint.preTransform * glm::mat4_cast(joint.preRotation * joint.rotation)));
|
||||
state.rotation = glm::angleAxis(-_owningHead->getRoll(), glm::normalize(inverse * axes[2])) *
|
||||
glm::angleAxis(_owningHead->getYaw(), glm::normalize(inverse * axes[1])) *
|
||||
glm::angleAxis(-_owningHead->getPitch(), glm::normalize(inverse * axes[0])) * joint.rotation;
|
||||
}
|
||||
|
||||
void FaceModel::maybeUpdateEyeRotation(const JointState& parentState, const FBXJoint& joint, JointState& state) {
|
||||
// likewise with the eye joints
|
||||
glm::mat4 inverse = glm::inverse(parentState.transform *
|
||||
joint.preTransform * glm::mat4_cast(joint.preRotation * joint.rotation));
|
||||
glm::vec3 front = glm::vec3(inverse * glm::vec4(_owningHead->getOrientation() * IDENTITY_FRONT, 0.0f));
|
||||
glm::vec3 lookAt = glm::vec3(inverse * glm::vec4(_owningHead->getLookAtPosition() +
|
||||
_owningHead->getSaccade(), 1.0f));
|
||||
state.rotation = rotationBetween(front, lookAt) * joint.rotation;
|
||||
}
|
36
interface/src/avatar/FaceModel.h
Normal file
36
interface/src/avatar/FaceModel.h
Normal file
|
@ -0,0 +1,36 @@
|
|||
//
|
||||
// FaceModel.h
|
||||
// interface
|
||||
//
|
||||
// Created by Andrzej Kapolka on 9/16/13.
|
||||
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef __interface__FaceModel__
|
||||
#define __interface__FaceModel__
|
||||
|
||||
#include "renderer/Model.h"
|
||||
|
||||
class Head;
|
||||
|
||||
/// A face formed from a linear mix of blendshapes according to a set of coefficients.
|
||||
class FaceModel : public Model {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
|
||||
FaceModel(Head* owningHead);
|
||||
|
||||
void simulate(float deltaTime);
|
||||
|
||||
protected:
|
||||
|
||||
virtual void maybeUpdateNeckRotation(const JointState& parentState, const FBXJoint& joint, JointState& state);
|
||||
virtual void maybeUpdateEyeRotation(const JointState& parentState, const FBXJoint& joint, JointState& state);
|
||||
|
||||
private:
|
||||
|
||||
Head* _owningHead;
|
||||
};
|
||||
|
||||
#endif /* defined(__interface__FaceModel__) */
|
|
@ -83,8 +83,8 @@ Head::Head(Avatar* owningAvatar) :
|
|||
_mousePitch(0.f),
|
||||
_cameraYaw(_yaw),
|
||||
_isCameraMoving(false),
|
||||
_face(this),
|
||||
_blendFace(this)
|
||||
_videoFace(this),
|
||||
_faceModel(this)
|
||||
{
|
||||
if (USING_PHYSICAL_MOHAWK) {
|
||||
resetHairPhysics();
|
||||
|
@ -104,7 +104,7 @@ void Head::init() {
|
|||
_irisTexture = Application::getInstance()->getTextureCache()->getTexture(QUrl::fromLocalFile(IRIS_TEXTURE_FILENAME),
|
||||
true).staticCast<DilatableNetworkTexture>();
|
||||
}
|
||||
_blendFace.init();
|
||||
_faceModel.init();
|
||||
}
|
||||
|
||||
void Head::reset() {
|
||||
|
@ -115,7 +115,7 @@ void Head::reset() {
|
|||
resetHairPhysics();
|
||||
}
|
||||
|
||||
_blendFace.reset();
|
||||
_faceModel.reset();
|
||||
}
|
||||
|
||||
void Head::resetHairPhysics() {
|
||||
|
@ -237,7 +237,7 @@ void Head::simulate(float deltaTime, bool isMine) {
|
|||
updateHairPhysics(deltaTime);
|
||||
}
|
||||
|
||||
_blendFace.simulate(deltaTime);
|
||||
_faceModel.simulate(deltaTime);
|
||||
}
|
||||
|
||||
void Head::calculateGeometry() {
|
||||
|
@ -285,7 +285,7 @@ void Head::calculateGeometry() {
|
|||
void Head::render(float alpha, bool isMine) {
|
||||
_renderAlpha = alpha;
|
||||
|
||||
if (!(_face.render(alpha) || _blendFace.render(alpha))) {
|
||||
if (!(_videoFace.render(alpha) || _faceModel.render(alpha))) {
|
||||
calculateGeometry();
|
||||
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
|
@ -300,9 +300,9 @@ void Head::render(float alpha, bool isMine) {
|
|||
renderEyeBrows();
|
||||
}
|
||||
|
||||
if (_blendFace.isActive()) {
|
||||
if (_faceModel.isActive()) {
|
||||
// the blend face may have custom eye meshes
|
||||
_blendFace.getEyePositions(_leftEyePosition, _rightEyePosition);
|
||||
_faceModel.getEyePositions(_leftEyePosition, _rightEyePosition);
|
||||
}
|
||||
|
||||
if (_renderLookatVectors) {
|
||||
|
|
|
@ -18,9 +18,9 @@
|
|||
#include <VoxelConstants.h>
|
||||
|
||||
#include "BendyLine.h"
|
||||
#include "BlendFace.h"
|
||||
#include "Face.h"
|
||||
#include "FaceModel.h"
|
||||
#include "InterfaceConfig.h"
|
||||
#include "VideoFace.h"
|
||||
#include "world.h"
|
||||
#include "devices/SerialInterface.h"
|
||||
#include "renderer/TextureCache.h"
|
||||
|
@ -76,8 +76,8 @@ public:
|
|||
|
||||
glm::quat getEyeRotation(const glm::vec3& eyePosition) const;
|
||||
|
||||
Face& getFace() { return _face; }
|
||||
BlendFace& getBlendFace() { return _blendFace; }
|
||||
VideoFace& getVideoFace() { return _videoFace; }
|
||||
FaceModel& getFaceModel() { return _faceModel; }
|
||||
|
||||
const bool getReturnToCenter() const { return _returnHeadToCenter; } // Do you want head to try to return to center (depends on interface detected)
|
||||
float getAverageLoudness() const { return _averageLoudness; }
|
||||
|
@ -132,8 +132,8 @@ private:
|
|||
float _mousePitch;
|
||||
float _cameraYaw;
|
||||
bool _isCameraMoving;
|
||||
Face _face;
|
||||
BlendFace _blendFace;
|
||||
VideoFace _videoFace;
|
||||
FaceModel _faceModel;
|
||||
|
||||
QSharedPointer<Texture> _dilatedIrisTexture;
|
||||
|
||||
|
@ -154,7 +154,7 @@ private:
|
|||
void resetHairPhysics();
|
||||
void updateHairPhysics(float deltaTime);
|
||||
|
||||
friend class BlendFace;
|
||||
friend class FaceModel;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -317,8 +317,13 @@ void MyAvatar::simulate(float deltaTime, Transmitter* transmitter) {
|
|||
}
|
||||
}
|
||||
|
||||
_skeletonModel.simulate(deltaTime);
|
||||
_head.setBodyRotation(glm::vec3(_bodyPitch, _bodyYaw, _bodyRoll));
|
||||
_head.setPosition(_bodyBall[ BODY_BALL_HEAD_BASE ].position);
|
||||
glm::vec3 headPosition;
|
||||
if (!_skeletonModel.getHeadPosition(headPosition)) {
|
||||
headPosition = _bodyBall[BODY_BALL_HEAD_BASE].position;
|
||||
}
|
||||
_head.setPosition(headPosition);
|
||||
_head.setScale(_scale);
|
||||
_head.setSkinColor(glm::vec3(SKIN_COLOR[0], SKIN_COLOR[1], SKIN_COLOR[2]));
|
||||
_head.simulate(deltaTime, true);
|
||||
|
@ -390,7 +395,7 @@ void MyAvatar::updateFromGyrosAndOrWebcam(float pitchFromTouch, bool turnWithHea
|
|||
_head.setMousePitch(pitchFromTouch);
|
||||
_head.setPitch(pitchFromTouch);
|
||||
}
|
||||
_head.getFace().clearFrame();
|
||||
_head.getVideoFace().clearFrame();
|
||||
|
||||
// restore rotation, lean to neutral positions
|
||||
const float RESTORE_RATE = 0.05f;
|
||||
|
@ -406,7 +411,7 @@ void MyAvatar::updateFromGyrosAndOrWebcam(float pitchFromTouch, bool turnWithHea
|
|||
estimatedPosition = webcam->getEstimatedPosition();
|
||||
|
||||
// apply face data
|
||||
_head.getFace().setFrameFromWebcam();
|
||||
_head.getVideoFace().setFrameFromWebcam();
|
||||
|
||||
// compute and store the joint rotations
|
||||
const JointVector& joints = webcam->getEstimatedJoints();
|
||||
|
@ -423,7 +428,7 @@ void MyAvatar::updateFromGyrosAndOrWebcam(float pitchFromTouch, bool turnWithHea
|
|||
}
|
||||
}
|
||||
} else {
|
||||
_head.getFace().clearFrame();
|
||||
_head.getVideoFace().clearFrame();
|
||||
}
|
||||
|
||||
// Set the rotation of the avatar's head (as seen by others, not affecting view frustum)
|
||||
|
@ -604,21 +609,16 @@ void MyAvatar::renderBody(bool lookingInMirror, bool renderAvatarBalls) {
|
|||
return;
|
||||
}
|
||||
|
||||
if (_head.getFace().isFullFrame()) {
|
||||
if (_head.getVideoFace().isFullFrame()) {
|
||||
// Render the full-frame video
|
||||
float alpha = getBallRenderAlpha(BODY_BALL_HEAD_BASE, lookingInMirror);
|
||||
if (alpha > 0.0f) {
|
||||
_head.getFace().render(1.0f);
|
||||
_head.getVideoFace().render(1.0f);
|
||||
}
|
||||
} else if (renderAvatarBalls || !_voxels.getVoxelURL().isValid()) {
|
||||
} else if (renderAvatarBalls || !(_voxels.getVoxelURL().isValid() || _skeletonModel.isActive())) {
|
||||
// Render the body as balls and cones
|
||||
glm::vec3 skinColor(SKIN_COLOR[0], SKIN_COLOR[1], SKIN_COLOR[2]);
|
||||
glm::vec3 darkSkinColor(DARK_SKIN_COLOR[0], DARK_SKIN_COLOR[1], DARK_SKIN_COLOR[2]);
|
||||
if (_head.getBlendFace().isActive()) {
|
||||
skinColor = glm::vec3(_head.getBlendFace().computeAverageColor());
|
||||
const float SKIN_DARKENING = 0.9f;
|
||||
darkSkinColor = skinColor * SKIN_DARKENING;
|
||||
}
|
||||
glm::vec3 skinColor, darkSkinColor;
|
||||
getSkinColors(skinColor, darkSkinColor);
|
||||
for (int b = 0; b < NUM_AVATAR_BODY_BALLS; b++) {
|
||||
float alpha = getBallRenderAlpha(b, lookingInMirror);
|
||||
|
||||
|
@ -649,7 +649,7 @@ void MyAvatar::renderBody(bool lookingInMirror, bool renderAvatarBalls) {
|
|||
alpha);
|
||||
}
|
||||
|
||||
if (b == BODY_BALL_NECK_BASE && _head.getBlendFace().isActive()) {
|
||||
if (b == BODY_BALL_NECK_BASE && _head.getFaceModel().isActive()) {
|
||||
continue; // don't render the neck if we have a face model
|
||||
}
|
||||
|
||||
|
@ -685,7 +685,9 @@ void MyAvatar::renderBody(bool lookingInMirror, bool renderAvatarBalls) {
|
|||
// Render the body's voxels and head
|
||||
float alpha = getBallRenderAlpha(BODY_BALL_HEAD_BASE, lookingInMirror);
|
||||
if (alpha > 0.0f) {
|
||||
_voxels.render(false);
|
||||
if (!_skeletonModel.render(alpha)) {
|
||||
_voxels.render(false);
|
||||
}
|
||||
_head.render(alpha, true);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -44,9 +44,15 @@ void Profile::setUUID(const QUuid& uuid) {
|
|||
void Profile::setFaceModelURL(const QUrl& faceModelURL) {
|
||||
_faceModelURL = faceModelURL;
|
||||
|
||||
QMetaObject::invokeMethod(&Application::getInstance()->getAvatar()->getHead().getBlendFace(),
|
||||
"setModelURL",
|
||||
Q_ARG(QUrl, _faceModelURL));
|
||||
QMetaObject::invokeMethod(&Application::getInstance()->getAvatar()->getHead().getFaceModel(),
|
||||
"setURL", Q_ARG(QUrl, _faceModelURL));
|
||||
}
|
||||
|
||||
void Profile::setSkeletonModelURL(const QUrl& skeletonModelURL) {
|
||||
_skeletonModelURL = skeletonModelURL;
|
||||
|
||||
QMetaObject::invokeMethod(&Application::getInstance()->getAvatar()->getSkeletonModel(),
|
||||
"setURL", Q_ARG(QUrl, _skeletonModelURL));
|
||||
}
|
||||
|
||||
void Profile::updateDomain(const QString& domain) {
|
||||
|
@ -91,6 +97,7 @@ void Profile::saveData(QSettings* settings) {
|
|||
settings->setValue("username", _username);
|
||||
settings->setValue("UUID", _uuid);
|
||||
settings->setValue("faceModelURL", _faceModelURL);
|
||||
settings->setValue("skeletonModelURL", _skeletonModelURL);
|
||||
|
||||
settings->endGroup();
|
||||
}
|
||||
|
@ -101,6 +108,7 @@ void Profile::loadData(QSettings* settings) {
|
|||
_username = settings->value("username").toString();
|
||||
this->setUUID(settings->value("UUID").toUuid());
|
||||
_faceModelURL = settings->value("faceModelURL").toUrl();
|
||||
_skeletonModelURL = settings->value("skeletonModelURL").toUrl();
|
||||
|
||||
settings->endGroup();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -29,6 +29,9 @@ public:
|
|||
void setFaceModelURL(const QUrl& faceModelURL);
|
||||
const QUrl& getFaceModelURL() const { return _faceModelURL; }
|
||||
|
||||
void setSkeletonModelURL(const QUrl& skeletonModelURL);
|
||||
const QUrl& getSkeletonModelURL() const { return _skeletonModelURL; }
|
||||
|
||||
void updateDomain(const QString& domain);
|
||||
void updatePosition(const glm::vec3 position);
|
||||
|
||||
|
@ -43,6 +46,7 @@ private:
|
|||
QString _lastDomain;
|
||||
glm::vec3 _lastPosition;
|
||||
QUrl _faceModelURL;
|
||||
QUrl _skeletonModelURL;
|
||||
};
|
||||
|
||||
#endif /* defined(__hifi__Profile__) */
|
||||
|
|
96
interface/src/avatar/SkeletonModel.cpp
Normal file
96
interface/src/avatar/SkeletonModel.cpp
Normal file
|
@ -0,0 +1,96 @@
|
|||
//
|
||||
// SkeletonModel.cpp
|
||||
// interface
|
||||
//
|
||||
// Created by Andrzej Kapolka on 10/17/13.
|
||||
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#include <glm/gtx/transform.hpp>
|
||||
|
||||
#include "Avatar.h"
|
||||
#include "SkeletonModel.h"
|
||||
|
||||
SkeletonModel::SkeletonModel(Avatar* owningAvatar) :
|
||||
_owningAvatar(owningAvatar) {
|
||||
}
|
||||
|
||||
void SkeletonModel::simulate(float deltaTime) {
|
||||
if (!isActive()) {
|
||||
return;
|
||||
}
|
||||
|
||||
setTranslation(_owningAvatar->getPosition());
|
||||
setRotation(_owningAvatar->getOrientation() * glm::angleAxis(180.0f, 0.0f, 1.0f, 0.0f));
|
||||
const float MODEL_SCALE = 0.0006f;
|
||||
setScale(glm::vec3(1.0f, 1.0f, 1.0f) * _owningAvatar->getScale() * MODEL_SCALE);
|
||||
|
||||
Model::simulate(deltaTime);
|
||||
}
|
||||
|
||||
bool SkeletonModel::render(float alpha) {
|
||||
if (_jointStates.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const FBXGeometry& geometry = _geometry->getFBXGeometry();
|
||||
|
||||
glm::vec3 skinColor, darkSkinColor;
|
||||
_owningAvatar->getSkinColors(skinColor, darkSkinColor);
|
||||
|
||||
for (int i = 0; i < _jointStates.size(); i++) {
|
||||
glPushMatrix();
|
||||
|
||||
glm::vec3 position;
|
||||
getJointPosition(i, position);
|
||||
glTranslatef(position.x, position.y, position.z);
|
||||
|
||||
glm::quat rotation;
|
||||
getJointRotation(i, rotation);
|
||||
glm::vec3 axis = glm::axis(rotation);
|
||||
glRotatef(glm::angle(rotation), axis.x, axis.y, axis.z);
|
||||
|
||||
glColor4f(skinColor.r, skinColor.g, skinColor.b, alpha);
|
||||
const float BALL_RADIUS = 0.02f;
|
||||
const int BALL_SUBDIVISIONS = 10;
|
||||
glutSolidSphere(BALL_RADIUS * _owningAvatar->getScale(), BALL_SUBDIVISIONS, BALL_SUBDIVISIONS);
|
||||
|
||||
glPopMatrix();
|
||||
|
||||
int parentIndex = geometry.joints[i].parentIndex;
|
||||
if (parentIndex == -1) {
|
||||
continue;
|
||||
}
|
||||
glColor4f(darkSkinColor.r, darkSkinColor.g, darkSkinColor.b, alpha);
|
||||
|
||||
glm::vec3 parentPosition;
|
||||
getJointPosition(parentIndex, parentPosition);
|
||||
const float STICK_RADIUS = BALL_RADIUS * 0.5f;
|
||||
Avatar::renderJointConnectingCone(parentPosition, position, STICK_RADIUS, STICK_RADIUS);
|
||||
}
|
||||
|
||||
Model::render(alpha);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void SkeletonModel::updateJointState(int index) {
|
||||
Model::updateJointState(index);
|
||||
|
||||
if (index == _geometry->getFBXGeometry().rootJointIndex) {
|
||||
JointState& state = _jointStates[index];
|
||||
state.transform[3][0] = _translation.x;
|
||||
state.transform[3][1] = _translation.y;
|
||||
state.transform[3][2] = _translation.z;
|
||||
}
|
||||
}
|
||||
|
||||
void SkeletonModel::maybeUpdateLeanRotation(const JointState& parentState, const FBXJoint& joint, JointState& state) {
|
||||
// get the rotation axes in joint space and use them to adjust the rotation
|
||||
glm::mat3 axes = glm::mat3_cast(_rotation);
|
||||
glm::mat3 inverse = glm::mat3(glm::inverse(parentState.transform *
|
||||
joint.preTransform * glm::mat4_cast(joint.preRotation * joint.rotation)));
|
||||
state.rotation = glm::angleAxis(-_owningAvatar->getHead().getLeanSideways(), glm::normalize(inverse * axes[2])) *
|
||||
glm::angleAxis(-_owningAvatar->getHead().getLeanForward(), glm::normalize(inverse * axes[0])) * joint.rotation;
|
||||
}
|
||||
|
39
interface/src/avatar/SkeletonModel.h
Normal file
39
interface/src/avatar/SkeletonModel.h
Normal file
|
@ -0,0 +1,39 @@
|
|||
//
|
||||
// SkeletonModel.h
|
||||
// interface
|
||||
//
|
||||
// Created by Andrzej Kapolka on 10/17/13.
|
||||
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef __interface__SkeletonModel__
|
||||
#define __interface__SkeletonModel__
|
||||
|
||||
#include "renderer/Model.h"
|
||||
|
||||
class Avatar;
|
||||
|
||||
/// A skeleton loaded from a model.
|
||||
class SkeletonModel : public Model {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
|
||||
SkeletonModel(Avatar* owningAvatar);
|
||||
|
||||
void simulate(float deltaTime);
|
||||
bool render(float alpha);
|
||||
|
||||
protected:
|
||||
|
||||
/// Updates the state of the joint at the specified index.
|
||||
virtual void updateJointState(int index);
|
||||
|
||||
virtual void maybeUpdateLeanRotation(const JointState& parentState, const FBXJoint& joint, JointState& state);
|
||||
|
||||
private:
|
||||
|
||||
Avatar* _owningAvatar;
|
||||
};
|
||||
|
||||
#endif /* defined(__interface__SkeletonModel__) */
|
|
@ -1,5 +1,5 @@
|
|||
//
|
||||
// Face.cpp
|
||||
// VideoFace.cpp
|
||||
// interface
|
||||
//
|
||||
// Created by Andrzej Kapolka on 7/11/13.
|
||||
|
@ -16,26 +16,26 @@
|
|||
#include "Application.h"
|
||||
#include "Avatar.h"
|
||||
#include "Head.h"
|
||||
#include "Face.h"
|
||||
#include "VideoFace.h"
|
||||
#include "renderer/ProgramObject.h"
|
||||
|
||||
using namespace cv;
|
||||
|
||||
bool Face::_initialized = false;
|
||||
ProgramObject Face::_videoProgram;
|
||||
Face::Locations Face::_videoProgramLocations;
|
||||
ProgramObject Face::_texturedProgram;
|
||||
Face::Locations Face::_texturedProgramLocations;
|
||||
GLuint Face::_vboID;
|
||||
GLuint Face::_iboID;
|
||||
bool VideoFace::_initialized = false;
|
||||
ProgramObject VideoFace::_videoProgram;
|
||||
VideoFace::Locations VideoFace::_videoProgramLocations;
|
||||
ProgramObject VideoFace::_texturedProgram;
|
||||
VideoFace::Locations VideoFace::_texturedProgramLocations;
|
||||
GLuint VideoFace::_vboID;
|
||||
GLuint VideoFace::_iboID;
|
||||
|
||||
Face::Face(Head* owningHead) : _owningHead(owningHead), _renderMode(MESH),
|
||||
VideoFace::VideoFace(Head* owningHead) : _owningHead(owningHead), _renderMode(MESH),
|
||||
_colorTextureID(0), _depthTextureID(0), _colorCodec(), _depthCodec(), _frameCount(0) {
|
||||
// we may have been created in the network thread, but we live in the main thread
|
||||
moveToThread(Application::getInstance()->thread());
|
||||
}
|
||||
|
||||
Face::~Face() {
|
||||
VideoFace::~VideoFace() {
|
||||
if (_colorCodec.name != 0) {
|
||||
vpx_codec_destroy(&_colorCodec);
|
||||
|
||||
|
@ -55,7 +55,7 @@ Face::~Face() {
|
|||
}
|
||||
}
|
||||
|
||||
void Face::setFrameFromWebcam() {
|
||||
void VideoFace::setFrameFromWebcam() {
|
||||
Webcam* webcam = Application::getInstance()->getWebcam();
|
||||
if (webcam->isSending()) {
|
||||
_colorTextureID = webcam->getColorTextureID();
|
||||
|
@ -68,12 +68,12 @@ void Face::setFrameFromWebcam() {
|
|||
}
|
||||
}
|
||||
|
||||
void Face::clearFrame() {
|
||||
void VideoFace::clearFrame() {
|
||||
_colorTextureID = 0;
|
||||
_depthTextureID = 0;
|
||||
}
|
||||
|
||||
int Face::processVideoMessage(unsigned char* packetData, size_t dataBytes) {
|
||||
int VideoFace::processVideoMessage(unsigned char* packetData, size_t dataBytes) {
|
||||
unsigned char* packetPosition = packetData;
|
||||
|
||||
int frameCount = *(uint32_t*)packetPosition;
|
||||
|
@ -243,7 +243,7 @@ int Face::processVideoMessage(unsigned char* packetData, size_t dataBytes) {
|
|||
return dataBytes;
|
||||
}
|
||||
|
||||
bool Face::render(float alpha) {
|
||||
bool VideoFace::render(float alpha) {
|
||||
if (!isActive()) {
|
||||
return false;
|
||||
}
|
||||
|
@ -404,11 +404,11 @@ bool Face::render(float alpha) {
|
|||
return true;
|
||||
}
|
||||
|
||||
void Face::cycleRenderMode() {
|
||||
void VideoFace::cycleRenderMode() {
|
||||
_renderMode = (RenderMode)((_renderMode + 1) % RENDER_MODE_COUNT);
|
||||
}
|
||||
|
||||
void Face::setFrame(const cv::Mat& color, const cv::Mat& depth, float aspectRatio) {
|
||||
void VideoFace::setFrame(const cv::Mat& color, const cv::Mat& depth, float aspectRatio) {
|
||||
Size2f textureSize = _textureSize;
|
||||
if (!color.empty()) {
|
||||
bool generate = (_colorTextureID == 0);
|
||||
|
@ -457,7 +457,7 @@ void Face::setFrame(const cv::Mat& color, const cv::Mat& depth, float aspectRati
|
|||
_textureSize = textureSize;
|
||||
}
|
||||
|
||||
void Face::destroyCodecs() {
|
||||
void VideoFace::destroyCodecs() {
|
||||
if (_colorCodec.name != 0) {
|
||||
vpx_codec_destroy(&_colorCodec);
|
||||
_colorCodec.name = 0;
|
||||
|
@ -468,7 +468,7 @@ void Face::destroyCodecs() {
|
|||
}
|
||||
}
|
||||
|
||||
void Face::loadProgram(ProgramObject& program, const QString& suffix, const char* secondTextureUniform, Locations& locations) {
|
||||
void VideoFace::loadProgram(ProgramObject& program, const QString& suffix, const char* secondTextureUniform, Locations& locations) {
|
||||
program.addShaderFromSourceFile(QGLShader::Vertex, "resources/shaders/face" + suffix + ".vert");
|
||||
program.addShaderFromSourceFile(QGLShader::Fragment, "resources/shaders/face" + suffix + ".frag");
|
||||
program.link();
|
|
@ -1,13 +1,13 @@
|
|||
//
|
||||
// Face.h
|
||||
// VideoFace.h
|
||||
// interface
|
||||
//
|
||||
// Created by Andrzej Kapolka on 7/11/13.
|
||||
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef __interface__Face__
|
||||
#define __interface__Face__
|
||||
#ifndef __interface__VideoFace__
|
||||
#define __interface__VideoFace__
|
||||
|
||||
#include <QObject>
|
||||
|
||||
|
@ -22,13 +22,13 @@ class ProgramObject;
|
|||
|
||||
const float FULL_FRAME_ASPECT = 0.0f;
|
||||
|
||||
class Face : public QObject {
|
||||
class VideoFace : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
|
||||
Face(Head* owningHead);
|
||||
~Face();
|
||||
VideoFace(Head* owningHead);
|
||||
~VideoFace();
|
||||
|
||||
bool isActive() const { return _colorTextureID != 0 || _depthTextureID != 0; }
|
||||
bool isFullFrame() const { return isActive() && _aspectRatio == FULL_FRAME_ASPECT; }
|
||||
|
@ -91,4 +91,4 @@ private:
|
|||
static GLuint _iboID;
|
||||
};
|
||||
|
||||
#endif /* defined(__interface__Face__) */
|
||||
#endif /* defined(__interface__VideoFace__) */
|
|
@ -19,7 +19,7 @@
|
|||
|
||||
#include "Application.h"
|
||||
#include "Webcam.h"
|
||||
#include "avatar/Face.h"
|
||||
#include "avatar/VideoFace.h"
|
||||
|
||||
using namespace cv;
|
||||
using namespace std;
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -14,6 +14,7 @@
|
|||
#include <QVector>
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
#include <glm/gtc/quaternion.hpp>
|
||||
|
||||
class FBXNode;
|
||||
|
||||
|
@ -42,10 +43,13 @@ class FBXJoint {
|
|||
public:
|
||||
|
||||
int parentIndex;
|
||||
glm::mat4 preRotation;
|
||||
glm::mat4 preTransform;
|
||||
glm::quat preRotation;
|
||||
glm::quat rotation;
|
||||
glm::mat4 postRotation;
|
||||
glm::quat postRotation;
|
||||
glm::mat4 postTransform;
|
||||
glm::mat4 transform;
|
||||
glm::quat inverseBindRotation;
|
||||
};
|
||||
|
||||
/// A single binding to a joint in an FBX document.
|
||||
|
@ -99,6 +103,7 @@ class FBXGeometry {
|
|||
public:
|
||||
|
||||
QVector<FBXJoint> joints;
|
||||
QHash<QString, int> jointIndices;
|
||||
|
||||
QVector<FBXMesh> meshes;
|
||||
|
||||
|
@ -107,6 +112,9 @@ public:
|
|||
int leftEyeJointIndex;
|
||||
int rightEyeJointIndex;
|
||||
int neckJointIndex;
|
||||
int rootJointIndex;
|
||||
int leanJointIndex;
|
||||
int headJointIndex;
|
||||
|
||||
glm::vec3 neckPivot;
|
||||
};
|
||||
|
|
|
@ -57,7 +57,7 @@ public:
|
|||
NetworkGeometry(const QUrl& url);
|
||||
~NetworkGeometry();
|
||||
|
||||
bool isLoaded() const { return !_meshes.isEmpty(); }
|
||||
bool isLoaded() const { return !_geometry.joints.isEmpty(); }
|
||||
|
||||
const FBXGeometry& getFBXGeometry() const { return _geometry; }
|
||||
const QVector<NetworkMesh>& getMeshes() const { return _meshes; }
|
||||
|
|
|
@ -1,52 +1,48 @@
|
|||
//
|
||||
// BlendFace.cpp
|
||||
// Model.cpp
|
||||
// interface
|
||||
//
|
||||
// Created by Andrzej Kapolka on 9/16/13.
|
||||
// Created by Andrzej Kapolka on 10/18/13.
|
||||
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#include <QNetworkReply>
|
||||
|
||||
#include <glm/gtx/transform.hpp>
|
||||
|
||||
#include "Application.h"
|
||||
#include "BlendFace.h"
|
||||
#include "Head.h"
|
||||
#include "Model.h"
|
||||
|
||||
using namespace fs;
|
||||
using namespace std;
|
||||
|
||||
BlendFace::BlendFace(Head* owningHead) :
|
||||
_owningHead(owningHead)
|
||||
Model::Model() :
|
||||
_pupilDilation(0.0f)
|
||||
{
|
||||
// we may have been created in the network thread, but we live in the main thread
|
||||
moveToThread(Application::getInstance()->thread());
|
||||
}
|
||||
|
||||
BlendFace::~BlendFace() {
|
||||
Model::~Model() {
|
||||
deleteGeometry();
|
||||
}
|
||||
|
||||
ProgramObject BlendFace::_program;
|
||||
ProgramObject BlendFace::_skinProgram;
|
||||
int BlendFace::_clusterMatricesLocation;
|
||||
int BlendFace::_clusterIndicesLocation;
|
||||
int BlendFace::_clusterWeightsLocation;
|
||||
ProgramObject Model::_program;
|
||||
ProgramObject Model::_skinProgram;
|
||||
int Model::_clusterMatricesLocation;
|
||||
int Model::_clusterIndicesLocation;
|
||||
int Model::_clusterWeightsLocation;
|
||||
|
||||
void BlendFace::init() {
|
||||
void Model::init() {
|
||||
if (!_program.isLinked()) {
|
||||
switchToResourcesParentIfRequired();
|
||||
_program.addShaderFromSourceFile(QGLShader::Vertex, "resources/shaders/blendface.vert");
|
||||
_program.addShaderFromSourceFile(QGLShader::Fragment, "resources/shaders/blendface.frag");
|
||||
_program.addShaderFromSourceFile(QGLShader::Vertex, "resources/shaders/model.vert");
|
||||
_program.addShaderFromSourceFile(QGLShader::Fragment, "resources/shaders/model.frag");
|
||||
_program.link();
|
||||
|
||||
_program.bind();
|
||||
_program.setUniformValue("texture", 0);
|
||||
_program.release();
|
||||
|
||||
_skinProgram.addShaderFromSourceFile(QGLShader::Vertex, "resources/shaders/skin_blendface.vert");
|
||||
_skinProgram.addShaderFromSourceFile(QGLShader::Fragment, "resources/shaders/blendface.frag");
|
||||
_skinProgram.addShaderFromSourceFile(QGLShader::Vertex, "resources/shaders/skin_model.vert");
|
||||
_skinProgram.addShaderFromSourceFile(QGLShader::Fragment, "resources/shaders/model.frag");
|
||||
_skinProgram.link();
|
||||
|
||||
_skinProgram.bind();
|
||||
|
@ -58,22 +54,18 @@ void BlendFace::init() {
|
|||
}
|
||||
}
|
||||
|
||||
void BlendFace::reset() {
|
||||
void Model::reset() {
|
||||
_resetStates = true;
|
||||
}
|
||||
|
||||
const glm::vec3 MODEL_TRANSLATION(0.0f, -60.0f, 40.0f); // temporary fudge factor
|
||||
const float MODEL_SCALE = 0.0006f;
|
||||
|
||||
void BlendFace::simulate(float deltaTime) {
|
||||
void Model::simulate(float deltaTime) {
|
||||
if (!isActive()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// set up world vertices on first simulate after load
|
||||
const FBXGeometry& geometry = _geometry->getFBXGeometry();
|
||||
if (_meshStates.isEmpty()) {
|
||||
QVector<glm::vec3> vertices;
|
||||
if (_jointStates.isEmpty()) {
|
||||
foreach (const FBXJoint& joint, geometry.joints) {
|
||||
JointState state;
|
||||
state.rotation = joint.rotation;
|
||||
|
@ -92,43 +84,9 @@ void BlendFace::simulate(float deltaTime) {
|
|||
_resetStates = true;
|
||||
}
|
||||
|
||||
const Skeleton& skeleton = static_cast<Avatar*>(_owningHead->_owningAvatar)->getSkeleton();
|
||||
glm::quat orientation = skeleton.joint[AVATAR_JOINT_NECK_BASE].absoluteRotation;
|
||||
glm::vec3 scale = glm::vec3(-1.0f, 1.0f, -1.0f) * _owningHead->getScale() * MODEL_SCALE;
|
||||
glm::vec3 offset = MODEL_TRANSLATION - geometry.neckPivot;
|
||||
glm::mat4 baseTransform = glm::translate(skeleton.joint[AVATAR_JOINT_NECK_BASE].position) * glm::mat4_cast(orientation) *
|
||||
glm::scale(scale) * glm::translate(offset);
|
||||
|
||||
// update the world space transforms for all joints
|
||||
for (int i = 0; i < _jointStates.size(); i++) {
|
||||
JointState& state = _jointStates[i];
|
||||
const FBXJoint& joint = geometry.joints.at(i);
|
||||
if (joint.parentIndex == -1) {
|
||||
state.transform = baseTransform * geometry.offset * joint.preRotation *
|
||||
glm::mat4_cast(state.rotation) * joint.postRotation;
|
||||
|
||||
} else {
|
||||
if (i == geometry.neckJointIndex) {
|
||||
// get the rotation axes in joint space and use them to adjust the rotation
|
||||
glm::mat3 axes = glm::mat3_cast(orientation);
|
||||
glm::mat3 inverse = glm::inverse(glm::mat3(_jointStates[joint.parentIndex].transform *
|
||||
joint.preRotation * glm::mat4_cast(joint.rotation)));
|
||||
state.rotation = glm::angleAxis(_owningHead->getRoll(), glm::normalize(inverse * axes[2])) *
|
||||
glm::angleAxis(_owningHead->getYaw(), glm::normalize(inverse * axes[1])) *
|
||||
glm::angleAxis(_owningHead->getPitch(), glm::normalize(inverse * axes[0])) * joint.rotation;
|
||||
|
||||
} else if (i == geometry.leftEyeJointIndex || i == geometry.rightEyeJointIndex) {
|
||||
// likewise with the lookat position
|
||||
glm::mat4 inverse = glm::inverse(_jointStates[joint.parentIndex].transform *
|
||||
joint.preRotation * glm::mat4_cast(joint.rotation));
|
||||
glm::vec3 front = glm::vec3(inverse * glm::vec4(_owningHead->getOrientation() * IDENTITY_FRONT, 0.0f));
|
||||
glm::vec3 lookAt = glm::vec3(inverse * glm::vec4(_owningHead->getLookAtPosition() +
|
||||
_owningHead->getSaccade(), 1.0f));
|
||||
state.rotation = rotationBetween(front, lookAt) * joint.rotation;
|
||||
}
|
||||
state.transform = _jointStates[joint.parentIndex].transform * joint.preRotation *
|
||||
glm::mat4_cast(state.rotation) * joint.postRotation;
|
||||
}
|
||||
updateJointState(i);
|
||||
}
|
||||
|
||||
for (int i = 0; i < _meshStates.size(); i++) {
|
||||
|
@ -152,9 +110,8 @@ void BlendFace::simulate(float deltaTime) {
|
|||
memcpy(_blendedVertices.data(), mesh.vertices.constData(), vertexCount * sizeof(glm::vec3));
|
||||
|
||||
// blend in each coefficient
|
||||
const vector<float>& coefficients = _owningHead->getBlendshapeCoefficients();
|
||||
for (int j = 0; j < coefficients.size(); j++) {
|
||||
float coefficient = coefficients[j];
|
||||
for (int j = 0; j < _blendshapeCoefficients.size(); j++) {
|
||||
float coefficient = _blendshapeCoefficients[j];
|
||||
if (coefficient == 0.0f || j >= mesh.blendshapes.size() || mesh.blendshapes[j].vertices.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
@ -217,11 +174,11 @@ void BlendFace::simulate(float deltaTime) {
|
|||
_resetStates = false;
|
||||
}
|
||||
|
||||
bool BlendFace::render(float alpha) {
|
||||
bool Model::render(float alpha) {
|
||||
if (_meshStates.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// set up blended buffer ids on first render after load/simulate
|
||||
const FBXGeometry& geometry = _geometry->getFBXGeometry();
|
||||
const QVector<NetworkMesh>& networkMeshes = _geometry->getMeshes();
|
||||
|
@ -300,9 +257,8 @@ bool BlendFace::render(float alpha) {
|
|||
memcpy(_blendedNormals.data(), mesh.normals.constData(), vertexCount * sizeof(glm::vec3));
|
||||
|
||||
// blend in each coefficient
|
||||
const vector<float>& coefficients = _owningHead->getBlendshapeCoefficients();
|
||||
for (int j = 0; j < coefficients.size(); j++) {
|
||||
float coefficient = coefficients[j];
|
||||
for (int j = 0; j < _blendshapeCoefficients.size(); j++) {
|
||||
float coefficient = _blendshapeCoefficients[j];
|
||||
if (coefficient == 0.0f || j >= mesh.blendshapes.size() || mesh.blendshapes[j].vertices.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
@ -342,7 +298,7 @@ bool BlendFace::render(float alpha) {
|
|||
if (mesh.isEye) {
|
||||
if (texture != NULL) {
|
||||
texture = (_dilatedTextures[i][j] = static_cast<DilatableNetworkTexture*>(texture)->getDilatedTexture(
|
||||
_owningHead->getPupilDilation())).data();
|
||||
_pupilDilation)).data();
|
||||
}
|
||||
}
|
||||
glBindTexture(GL_TEXTURE_2D, texture == NULL ? Application::getInstance()->getTextureCache()->getWhiteTextureID() :
|
||||
|
@ -386,32 +342,33 @@ bool BlendFace::render(float alpha) {
|
|||
return true;
|
||||
}
|
||||
|
||||
bool BlendFace::getEyePositions(glm::vec3& firstEyePosition, glm::vec3& secondEyePosition, bool upright) const {
|
||||
if (!isActive() || _jointStates.isEmpty()) {
|
||||
bool Model::getHeadPosition(glm::vec3& headPosition) const {
|
||||
return isActive() && getJointPosition(_geometry->getFBXGeometry().headJointIndex, headPosition);
|
||||
}
|
||||
|
||||
bool Model::getNeckPosition(glm::vec3& neckPosition) const {
|
||||
return isActive() && getJointPosition(_geometry->getFBXGeometry().neckJointIndex, neckPosition);
|
||||
}
|
||||
|
||||
bool Model::getNeckRotation(glm::quat& neckRotation) const {
|
||||
return isActive() && getJointRotation(_geometry->getFBXGeometry().neckJointIndex, neckRotation);
|
||||
}
|
||||
|
||||
bool Model::getEyePositions(glm::vec3& firstEyePosition, glm::vec3& secondEyePosition) const {
|
||||
if (!isActive()) {
|
||||
return false;
|
||||
}
|
||||
const FBXGeometry& geometry = _geometry->getFBXGeometry();
|
||||
if (geometry.leftEyeJointIndex != -1) {
|
||||
const glm::mat4& transform = _jointStates[geometry.leftEyeJointIndex].transform;
|
||||
firstEyePosition = glm::vec3(transform[3][0], transform[3][1], transform[3][2]);
|
||||
}
|
||||
if (geometry.rightEyeJointIndex != -1) {
|
||||
const glm::mat4& transform = _jointStates[geometry.rightEyeJointIndex].transform;
|
||||
secondEyePosition = glm::vec3(transform[3][0], transform[3][1], transform[3][2]);
|
||||
}
|
||||
return geometry.leftEyeJointIndex != -1 && geometry.rightEyeJointIndex != -1;
|
||||
return getJointPosition(geometry.leftEyeJointIndex, firstEyePosition) &&
|
||||
getJointPosition(geometry.rightEyeJointIndex, secondEyePosition);
|
||||
}
|
||||
|
||||
glm::vec4 BlendFace::computeAverageColor() const {
|
||||
return _geometry ? _geometry->computeAverageColor() : glm::vec4(1.0f, 1.0f, 1.0f, 1.0f);
|
||||
}
|
||||
|
||||
void BlendFace::setModelURL(const QUrl& url) {
|
||||
void Model::setURL(const QUrl& url) {
|
||||
// don't recreate the geometry if it's the same URL
|
||||
if (_modelURL == url) {
|
||||
if (_url == url) {
|
||||
return;
|
||||
}
|
||||
_modelURL = url;
|
||||
_url = url;
|
||||
|
||||
// delete our local geometry and custom textures
|
||||
deleteGeometry();
|
||||
|
@ -420,7 +377,72 @@ void BlendFace::setModelURL(const QUrl& url) {
|
|||
_geometry = Application::getInstance()->getGeometryCache()->getGeometry(url);
|
||||
}
|
||||
|
||||
void BlendFace::deleteGeometry() {
|
||||
glm::vec4 Model::computeAverageColor() const {
|
||||
return _geometry ? _geometry->computeAverageColor() : glm::vec4(1.0f, 1.0f, 1.0f, 1.0f);
|
||||
}
|
||||
|
||||
void Model::updateJointState(int index) {
|
||||
JointState& state = _jointStates[index];
|
||||
const FBXGeometry& geometry = _geometry->getFBXGeometry();
|
||||
const FBXJoint& joint = geometry.joints.at(index);
|
||||
|
||||
if (joint.parentIndex == -1) {
|
||||
glm::mat4 baseTransform = glm::translate(_translation) * glm::mat4_cast(_rotation) *
|
||||
glm::scale(_scale) * glm::translate(_offset);
|
||||
|
||||
glm::quat combinedRotation = joint.preRotation * state.rotation * joint.postRotation;
|
||||
state.transform = baseTransform * geometry.offset * joint.preTransform *
|
||||
glm::mat4_cast(combinedRotation) * joint.postTransform;
|
||||
state.combinedRotation = _rotation * combinedRotation;
|
||||
|
||||
} else {
|
||||
const JointState& parentState = _jointStates.at(joint.parentIndex);
|
||||
if (index == geometry.leanJointIndex) {
|
||||
maybeUpdateLeanRotation(parentState, joint, state);
|
||||
|
||||
} else if (index == geometry.neckJointIndex) {
|
||||
maybeUpdateNeckRotation(parentState, joint, state);
|
||||
|
||||
} else if (index == geometry.leftEyeJointIndex || index == geometry.rightEyeJointIndex) {
|
||||
maybeUpdateEyeRotation(parentState, joint, state);
|
||||
}
|
||||
glm::quat combinedRotation = joint.preRotation * state.rotation * joint.postRotation;
|
||||
state.transform = parentState.transform * joint.preTransform *
|
||||
glm::mat4_cast(combinedRotation) * joint.postTransform;
|
||||
state.combinedRotation = parentState.combinedRotation * combinedRotation;
|
||||
}
|
||||
}
|
||||
|
||||
void Model::maybeUpdateLeanRotation(const JointState& parentState, const FBXJoint& joint, JointState& state) {
|
||||
// nothing by default
|
||||
}
|
||||
|
||||
void Model::maybeUpdateNeckRotation(const JointState& parentState, const FBXJoint& joint, JointState& state) {
|
||||
// nothing by default
|
||||
}
|
||||
|
||||
void Model::maybeUpdateEyeRotation(const JointState& parentState, const FBXJoint& joint, JointState& state) {
|
||||
// nothing by default
|
||||
}
|
||||
|
||||
bool Model::getJointPosition(int jointIndex, glm::vec3& position) const {
|
||||
if (jointIndex == -1 || _jointStates.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
position = extractTranslation(_jointStates[jointIndex].transform);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Model::getJointRotation(int jointIndex, glm::quat& rotation) const {
|
||||
if (jointIndex == -1 || _jointStates.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
rotation = _jointStates[jointIndex].combinedRotation *
|
||||
_geometry->getFBXGeometry().joints[jointIndex].inverseBindRotation;
|
||||
return true;
|
||||
}
|
||||
|
||||
void Model::deleteGeometry() {
|
||||
foreach (GLuint id, _blendedVertexBufferIDs) {
|
||||
glDeleteBuffers(1, &id);
|
||||
}
|
136
interface/src/renderer/Model.h
Normal file
136
interface/src/renderer/Model.h
Normal file
|
@ -0,0 +1,136 @@
|
|||
//
|
||||
// Model.h
|
||||
// interface
|
||||
//
|
||||
// Created by Andrzej Kapolka on 10/18/13.
|
||||
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef __interface__Model__
|
||||
#define __interface__Model__
|
||||
|
||||
#include <QObject>
|
||||
#include <QUrl>
|
||||
|
||||
#include "GeometryCache.h"
|
||||
#include "InterfaceConfig.h"
|
||||
#include "ProgramObject.h"
|
||||
#include "TextureCache.h"
|
||||
|
||||
/// A generic 3D model displaying geometry loaded from a URL.
|
||||
class Model : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
|
||||
Model();
|
||||
virtual ~Model();
|
||||
|
||||
void setTranslation(const glm::vec3& translation) { _translation = translation; }
|
||||
const glm::vec3& getTranslation() const { return _translation; }
|
||||
|
||||
void setRotation(const glm::quat& rotation) { _rotation = rotation; }
|
||||
const glm::quat& getRotation() const { return _rotation; }
|
||||
|
||||
void setScale(const glm::vec3& scale) { _scale = scale; }
|
||||
const glm::vec3& getScale() const { return _scale; }
|
||||
|
||||
void setOffset(const glm::vec3& offset) { _offset = offset; }
|
||||
const glm::vec3& getOffset() const { return _offset; }
|
||||
|
||||
void setPupilDilation(float dilation) { _pupilDilation = dilation; }
|
||||
float getPupilDilation() const { return _pupilDilation; }
|
||||
|
||||
void setBlendshapeCoefficients(const std::vector<float>& coefficients) { _blendshapeCoefficients = coefficients; }
|
||||
const std::vector<float>& getBlendshapeCoefficients() const { return _blendshapeCoefficients; }
|
||||
|
||||
bool isActive() const { return _geometry && _geometry->isLoaded(); }
|
||||
|
||||
void init();
|
||||
void reset();
|
||||
void simulate(float deltaTime);
|
||||
bool render(float alpha);
|
||||
|
||||
Q_INVOKABLE void setURL(const QUrl& url);
|
||||
const QUrl& getURL() const { return _url; }
|
||||
|
||||
/// Returns the position of the head joint.
|
||||
/// \return whether or not the head was found
|
||||
bool getHeadPosition(glm::vec3& headPosition) const;
|
||||
|
||||
/// Returns the position of the neck joint.
|
||||
/// \return whether or not the neck was found
|
||||
bool getNeckPosition(glm::vec3& neckPosition) const;
|
||||
|
||||
/// Returns the rotation of the neck joint.
|
||||
/// \return whether or not the neck was found
|
||||
bool getNeckRotation(glm::quat& neckRotation) const;
|
||||
|
||||
/// Retrieve the positions of up to two eye meshes.
|
||||
/// \return whether or not both eye meshes were found
|
||||
bool getEyePositions(glm::vec3& firstEyePosition, glm::vec3& secondEyePosition) const;
|
||||
|
||||
/// Returns the average color of all meshes in the geometry.
|
||||
glm::vec4 computeAverageColor() const;
|
||||
|
||||
protected:
|
||||
|
||||
QSharedPointer<NetworkGeometry> _geometry;
|
||||
|
||||
glm::vec3 _translation;
|
||||
glm::quat _rotation;
|
||||
glm::vec3 _scale;
|
||||
glm::vec3 _offset;
|
||||
|
||||
class JointState {
|
||||
public:
|
||||
glm::quat rotation;
|
||||
glm::mat4 transform;
|
||||
glm::quat combinedRotation;
|
||||
};
|
||||
|
||||
QVector<JointState> _jointStates;
|
||||
|
||||
/// Updates the state of the joint at the specified index.
|
||||
virtual void updateJointState(int index);
|
||||
|
||||
virtual void maybeUpdateLeanRotation(const JointState& parentState, const FBXJoint& joint, JointState& state);
|
||||
virtual void maybeUpdateNeckRotation(const JointState& parentState, const FBXJoint& joint, JointState& state);
|
||||
virtual void maybeUpdateEyeRotation(const JointState& parentState, const FBXJoint& joint, JointState& state);
|
||||
|
||||
bool getJointPosition(int jointIndex, glm::vec3& position) const;
|
||||
bool getJointRotation(int jointIndex, glm::quat& rotation) const;
|
||||
|
||||
private:
|
||||
|
||||
void deleteGeometry();
|
||||
|
||||
float _pupilDilation;
|
||||
std::vector<float> _blendshapeCoefficients;
|
||||
|
||||
QUrl _url;
|
||||
|
||||
class MeshState {
|
||||
public:
|
||||
QVector<glm::mat4> clusterMatrices;
|
||||
QVector<glm::vec3> worldSpaceVertices;
|
||||
QVector<glm::vec3> vertexVelocities;
|
||||
QVector<glm::vec3> worldSpaceNormals;
|
||||
};
|
||||
|
||||
QVector<MeshState> _meshStates;
|
||||
QVector<GLuint> _blendedVertexBufferIDs;
|
||||
QVector<QVector<QSharedPointer<Texture> > > _dilatedTextures;
|
||||
bool _resetStates;
|
||||
|
||||
QVector<glm::vec3> _blendedVertices;
|
||||
QVector<glm::vec3> _blendedNormals;
|
||||
|
||||
static ProgramObject _program;
|
||||
static ProgramObject _skinProgram;
|
||||
static int _clusterMatricesLocation;
|
||||
static int _clusterIndicesLocation;
|
||||
static int _clusterWeightsLocation;
|
||||
};
|
||||
|
||||
#endif /* defined(__interface__Model__) */
|
Loading…
Reference in a new issue