Merge pull request #3443 from ey6es/master

Allow blenders to stack up to a point in order to reduce latency with fewer avatars (but still cap work with lots of avatars).
This commit is contained in:
Brad Hefta-Gaub 2014-09-17 23:30:36 -07:00
commit cfc8cf0d7b
4 changed files with 91 additions and 59 deletions

View file

@ -20,6 +20,10 @@
#include "Model.h" #include "Model.h"
#include "world.h" #include "world.h"
GeometryCache::GeometryCache() :
_pendingBlenders(0) {
}
GeometryCache::~GeometryCache() { GeometryCache::~GeometryCache() {
foreach (const VerticesIndices& vbo, _hemisphereVBOs) { foreach (const VerticesIndices& vbo, _hemisphereVBOs) {
glDeleteBuffers(1, &vbo.first); glDeleteBuffers(1, &vbo.first);
@ -296,10 +300,30 @@ 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, void GeometryCache::noteRequiresBlend(Model* model) {
const QVector<glm::vec3>& vertices, const QVector<glm::vec3>& normals) { if (_pendingBlenders < QThread::idealThreadCount()) {
if (model->maybeStartBlender()) {
_pendingBlenders++;
}
return;
}
if (!_modelsRequiringBlends.contains(model)) {
_modelsRequiringBlends.append(model);
}
}
void GeometryCache::setBlendedVertices(const QPointer<Model>& model, int blendNumber,
const QWeakPointer<NetworkGeometry>& geometry, const QVector<glm::vec3>& vertices, const QVector<glm::vec3>& normals) {
if (!model.isNull()) { if (!model.isNull()) {
model->setBlendedVertices(geometry, vertices, normals); model->setBlendedVertices(blendNumber, geometry, vertices, normals);
}
_pendingBlenders--;
while (!_modelsRequiringBlends.isEmpty()) {
Model* nextModel = _modelsRequiringBlends.takeFirst();
if (nextModel && nextModel->maybeStartBlender()) {
_pendingBlenders++;
return;
}
} }
} }

View file

@ -35,6 +35,7 @@ class GeometryCache : public ResourceCache {
public: public:
GeometryCache();
virtual ~GeometryCache(); virtual ~GeometryCache();
void renderHemisphere(int slices, int stacks); void renderHemisphere(int slices, int stacks);
@ -47,9 +48,12 @@ 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);
/// Adds the specified model to the list requiring vertex blends.
void noteRequiresBlend(Model* model);
public slots: public slots:
void setBlendedVertices(const QPointer<Model>& model, const QWeakPointer<NetworkGeometry>& geometry, void setBlendedVertices(const QPointer<Model>& model, int blendNumber, const QWeakPointer<NetworkGeometry>& geometry,
const QVector<glm::vec3>& vertices, const QVector<glm::vec3>& normals); const QVector<glm::vec3>& vertices, const QVector<glm::vec3>& normals);
protected: protected:
@ -68,6 +72,9 @@ private:
QHash<IntPair, QOpenGLBuffer> _gridBuffers; QHash<IntPair, QOpenGLBuffer> _gridBuffers;
QHash<QUrl, QWeakPointer<NetworkGeometry> > _networkGeometry; QHash<QUrl, QWeakPointer<NetworkGeometry> > _networkGeometry;
QList<QPointer<Model> > _modelsRequiringBlends;
int _pendingBlenders;
}; };
/// Geometry loaded from the network. /// Geometry loaded from the network.

View file

@ -62,8 +62,8 @@ Model::Model(QObject* parent) :
_lodDistance(0.0f), _lodDistance(0.0f),
_pupilDilation(0.0f), _pupilDilation(0.0f),
_url("http://invalid.com"), _url("http://invalid.com"),
_blenderPending(false), _blendNumber(0),
_blendRequired(false) { _appliedBlendNumber(0) {
// we may have been created in the network thread, but we live in the main thread // we may have been created in the network thread, but we live in the main thread
moveToThread(Application::getInstance()->thread()); moveToThread(Application::getInstance()->thread());
@ -826,7 +826,7 @@ void Model::updateShapePositions() {
class Blender : public QRunnable { class Blender : public QRunnable {
public: public:
Blender(Model* model, const QWeakPointer<NetworkGeometry>& geometry, Blender(Model* model, int blendNumber, const QWeakPointer<NetworkGeometry>& geometry,
const QVector<FBXMesh>& meshes, const QVector<float>& blendshapeCoefficients); const QVector<FBXMesh>& meshes, const QVector<float>& blendshapeCoefficients);
virtual void run(); virtual void run();
@ -834,55 +834,55 @@ public:
private: private:
QPointer<Model> _model; QPointer<Model> _model;
int _blendNumber;
QWeakPointer<NetworkGeometry> _geometry; QWeakPointer<NetworkGeometry> _geometry;
QVector<FBXMesh> _meshes; QVector<FBXMesh> _meshes;
QVector<float> _blendshapeCoefficients; QVector<float> _blendshapeCoefficients;
}; };
Blender::Blender(Model* model, const QWeakPointer<NetworkGeometry>& geometry, Blender::Blender(Model* model, int blendNumber, const QWeakPointer<NetworkGeometry>& geometry,
const QVector<FBXMesh>& meshes, const QVector<float>& blendshapeCoefficients) : const QVector<FBXMesh>& meshes, const QVector<float>& blendshapeCoefficients) :
_model(model), _model(model),
_blendNumber(blendNumber),
_geometry(geometry), _geometry(geometry),
_meshes(meshes), _meshes(meshes),
_blendshapeCoefficients(blendshapeCoefficients) { _blendshapeCoefficients(blendshapeCoefficients) {
} }
void Blender::run() { void Blender::run() {
// make sure the model still exists
if (_model.isNull()) {
return;
}
QVector<glm::vec3> vertices, normals; QVector<glm::vec3> vertices, normals;
int offset = 0; if (!_model.isNull()) {
foreach (const FBXMesh& mesh, _meshes) { int offset = 0;
if (mesh.blendshapes.isEmpty()) { foreach (const FBXMesh& mesh, _meshes) {
continue; if (mesh.blendshapes.isEmpty()) {
}
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; continue;
} }
float normalCoefficient = vertexCoefficient * NORMAL_COEFFICIENT_SCALE; vertices += mesh.vertices;
const FBXBlendshape& blendshape = mesh.blendshapes.at(i); normals += mesh.normals;
for (int j = 0; j < blendshape.indices.size(); j++) { glm::vec3* meshVertices = vertices.data() + offset;
int index = blendshape.indices.at(j); glm::vec3* meshNormals = normals.data() + offset;
meshVertices[index] += blendshape.vertices.at(j) * vertexCoefficient; offset += mesh.vertices.size();
meshNormals[index] += blendshape.normals.at(j) * normalCoefficient; 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 // post the result to the geometry cache, which will dispatch to the model if still alive
QMetaObject::invokeMethod(Application::getInstance()->getGeometryCache(), "setBlendedVertices", QMetaObject::invokeMethod(Application::getInstance()->getGeometryCache(), "setBlendedVertices",
Q_ARG(const QPointer<Model>&, _model), Q_ARG(const QWeakPointer<NetworkGeometry>&, _geometry), Q_ARG(const QPointer<Model>&, _model), Q_ARG(int, _blendNumber),
Q_ARG(const QVector<glm::vec3>&, vertices), Q_ARG(const QVector<glm::vec3>&, normals)); Q_ARG(const QWeakPointer<NetworkGeometry>&, _geometry), Q_ARG(const QVector<glm::vec3>&, vertices),
Q_ARG(const QVector<glm::vec3>&, normals));
} }
void Model::setScaleToFit(bool scaleToFit, const glm::vec3& dimensions) { void Model::setScaleToFit(bool scaleToFit, const glm::vec3& dimensions) {
@ -1020,14 +1020,9 @@ void Model::simulateInternal(float deltaTime) {
} }
// post the blender if we're not currently waiting for one to finish // post the blender if we're not currently waiting for one to finish
if (geometry.hasBlendedMeshes()) { if (geometry.hasBlendedMeshes() && _blendshapeCoefficients != _blendedBlendshapeCoefficients) {
if (_blenderPending) { _blendedBlendshapeCoefficients = _blendshapeCoefficients;
_blendRequired = true; Application::getInstance()->getGeometryCache()->noteRequiresBlend(this);
} else {
_blendRequired = false;
_blenderPending = true;
QThreadPool::globalInstance()->start(new Blender(this, _geometry, geometry.meshes, _blendshapeCoefficients));
}
} }
} }
@ -1290,22 +1285,23 @@ void Model::renderJointCollisionShapes(float alpha) {
// implement this when we have shapes for regular models // implement this when we have shapes for regular models
} }
void Model::setBlendedVertices(const QWeakPointer<NetworkGeometry>& geometry, const QVector<glm::vec3>& vertices, bool Model::maybeStartBlender() {
const QVector<glm::vec3>& normals) {
_blenderPending = false;
// start the next blender if required
const FBXGeometry& fbxGeometry = _geometry->getFBXGeometry(); const FBXGeometry& fbxGeometry = _geometry->getFBXGeometry();
if (_blendRequired) { if (fbxGeometry.hasBlendedMeshes()) {
_blendRequired = false; QThreadPool::globalInstance()->start(new Blender(this, ++_blendNumber, _geometry,
if (fbxGeometry.hasBlendedMeshes()) { fbxGeometry.meshes, _blendshapeCoefficients));
_blenderPending = true; return true;
QThreadPool::globalInstance()->start(new Blender(this, _geometry, fbxGeometry.meshes, _blendshapeCoefficients));
}
} }
if (_geometry != geometry || _blendedVertexBuffers.isEmpty()) { return false;
}
void Model::setBlendedVertices(int blendNumber, const QWeakPointer<NetworkGeometry>& geometry,
const QVector<glm::vec3>& vertices, const QVector<glm::vec3>& normals) {
if (_geometry != geometry || _blendedVertexBuffers.isEmpty() || blendNumber < _appliedBlendNumber) {
return; return;
} }
_appliedBlendNumber = blendNumber;
const FBXGeometry& fbxGeometry = _geometry->getFBXGeometry();
int index = 0; int index = 0;
for (int i = 0; i < fbxGeometry.meshes.size(); i++) { for (int i = 0; i < fbxGeometry.meshes.size(); i++) {
const FBXMesh& mesh = fbxGeometry.meshes.at(i); const FBXMesh& mesh = fbxGeometry.meshes.at(i);
@ -1358,6 +1354,8 @@ void Model::deleteGeometry() {
if (_geometry) { if (_geometry) {
_geometry->clearLoadPriority(this); _geometry->clearLoadPriority(this);
} }
_blendedBlendshapeCoefficients.clear();
} }
void Model::renderMeshes(RenderMode mode, bool translucent, bool receiveShadows) { void Model::renderMeshes(RenderMode mode, bool translucent, bool receiveShadows) {

View file

@ -165,9 +165,11 @@ public:
virtual void renderJointCollisionShapes(float alpha); virtual void renderJointCollisionShapes(float alpha);
bool maybeStartBlender();
/// Sets blended vertices computed in a separate thread. /// Sets blended vertices computed in a separate thread.
void setBlendedVertices(const QWeakPointer<NetworkGeometry>& geometry, const QVector<glm::vec3>& vertices, void setBlendedVertices(int blendNumber, const QWeakPointer<NetworkGeometry>& geometry,
const QVector<glm::vec3>& normals); const QVector<glm::vec3>& vertices, const QVector<glm::vec3>& normals);
class LocalLight { class LocalLight {
public: public:
@ -285,8 +287,9 @@ private:
glm::vec4 _localLightColors[MAX_LOCAL_LIGHTS]; glm::vec4 _localLightColors[MAX_LOCAL_LIGHTS];
glm::vec4 _localLightDirections[MAX_LOCAL_LIGHTS]; glm::vec4 _localLightDirections[MAX_LOCAL_LIGHTS];
bool _blenderPending; QVector<float> _blendedBlendshapeCoefficients;
bool _blendRequired; int _blendNumber;
int _appliedBlendNumber;
static ProgramObject _program; static ProgramObject _program;
static ProgramObject _normalMapProgram; static ProgramObject _normalMapProgram;