mirror of
https://github.com/overte-org/overte.git
synced 2025-08-09 23:14:34 +02:00
Working on avatar billboards.
This commit is contained in:
parent
7010c45b16
commit
6b90a3994d
8 changed files with 137 additions and 71 deletions
|
@ -527,73 +527,8 @@ void Application::paintGL() {
|
||||||
_glowEffect.render();
|
_glowEffect.render();
|
||||||
|
|
||||||
if (Menu::getInstance()->isOptionChecked(MenuOption::Mirror)) {
|
if (Menu::getInstance()->isOptionChecked(MenuOption::Mirror)) {
|
||||||
|
renderRearViewMirror();
|
||||||
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);
|
|
||||||
} else if (Menu::getInstance()->isOptionChecked(MenuOption::FullscreenMirror)) {
|
} else if (Menu::getInstance()->isOptionChecked(MenuOption::FullscreenMirror)) {
|
||||||
_rearMirrorTools->render(true);
|
_rearMirrorTools->render(true);
|
||||||
}
|
}
|
||||||
|
@ -2743,6 +2678,14 @@ void Application::setupWorldLight() {
|
||||||
glMateriali(GL_FRONT, GL_SHININESS, 96);
|
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) {
|
void Application::displaySide(Camera& whichCamera, bool selfAvatarOnly) {
|
||||||
PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), "Application::displaySide()");
|
PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), "Application::displaySide()");
|
||||||
// transform by eye offset
|
// 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()
|
// renderViewFrustum()
|
||||||
//
|
//
|
||||||
// Description: this will render the view frustum bounds for EITHER the head
|
// Description: this will render the view frustum bounds for EITHER the head
|
||||||
|
|
|
@ -14,6 +14,7 @@
|
||||||
|
|
||||||
#include <QApplication>
|
#include <QApplication>
|
||||||
#include <QAction>
|
#include <QAction>
|
||||||
|
#include <QImage>
|
||||||
#include <QSettings>
|
#include <QSettings>
|
||||||
#include <QTouchEvent>
|
#include <QTouchEvent>
|
||||||
#include <QList>
|
#include <QList>
|
||||||
|
@ -185,6 +186,8 @@ public:
|
||||||
|
|
||||||
void setupWorldLight();
|
void setupWorldLight();
|
||||||
|
|
||||||
|
QImage renderAvatarBillboard();
|
||||||
|
|
||||||
void displaySide(Camera& whichCamera, bool selfAvatarOnly = false);
|
void displaySide(Camera& whichCamera, bool selfAvatarOnly = false);
|
||||||
|
|
||||||
/// Loads a view matrix that incorporates the specified model translation without the precision issues that can
|
/// 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,
|
void computeOffAxisFrustum(float& left, float& right, float& bottom, float& top, float& nearVal,
|
||||||
float& farVal, glm::vec4& nearClipPlane, glm::vec4& farClipPlane) const;
|
float& farVal, glm::vec4& nearClipPlane, glm::vec4& farClipPlane) const;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
VoxelShader& getVoxelShader() { return _voxelShader; }
|
VoxelShader& getVoxelShader() { return _voxelShader; }
|
||||||
PointShader& getPointShader() { return _pointShader; }
|
PointShader& getPointShader() { return _pointShader; }
|
||||||
FileLogger* getLogger() { return _logger; }
|
FileLogger* getLogger() { return _logger; }
|
||||||
|
@ -328,7 +333,7 @@ private:
|
||||||
void displayStats();
|
void displayStats();
|
||||||
void checkStatsClick();
|
void checkStatsClick();
|
||||||
void toggleStatsExpanded();
|
void toggleStatsExpanded();
|
||||||
void renderAvatars(bool forceRenderHead, bool selfAvatarOnly = false);
|
void renderRearViewMirror(bool billboard = false);
|
||||||
void renderViewFrustum(ViewFrustum& viewFrustum);
|
void renderViewFrustum(ViewFrustum& viewFrustum);
|
||||||
|
|
||||||
void checkBandwidthMeterClick();
|
void checkBandwidthMeterClick();
|
||||||
|
|
|
@ -57,7 +57,8 @@ MyAvatar::MyAvatar() :
|
||||||
_thrustMultiplier(1.0f),
|
_thrustMultiplier(1.0f),
|
||||||
_moveTarget(0,0,0),
|
_moveTarget(0,0,0),
|
||||||
_moveTargetStepCounter(0),
|
_moveTargetStepCounter(0),
|
||||||
_lookAtTargetAvatar()
|
_lookAtTargetAvatar(),
|
||||||
|
_billboardValid(false)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < MAX_DRIVE_KEYS; i++) {
|
for (int i = 0; i < MAX_DRIVE_KEYS; i++) {
|
||||||
_driveKeys[i] = 0.0f;
|
_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
|
// Zero thrust out now that we've added it to velocity in this frame
|
||||||
_thrust = glm::vec3(0, 0, 0);
|
_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;
|
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);
|
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) {
|
void MyAvatar::renderBody(bool forceRenderHead) {
|
||||||
// Render the body's voxels and head
|
// Render the body's voxels and head
|
||||||
_skeletonModel.render(1.0f);
|
_skeletonModel.render(1.0f);
|
||||||
|
|
|
@ -84,6 +84,9 @@ public:
|
||||||
void updateLookAtTargetAvatar(glm::vec3& eyePosition);
|
void updateLookAtTargetAvatar(glm::vec3& eyePosition);
|
||||||
void clearLookAtTargetAvatar();
|
void clearLookAtTargetAvatar();
|
||||||
|
|
||||||
|
virtual void setFaceModelURL(const QUrl& faceModelURL);
|
||||||
|
virtual void setSkeletonModelURL(const QUrl& skeletonModelURL);
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void goHome();
|
void goHome();
|
||||||
void increaseSize();
|
void increaseSize();
|
||||||
|
@ -119,6 +122,8 @@ private:
|
||||||
glm::vec3 _transmitterPickStart;
|
glm::vec3 _transmitterPickStart;
|
||||||
glm::vec3 _transmitterPickEnd;
|
glm::vec3 _transmitterPickEnd;
|
||||||
|
|
||||||
|
bool _billboardValid;
|
||||||
|
|
||||||
// private methods
|
// private methods
|
||||||
void renderBody(bool forceRenderHead);
|
void renderBody(bool forceRenderHead);
|
||||||
void updateThrust(float deltaTime);
|
void updateThrust(float deltaTime);
|
||||||
|
|
|
@ -46,6 +46,21 @@ void Model::initSkinProgram(ProgramObject& program, Model::SkinLocations& locati
|
||||||
program.release();
|
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() {
|
void Model::init() {
|
||||||
if (!_program.isLinked()) {
|
if (!_program.isLinked()) {
|
||||||
switchToResourcesParentIfRequired();
|
switchToResourcesParentIfRequired();
|
||||||
|
|
|
@ -46,6 +46,8 @@ public:
|
||||||
|
|
||||||
bool isActive() const { return _geometry && _geometry->isLoaded(); }
|
bool isActive() const { return _geometry && _geometry->isLoaded(); }
|
||||||
|
|
||||||
|
bool isLoadedWithTextures() const;
|
||||||
|
|
||||||
void init();
|
void init();
|
||||||
void reset();
|
void reset();
|
||||||
void simulate(float deltaTime);
|
void simulate(float deltaTime);
|
||||||
|
|
|
@ -258,9 +258,11 @@ NetworkTexture::NetworkTexture(const QUrl& url, bool normalMap) :
|
||||||
_reply(NULL),
|
_reply(NULL),
|
||||||
_attempts(0),
|
_attempts(0),
|
||||||
_averageColor(1.0f, 1.0f, 1.0f, 1.0f),
|
_averageColor(1.0f, 1.0f, 1.0f, 1.0f),
|
||||||
_translucent(false) {
|
_translucent(false),
|
||||||
|
_loaded(false) {
|
||||||
|
|
||||||
if (!url.isValid()) {
|
if (!url.isValid()) {
|
||||||
|
_loaded = true;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
_request.setAttribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferCache);
|
_request.setAttribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferCache);
|
||||||
|
@ -298,6 +300,7 @@ void NetworkTexture::handleDownloadProgress(qint64 bytesReceived, qint64 bytesTo
|
||||||
_reply->disconnect(this);
|
_reply->disconnect(this);
|
||||||
_reply->deleteLater();
|
_reply->deleteLater();
|
||||||
_reply = NULL;
|
_reply = NULL;
|
||||||
|
_loaded = true;
|
||||||
|
|
||||||
QImage image = QImage::fromData(entirety).convertToFormat(QImage::Format_ARGB32);
|
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()));
|
QTimer::singleShot(BASE_DELAY_MS * (int)pow(2.0, _attempts), this, SLOT(makeRequest()));
|
||||||
debug << " -- retrying...";
|
debug << " -- retrying...";
|
||||||
|
|
||||||
|
} else {
|
||||||
|
_loaded = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -118,6 +118,8 @@ public:
|
||||||
NetworkTexture(const QUrl& url, bool normalMap);
|
NetworkTexture(const QUrl& url, bool normalMap);
|
||||||
~NetworkTexture();
|
~NetworkTexture();
|
||||||
|
|
||||||
|
bool isLoaded() const { return _loaded; }
|
||||||
|
|
||||||
/// Returns the average color over the entire texture.
|
/// Returns the average color over the entire texture.
|
||||||
const glm::vec4& getAverageColor() const { return _averageColor; }
|
const glm::vec4& getAverageColor() const { return _averageColor; }
|
||||||
|
|
||||||
|
@ -142,6 +144,7 @@ private:
|
||||||
int _attempts;
|
int _attempts;
|
||||||
glm::vec4 _averageColor;
|
glm::vec4 _averageColor;
|
||||||
bool _translucent;
|
bool _translucent;
|
||||||
|
bool _loaded;
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Caches derived, dilated textures.
|
/// Caches derived, dilated textures.
|
||||||
|
|
Loading…
Reference in a new issue