Merge pull request #2369 from ey6es/master

Perform blending in worker threads and only do it when we have new data.  Removed some unused code and fixed a couple warnings.
This commit is contained in:
Philip Rosedale 2014-03-19 08:59:20 -07:00
commit a1e07374d4
14 changed files with 206 additions and 272 deletions

View file

@ -40,7 +40,7 @@ detect_strip_roi_width 2
detect_strip_roi_height 4 detect_strip_roi_height 4
smoothing_factors smoothing_factors
150 15 -2 100 -1 50 50 0 150 5 -2 100 -1 50 50 0
#translation rotation action_units eyebrows mouth gaze eye_closure other #translation rotation action_units eyebrows mouth gaze eye_closure other
process_eyes 1 process_eyes 1

View file

@ -424,11 +424,6 @@ void MyAvatar::updateFromGyros(float deltaTime) {
} }
} }
static TextRenderer* textRenderer() {
static TextRenderer* renderer = new TextRenderer(SANS_FONT_FAMILY, 24, -1, false, TextRenderer::SHADOW_EFFECT);
return renderer;
}
void MyAvatar::renderDebugBodyPoints() { void MyAvatar::renderDebugBodyPoints() {
glm::vec3 torsoPosition(getPosition()); glm::vec3 torsoPosition(getPosition());
glm::vec3 headPosition(getHead()->getEyePosition()); glm::vec3 headPosition(getHead()->getEyePosition());

View file

@ -103,7 +103,7 @@ void Faceshift::reset() {
} }
void Faceshift::updateFakeCoefficients(float leftBlink, float rightBlink, float browUp, void Faceshift::updateFakeCoefficients(float leftBlink, float rightBlink, float browUp,
float jawOpen, std::vector<float>& coefficients) const { float jawOpen, QVector<float>& coefficients) const {
coefficients.resize(max((int)coefficients.size(), _jawOpenIndex + 1)); coefficients.resize(max((int)coefficients.size(), _jawOpenIndex + 1));
qFill(coefficients.begin(), coefficients.end(), 0.0f); qFill(coefficients.begin(), coefficients.end(), 0.0f);
coefficients[_leftBlinkIndex] = leftBlink; coefficients[_leftBlinkIndex] = leftBlink;
@ -204,7 +204,7 @@ void Faceshift::receive(const QByteArray& buffer) {
_eyeGazeLeftYaw = data.m_eyeGazeLeftYaw; _eyeGazeLeftYaw = data.m_eyeGazeLeftYaw;
_eyeGazeRightPitch = -data.m_eyeGazeRightPitch; _eyeGazeRightPitch = -data.m_eyeGazeRightPitch;
_eyeGazeRightYaw = data.m_eyeGazeRightYaw; _eyeGazeRightYaw = data.m_eyeGazeRightYaw;
_blendshapeCoefficients = data.m_coeffs; _blendshapeCoefficients = QVector<float>::fromStdVector(data.m_coeffs);
_lastTrackingStateReceived = usecTimestampNow(); _lastTrackingStateReceived = usecTimestampNow();
} }

View file

@ -9,8 +9,6 @@
#ifndef __interface__Faceshift__ #ifndef __interface__Faceshift__
#define __interface__Faceshift__ #define __interface__Faceshift__
#include <vector>
#include <QTcpSocket> #include <QTcpSocket>
#include <QUdpSocket> #include <QUdpSocket>
@ -47,7 +45,7 @@ public:
float getEstimatedEyePitch() const { return _estimatedEyePitch; } float getEstimatedEyePitch() const { return _estimatedEyePitch; }
float getEstimatedEyeYaw() const { return _estimatedEyeYaw; } float getEstimatedEyeYaw() const { return _estimatedEyeYaw; }
const std::vector<float>& getBlendshapeCoefficients() const { return _blendshapeCoefficients; } const QVector<float>& getBlendshapeCoefficients() const { return _blendshapeCoefficients; }
float getLeftBlink() const { return getBlendshapeCoefficient(_leftBlinkIndex); } float getLeftBlink() const { return getBlendshapeCoefficient(_leftBlinkIndex); }
float getRightBlink() const { return getBlendshapeCoefficient(_rightBlinkIndex); } float getRightBlink() const { return getBlendshapeCoefficient(_rightBlinkIndex); }
@ -68,7 +66,7 @@ public:
void reset(); void reset();
void updateFakeCoefficients(float leftBlink, float rightBlink, float browUp, void updateFakeCoefficients(float leftBlink, float rightBlink, float browUp,
float jawOpen, std::vector<float>& coefficients) const; float jawOpen, QVector<float>& coefficients) const;
signals: signals:
@ -111,7 +109,7 @@ private:
float _eyeGazeRightPitch; float _eyeGazeRightPitch;
float _eyeGazeRightYaw; float _eyeGazeRightYaw;
std::vector<float> _blendshapeCoefficients; QVector<float> _blendshapeCoefficients;
int _leftBlinkIndex; int _leftBlinkIndex;
int _rightBlinkIndex; int _rightBlinkIndex;

View file

@ -9,8 +9,6 @@
#ifndef __interface__Visage__ #ifndef __interface__Visage__
#define __interface__Visage__ #define __interface__Visage__
#include <vector>
#include <QMultiHash> #include <QMultiHash>
#include <QPair> #include <QPair>
#include <QVector> #include <QVector>
@ -42,7 +40,7 @@ public:
float getEstimatedEyePitch() const { return _estimatedEyePitch; } float getEstimatedEyePitch() const { return _estimatedEyePitch; }
float getEstimatedEyeYaw() const { return _estimatedEyeYaw; } float getEstimatedEyeYaw() const { return _estimatedEyeYaw; }
const std::vector<float>& getBlendshapeCoefficients() const { return _blendshapeCoefficients; } const QVector<float>& getBlendshapeCoefficients() const { return _blendshapeCoefficients; }
void update(); void update();
void reset(); void reset();
@ -71,7 +69,7 @@ private:
float _estimatedEyePitch; float _estimatedEyePitch;
float _estimatedEyeYaw; float _estimatedEyeYaw;
std::vector<float> _blendshapeCoefficients; QVector<float> _blendshapeCoefficients;
}; };
#endif /* defined(__interface__Visage__) */ #endif /* defined(__interface__Visage__) */

View file

@ -54,6 +54,15 @@ QStringList FBXGeometry::getJointNames() const {
return names; return names;
} }
bool FBXGeometry::hasBlendedMeshes() const {
foreach (const FBXMesh& mesh, meshes) {
if (!mesh.blendshapes.isEmpty()) {
return true;
}
}
return false;
}
static int fbxGeometryMetaTypeId = qRegisterMetaType<FBXGeometry>(); static int fbxGeometryMetaTypeId = qRegisterMetaType<FBXGeometry>();
template<class T> QVariant readBinaryArray(QDataStream& in) { template<class T> QVariant readBinaryArray(QDataStream& in) {
@ -1331,14 +1340,11 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping)
geometry.staticExtents.reset(); geometry.staticExtents.reset();
geometry.meshExtents.reset(); geometry.meshExtents.reset();
QVariantHash springs = mapping.value("spring").toHash();
QVariant defaultSpring = springs.value("default");
for (QHash<QString, ExtractedMesh>::iterator it = meshes.begin(); it != meshes.end(); it++) { for (QHash<QString, ExtractedMesh>::iterator it = meshes.begin(); it != meshes.end(); it++) {
ExtractedMesh& extracted = it.value(); ExtractedMesh& extracted = it.value();
// accumulate local transforms // accumulate local transforms
QString modelID = models.contains(it.key()) ? it.key() : parentMap.value(it.key()); QString modelID = models.contains(it.key()) ? it.key() : parentMap.value(it.key());
extracted.mesh.springiness = springs.value(models.value(modelID).name, defaultSpring).toFloat();
glm::mat4 modelTransform = getGlobalTransform(parentMap, models, modelID); glm::mat4 modelTransform = getGlobalTransform(parentMap, models, modelID);
// compute the mesh extents from the transformed vertices // compute the mesh extents from the transformed vertices
@ -1592,48 +1598,6 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping)
} }
extracted.mesh.isEye = (maxJointIndex == geometry.leftEyeJointIndex || maxJointIndex == geometry.rightEyeJointIndex); extracted.mesh.isEye = (maxJointIndex == geometry.leftEyeJointIndex || maxJointIndex == geometry.rightEyeJointIndex);
// extract spring edges, connections if springy
if (extracted.mesh.springiness > 0.0f) {
QSet<QPair<int, int> > edges;
extracted.mesh.vertexConnections.resize(extracted.mesh.vertices.size());
foreach (const FBXMeshPart& part, extracted.mesh.parts) {
for (int i = 0; i < part.quadIndices.size(); i += 4) {
int index0 = part.quadIndices.at(i);
int index1 = part.quadIndices.at(i + 1);
int index2 = part.quadIndices.at(i + 2);
int index3 = part.quadIndices.at(i + 3);
edges.insert(QPair<int, int>(qMin(index0, index1), qMax(index0, index1)));
edges.insert(QPair<int, int>(qMin(index1, index2), qMax(index1, index2)));
edges.insert(QPair<int, int>(qMin(index2, index3), qMax(index2, index3)));
edges.insert(QPair<int, int>(qMin(index3, index0), qMax(index3, index0)));
extracted.mesh.vertexConnections[index0].append(QPair<int, int>(index3, index1));
extracted.mesh.vertexConnections[index1].append(QPair<int, int>(index0, index2));
extracted.mesh.vertexConnections[index2].append(QPair<int, int>(index1, index3));
extracted.mesh.vertexConnections[index3].append(QPair<int, int>(index2, index0));
}
for (int i = 0; i < part.triangleIndices.size(); i += 3) {
int index0 = part.triangleIndices.at(i);
int index1 = part.triangleIndices.at(i + 1);
int index2 = part.triangleIndices.at(i + 2);
edges.insert(QPair<int, int>(qMin(index0, index1), qMax(index0, index1)));
edges.insert(QPair<int, int>(qMin(index1, index2), qMax(index1, index2)));
edges.insert(QPair<int, int>(qMin(index2, index0), qMax(index2, index0)));
extracted.mesh.vertexConnections[index0].append(QPair<int, int>(index2, index1));
extracted.mesh.vertexConnections[index1].append(QPair<int, int>(index0, index2));
extracted.mesh.vertexConnections[index2].append(QPair<int, int>(index1, index0));
}
}
for (QSet<QPair<int, int> >::const_iterator edge = edges.constBegin(); edge != edges.constEnd(); edge++) {
extracted.mesh.springEdges.append(*edge);
}
}
geometry.meshes.append(extracted.mesh); geometry.meshes.append(extracted.mesh);
} }
@ -1797,7 +1761,6 @@ FBXGeometry readSVO(const QByteArray& model) {
// and one mesh with one cluster and one part // and one mesh with one cluster and one part
FBXMesh mesh; FBXMesh mesh;
mesh.isEye = false; mesh.isEye = false;
mesh.springiness = 0.0f;
FBXCluster cluster = { 0 }; FBXCluster cluster = { 0 };
mesh.clusters.append(cluster); mesh.clusters.append(cluster);

View file

@ -130,10 +130,6 @@ public:
bool isEye; bool isEye;
QVector<FBXBlendshape> blendshapes; QVector<FBXBlendshape> blendshapes;
float springiness;
QVector<QPair<int, int> > springEdges;
QVector<QVarLengthArray<QPair<int, int>, 4> > vertexConnections;
}; };
/// An attachment to an FBX document. /// An attachment to an FBX document.
@ -185,6 +181,8 @@ public:
int getJointIndex(const QString& name) const { return jointIndices.value(name) - 1; } int getJointIndex(const QString& name) const { return jointIndices.value(name) - 1; }
QStringList getJointNames() const; QStringList getJointNames() const;
bool hasBlendedMeshes() const;
}; };
Q_DECLARE_METATYPE(FBXGeometry) Q_DECLARE_METATYPE(FBXGeometry)

View file

@ -13,6 +13,7 @@
#include "Application.h" #include "Application.h"
#include "GeometryCache.h" #include "GeometryCache.h"
#include "Model.h"
#include "world.h" #include "world.h"
GeometryCache::~GeometryCache() { GeometryCache::~GeometryCache() {
@ -291,6 +292,13 @@ QSharedPointer<NetworkGeometry> GeometryCache::getGeometry(const QUrl& url, cons
return getResource(url, fallback, delayLoad).staticCast<NetworkGeometry>(); return getResource(url, fallback, delayLoad).staticCast<NetworkGeometry>();
} }
void GeometryCache::setBlendedVertices(const QPointer<Model>& model, const QWeakPointer<NetworkGeometry>& geometry,
const QVector<glm::vec3>& vertices, const QVector<glm::vec3>& normals) {
if (!model.isNull() && model->getGeometry() == geometry) {
model->setBlendedVertices(vertices, normals);
}
}
QSharedPointer<Resource> GeometryCache::createResource(const QUrl& url, QSharedPointer<Resource> GeometryCache::createResource(const QUrl& url,
const QSharedPointer<Resource>& fallback, bool delayLoad, const void* extra) { const QSharedPointer<Resource>& fallback, bool delayLoad, const void* extra) {
@ -565,8 +573,8 @@ void NetworkGeometry::setGeometry(const FBXGeometry& geometry) {
networkMesh.vertexBuffer.bind(); networkMesh.vertexBuffer.bind();
networkMesh.vertexBuffer.setUsagePattern(QOpenGLBuffer::StaticDraw); networkMesh.vertexBuffer.setUsagePattern(QOpenGLBuffer::StaticDraw);
// if we don't need to do any blending or springing, then the positions/normals can be static // if we don't need to do any blending, the positions/normals can be static
if (mesh.blendshapes.isEmpty() && mesh.springiness == 0.0f) { if (mesh.blendshapes.isEmpty()) {
int normalsOffset = mesh.vertices.size() * sizeof(glm::vec3); int normalsOffset = mesh.vertices.size() * sizeof(glm::vec3);
int tangentsOffset = normalsOffset + mesh.normals.size() * sizeof(glm::vec3); int tangentsOffset = normalsOffset + mesh.normals.size() * sizeof(glm::vec3);
int colorsOffset = tangentsOffset + mesh.tangents.size() * sizeof(glm::vec3); int colorsOffset = tangentsOffset + mesh.tangents.size() * sizeof(glm::vec3);
@ -587,8 +595,8 @@ void NetworkGeometry::setGeometry(const FBXGeometry& geometry) {
networkMesh.vertexBuffer.write(clusterWeightsOffset, mesh.clusterWeights.constData(), networkMesh.vertexBuffer.write(clusterWeightsOffset, mesh.clusterWeights.constData(),
mesh.clusterWeights.size() * sizeof(glm::vec4)); mesh.clusterWeights.size() * sizeof(glm::vec4));
// if there's no springiness, then the cluster indices/weights can be static // otherwise, at least the cluster indices/weights can be static
} else if (mesh.springiness == 0.0f) { } else {
int colorsOffset = mesh.tangents.size() * sizeof(glm::vec3); int colorsOffset = mesh.tangents.size() * sizeof(glm::vec3);
int texCoordsOffset = colorsOffset + mesh.colors.size() * sizeof(glm::vec3); int texCoordsOffset = colorsOffset + mesh.colors.size() * sizeof(glm::vec3);
int clusterIndicesOffset = texCoordsOffset + mesh.texCoords.size() * sizeof(glm::vec2); int clusterIndicesOffset = texCoordsOffset + mesh.texCoords.size() * sizeof(glm::vec2);
@ -602,15 +610,6 @@ void NetworkGeometry::setGeometry(const FBXGeometry& geometry) {
mesh.clusterIndices.size() * sizeof(glm::vec4)); mesh.clusterIndices.size() * sizeof(glm::vec4));
networkMesh.vertexBuffer.write(clusterWeightsOffset, mesh.clusterWeights.constData(), networkMesh.vertexBuffer.write(clusterWeightsOffset, mesh.clusterWeights.constData(),
mesh.clusterWeights.size() * sizeof(glm::vec4)); mesh.clusterWeights.size() * sizeof(glm::vec4));
} else {
int colorsOffset = mesh.tangents.size() * sizeof(glm::vec3);
int texCoordsOffset = colorsOffset + mesh.colors.size() * sizeof(glm::vec3);
networkMesh.vertexBuffer.allocate(texCoordsOffset + mesh.texCoords.size() * sizeof(glm::vec2));
networkMesh.vertexBuffer.write(0, mesh.tangents.constData(), mesh.tangents.size() * sizeof(glm::vec3));
networkMesh.vertexBuffer.write(colorsOffset, mesh.colors.constData(), mesh.colors.size() * sizeof(glm::vec3));
networkMesh.vertexBuffer.write(texCoordsOffset, mesh.texCoords.constData(),
mesh.texCoords.size() * sizeof(glm::vec2));
} }
networkMesh.vertexBuffer.release(); networkMesh.vertexBuffer.release();

View file

@ -19,15 +19,18 @@
#include "FBXReader.h" #include "FBXReader.h"
class Model;
class NetworkGeometry; class NetworkGeometry;
class NetworkMesh; class NetworkMesh;
class NetworkTexture; class NetworkTexture;
/// Stores cached geometry. /// Stores cached geometry.
class GeometryCache : public ResourceCache { class GeometryCache : public ResourceCache {
Q_OBJECT
public: public:
~GeometryCache(); virtual ~GeometryCache();
void renderHemisphere(int slices, int stacks); void renderHemisphere(int slices, int stacks);
void renderSquare(int xDivisions, int yDivisions); void renderSquare(int xDivisions, int yDivisions);
@ -39,6 +42,11 @@ public:
/// \param delayLoad if true, don't load the geometry immediately; wait until load is first requested /// \param delayLoad if true, don't load the geometry immediately; wait until load is first requested
QSharedPointer<NetworkGeometry> getGeometry(const QUrl& url, const QUrl& fallback = QUrl(), bool delayLoad = false); QSharedPointer<NetworkGeometry> getGeometry(const QUrl& url, const QUrl& fallback = QUrl(), bool delayLoad = false);
public slots:
void setBlendedVertices(const QPointer<Model>& model, const QWeakPointer<NetworkGeometry>& geometry,
const QVector<glm::vec3>& vertices, const QVector<glm::vec3>& normals);
protected: protected:
virtual QSharedPointer<Resource> createResource(const QUrl& url, virtual QSharedPointer<Resource> createResource(const QUrl& url,

View file

@ -6,6 +6,10 @@
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved. // Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
// //
#include <QMetaType>
#include <QRunnable>
#include <QThreadPool>
#include <glm/gtx/transform.hpp> #include <glm/gtx/transform.hpp>
#include <GeometryUtil.h> #include <GeometryUtil.h>
@ -19,6 +23,10 @@
using namespace std; using namespace std;
static int modelPointerTypeId = qRegisterMetaType<QPointer<Model> >();
static int weakNetworkGeometryPointerTypeId = qRegisterMetaType<QWeakPointer<NetworkGeometry> >();
static int vec3VectorTypeId = qRegisterMetaType<QVector<glm::vec3> >();
Model::Model(QObject* parent) : Model::Model(QObject* parent) :
QObject(parent), QObject(parent),
_scale(1.0f, 1.0f, 1.0f), _scale(1.0f, 1.0f, 1.0f),
@ -104,8 +112,6 @@ void Model::init() {
} }
void Model::reset() { void Model::reset() {
_resetStates = true;
foreach (Model* attachment, _attachments) { foreach (Model* attachment, _attachments) {
attachment->reset(); attachment->reset();
} }
@ -170,20 +176,10 @@ bool Model::render(float alpha) {
return false; return false;
} }
// set up blended buffer ids on first render after load/simulate // set up dilated textures on first render after load/simulate
const FBXGeometry& geometry = _geometry->getFBXGeometry(); const FBXGeometry& geometry = _geometry->getFBXGeometry();
if (_blendedVertexBufferIDs.isEmpty()) { if (_dilatedTextures.isEmpty()) {
foreach (const FBXMesh& mesh, geometry.meshes) { foreach (const FBXMesh& mesh, geometry.meshes) {
GLuint id = 0;
if (!mesh.blendshapes.isEmpty() || mesh.springiness > 0.0f) {
glGenBuffers(1, &id);
glBindBuffer(GL_ARRAY_BUFFER, id);
glBufferData(GL_ARRAY_BUFFER, (mesh.vertices.size() + mesh.normals.size()) * sizeof(glm::vec3),
NULL, GL_DYNAMIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, 0);
}
_blendedVertexBufferIDs.append(id);
QVector<QSharedPointer<Texture> > dilated; QVector<QSharedPointer<Texture> > dilated;
dilated.resize(mesh.parts.size()); dilated.resize(mesh.parts.size());
_dilatedTextures.append(dilated); _dilatedTextures.append(dilated);
@ -478,6 +474,68 @@ QVector<Model::JointState> Model::updateGeometry() {
return newJointStates; return newJointStates;
} }
class Blender : public QRunnable {
public:
Blender(Model* model, const QWeakPointer<NetworkGeometry>& geometry,
const QVector<FBXMesh>& meshes, const QVector<float>& blendshapeCoefficients);
virtual void run();
private:
QPointer<Model> _model;
QWeakPointer<NetworkGeometry> _geometry;
QVector<FBXMesh> _meshes;
QVector<float> _blendshapeCoefficients;
};
Blender::Blender(Model* model, const QWeakPointer<NetworkGeometry>& geometry,
const QVector<FBXMesh>& meshes, const QVector<float>& blendshapeCoefficients) :
_model(model),
_geometry(geometry),
_meshes(meshes),
_blendshapeCoefficients(blendshapeCoefficients) {
}
void Blender::run() {
// make sure the model/geometry still exists
if (_model.isNull() || _geometry.isNull()) {
return;
}
QVector<glm::vec3> vertices, normals;
int offset = 0;
foreach (const FBXMesh& mesh, _meshes) {
if (mesh.blendshapes.isEmpty()) {
continue;
}
vertices += mesh.vertices;
normals += mesh.normals;
glm::vec3* meshVertices = vertices.data() + offset;
glm::vec3* meshNormals = normals.data() + offset;
offset += mesh.vertices.size();
const float NORMAL_COEFFICIENT_SCALE = 0.01f;
for (int i = 0, n = qMin(_blendshapeCoefficients.size(), mesh.blendshapes.size()); i < n; i++) {
float vertexCoefficient = _blendshapeCoefficients.at(i);
if (vertexCoefficient < EPSILON) {
continue;
}
float normalCoefficient = vertexCoefficient * NORMAL_COEFFICIENT_SCALE;
const FBXBlendshape& blendshape = mesh.blendshapes.at(i);
for (int j = 0; j < blendshape.indices.size(); j++) {
int index = blendshape.indices.at(j);
meshVertices[index] += blendshape.vertices.at(j) * vertexCoefficient;
meshNormals[index] += blendshape.normals.at(j) * normalCoefficient;
}
}
}
// post the result to the geometry cache, which will dispatch to the model if still alive
QMetaObject::invokeMethod(Application::getInstance()->getGeometryCache(), "setBlendedVertices",
Q_ARG(const QPointer<Model>&, _model), Q_ARG(const QWeakPointer<NetworkGeometry>&, _geometry),
Q_ARG(const QVector<glm::vec3>&, vertices), Q_ARG(const QVector<glm::vec3>&, normals));
}
void Model::simulate(float deltaTime, bool fullUpdate, const QVector<JointState>& newJointStates) { void Model::simulate(float deltaTime, bool fullUpdate, const QVector<JointState>& newJointStates) {
if (!isActive()) { if (!isActive()) {
return; return;
@ -490,12 +548,20 @@ void Model::simulate(float deltaTime, bool fullUpdate, const QVector<JointState>
foreach (const FBXMesh& mesh, geometry.meshes) { foreach (const FBXMesh& mesh, geometry.meshes) {
MeshState state; MeshState state;
state.clusterMatrices.resize(mesh.clusters.size()); state.clusterMatrices.resize(mesh.clusters.size());
if (mesh.springiness > 0.0f) {
state.worldSpaceVertices.resize(mesh.vertices.size());
state.vertexVelocities.resize(mesh.vertices.size());
state.worldSpaceNormals.resize(mesh.vertices.size());
}
_meshStates.append(state); _meshStates.append(state);
QOpenGLBuffer buffer;
if (!mesh.blendshapes.isEmpty()) {
buffer.setUsagePattern(QOpenGLBuffer::DynamicDraw);
buffer.create();
buffer.bind();
buffer.allocate((mesh.vertices.size() + mesh.normals.size()) * sizeof(glm::vec3));
buffer.write(0, mesh.vertices.constData(), mesh.vertices.size() * sizeof(glm::vec3));
buffer.write(mesh.vertices.size() * sizeof(glm::vec3), mesh.normals.constData(),
mesh.normals.size() * sizeof(glm::vec3));
buffer.release();
}
_blendedVertexBuffers.append(buffer);
} }
foreach (const FBXAttachment& attachment, geometry.attachments) { foreach (const FBXAttachment& attachment, geometry.attachments) {
Model* model = new Model(this); Model* model = new Model(this);
@ -503,12 +569,12 @@ void Model::simulate(float deltaTime, bool fullUpdate, const QVector<JointState>
model->setURL(attachment.url); model->setURL(attachment.url);
_attachments.append(model); _attachments.append(model);
} }
_resetStates = fullUpdate = true; fullUpdate = true;
createCollisionShapes(); createCollisionShapes();
} }
// exit early if we don't have to perform a full update // exit early if we don't have to perform a full update
if (!(fullUpdate || _resetStates)) { if (!fullUpdate) {
return; return;
} }
@ -541,82 +607,12 @@ void Model::simulate(float deltaTime, bool fullUpdate, const QVector<JointState>
const FBXCluster& cluster = mesh.clusters.at(j); const FBXCluster& cluster = mesh.clusters.at(j);
state.clusterMatrices[j] = _jointStates[cluster.jointIndex].transform * cluster.inverseBindMatrix; state.clusterMatrices[j] = _jointStates[cluster.jointIndex].transform * cluster.inverseBindMatrix;
} }
int vertexCount = state.worldSpaceVertices.size();
if (vertexCount == 0) {
continue;
} }
glm::vec3* destVertices = state.worldSpaceVertices.data();
glm::vec3* destVelocities = state.vertexVelocities.data();
glm::vec3* destNormals = state.worldSpaceNormals.data();
const glm::vec3* sourceVertices = mesh.vertices.constData(); // post the blender
if (!mesh.blendshapes.isEmpty()) { if (geometry.hasBlendedMeshes()) {
_blendedVertices.resize(max(_blendedVertices.size(), vertexCount)); QThreadPool::globalInstance()->start(new Blender(this, _geometry, geometry.meshes, _blendshapeCoefficients));
memcpy(_blendedVertices.data(), mesh.vertices.constData(), vertexCount * sizeof(glm::vec3));
// blend in each coefficient
for (unsigned int j = 0; j < _blendshapeCoefficients.size(); j++) {
float coefficient = _blendshapeCoefficients[j];
if (coefficient == 0.0f || j >= (unsigned int)mesh.blendshapes.size() || mesh.blendshapes[j].vertices.isEmpty()) {
continue;
} }
const glm::vec3* vertex = mesh.blendshapes[j].vertices.constData();
for (const int* index = mesh.blendshapes[j].indices.constData(),
*end = index + mesh.blendshapes[j].indices.size(); index != end; index++, vertex++) {
_blendedVertices[*index] += *vertex * coefficient;
}
}
sourceVertices = _blendedVertices.constData();
}
glm::mat4 transform = glm::translate(_translation);
if (mesh.clusters.size() > 1) {
_blendedVertices.resize(max(_blendedVertices.size(), vertexCount));
// skin each vertex
const glm::vec4* clusterIndices = mesh.clusterIndices.constData();
const glm::vec4* clusterWeights = mesh.clusterWeights.constData();
for (int j = 0; j < vertexCount; j++) {
_blendedVertices[j] =
glm::vec3(state.clusterMatrices[clusterIndices[j][0]] *
glm::vec4(sourceVertices[j], 1.0f)) * clusterWeights[j][0] +
glm::vec3(state.clusterMatrices[clusterIndices[j][1]] *
glm::vec4(sourceVertices[j], 1.0f)) * clusterWeights[j][1] +
glm::vec3(state.clusterMatrices[clusterIndices[j][2]] *
glm::vec4(sourceVertices[j], 1.0f)) * clusterWeights[j][2] +
glm::vec3(state.clusterMatrices[clusterIndices[j][3]] *
glm::vec4(sourceVertices[j], 1.0f)) * clusterWeights[j][3];
}
sourceVertices = _blendedVertices.constData();
} else {
transform = state.clusterMatrices[0];
}
if (_resetStates) {
for (int j = 0; j < vertexCount; j++) {
destVertices[j] = glm::vec3(transform * glm::vec4(sourceVertices[j], 1.0f));
destVelocities[j] = glm::vec3();
}
} else {
const float SPRINGINESS_MULTIPLIER = 200.0f;
const float DAMPING = 5.0f;
for (int j = 0; j < vertexCount; j++) {
destVelocities[j] += ((glm::vec3(transform * glm::vec4(sourceVertices[j], 1.0f)) - destVertices[j]) *
mesh.springiness * SPRINGINESS_MULTIPLIER - destVelocities[j] * DAMPING) * deltaTime;
destVertices[j] += destVelocities[j] * deltaTime;
}
}
for (int j = 0; j < vertexCount; j++) {
destNormals[j] = glm::vec3();
const glm::vec3& middle = destVertices[j];
for (QVarLengthArray<QPair<int, int>, 4>::const_iterator connection = mesh.vertexConnections.at(j).constBegin();
connection != mesh.vertexConnections.at(j).constEnd(); connection++) {
destNormals[j] += glm::normalize(glm::cross(destVertices[connection->second] - middle,
destVertices[connection->first] - middle));
}
}
}
_resetStates = false;
} }
void Model::updateJointState(int index) { void Model::updateJointState(int index) {
@ -915,6 +911,27 @@ void Model::applyCollision(CollisionInfo& collision) {
} }
} }
void Model::setBlendedVertices(const QVector<glm::vec3>& vertices, const QVector<glm::vec3>& normals) {
if (_blendedVertexBuffers.isEmpty()) {
return;
}
const FBXGeometry& geometry = _geometry->getFBXGeometry();
int index = 0;
for (int i = 0; i < geometry.meshes.size(); i++) {
const FBXMesh& mesh = geometry.meshes.at(i);
if (mesh.blendshapes.isEmpty()) {
continue;
}
QOpenGLBuffer& buffer = _blendedVertexBuffers[i];
buffer.bind();
buffer.write(0, vertices.constData() + index, mesh.vertices.size() * sizeof(glm::vec3));
buffer.write(mesh.vertices.size() * sizeof(glm::vec3), normals.constData() + index,
mesh.normals.size() * sizeof(glm::vec3));
buffer.release();
index += mesh.vertices.size();
}
}
void Model::applyNextGeometry() { void Model::applyNextGeometry() {
// delete our local geometry and custom textures // delete our local geometry and custom textures
deleteGeometry(); deleteGeometry();
@ -933,10 +950,7 @@ void Model::deleteGeometry() {
delete attachment; delete attachment;
} }
_attachments.clear(); _attachments.clear();
foreach (GLuint id, _blendedVertexBufferIDs) { _blendedVertexBuffers.clear();
glDeleteBuffers(1, &id);
}
_blendedVertexBufferIDs.clear();
_jointStates.clear(); _jointStates.clear();
_meshStates.clear(); _meshStates.clear();
clearShapes(); clearShapes();
@ -980,7 +994,6 @@ void Model::renderMeshes(float alpha, bool translucent) {
const MeshState& state = _meshStates.at(i); const MeshState& state = _meshStates.at(i);
ProgramObject* activeProgram = program; ProgramObject* activeProgram = program;
int tangentLocation = _normalMapTangentLocation; int tangentLocation = _normalMapTangentLocation;
if (state.worldSpaceVertices.isEmpty()) {
glPushMatrix(); glPushMatrix();
Application::getInstance()->loadTranslatedViewMatrix(_translation); Application::getInstance()->loadTranslatedViewMatrix(_translation);
@ -1003,11 +1016,8 @@ void Model::renderMeshes(float alpha, bool translucent) {
glMultMatrixf((const GLfloat*)&state.clusterMatrices[0]); glMultMatrixf((const GLfloat*)&state.clusterMatrices[0]);
program->bind(); program->bind();
} }
} else {
program->bind();
}
if (mesh.blendshapes.isEmpty() && mesh.springiness == 0.0f) { if (mesh.blendshapes.isEmpty()) {
if (!mesh.tangents.isEmpty()) { if (!mesh.tangents.isEmpty()) {
activeProgram->setAttributeBuffer(tangentLocation, GL_FLOAT, vertexCount * 2 * sizeof(glm::vec3), 3); activeProgram->setAttributeBuffer(tangentLocation, GL_FLOAT, vertexCount * 2 * sizeof(glm::vec3), 3);
activeProgram->enableAttributeArray(tangentLocation); activeProgram->enableAttributeArray(tangentLocation);
@ -1024,40 +1034,7 @@ void Model::renderMeshes(float alpha, bool translucent) {
} }
glColorPointer(3, GL_FLOAT, 0, (void*)(mesh.tangents.size() * sizeof(glm::vec3))); glColorPointer(3, GL_FLOAT, 0, (void*)(mesh.tangents.size() * sizeof(glm::vec3)));
glTexCoordPointer(2, GL_FLOAT, 0, (void*)((mesh.tangents.size() + mesh.colors.size()) * sizeof(glm::vec3))); glTexCoordPointer(2, GL_FLOAT, 0, (void*)((mesh.tangents.size() + mesh.colors.size()) * sizeof(glm::vec3)));
glBindBuffer(GL_ARRAY_BUFFER, _blendedVertexBufferIDs.at(i)); _blendedVertexBuffers[i].bind();
if (!state.worldSpaceVertices.isEmpty()) {
glBufferSubData(GL_ARRAY_BUFFER, 0, vertexCount * sizeof(glm::vec3), state.worldSpaceVertices.constData());
glBufferSubData(GL_ARRAY_BUFFER, vertexCount * sizeof(glm::vec3),
vertexCount * sizeof(glm::vec3), state.worldSpaceNormals.constData());
} else {
_blendedVertices.resize(max(_blendedVertices.size(), vertexCount));
_blendedNormals.resize(_blendedVertices.size());
memcpy(_blendedVertices.data(), mesh.vertices.constData(), vertexCount * sizeof(glm::vec3));
memcpy(_blendedNormals.data(), mesh.normals.constData(), vertexCount * sizeof(glm::vec3));
// blend in each coefficient
for (unsigned int j = 0; j < _blendshapeCoefficients.size(); j++) {
float coefficient = _blendshapeCoefficients[j];
if (coefficient == 0.0f || j >= (unsigned int)mesh.blendshapes.size() || mesh.blendshapes[j].vertices.isEmpty()) {
continue;
}
const float NORMAL_COEFFICIENT_SCALE = 0.01f;
float normalCoefficient = coefficient * NORMAL_COEFFICIENT_SCALE;
const glm::vec3* vertex = mesh.blendshapes[j].vertices.constData();
const glm::vec3* normal = mesh.blendshapes[j].normals.constData();
for (const int* index = mesh.blendshapes[j].indices.constData(),
*end = index + mesh.blendshapes[j].indices.size(); index != end; index++, vertex++, normal++) {
_blendedVertices[*index] += *vertex * coefficient;
_blendedNormals[*index] += *normal * normalCoefficient;
}
}
glBufferSubData(GL_ARRAY_BUFFER, 0, vertexCount * sizeof(glm::vec3), _blendedVertices.constData());
glBufferSubData(GL_ARRAY_BUFFER, vertexCount * sizeof(glm::vec3),
vertexCount * sizeof(glm::vec3), _blendedNormals.constData());
}
} }
glVertexPointer(3, GL_FLOAT, 0, 0); glVertexPointer(3, GL_FLOAT, 0, 0);
glNormalPointer(GL_FLOAT, 0, (void*)(vertexCount * sizeof(glm::vec3))); glNormalPointer(GL_FLOAT, 0, (void*)(vertexCount * sizeof(glm::vec3)));
@ -1127,13 +1104,12 @@ void Model::renderMeshes(float alpha, bool translucent) {
activeProgram->disableAttributeArray(tangentLocation); activeProgram->disableAttributeArray(tangentLocation);
} }
if (state.worldSpaceVertices.isEmpty()) {
if (state.clusterMatrices.size() > 1) { if (state.clusterMatrices.size() > 1) {
skinProgram->disableAttributeArray(skinLocations->clusterIndices); skinProgram->disableAttributeArray(skinLocations->clusterIndices);
skinProgram->disableAttributeArray(skinLocations->clusterWeights); skinProgram->disableAttributeArray(skinLocations->clusterWeights);
} }
glPopMatrix(); glPopMatrix();
}
activeProgram->release(); activeProgram->release();
} }
} }

View file

@ -43,8 +43,8 @@ public:
void setPupilDilation(float dilation) { _pupilDilation = dilation; } void setPupilDilation(float dilation) { _pupilDilation = dilation; }
float getPupilDilation() const { return _pupilDilation; } float getPupilDilation() const { return _pupilDilation; }
void setBlendshapeCoefficients(const std::vector<float>& coefficients) { _blendshapeCoefficients = coefficients; } void setBlendshapeCoefficients(const QVector<float>& coefficients) { _blendshapeCoefficients = coefficients; }
const std::vector<float>& getBlendshapeCoefficients() const { return _blendshapeCoefficients; } const QVector<float>& getBlendshapeCoefficients() const { return _blendshapeCoefficients; }
bool isActive() const { return _geometry && _geometry->isLoaded(); } bool isActive() const { return _geometry && _geometry->isLoaded(); }
@ -195,6 +195,9 @@ public:
/// Use the collision to affect the model /// Use the collision to affect the model
void applyCollision(CollisionInfo& collision); void applyCollision(CollisionInfo& collision);
/// Sets blended vertices computed in a separate thread.
void setBlendedVertices(const QVector<glm::vec3>& vertices, const QVector<glm::vec3>& normals);
protected: protected:
QSharedPointer<NetworkGeometry> _geometry; QSharedPointer<NetworkGeometry> _geometry;
@ -219,9 +222,6 @@ protected:
class MeshState { class MeshState {
public: public:
QVector<glm::mat4> clusterMatrices; QVector<glm::mat4> clusterMatrices;
QVector<glm::vec3> worldSpaceVertices;
QVector<glm::vec3> vertexVelocities;
QVector<glm::vec3> worldSpaceNormals;
}; };
QVector<MeshState> _meshStates; QVector<MeshState> _meshStates;
@ -271,16 +271,13 @@ private:
float _nextLODHysteresis; float _nextLODHysteresis;
float _pupilDilation; float _pupilDilation;
std::vector<float> _blendshapeCoefficients; QVector<float> _blendshapeCoefficients;
QUrl _url; QUrl _url;
QVector<GLuint> _blendedVertexBufferIDs; QVector<QOpenGLBuffer> _blendedVertexBuffers;
QVector<QVector<QSharedPointer<Texture> > > _dilatedTextures;
bool _resetStates;
QVector<glm::vec3> _blendedVertices; QVector<QVector<QSharedPointer<Texture> > > _dilatedTextures;
QVector<glm::vec3> _blendedNormals;
QVector<Model*> _attachments; QVector<Model*> _attachments;
@ -306,4 +303,8 @@ private:
static QVector<JointState> createJointStates(const FBXGeometry& geometry); static QVector<JointState> createJointStates(const FBXGeometry& geometry);
}; };
Q_DECLARE_METATYPE(QPointer<Model>)
Q_DECLARE_METATYPE(QWeakPointer<NetworkGeometry>)
Q_DECLARE_METATYPE(QVector<glm::vec3>)
#endif /* defined(__interface__Model__) */ #endif /* defined(__interface__Model__) */

View file

@ -27,7 +27,7 @@ class TextureCache : public ResourceCache {
public: public:
TextureCache(); TextureCache();
~TextureCache(); virtual ~TextureCache();
/// Returns the ID of the permutation/normal texture used for Perlin noise shader programs. This texture /// Returns the ID of the permutation/normal texture used for Perlin noise shader programs. This texture
/// has two lines: the first, a set of random numbers in [0, 255] to be used as permutation offsets, and /// has two lines: the first, a set of random numbers in [0, 255] to be used as permutation offsets, and

View file

@ -8,14 +8,11 @@
#include <QtCore/QDataStream> #include <QtCore/QDataStream>
#include "HandData.h"
#include "AvatarData.h"
#include <SharedUtil.h>
#include <GeometryUtil.h> #include <GeometryUtil.h>
#include <SharedUtil.h>
#include "AvatarData.h"
// When converting between fixed and float, use this as the radix. #include "HandData.h"
const int fingerVectorRadix = 4;
HandData::HandData(AvatarData* owningAvatar) : HandData::HandData(AvatarData* owningAvatar) :
_owningAvatarData(owningAvatar) _owningAvatarData(owningAvatar)

View file

@ -10,7 +10,8 @@
#define __hifi__HeadData__ #define __hifi__HeadData__
#include <iostream> #include <iostream>
#include <vector>
#include <QVector>
#include <glm/glm.hpp> #include <glm/glm.hpp>
#include <glm/gtc/quaternion.hpp> #include <glm/gtc/quaternion.hpp>
@ -54,7 +55,7 @@ public:
float getAudioAverageLoudness() const { return _audioAverageLoudness; } float getAudioAverageLoudness() const { return _audioAverageLoudness; }
void setAudioAverageLoudness(float audioAverageLoudness) { _audioAverageLoudness = audioAverageLoudness; } void setAudioAverageLoudness(float audioAverageLoudness) { _audioAverageLoudness = audioAverageLoudness; }
const std::vector<float>& getBlendshapeCoefficients() const { return _blendshapeCoefficients; } const QVector<float>& getBlendshapeCoefficients() const { return _blendshapeCoefficients; }
float getPupilDilation() const { return _pupilDilation; } float getPupilDilation() const { return _pupilDilation; }
void setPupilDilation(float pupilDilation) { _pupilDilation = pupilDilation; } void setPupilDilation(float pupilDilation) { _pupilDilation = pupilDilation; }
@ -86,7 +87,7 @@ protected:
float _averageLoudness; float _averageLoudness;
float _browAudioLift; float _browAudioLift;
float _audioAverageLoudness; float _audioAverageLoudness;
std::vector<float> _blendshapeCoefficients; QVector<float> _blendshapeCoefficients;
float _pupilDilation; float _pupilDilation;
AvatarData* _owningAvatar; AvatarData* _owningAvatar;