Merge branch 'master' of https://github.com/worklist/hifi into fix_mirror_mode

This commit is contained in:
ZappoMan 2013-10-23 11:58:47 -07:00
commit 46eadb5fb7
29 changed files with 1206 additions and 534 deletions

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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__) */

View 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;
}

View 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__) */

View file

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

View file

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

View file

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

View file

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

View file

@ -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__) */

View 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;
}

View 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__) */

View file

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

View file

@ -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__) */

View file

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

View file

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

View file

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

View file

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

View 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__) */