mirror of
https://github.com/overte-org/overte.git
synced 2025-04-22 05:04:14 +02:00
Merge pull request #5994 from Atlante45/display_name
Display name in terms of arc size not pixels
This commit is contained in:
commit
ae93c020be
4 changed files with 109 additions and 132 deletions
|
@ -549,7 +549,12 @@ void Avatar::render(RenderArgs* renderArgs, const glm::vec3& cameraPosition) {
|
|||
|
||||
auto cameraMode = qApp->getCamera()->getMode();
|
||||
if (!isMyAvatar() || cameraMode != CAMERA_MODE_FIRST_PERSON) {
|
||||
renderDisplayName(batch, *renderArgs->_viewFrustum, renderArgs->_viewport);
|
||||
auto& frustum = *renderArgs->_viewFrustum;
|
||||
auto textPosition = getDisplayNamePosition();
|
||||
|
||||
if (frustum.pointInFrustum(textPosition, true) == ViewFrustum::INSIDE) {
|
||||
renderDisplayName(batch, frustum, textPosition);
|
||||
}
|
||||
}
|
||||
endRender();
|
||||
}
|
||||
|
@ -685,120 +690,85 @@ void Avatar::renderBillboard(RenderArgs* renderArgs) {
|
|||
}
|
||||
|
||||
float Avatar::getBillboardSize() const {
|
||||
return _scale * BILLBOARD_DISTANCE * tanf(glm::radians(BILLBOARD_FIELD_OF_VIEW / 2.0f));
|
||||
return _scale * BILLBOARD_DISTANCE * glm::tan(glm::radians(BILLBOARD_FIELD_OF_VIEW / 2.0f));
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
void debugValue(const QString& str, const glm::vec3& value) {
|
||||
if (glm::any(glm::isnan(value)) || glm::any(glm::isinf(value))) {
|
||||
qCWarning(interfaceapp) << "debugValue() " << str << value;
|
||||
}
|
||||
};
|
||||
void debugValue(const QString& str, const float& value) {
|
||||
if (glm::isnan(value) || glm::isinf(value)) {
|
||||
qCWarning(interfaceapp) << "debugValue() " << str << value;
|
||||
}
|
||||
};
|
||||
#define DEBUG_VALUE(str, value) debugValue(str, value)
|
||||
#else
|
||||
#define DEBUG_VALUE(str, value)
|
||||
#endif
|
||||
|
||||
glm::vec3 Avatar::getDisplayNamePosition() const {
|
||||
glm::vec3 namePosition(0.0f);
|
||||
glm::vec3 bodyUpDirection = getBodyUpDirection();
|
||||
DEBUG_VALUE("bodyUpDirection =", bodyUpDirection);
|
||||
|
||||
if (getSkeletonModel().getNeckPosition(namePosition)) {
|
||||
namePosition += getBodyUpDirection() * getHeadHeight() * 1.1f;
|
||||
float headHeight = getHeadHeight();
|
||||
DEBUG_VALUE("namePosition =", namePosition);
|
||||
DEBUG_VALUE("headHeight =", headHeight);
|
||||
|
||||
static const float SLIGHTLY_ABOVE = 1.1f;
|
||||
namePosition += bodyUpDirection * headHeight * SLIGHTLY_ABOVE;
|
||||
} else {
|
||||
const float HEAD_PROPORTION = 0.75f;
|
||||
namePosition = _position + getBodyUpDirection() * (getBillboardSize() * HEAD_PROPORTION);
|
||||
float billboardSize = getBillboardSize();
|
||||
|
||||
DEBUG_VALUE("_position =", _position);
|
||||
DEBUG_VALUE("billboardSize =", billboardSize);
|
||||
namePosition = _position + bodyUpDirection * (billboardSize * HEAD_PROPORTION);
|
||||
}
|
||||
#ifdef DEBUG
|
||||
// TODO: Temporary logging to track cause of invalid scale value; remove once cause has been fixed.
|
||||
// See other TODO below.
|
||||
if (glm::isnan(namePosition.x) || glm::isnan(namePosition.y) || glm::isnan(namePosition.z)
|
||||
|| glm::isinf(namePosition.x) || glm::isinf(namePosition.y) || glm::isinf(namePosition.z)) {
|
||||
qDebug() << "namePosition =" << namePosition;
|
||||
glm::vec3 tempPosition(0.0f);
|
||||
if (getSkeletonModel().getNeckPosition(tempPosition)) {
|
||||
qDebug() << "getBodyUpDirection() =" << getBodyUpDirection();
|
||||
qDebug() << "getHeadHeight() =" << getHeadHeight();
|
||||
} else {
|
||||
qDebug() << "_position =" << _position;
|
||||
qDebug() << "getBodyUpDirection() =" << getBodyUpDirection();
|
||||
qDebug() << "getBillboardSize() =" << getBillboardSize();
|
||||
}
|
||||
|
||||
if (glm::any(glm::isnan(namePosition)) || glm::any(glm::isinf(namePosition))) {
|
||||
qCWarning(interfaceapp) << "Invalid display name position" << namePosition
|
||||
<< ", setting is to (0.0f, 0.5f, 0.0f)";
|
||||
namePosition = glm::vec3(0.0f, 0.5f, 0.0f);
|
||||
}
|
||||
#endif
|
||||
|
||||
return namePosition;
|
||||
}
|
||||
|
||||
Transform Avatar::calculateDisplayNameTransform(const ViewFrustum& frustum, float fontSize, const glm::ivec4& viewport) const {
|
||||
Transform result;
|
||||
// We assume textPosition is whithin the frustum
|
||||
glm::vec3 textPosition = getDisplayNamePosition();
|
||||
|
||||
// Compute viewProjection matrix
|
||||
glm::mat4 projMat, viewMat;
|
||||
Transform view;
|
||||
frustum.evalProjectionMatrix(projMat);
|
||||
frustum.evalViewTransform(view);
|
||||
glm::mat4 viewProj = projMat * view.getInverseMatrix(viewMat);
|
||||
|
||||
// Used to determine correct scale
|
||||
glm::vec3 testPoint0 = textPosition;
|
||||
glm::vec3 testPoint1 = testPoint0 + glm::normalize(frustum.getUp());
|
||||
// testPoints projections
|
||||
glm::vec4 p0 = viewProj * glm::vec4(testPoint0, 1.0);
|
||||
glm::vec4 p1 = viewProj * glm::vec4(testPoint1, 1.0);
|
||||
|
||||
float windowSizeY = viewport.w;
|
||||
|
||||
const float DESIRED_HIGHT_ON_SCREEN = 20; // In pixels (this is double on retinas)
|
||||
|
||||
// Projected point are between -1.0f and 1.0f, hence 0.5f * windowSizeY
|
||||
float pixelHeight = 0.5f * windowSizeY * glm::abs((p1.y / p1.w) - (p0.y / p0.w)); //
|
||||
// Handles pixel density (especially for macs retina displays)
|
||||
float devicePixelRatio = (float)qApp->getDevicePixelRatio() * qApp->getRenderResolutionScale(); // pixels / unit
|
||||
|
||||
// Compute correct scale to apply
|
||||
float scale = DESIRED_HIGHT_ON_SCREEN / (fontSize * pixelHeight) * devicePixelRatio;
|
||||
#ifdef DEBUG
|
||||
// TODO: Temporary logging to track cause of invalid scale value; remove once cause has been fixed.
|
||||
// Problem is probably due to an invalid getDisplayNamePosition(). See extra logging above.
|
||||
if (scale == 0.0f || glm::isnan(scale) || glm::isinf(scale)) {
|
||||
if (scale == 0.0f) {
|
||||
qDebug() << "ASSERT because scale == 0.0f";
|
||||
}
|
||||
if (glm::isnan(scale)) {
|
||||
qDebug() << "ASSERT because isnan(scale)";
|
||||
}
|
||||
if (glm::isinf(scale)) {
|
||||
qDebug() << "ASSERT because isinf(scale)";
|
||||
}
|
||||
qDebug() << "textPosition =" << textPosition;
|
||||
qDebug() << "projMat =" << projMat;
|
||||
qDebug() << "viewMat =" << viewMat;
|
||||
qDebug() << "viewProj =" << viewProj;
|
||||
qDebug() << "windowSizeY =" << windowSizeY;
|
||||
qDebug() << "p1 =" << p1;
|
||||
qDebug() << "p0 =" << p0;
|
||||
qDebug() << "qApp->getDevicePixelRatio() =" << qApp->getDevicePixelRatio();
|
||||
qDebug() << "fontSize =" << fontSize;
|
||||
qDebug() << "pixelHeight =" << pixelHeight;
|
||||
qDebug() << "devicePixelRatio =" << devicePixelRatio;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Compute pixel alignment offset
|
||||
float clipToPix = 0.5f * windowSizeY / p1.w; // Got from clip to pixel coordinates
|
||||
glm::vec4 screenPos = clipToPix * p1; // in pixels coords
|
||||
glm::vec4 screenOffset = (glm::round(screenPos) - screenPos) / clipToPix; // in clip coords
|
||||
glm::vec3 worldOffset = glm::vec3(screenOffset.x, screenOffset.y, 0.0f) / (float)pixelHeight;
|
||||
Transform Avatar::calculateDisplayNameTransform(const ViewFrustum& frustum, const glm::vec3& textPosition) const {
|
||||
Q_ASSERT_X(frustum.pointInFrustum(textPosition, true) == ViewFrustum::INSIDE,
|
||||
"Avatar::calculateDisplayNameTransform", "Text not in viewfrustum.");
|
||||
glm::vec3 toFrustum = frustum.getPosition() - textPosition;
|
||||
|
||||
// Compute orientation
|
||||
glm::vec3 dPosition = frustum.getPosition() - getPosition();
|
||||
// If x and z are 0, atan(x, z) is undefined, so default to 0 degrees
|
||||
float yawRotation = dPosition.x == 0.0f && dPosition.z == 0.0f ? 0.0f : glm::atan(dPosition.x, dPosition.z);
|
||||
// If x and z are 0, atan(x, z) adais undefined, so default to 0 degrees
|
||||
const float yawRotation = (toFrustum.x == 0.0f && toFrustum.z == 0.0f) ? 0.0f : glm::atan(toFrustum.x, toFrustum.z);
|
||||
glm::quat orientation = glm::quat(glm::vec3(0.0f, yawRotation, 0.0f));
|
||||
|
||||
// Set transform (The order IS important)
|
||||
// Compute correct scale to apply
|
||||
static const float DESIRED_HEIGHT_RAD = glm::radians(1.5f);
|
||||
float scale = glm::length(toFrustum) * glm::tan(DESIRED_HEIGHT_RAD);
|
||||
|
||||
// Set transform
|
||||
Transform result;
|
||||
result.setTranslation(textPosition);
|
||||
result.setRotation(orientation); // Always face the screen
|
||||
result.postTranslate(worldOffset); // Pixel alignment
|
||||
result.setScale(scale);
|
||||
// raise by half the scale up so that textPosition be the bottom
|
||||
result.postTranslate(Vectors::UP / 2.0f);
|
||||
|
||||
return result;
|
||||
|
||||
}
|
||||
|
||||
void Avatar::renderDisplayName(gpu::Batch& batch, const ViewFrustum& frustum, const glm::ivec4& viewport) const {
|
||||
void Avatar::renderDisplayName(gpu::Batch& batch, const ViewFrustum& frustum, const glm::vec3& textPosition) const {
|
||||
bool shouldShowReceiveStats = DependencyManager::get<AvatarManager>()->shouldShowReceiveStats() && !isMyAvatar();
|
||||
|
||||
// If we have nothing to draw, or it's totally transparent, or it's too close or behind the camera, return
|
||||
const float CLIP_DISTANCE = 0.2f;
|
||||
static const float CLIP_DISTANCE = 0.2f;
|
||||
if ((_displayName.isEmpty() && !shouldShowReceiveStats) || _displayNameAlpha == 0.0f
|
||||
|| (glm::dot(frustum.getDirection(), getDisplayNamePosition() - frustum.getPosition()) <= CLIP_DISTANCE)) {
|
||||
return;
|
||||
|
@ -818,39 +788,45 @@ void Avatar::renderDisplayName(gpu::Batch& batch, const ViewFrustum& frustum, co
|
|||
}
|
||||
|
||||
// Compute display name extent/position offset
|
||||
glm::vec2 extent = renderer->computeExtent(renderedDisplayName);
|
||||
QRect nameDynamicRect = QRect(0, 0, (int)extent.x, (int)extent.y);
|
||||
const int text_x = -nameDynamicRect.width() / 2;
|
||||
const int text_y = -nameDynamicRect.height() / 2;
|
||||
|
||||
// Compute background position/size
|
||||
static const float SLIGHTLY_IN_FRONT = 0.1f;
|
||||
const int border = 0.1f * nameDynamicRect.height();
|
||||
const int left = text_x - border;
|
||||
const int bottom = text_y - border;
|
||||
const int width = nameDynamicRect.width() + 2.0f * border;
|
||||
const int height = nameDynamicRect.height() + 2.0f * border;
|
||||
const int bevelDistance = 0.1f * height;
|
||||
|
||||
// Display name and background colors
|
||||
glm::vec4 textColor(0.93f, 0.93f, 0.93f, _displayNameAlpha);
|
||||
glm::vec4 backgroundColor(0.2f, 0.2f, 0.2f,
|
||||
(_displayNameAlpha / DISPLAYNAME_ALPHA) * DISPLAYNAME_BACKGROUND_ALPHA);
|
||||
|
||||
// Compute display name transform
|
||||
auto textTransform = calculateDisplayNameTransform(frustum, renderer->getFontSize(), viewport);
|
||||
batch.setModelTransform(textTransform);
|
||||
|
||||
DependencyManager::get<DeferredLightingEffect>()->bindSimpleProgram(batch, false, true, true, true);
|
||||
DependencyManager::get<GeometryCache>()->renderBevelCornersRect(batch, left, bottom, width, height,
|
||||
bevelDistance, backgroundColor);
|
||||
// Render actual name
|
||||
QByteArray nameUTF8 = renderedDisplayName.toLocal8Bit();
|
||||
|
||||
// Render text slightly in front to avoid z-fighting
|
||||
textTransform.postTranslate(glm::vec3(0.0f, 0.0f, SLIGHTLY_IN_FRONT * renderer->getFontSize()));
|
||||
batch.setModelTransform(textTransform);
|
||||
renderer->draw(batch, text_x, -text_y, nameUTF8.data(), textColor);
|
||||
const glm::vec2 extent = renderer->computeExtent(renderedDisplayName);
|
||||
if (!glm::any(glm::isCompNull(extent, EPSILON))) {
|
||||
const QRect nameDynamicRect = QRect(0, 0, (int)extent.x, (int)extent.y);
|
||||
const int text_x = -nameDynamicRect.width() / 2;
|
||||
const int text_y = -nameDynamicRect.height() / 2;
|
||||
|
||||
// Compute background position/size
|
||||
static const float SLIGHTLY_IN_FRONT = 0.1f;
|
||||
static const float BORDER_RELATIVE_SIZE = 0.1f;
|
||||
static const float BEVEL_FACTOR = 0.1f;
|
||||
const int border = BORDER_RELATIVE_SIZE * nameDynamicRect.height();
|
||||
const int left = text_x - border;
|
||||
const int bottom = text_y - border;
|
||||
const int width = nameDynamicRect.width() + 2.0f * border;
|
||||
const int height = nameDynamicRect.height() + 2.0f * border;
|
||||
const int bevelDistance = BEVEL_FACTOR * height;
|
||||
|
||||
// Display name and background colors
|
||||
glm::vec4 textColor(0.93f, 0.93f, 0.93f, _displayNameAlpha);
|
||||
glm::vec4 backgroundColor(0.2f, 0.2f, 0.2f,
|
||||
(_displayNameAlpha / DISPLAYNAME_ALPHA) * DISPLAYNAME_BACKGROUND_ALPHA);
|
||||
|
||||
// Compute display name transform
|
||||
auto textTransform = calculateDisplayNameTransform(frustum, textPosition);
|
||||
// Test on extent above insures abs(height) > 0.0f
|
||||
textTransform.postScale(1.0f / height);
|
||||
batch.setModelTransform(textTransform);
|
||||
|
||||
DependencyManager::get<DeferredLightingEffect>()->bindSimpleProgram(batch, false, true, true, true);
|
||||
DependencyManager::get<GeometryCache>()->renderBevelCornersRect(batch, left, bottom, width, height,
|
||||
bevelDistance, backgroundColor);
|
||||
// Render actual name
|
||||
QByteArray nameUTF8 = renderedDisplayName.toLocal8Bit();
|
||||
|
||||
// Render text slightly in front to avoid z-fighting
|
||||
textTransform.postTranslate(glm::vec3(0.0f, 0.0f, SLIGHTLY_IN_FRONT * renderer->getFontSize()));
|
||||
batch.setModelTransform(textTransform);
|
||||
renderer->draw(batch, text_x, -text_y, nameUTF8.data(), textColor);
|
||||
}
|
||||
}
|
||||
|
||||
void Avatar::setSkeletonOffset(const glm::vec3& offset) {
|
||||
|
|
|
@ -217,8 +217,8 @@ protected:
|
|||
float getPelvisFloatingHeight() const;
|
||||
glm::vec3 getDisplayNamePosition() const;
|
||||
|
||||
Transform calculateDisplayNameTransform(const ViewFrustum& frustum, float fontSize, const glm::ivec4& viewport) const;
|
||||
void renderDisplayName(gpu::Batch& batch, const ViewFrustum& frustum, const glm::ivec4& viewport) const;
|
||||
Transform calculateDisplayNameTransform(const ViewFrustum& frustum, const glm::vec3& textPosition) const;
|
||||
void renderDisplayName(gpu::Batch& batch, const ViewFrustum& frustum, const glm::vec3& textPosition) const;
|
||||
virtual void renderBody(RenderArgs* renderArgs, ViewFrustum* renderFrustum, float glowLevel = 0.0f);
|
||||
virtual bool shouldRenderHead(const RenderArgs* renderArgs) const;
|
||||
virtual void fixupModelsInScene();
|
||||
|
|
|
@ -241,20 +241,21 @@ ViewFrustum::location ViewFrustum::boxInKeyhole(const AABox& box) const {
|
|||
return result;
|
||||
}
|
||||
|
||||
ViewFrustum::location ViewFrustum::pointInFrustum(const glm::vec3& point) const {
|
||||
ViewFrustum::location ViewFrustum::pointInFrustum(const glm::vec3& point, bool ignoreKeyhole) const {
|
||||
ViewFrustum::location regularResult = INSIDE;
|
||||
ViewFrustum::location keyholeResult = OUTSIDE;
|
||||
|
||||
// If we have a keyholeRadius, check that first, since it's cheaper
|
||||
if (_keyholeRadius >= 0.0f) {
|
||||
if (!ignoreKeyhole && _keyholeRadius >= 0.0f) {
|
||||
keyholeResult = pointInKeyhole(point);
|
||||
}
|
||||
if (keyholeResult == INSIDE) {
|
||||
return keyholeResult;
|
||||
|
||||
if (keyholeResult == INSIDE) {
|
||||
return keyholeResult;
|
||||
}
|
||||
}
|
||||
|
||||
// If we're not known to be INSIDE the keyhole, then check the regular frustum
|
||||
for(int i=0; i < 6; i++) {
|
||||
for(int i = 0; i < 6; ++i) {
|
||||
float distance = _planes[i].distance(point);
|
||||
if (distance < 0) {
|
||||
return keyholeResult; // escape early will be the value from checking the keyhole
|
||||
|
|
|
@ -79,7 +79,7 @@ public:
|
|||
|
||||
typedef enum {OUTSIDE, INTERSECT, INSIDE} location;
|
||||
|
||||
ViewFrustum::location pointInFrustum(const glm::vec3& point) const;
|
||||
ViewFrustum::location pointInFrustum(const glm::vec3& point, bool ignoreKeyhole = false) const;
|
||||
ViewFrustum::location sphereInFrustum(const glm::vec3& center, float radius) const;
|
||||
ViewFrustum::location cubeInFrustum(const AACube& cube) const;
|
||||
ViewFrustum::location boxInFrustum(const AABox& box) const;
|
||||
|
|
Loading…
Reference in a new issue