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
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
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() {
glm::vec3 torsoPosition(getPosition());
glm::vec3 headPosition(getHead()->getEyePosition());

View file

@ -103,7 +103,7 @@ void Faceshift::reset() {
}
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));
qFill(coefficients.begin(), coefficients.end(), 0.0f);
coefficients[_leftBlinkIndex] = leftBlink;
@ -204,7 +204,7 @@ void Faceshift::receive(const QByteArray& buffer) {
_eyeGazeLeftYaw = data.m_eyeGazeLeftYaw;
_eyeGazeRightPitch = -data.m_eyeGazeRightPitch;
_eyeGazeRightYaw = data.m_eyeGazeRightYaw;
_blendshapeCoefficients = data.m_coeffs;
_blendshapeCoefficients = QVector<float>::fromStdVector(data.m_coeffs);
_lastTrackingStateReceived = usecTimestampNow();
}

View file

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

View file

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

View file

@ -54,6 +54,15 @@ QStringList FBXGeometry::getJointNames() const {
return names;
}
bool FBXGeometry::hasBlendedMeshes() const {
foreach (const FBXMesh& mesh, meshes) {
if (!mesh.blendshapes.isEmpty()) {
return true;
}
}
return false;
}
static int fbxGeometryMetaTypeId = qRegisterMetaType<FBXGeometry>();
template<class T> QVariant readBinaryArray(QDataStream& in) {
@ -1331,14 +1340,11 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping)
geometry.staticExtents.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++) {
ExtractedMesh& extracted = it.value();
// accumulate local transforms
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);
// compute the mesh extents from the transformed vertices
@ -1591,49 +1597,7 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping)
}
}
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);
}
@ -1797,7 +1761,6 @@ FBXGeometry readSVO(const QByteArray& model) {
// and one mesh with one cluster and one part
FBXMesh mesh;
mesh.isEye = false;
mesh.springiness = 0.0f;
FBXCluster cluster = { 0 };
mesh.clusters.append(cluster);

View file

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

View file

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

View file

@ -19,15 +19,18 @@
#include "FBXReader.h"
class Model;
class NetworkGeometry;
class NetworkMesh;
class NetworkTexture;
/// Stores cached geometry.
class GeometryCache : public ResourceCache {
Q_OBJECT
public:
~GeometryCache();
virtual ~GeometryCache();
void renderHemisphere(int slices, int stacks);
void renderSquare(int xDivisions, int yDivisions);
@ -38,7 +41,12 @@ public:
/// \param fallback a fallback URL to load if the desired one is unavailable
/// \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);
public slots:
void setBlendedVertices(const QPointer<Model>& model, const QWeakPointer<NetworkGeometry>& geometry,
const QVector<glm::vec3>& vertices, const QVector<glm::vec3>& normals);
protected:
virtual QSharedPointer<Resource> createResource(const QUrl& url,

View file

@ -6,6 +6,10 @@
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
//
#include <QMetaType>
#include <QRunnable>
#include <QThreadPool>
#include <glm/gtx/transform.hpp>
#include <GeometryUtil.h>
@ -19,6 +23,10 @@
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) :
QObject(parent),
_scale(1.0f, 1.0f, 1.0f),
@ -104,8 +112,6 @@ void Model::init() {
}
void Model::reset() {
_resetStates = true;
foreach (Model* attachment, _attachments) {
attachment->reset();
}
@ -170,20 +176,10 @@ bool Model::render(float alpha) {
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();
if (_blendedVertexBufferIDs.isEmpty()) {
if (_dilatedTextures.isEmpty()) {
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;
dilated.resize(mesh.parts.size());
_dilatedTextures.append(dilated);
@ -478,6 +474,68 @@ QVector<Model::JointState> Model::updateGeometry() {
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) {
if (!isActive()) {
return;
@ -490,12 +548,20 @@ void Model::simulate(float deltaTime, bool fullUpdate, const QVector<JointState>
foreach (const FBXMesh& mesh, geometry.meshes) {
MeshState state;
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);
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) {
Model* model = new Model(this);
@ -503,12 +569,12 @@ void Model::simulate(float deltaTime, bool fullUpdate, const QVector<JointState>
model->setURL(attachment.url);
_attachments.append(model);
}
_resetStates = fullUpdate = true;
fullUpdate = true;
createCollisionShapes();
}
// exit early if we don't have to perform a full update
if (!(fullUpdate || _resetStates)) {
if (!fullUpdate) {
return;
}
@ -541,82 +607,12 @@ void Model::simulate(float deltaTime, bool fullUpdate, const QVector<JointState>
const FBXCluster& cluster = mesh.clusters.at(j);
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();
if (!mesh.blendshapes.isEmpty()) {
_blendedVertices.resize(max(_blendedVertices.size(), vertexCount));
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;
// post the blender
if (geometry.hasBlendedMeshes()) {
QThreadPool::globalInstance()->start(new Blender(this, _geometry, geometry.meshes, _blendshapeCoefficients));
}
}
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() {
// delete our local geometry and custom textures
deleteGeometry();
@ -933,10 +950,7 @@ void Model::deleteGeometry() {
delete attachment;
}
_attachments.clear();
foreach (GLuint id, _blendedVertexBufferIDs) {
glDeleteBuffers(1, &id);
}
_blendedVertexBufferIDs.clear();
_blendedVertexBuffers.clear();
_jointStates.clear();
_meshStates.clear();
clearShapes();
@ -980,34 +994,30 @@ void Model::renderMeshes(float alpha, bool translucent) {
const MeshState& state = _meshStates.at(i);
ProgramObject* activeProgram = program;
int tangentLocation = _normalMapTangentLocation;
if (state.worldSpaceVertices.isEmpty()) {
glPushMatrix();
Application::getInstance()->loadTranslatedViewMatrix(_translation);
if (state.clusterMatrices.size() > 1) {
skinProgram->bind();
glUniformMatrix4fvARB(skinLocations->clusterMatrices, state.clusterMatrices.size(), false,
(const float*)state.clusterMatrices.constData());
int offset = (mesh.tangents.size() + mesh.colors.size()) * sizeof(glm::vec3) +
mesh.texCoords.size() * sizeof(glm::vec2) +
(mesh.blendshapes.isEmpty() ? vertexCount * 2 * sizeof(glm::vec3) : 0);
skinProgram->setAttributeBuffer(skinLocations->clusterIndices, GL_FLOAT, offset, 4);
skinProgram->setAttributeBuffer(skinLocations->clusterWeights, GL_FLOAT,
offset + vertexCount * sizeof(glm::vec4), 4);
skinProgram->enableAttributeArray(skinLocations->clusterIndices);
skinProgram->enableAttributeArray(skinLocations->clusterWeights);
activeProgram = skinProgram;
tangentLocation = skinLocations->tangent;
} else {
glMultMatrixf((const GLfloat*)&state.clusterMatrices[0]);
program->bind();
}
} else {
glPushMatrix();
Application::getInstance()->loadTranslatedViewMatrix(_translation);
if (state.clusterMatrices.size() > 1) {
skinProgram->bind();
glUniformMatrix4fvARB(skinLocations->clusterMatrices, state.clusterMatrices.size(), false,
(const float*)state.clusterMatrices.constData());
int offset = (mesh.tangents.size() + mesh.colors.size()) * sizeof(glm::vec3) +
mesh.texCoords.size() * sizeof(glm::vec2) +
(mesh.blendshapes.isEmpty() ? vertexCount * 2 * sizeof(glm::vec3) : 0);
skinProgram->setAttributeBuffer(skinLocations->clusterIndices, GL_FLOAT, offset, 4);
skinProgram->setAttributeBuffer(skinLocations->clusterWeights, GL_FLOAT,
offset + vertexCount * sizeof(glm::vec4), 4);
skinProgram->enableAttributeArray(skinLocations->clusterIndices);
skinProgram->enableAttributeArray(skinLocations->clusterWeights);
activeProgram = skinProgram;
tangentLocation = skinLocations->tangent;
} else {
glMultMatrixf((const GLfloat*)&state.clusterMatrices[0]);
program->bind();
}
if (mesh.blendshapes.isEmpty() && mesh.springiness == 0.0f) {
if (mesh.blendshapes.isEmpty()) {
if (!mesh.tangents.isEmpty()) {
activeProgram->setAttributeBuffer(tangentLocation, GL_FLOAT, vertexCount * 2 * sizeof(glm::vec3), 3);
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)));
glTexCoordPointer(2, GL_FLOAT, 0, (void*)((mesh.tangents.size() + mesh.colors.size()) * sizeof(glm::vec3)));
glBindBuffer(GL_ARRAY_BUFFER, _blendedVertexBufferIDs.at(i));
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());
}
_blendedVertexBuffers[i].bind();
}
glVertexPointer(3, GL_FLOAT, 0, 0);
glNormalPointer(GL_FLOAT, 0, (void*)(vertexCount * sizeof(glm::vec3)));
@ -1126,14 +1103,13 @@ void Model::renderMeshes(float alpha, bool translucent) {
activeProgram->disableAttributeArray(tangentLocation);
}
if (state.worldSpaceVertices.isEmpty()) {
if (state.clusterMatrices.size() > 1) {
skinProgram->disableAttributeArray(skinLocations->clusterIndices);
skinProgram->disableAttributeArray(skinLocations->clusterWeights);
}
glPopMatrix();
}
if (state.clusterMatrices.size() > 1) {
skinProgram->disableAttributeArray(skinLocations->clusterIndices);
skinProgram->disableAttributeArray(skinLocations->clusterWeights);
}
glPopMatrix();
activeProgram->release();
}
}

View file

@ -43,8 +43,8 @@ public:
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; }
void setBlendshapeCoefficients(const QVector<float>& coefficients) { _blendshapeCoefficients = coefficients; }
const QVector<float>& getBlendshapeCoefficients() const { return _blendshapeCoefficients; }
bool isActive() const { return _geometry && _geometry->isLoaded(); }
@ -195,6 +195,9 @@ public:
/// Use the collision to affect the model
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:
QSharedPointer<NetworkGeometry> _geometry;
@ -219,9 +222,6 @@ protected:
class MeshState {
public:
QVector<glm::mat4> clusterMatrices;
QVector<glm::vec3> worldSpaceVertices;
QVector<glm::vec3> vertexVelocities;
QVector<glm::vec3> worldSpaceNormals;
};
QVector<MeshState> _meshStates;
@ -271,16 +271,13 @@ private:
float _nextLODHysteresis;
float _pupilDilation;
std::vector<float> _blendshapeCoefficients;
QVector<float> _blendshapeCoefficients;
QUrl _url;
QVector<GLuint> _blendedVertexBufferIDs;
QVector<QVector<QSharedPointer<Texture> > > _dilatedTextures;
bool _resetStates;
QVector<QOpenGLBuffer> _blendedVertexBuffers;
QVector<glm::vec3> _blendedVertices;
QVector<glm::vec3> _blendedNormals;
QVector<QVector<QSharedPointer<Texture> > > _dilatedTextures;
QVector<Model*> _attachments;
@ -306,4 +303,8 @@ private:
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__) */

View file

@ -27,7 +27,7 @@ class TextureCache : public ResourceCache {
public:
TextureCache();
~TextureCache();
virtual ~TextureCache();
/// 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

View file

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

View file

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