Dilate our network-downloaded textures.

This commit is contained in:
Andrzej Kapolka 2013-10-02 11:59:14 -07:00
parent d87fe204e0
commit c98dec8a65
9 changed files with 94 additions and 47 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 862 KiB

View file

@ -27,7 +27,6 @@ BlendFace::~BlendFace() {
} }
ProgramObject BlendFace::_eyeProgram; ProgramObject BlendFace::_eyeProgram;
DilatedTextureCache BlendFace::_eyeTextureCache("resources/images/eye.png", 50, 210);
void BlendFace::init() { void BlendFace::init() {
if (!_eyeProgram.isLinked()) { if (!_eyeProgram.isLinked()) {
@ -65,6 +64,9 @@ bool BlendFace::render(float alpha) {
} }
_blendedVertexBufferIDs.append(id); _blendedVertexBufferIDs.append(id);
} }
// make sure we have the right number of dilated texture pointers
_dilatedTextures.resize(geometry.meshes.size());
} }
glPushMatrix(); glPushMatrix();
@ -99,6 +101,7 @@ bool BlendFace::render(float alpha) {
glPushMatrix(); glPushMatrix();
// apply eye rotation if appropriate // apply eye rotation if appropriate
Texture* texture = networkMesh.diffuseTexture.data();
if (mesh.isEye) { if (mesh.isEye) {
glTranslatef(mesh.pivot.x, mesh.pivot.y, mesh.pivot.z); glTranslatef(mesh.pivot.x, mesh.pivot.y, mesh.pivot.z);
glm::quat rotation = glm::inverse(orientation) * _owningHead->getEyeRotation(orientation * glm::quat rotation = glm::inverse(orientation) * _owningHead->getEyeRotation(orientation *
@ -108,6 +111,11 @@ bool BlendFace::render(float alpha) {
glTranslatef(-mesh.pivot.x, -mesh.pivot.y, -mesh.pivot.z); glTranslatef(-mesh.pivot.x, -mesh.pivot.y, -mesh.pivot.z);
_eyeProgram.bind(); _eyeProgram.bind();
if (texture != NULL) {
texture = (_dilatedTextures[i] = static_cast<DilatableNetworkTexture*>(texture)->getDilatedTexture(
_owningHead->getPupilDilation())).data();
}
} }
glMultMatrixf((const GLfloat*)&mesh.transform); glMultMatrixf((const GLfloat*)&mesh.transform);
@ -117,7 +125,7 @@ bool BlendFace::render(float alpha) {
glColor4f(1.0f, 1.0f, 1.0f, alpha); glColor4f(1.0f, 1.0f, 1.0f, alpha);
} }
glBindTexture(GL_TEXTURE_2D, networkMesh.diffuseTexture ? networkMesh.diffuseTexture->getID() : 0); glBindTexture(GL_TEXTURE_2D, texture == NULL ? 0 : texture->getID());
glBindBuffer(GL_ARRAY_BUFFER, networkMesh.vertexBufferID); glBindBuffer(GL_ARRAY_BUFFER, networkMesh.vertexBufferID);
if (mesh.blendshapes.isEmpty()) { if (mesh.blendshapes.isEmpty()) {
@ -218,8 +226,9 @@ void BlendFace::setModelURL(const QUrl& url) {
} }
_modelURL = url; _modelURL = url;
// delete our local geometry // delete our local geometry and custom textures
deleteGeometry(); deleteGeometry();
_dilatedTextures.clear();
_geometry = Application::getInstance()->getGeometryCache()->getGeometry(url); _geometry = Application::getInstance()->getGeometryCache()->getGeometry(url);
} }

View file

@ -51,14 +51,12 @@ private:
QSharedPointer<NetworkGeometry> _geometry; QSharedPointer<NetworkGeometry> _geometry;
QVector<GLuint> _blendedVertexBufferIDs; QVector<GLuint> _blendedVertexBufferIDs;
QVector<QSharedPointer<Texture> > _dilatedTextures;
QVector<glm::vec3> _blendedVertices; QVector<glm::vec3> _blendedVertices;
QVector<glm::vec3> _blendedNormals; QVector<glm::vec3> _blendedNormals;
QSharedPointer<Texture> _eyeTexture;
static ProgramObject _eyeProgram; static ProgramObject _eyeProgram;
static DilatedTextureCache _eyeTextureCache;
}; };
#endif /* defined(__interface__BlendFace__) */ #endif /* defined(__interface__BlendFace__) */

View file

@ -46,7 +46,7 @@ const float IRIS_PROTRUSION = 0.0145f;
const char IRIS_TEXTURE_FILENAME[] = "resources/images/iris.png"; const char IRIS_TEXTURE_FILENAME[] = "resources/images/iris.png";
ProgramObject Head::_irisProgram; ProgramObject Head::_irisProgram;
DilatedTextureCache Head::_irisTextureCache(IRIS_TEXTURE_FILENAME, 53, 127); QSharedPointer<DilatableNetworkTexture> Head::_irisTexture;
int Head::_eyePositionLocation; int Head::_eyePositionLocation;
Head::Head(Avatar* owningAvatar) : Head::Head(Avatar* owningAvatar) :
@ -103,6 +103,9 @@ void Head::init() {
_irisProgram.setUniformValue("texture", 0); _irisProgram.setUniformValue("texture", 0);
_eyePositionLocation = _irisProgram.uniformLocation("eyePosition"); _eyePositionLocation = _irisProgram.uniformLocation("eyePosition");
_irisTexture = Application::getInstance()->getTextureCache()->getTexture(QUrl::fromLocalFile(IRIS_TEXTURE_FILENAME),
true).staticCast<DilatableNetworkTexture>();
} }
_blendFace.init(); _blendFace.init();
} }
@ -624,8 +627,8 @@ void Head::renderEyeBalls() {
_irisProgram.bind(); _irisProgram.bind();
_irisTexture = _irisTextureCache.getTexture(_pupilDilation); _dilatedIrisTexture = _irisTexture->getDilatedTexture(_pupilDilation);
glBindTexture(GL_TEXTURE_2D, _irisTexture->getID()); glBindTexture(GL_TEXTURE_2D, _dilatedIrisTexture->getID());
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);

View file

@ -139,10 +139,10 @@ private:
PerlinFace _perlinFace; PerlinFace _perlinFace;
BlendFace _blendFace; BlendFace _blendFace;
QSharedPointer<Texture> _irisTexture; QSharedPointer<Texture> _dilatedIrisTexture;
static ProgramObject _irisProgram; static ProgramObject _irisProgram;
static DilatedTextureCache _irisTextureCache; static QSharedPointer<DilatableNetworkTexture> _irisTexture;
static int _eyePositionLocation; static int _eyePositionLocation;
// private methods // private methods

View file

@ -248,8 +248,8 @@ void PerlinFace::render() {
Head::_irisProgram.bind(); Head::_irisProgram.bind();
_owningHead->_irisTexture = Head::_irisTextureCache.getTexture(_owningHead->_pupilDilation); _owningHead->_dilatedIrisTexture = Head::_irisTexture->getDilatedTexture(_owningHead->_pupilDilation);
glBindTexture(GL_TEXTURE_2D, _owningHead->_irisTexture->getID()); glBindTexture(GL_TEXTURE_2D, _owningHead->_dilatedIrisTexture->getID());
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);

View file

@ -329,7 +329,7 @@ void NetworkGeometry::handleDownloadProgress(qint64 bytesReceived, qint64 bytesT
} }
if (!mesh.diffuseFilename.isEmpty()) { if (!mesh.diffuseFilename.isEmpty()) {
url.setPath(basePath + mesh.diffuseFilename); url.setPath(basePath + mesh.diffuseFilename);
networkMesh.diffuseTexture = Application::getInstance()->getTextureCache()->getTexture(url); networkMesh.diffuseTexture = Application::getInstance()->getTextureCache()->getTexture(url, mesh.isEye);
} }
if (!mesh.normalFilename.isEmpty()) { if (!mesh.normalFilename.isEmpty()) {
url.setPath(basePath + mesh.normalFilename); url.setPath(basePath + mesh.normalFilename);

View file

@ -84,10 +84,10 @@ GLuint TextureCache::getFileTextureID(const QString& filename) {
return id; return id;
} }
QSharedPointer<NetworkTexture> TextureCache::getTexture(const QUrl& url) { QSharedPointer<NetworkTexture> TextureCache::getTexture(const QUrl& url, bool dilatable) {
QSharedPointer<NetworkTexture> texture = _networkTextures.value(url); QSharedPointer<NetworkTexture> texture = _networkTextures.value(url);
if (texture.isNull()) { if (texture.isNull()) {
texture = QSharedPointer<NetworkTexture>(new NetworkTexture(url)); texture = QSharedPointer<NetworkTexture>(dilatable ? new DilatableNetworkTexture(url) : new NetworkTexture(url));
_networkTextures.insert(url, texture); _networkTextures.insert(url, texture);
} }
return texture; return texture;
@ -191,6 +191,10 @@ NetworkTexture::~NetworkTexture() {
} }
} }
void NetworkTexture::imageLoaded(const QImage& image) {
// nothing by default
}
void NetworkTexture::handleDownloadProgress(qint64 bytesReceived, qint64 bytesTotal) { void NetworkTexture::handleDownloadProgress(qint64 bytesReceived, qint64 bytesTotal) {
if (bytesReceived < bytesTotal && !_reply->isFinished()) { if (bytesReceived < bytesTotal && !_reply->isFinished()) {
return; return;
@ -202,6 +206,7 @@ void NetworkTexture::handleDownloadProgress(qint64 bytesReceived, qint64 bytesTo
_reply = NULL; _reply = NULL;
QImage image = QImage::fromData(entirety).convertToFormat(QImage::Format_ARGB32); QImage image = QImage::fromData(entirety).convertToFormat(QImage::Format_ARGB32);
imageLoaded(image);
glBindTexture(GL_TEXTURE_2D, getID()); glBindTexture(GL_TEXTURE_2D, getID());
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, image.width(), image.height(), 1, glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, image.width(), image.height(), 1,
GL_BGRA, GL_UNSIGNED_BYTE, image.constBits()); GL_BGRA, GL_UNSIGNED_BYTE, image.constBits());
@ -217,35 +222,57 @@ void NetworkTexture::handleReplyError() {
_reply = NULL; _reply = NULL;
} }
DilatedTextureCache::DilatedTextureCache(const QString& filename, int innerRadius, int outerRadius) : DilatableNetworkTexture::DilatableNetworkTexture(const QUrl& url) :
_innerRadius(innerRadius), NetworkTexture(url),
_outerRadius(outerRadius) _innerRadius(0),
_outerRadius(0)
{ {
switchToResourcesParentIfRequired();
_image = QImage(filename).convertToFormat(QImage::Format_ARGB32);
} }
QSharedPointer<Texture> DilatedTextureCache::getTexture(float dilation) { void DilatableNetworkTexture::imageLoaded(const QImage& image) {
QSharedPointer<Texture> texture = _textures.value(dilation); _image = image;
// scan out from the center to find inner and outer radii
int halfWidth = image.width() / 2;
int halfHeight = image.height() / 2;
const int BLACK_THRESHOLD = 32;
while (_innerRadius < halfWidth && qGray(image.pixel(halfWidth + _innerRadius, halfHeight)) < BLACK_THRESHOLD) {
_innerRadius++;
}
_outerRadius = _innerRadius;
const int TRANSPARENT_THRESHOLD = 32;
while (_outerRadius < halfWidth && qAlpha(image.pixel(halfWidth + _outerRadius, halfHeight)) > TRANSPARENT_THRESHOLD) {
_outerRadius++;
}
// clear out any textures we generated before loading
_dilatedTextures.clear();
}
QSharedPointer<Texture> DilatableNetworkTexture::getDilatedTexture(float dilation) {
QSharedPointer<Texture> texture = _dilatedTextures.value(dilation);
if (texture.isNull()) { if (texture.isNull()) {
texture = QSharedPointer<Texture>(new Texture()); texture = QSharedPointer<Texture>(new Texture());
QImage dilatedImage = _image; if (!_image.isNull()) {
QPainter painter; QImage dilatedImage = _image;
painter.begin(&dilatedImage); QPainter painter;
QPainterPath path; painter.begin(&dilatedImage);
qreal radius = glm::mix(_innerRadius, _outerRadius, dilation); QPainterPath path;
path.addEllipse(QPointF(_image.width() / 2.0, _image.height() / 2.0), radius, radius); qreal radius = glm::mix(_innerRadius, _outerRadius, dilation);
painter.fillPath(path, Qt::black); path.addEllipse(QPointF(_image.width() / 2.0, _image.height() / 2.0), radius, radius);
painter.end(); painter.fillPath(path, Qt::black);
painter.end();
glBindTexture(GL_TEXTURE_2D, texture->getID());
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, dilatedImage.width(), dilatedImage.height(), 1,
GL_BGRA, GL_UNSIGNED_BYTE, dilatedImage.constBits());
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glBindTexture(GL_TEXTURE_2D, 0);
}
glBindTexture(GL_TEXTURE_2D, texture->getID()); _dilatedTextures.insert(dilation, texture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, dilatedImage.width(), dilatedImage.height(), 1,
GL_BGRA, GL_UNSIGNED_BYTE, dilatedImage.constBits());
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glBindTexture(GL_TEXTURE_2D, 0);
_textures.insert(dilation, texture);
} }
return texture; return texture;
} }

View file

@ -41,7 +41,7 @@ public:
GLuint getFileTextureID(const QString& filename); GLuint getFileTextureID(const QString& filename);
/// Loads a texture from the specified URL. /// Loads a texture from the specified URL.
QSharedPointer<NetworkTexture> getTexture(const QUrl& url); QSharedPointer<NetworkTexture> getTexture(const QUrl& url, bool dilatable = false);
/// Returns a pointer to the primary framebuffer object. This render target includes a depth component, and is /// Returns a pointer to the primary framebuffer object. This render target includes a depth component, and is
/// used for scene rendering. /// used for scene rendering.
@ -99,6 +99,10 @@ public:
NetworkTexture(const QUrl& url); NetworkTexture(const QUrl& url);
~NetworkTexture(); ~NetworkTexture();
protected:
virtual void imageLoaded(const QImage& image);
private slots: private slots:
void handleDownloadProgress(qint64 bytesReceived, qint64 bytesTotal); void handleDownloadProgress(qint64 bytesReceived, qint64 bytesTotal);
@ -109,22 +113,28 @@ private:
QNetworkReply* _reply; QNetworkReply* _reply;
}; };
/// Caches textures according to pupillary dilation. /// Caches derived, dilated textures.
class DilatedTextureCache { class DilatableNetworkTexture : public NetworkTexture {
Q_OBJECT
public: public:
DilatedTextureCache(const QString& filename, int innerRadius, int outerRadius); DilatableNetworkTexture(const QUrl& url);
/// Returns a pointer to a texture with the requested amount of dilation. /// Returns a pointer to a texture with the requested amount of dilation.
QSharedPointer<Texture> getTexture(float dilation); QSharedPointer<Texture> getDilatedTexture(float dilation);
protected:
virtual void imageLoaded(const QImage& image);
private: private:
QImage _image; QImage _image;
int _innerRadius; int _innerRadius;
int _outerRadius; int _outerRadius;
QMap<float, QWeakPointer<Texture> > _textures; QMap<float, QWeakPointer<Texture> > _dilatedTextures;
}; };
#endif /* defined(__interface__TextureCache__) */ #endif /* defined(__interface__TextureCache__) */