Working on avatar billboards.

This commit is contained in:
Andrzej Kapolka 2014-02-20 19:22:59 -08:00
parent 7010c45b16
commit 6b90a3994d
8 changed files with 137 additions and 71 deletions

View file

@ -527,73 +527,8 @@ void Application::paintGL() {
_glowEffect.render();
if (Menu::getInstance()->isOptionChecked(MenuOption::Mirror)) {
bool eyeRelativeCamera = false;
if (_rearMirrorTools->getZoomLevel() == BODY) {
_mirrorCamera.setDistance(MIRROR_REARVIEW_BODY_DISTANCE * _myAvatar->getScale());
_mirrorCamera.setTargetPosition(_myAvatar->getChestPosition());
} else { // HEAD zoom level
_mirrorCamera.setDistance(MIRROR_REARVIEW_DISTANCE * _myAvatar->getScale());
if (_myAvatar->getSkeletonModel().isActive() && _myAvatar->getHead()->getFaceModel().isActive()) {
// as a hack until we have a better way of dealing with coordinate precision issues, reposition the
// face/body so that the average eye position lies at the origin
eyeRelativeCamera = true;
_mirrorCamera.setTargetPosition(glm::vec3());
} else {
_mirrorCamera.setTargetPosition(_myAvatar->getHead()->calculateAverageEyePosition());
}
}
_mirrorCamera.setTargetRotation(_myAvatar->getWorldAlignedOrientation() * glm::quat(glm::vec3(0.0f, PIf, 0.0f)));
_mirrorCamera.update(1.0f/_fps);
// set the bounds of rear mirror view
glViewport(_mirrorViewRect.x(), _glWidget->height() - _mirrorViewRect.y() - _mirrorViewRect.height(),
_mirrorViewRect.width(), _mirrorViewRect.height());
glScissor(_mirrorViewRect.x(), _glWidget->height() - _mirrorViewRect.y() - _mirrorViewRect.height(),
_mirrorViewRect.width(), _mirrorViewRect.height());
bool updateViewFrustum = false;
updateProjectionMatrix(_mirrorCamera, updateViewFrustum);
glEnable(GL_SCISSOR_TEST);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// render rear mirror view
glPushMatrix();
if (eyeRelativeCamera) {
// save absolute translations
glm::vec3 absoluteSkeletonTranslation = _myAvatar->getSkeletonModel().getTranslation();
glm::vec3 absoluteFaceTranslation = _myAvatar->getHead()->getFaceModel().getTranslation();
// get the eye positions relative to the neck and use them to set the face translation
glm::vec3 leftEyePosition, rightEyePosition;
_myAvatar->getHead()->getFaceModel().setTranslation(glm::vec3());
_myAvatar->getHead()->getFaceModel().getEyePositions(leftEyePosition, rightEyePosition);
_myAvatar->getHead()->getFaceModel().setTranslation((leftEyePosition + rightEyePosition) * -0.5f);
// get the neck position relative to the body and use it to set the skeleton translation
glm::vec3 neckPosition;
_myAvatar->getSkeletonModel().setTranslation(glm::vec3());
_myAvatar->getSkeletonModel().getNeckPosition(neckPosition);
_myAvatar->getSkeletonModel().setTranslation(_myAvatar->getHead()->getFaceModel().getTranslation() -
neckPosition);
displaySide(_mirrorCamera, true);
// restore absolute translations
_myAvatar->getSkeletonModel().setTranslation(absoluteSkeletonTranslation);
_myAvatar->getHead()->getFaceModel().setTranslation(absoluteFaceTranslation);
} else {
displaySide(_mirrorCamera, true);
}
glPopMatrix();
_rearMirrorTools->render(false);
// reset Viewport and projection matrix
glViewport(0, 0, _glWidget->width(), _glWidget->height());
glDisable(GL_SCISSOR_TEST);
updateProjectionMatrix(_myCamera, updateViewFrustum);
renderRearViewMirror();
} else if (Menu::getInstance()->isOptionChecked(MenuOption::FullscreenMirror)) {
_rearMirrorTools->render(true);
}
@ -2743,6 +2678,14 @@ void Application::setupWorldLight() {
glMateriali(GL_FRONT, GL_SHININESS, 96);
}
QImage Application::renderAvatarBillboard() {
renderRearViewMirror(true);
QImage image(_glWidget->width(), _glWidget->height(), QImage::Format_ARGB32);
glReadPixels(0, 0, _glWidget->width(), _glWidget->height(), GL_BGRA, GL_UNSIGNED_BYTE, image.bits());
return image;
}
void Application::displaySide(Camera& whichCamera, bool selfAvatarOnly) {
PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), "Application::displaySide()");
// transform by eye offset
@ -3660,6 +3603,77 @@ void Application::renderCoverageMapsRecursively(CoverageMap* map) {
}
}
void Application::renderRearViewMirror(bool billboard) {
bool eyeRelativeCamera = false;
if (_rearMirrorTools->getZoomLevel() == BODY && !billboard) {
_mirrorCamera.setDistance(MIRROR_REARVIEW_BODY_DISTANCE * _myAvatar->getScale());
_mirrorCamera.setTargetPosition(_myAvatar->getChestPosition());
} else { // HEAD zoom level
_mirrorCamera.setDistance(MIRROR_REARVIEW_DISTANCE * _myAvatar->getScale());
if (_myAvatar->getSkeletonModel().isActive() && _myAvatar->getHead()->getFaceModel().isActive()) {
// as a hack until we have a better way of dealing with coordinate precision issues, reposition the
// face/body so that the average eye position lies at the origin
eyeRelativeCamera = true;
_mirrorCamera.setTargetPosition(glm::vec3());
} else {
_mirrorCamera.setTargetPosition(_myAvatar->getHead()->calculateAverageEyePosition());
}
}
_mirrorCamera.setTargetRotation(_myAvatar->getWorldAlignedOrientation() * glm::quat(glm::vec3(0.0f, PIf, 0.0f)));
_mirrorCamera.update(1.0f/_fps);
// set the bounds of rear mirror view
glViewport(_mirrorViewRect.x(), _glWidget->height() - _mirrorViewRect.y() - _mirrorViewRect.height(),
_mirrorViewRect.width(), _mirrorViewRect.height());
glScissor(_mirrorViewRect.x(), _glWidget->height() - _mirrorViewRect.y() - _mirrorViewRect.height(),
_mirrorViewRect.width(), _mirrorViewRect.height());
bool updateViewFrustum = false;
updateProjectionMatrix(_mirrorCamera, updateViewFrustum);
glEnable(GL_SCISSOR_TEST);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// render rear mirror view
glPushMatrix();
if (eyeRelativeCamera) {
// save absolute translations
glm::vec3 absoluteSkeletonTranslation = _myAvatar->getSkeletonModel().getTranslation();
glm::vec3 absoluteFaceTranslation = _myAvatar->getHead()->getFaceModel().getTranslation();
// get the eye positions relative to the neck and use them to set the face translation
glm::vec3 leftEyePosition, rightEyePosition;
_myAvatar->getHead()->getFaceModel().setTranslation(glm::vec3());
_myAvatar->getHead()->getFaceModel().getEyePositions(leftEyePosition, rightEyePosition);
_myAvatar->getHead()->getFaceModel().setTranslation((leftEyePosition + rightEyePosition) * -0.5f);
// get the neck position relative to the body and use it to set the skeleton translation
glm::vec3 neckPosition;
_myAvatar->getSkeletonModel().setTranslation(glm::vec3());
_myAvatar->getSkeletonModel().getNeckPosition(neckPosition);
_myAvatar->getSkeletonModel().setTranslation(_myAvatar->getHead()->getFaceModel().getTranslation() -
neckPosition);
displaySide(_mirrorCamera, true);
// restore absolute translations
_myAvatar->getSkeletonModel().setTranslation(absoluteSkeletonTranslation);
_myAvatar->getHead()->getFaceModel().setTranslation(absoluteFaceTranslation);
} else {
displaySide(_mirrorCamera, true);
}
glPopMatrix();
if (!billboard) {
_rearMirrorTools->render(false);
}
// reset Viewport and projection matrix
glViewport(0, 0, _glWidget->width(), _glWidget->height());
glDisable(GL_SCISSOR_TEST);
updateProjectionMatrix(_myCamera, updateViewFrustum);
}
// renderViewFrustum()
//
// Description: this will render the view frustum bounds for EITHER the head

View file

@ -14,6 +14,7 @@
#include <QApplication>
#include <QAction>
#include <QImage>
#include <QSettings>
#include <QTouchEvent>
#include <QList>
@ -185,6 +186,8 @@ public:
void setupWorldLight();
QImage renderAvatarBillboard();
void displaySide(Camera& whichCamera, bool selfAvatarOnly = false);
/// Loads a view matrix that incorporates the specified model translation without the precision issues that can
@ -200,6 +203,8 @@ public:
void computeOffAxisFrustum(float& left, float& right, float& bottom, float& top, float& nearVal,
float& farVal, glm::vec4& nearClipPlane, glm::vec4& farClipPlane) const;
VoxelShader& getVoxelShader() { return _voxelShader; }
PointShader& getPointShader() { return _pointShader; }
FileLogger* getLogger() { return _logger; }
@ -328,7 +333,7 @@ private:
void displayStats();
void checkStatsClick();
void toggleStatsExpanded();
void renderAvatars(bool forceRenderHead, bool selfAvatarOnly = false);
void renderRearViewMirror(bool billboard = false);
void renderViewFrustum(ViewFrustum& viewFrustum);
void checkBandwidthMeterClick();

View file

@ -57,7 +57,8 @@ MyAvatar::MyAvatar() :
_thrustMultiplier(1.0f),
_moveTarget(0,0,0),
_moveTargetStepCounter(0),
_lookAtTargetAvatar()
_lookAtTargetAvatar(),
_billboardValid(false)
{
for (int i = 0; i < MAX_DRIVE_KEYS; i++) {
_driveKeys[i] = 0.0f;
@ -332,7 +333,13 @@ void MyAvatar::simulate(float deltaTime) {
// Zero thrust out now that we've added it to velocity in this frame
_thrust = glm::vec3(0, 0, 0);
// consider updating our billboard
if (!_billboardValid && _skeletonModel.isLoadedWithTextures() && getHead()->getFaceModel().isLoadedWithTextures()) {
QImage image = Application::getInstance()->renderAvatarBillboard();
image.save("test.png");
_billboardValid = true;
}
}
const float MAX_PITCH = 90.0f;
@ -712,6 +719,16 @@ glm::vec3 MyAvatar::getUprightHeadPosition() const {
return _position + getWorldAlignedOrientation() * glm::vec3(0.0f, getPelvisToHeadLength(), 0.0f);
}
void MyAvatar::setFaceModelURL(const QUrl& faceModelURL) {
Avatar::setFaceModelURL(faceModelURL);
_billboardValid = false;
}
void MyAvatar::setSkeletonModelURL(const QUrl& skeletonModelURL) {
Avatar::setSkeletonModelURL(skeletonModelURL);
_billboardValid = false;
}
void MyAvatar::renderBody(bool forceRenderHead) {
// Render the body's voxels and head
_skeletonModel.render(1.0f);

View file

@ -84,6 +84,9 @@ public:
void updateLookAtTargetAvatar(glm::vec3& eyePosition);
void clearLookAtTargetAvatar();
virtual void setFaceModelURL(const QUrl& faceModelURL);
virtual void setSkeletonModelURL(const QUrl& skeletonModelURL);
public slots:
void goHome();
void increaseSize();
@ -119,6 +122,8 @@ private:
glm::vec3 _transmitterPickStart;
glm::vec3 _transmitterPickEnd;
bool _billboardValid;
// private methods
void renderBody(bool forceRenderHead);
void updateThrust(float deltaTime);

View file

@ -46,6 +46,21 @@ void Model::initSkinProgram(ProgramObject& program, Model::SkinLocations& locati
program.release();
}
bool Model::isLoadedWithTextures() const {
if (!isActive()) {
return false;
}
foreach (const NetworkMesh& mesh, _geometry->getMeshes()) {
foreach (const NetworkMeshPart& part, mesh.parts) {
if (part.diffuseTexture && !part.diffuseTexture->isLoaded() ||
part.normalTexture && !part.normalTexture->isLoaded()) {
return false;
}
}
}
return true;
}
void Model::init() {
if (!_program.isLinked()) {
switchToResourcesParentIfRequired();

View file

@ -46,6 +46,8 @@ public:
bool isActive() const { return _geometry && _geometry->isLoaded(); }
bool isLoadedWithTextures() const;
void init();
void reset();
void simulate(float deltaTime);

View file

@ -258,9 +258,11 @@ NetworkTexture::NetworkTexture(const QUrl& url, bool normalMap) :
_reply(NULL),
_attempts(0),
_averageColor(1.0f, 1.0f, 1.0f, 1.0f),
_translucent(false) {
_translucent(false),
_loaded(false) {
if (!url.isValid()) {
_loaded = true;
return;
}
_request.setAttribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferCache);
@ -298,6 +300,7 @@ void NetworkTexture::handleDownloadProgress(qint64 bytesReceived, qint64 bytesTo
_reply->disconnect(this);
_reply->deleteLater();
_reply = NULL;
_loaded = true;
QImage image = QImage::fromData(entirety).convertToFormat(QImage::Format_ARGB32);
@ -345,6 +348,8 @@ void NetworkTexture::handleReplyError() {
QTimer::singleShot(BASE_DELAY_MS * (int)pow(2.0, _attempts), this, SLOT(makeRequest()));
debug << " -- retrying...";
} else {
_loaded = true;
}
}

View file

@ -118,6 +118,8 @@ public:
NetworkTexture(const QUrl& url, bool normalMap);
~NetworkTexture();
bool isLoaded() const { return _loaded; }
/// Returns the average color over the entire texture.
const glm::vec4& getAverageColor() const { return _averageColor; }
@ -142,6 +144,7 @@ private:
int _attempts;
glm::vec4 _averageColor;
bool _translucent;
bool _loaded;
};
/// Caches derived, dilated textures.