From 57fa3d8c5325a07b0a32aa8cffe30727a2b6983a Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Wed, 13 May 2015 14:43:31 -0700 Subject: [PATCH] fixes for other avatar receive stats rendering --- interface/src/avatar/Avatar.cpp | 217 +++++++++++++++++--------------- 1 file changed, 117 insertions(+), 100 deletions(-) diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index 09a3407d08..8a627c019c 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -81,7 +81,7 @@ Avatar::Avatar() : { // we may have been created in the network thread, but we live in the main thread moveToThread(Application::getInstance()->thread()); - + // give the pointer to our head to inherited _headData variable from AvatarData _headData = static_cast(new Head(this)); _handData = static_cast(new Hand(this)); @@ -122,7 +122,7 @@ float Avatar::getLODDistance() const { void Avatar::simulate(float deltaTime) { PerformanceTimer perfTimer("simulate"); - + // update the avatar's position according to its referential if (_referential) { if (_referential->hasExtraData()) { @@ -143,10 +143,10 @@ void Avatar::simulate(float deltaTime) { break; } } - + _referential->update(); } - + if (_scale != _targetScale) { setScale(_targetScale); } @@ -171,7 +171,7 @@ void Avatar::simulate(float deltaTime) { getHand()->simulate(deltaTime, false); } _skeletonModel.setLODDistance(getLODDistance()); - + if (!_shouldRenderBillboard && inViewFrustum) { { PerformanceTimer perfTimer("skeleton"); @@ -198,7 +198,7 @@ void Avatar::simulate(float deltaTime) { // update animation for display name fade in/out if ( _displayNameTargetAlpha != _displayNameAlpha) { - // the alpha function is + // the alpha function is // Fade out => alpha(t) = factor ^ t => alpha(t+dt) = alpha(t) * factor^(dt) // Fade in => alpha(t) = 1 - factor^t => alpha(t+dt) = 1-(1-alpha(t))*coef^(dt) // factor^(dt) = coef @@ -213,17 +213,17 @@ void Avatar::simulate(float deltaTime) { _displayNameAlpha = abs(_displayNameAlpha - _displayNameTargetAlpha) < 0.01f ? _displayNameTargetAlpha : _displayNameAlpha; } - // NOTE: we shouldn't extrapolate an Avatar instance forward in time... + // NOTE: we shouldn't extrapolate an Avatar instance forward in time... // until velocity is included in AvatarData update message. //_position += _velocity * deltaTime; measureMotionDerivatives(deltaTime); } -void Avatar::slamPosition(const glm::vec3& newPosition) { +void Avatar::slamPosition(const glm::vec3& newPosition) { AvatarData::setPosition(newPosition); _positionDeltaAccumulator = glm::vec3(0.0f); _velocity = glm::vec3(0.0f); - _lastVelocity = glm::vec3(0.0f); + _lastVelocity = glm::vec3(0.0f); } void Avatar::applyPositionDelta(const glm::vec3& delta) { @@ -249,7 +249,7 @@ void Avatar::measureMotionDerivatives(float deltaTime) { } enum TextRendererType { - CHAT, + CHAT, DISPLAYNAME }; @@ -272,7 +272,7 @@ void Avatar::render(const glm::vec3& cameraPosition, RenderArgs::RenderMode rend if (_referential) { _referential->update(); } - + if (postLighting && glm::distance(DependencyManager::get()->getMyAvatar()->getPosition(), _position) < 10.0f) { auto geometryCache = DependencyManager::get(); @@ -303,15 +303,15 @@ void Avatar::render(const glm::vec3& cameraPosition, RenderArgs::RenderMode rend float angle = glm::degrees(glm::angle(rotation)); glm::vec3 axis = glm::axis(rotation); glRotatef(angle, axis.x, axis.y, axis.z); - + geometryCache->renderLine(glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f, laserLength, 0.0f), laserColor); - + } glPopMatrix(); } } if (_handState & RIGHT_HAND_POINTING_FLAG) { - + if (_handState & IS_FINGER_POINTING_FLAG) { int rightIndexTip = getJointIndex("RightHandIndex4"); int rightIndexTipJoint = getJointIndex("RightHandIndex3"); @@ -330,12 +330,12 @@ void Avatar::render(const glm::vec3& cameraPosition, RenderArgs::RenderMode rend glm::vec3 axis = glm::axis(rotation); glRotatef(angle, axis.x, axis.y, axis.z); geometryCache->renderLine(glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f, laserLength, 0.0f), laserColor); - + } glPopMatrix(); } } } - + // simple frustum check float boundingRadius = getBillboardSize(); ViewFrustum* frustum = nullptr; @@ -351,24 +351,24 @@ void Avatar::render(const glm::vec3& cameraPosition, RenderArgs::RenderMode rend glm::vec3 toTarget = cameraPosition - getPosition(); float distanceToTarget = glm::length(toTarget); - + { // glow when moving far away const float GLOW_DISTANCE = 20.0f; const float GLOW_MAX_LOUDNESS = 2500.0f; const float MAX_GLOW = 0.5f; - + float GLOW_FROM_AVERAGE_LOUDNESS = ((this == DependencyManager::get()->getMyAvatar()) ? 0.0f : MAX_GLOW * getHeadData()->getAudioLoudness() / GLOW_MAX_LOUDNESS); if (!Menu::getInstance()->isOptionChecked(MenuOption::GlowWhenSpeaking)) { GLOW_FROM_AVERAGE_LOUDNESS = 0.0f; } - + float glowLevel = _moving && distanceToTarget > GLOW_DISTANCE && renderMode == RenderArgs::NORMAL_RENDER_MODE ? 1.0f : GLOW_FROM_AVERAGE_LOUDNESS; - + // render body renderBody(frustum, renderMode, postLighting, glowLevel); @@ -386,7 +386,7 @@ void Avatar::render(const glm::vec3& cameraPosition, RenderArgs::RenderMode rend distance * 2.0f, light.color, 0.5f, orientation, LIGHT_EXPONENT, LIGHT_CUTOFF); } } - + if (postLighting) { bool renderSkeleton = Menu::getInstance()->isOptionChecked(MenuOption::RenderSkeletonCollisionShapes); bool renderHead = Menu::getInstance()->isOptionChecked(MenuOption::RenderHeadCollisionShapes); @@ -435,7 +435,7 @@ void Avatar::render(const glm::vec3& cameraPosition, RenderArgs::RenderMode rend glm::vec3 delta = height * (getHead()->getCameraOrientation() * IDENTITY_UP) / 2.0f; float angle = abs(angleBetween(toTarget + delta, toTarget - delta)); float sphereRadius = getHead()->getAverageLoudness() * SPHERE_LOUDNESS_SCALING; - + if (renderMode == RenderArgs::NORMAL_RENDER_MODE && (sphereRadius > MIN_SPHERE_SIZE) && (angle < MAX_SPHERE_ANGLE) && (angle > MIN_SPHERE_ANGLE)) { glPushMatrix(); @@ -483,7 +483,7 @@ void Avatar::renderBody(ViewFrustum* renderFrustum, RenderArgs::RenderMode rende Model::RenderMode modelRenderMode = renderMode; { Glower glower(glowLevel); - + if (_shouldRenderBillboard || !(_skeletonModel.isRenderable() && getHead()->getFaceModel().isRenderable())) { if (postLighting || renderMode == RenderArgs::SHADOW_RENDER_MODE) { // render the billboard until both models are loaded @@ -491,7 +491,7 @@ void Avatar::renderBody(ViewFrustum* renderFrustum, RenderArgs::RenderMode rende } return; } - + if (postLighting) { getHand()->render(false, modelRenderMode); } else { @@ -553,43 +553,43 @@ void Avatar::renderBillboard() { if (!_billboardTexture->isLoaded()) { return; } - + glEnable(GL_ALPHA_TEST); glAlphaFunc(GL_GREATER, 0.5f); - + glEnable(GL_TEXTURE_2D); glDisable(GL_LIGHTING); glBindTexture(GL_TEXTURE_2D, _billboardTexture->getID()); - + glPushMatrix(); glTranslatef(_position.x, _position.y, _position.z); - + // rotate about vertical to face the camera glm::quat rotation = getOrientation(); glm::vec3 cameraVector = glm::inverse(rotation) * (Application::getInstance()->getCamera()->getPosition() - _position); rotation = rotation * glm::angleAxis(atan2f(-cameraVector.x, -cameraVector.z), glm::vec3(0.0f, 1.0f, 0.0f)); glm::vec3 axis = glm::axis(rotation); glRotatef(glm::degrees(glm::angle(rotation)), axis.x, axis.y, axis.z); - + // compute the size from the billboard camera parameters and scale float size = getBillboardSize(); glScalef(size, size, size); - + glm::vec2 topLeft(-1.0f, -1.0f); glm::vec2 bottomRight(1.0f, 1.0f); glm::vec2 texCoordTopLeft(0.0f, 0.0f); glm::vec2 texCoordBottomRight(1.0f, 1.0f); - DependencyManager::get()->renderQuad(topLeft, bottomRight, texCoordTopLeft, texCoordBottomRight, + DependencyManager::get()->renderQuad(topLeft, bottomRight, texCoordTopLeft, texCoordBottomRight, glm::vec4(1.0f, 1.0f, 1.0f, 1.0f)); - + glPopMatrix(); - + glDisable(GL_TEXTURE_2D); glEnable(GL_LIGHTING); glDisable(GL_ALPHA_TEST); - + glBindTexture(GL_TEXTURE_2D, 0); } @@ -611,26 +611,26 @@ glm::vec3 Avatar::getDisplayNamePosition() { float Avatar::calculateDisplayNameScaleFactor(const glm::vec3& textPosition, bool inHMD) { // We need to compute the scale factor such as the text remains with fixed size respect to window coordinates - // We project a unit vector and check the difference in screen coordinates, to check which is the + // We project a unit vector and check the difference in screen coordinates, to check which is the // correction scale needed - // save the matrices for later scale correction factor + // save the matrices for later scale correction factor // The up vector must be relative to the rotation current rotation matrix: // we set the identity glm::vec3 testPoint0 = textPosition; glm::vec3 testPoint1 = textPosition + (Application::getInstance()->getCamera()->getRotation() * IDENTITY_UP); - + double textWindowHeight; - + GLint viewportMatrix[4]; glGetIntegerv(GL_VIEWPORT, viewportMatrix); glm::dmat4 modelViewMatrix; float windowSizeX = viewportMatrix[2] - viewportMatrix[0]; float windowSizeY = viewportMatrix[3] - viewportMatrix[1]; - + glm::dmat4 projectionMatrix; Application::getInstance()->getModelViewMatrix(&modelViewMatrix); Application::getInstance()->getProjectionMatrix(&projectionMatrix); - + glm::dvec4 p0 = modelViewMatrix * glm::dvec4(testPoint0, 1.0); p0 = projectionMatrix * p0; @@ -655,23 +655,25 @@ float Avatar::calculateDisplayNameScaleFactor(const glm::vec3& textPosition, boo void Avatar::renderDisplayName() { - if (_displayName.isEmpty() || _displayNameAlpha == 0.0f) { + bool shouldShowReceiveStats = DependencyManager::get()->shouldShowReceiveStats(); + + if ((_displayName.isEmpty() && !shouldShowReceiveStats) || _displayNameAlpha == 0.0f) { return; } - + // which viewing mode? bool inHMD = Application::getInstance()->isHMDMode(); - + glDisable(GL_LIGHTING); - + glPushMatrix(); glm::vec3 textPosition = getDisplayNamePosition(); - - glTranslatef(textPosition.x, textPosition.y, textPosition.z); + + glTranslatef(textPosition.x, textPosition.y, textPosition.z); // we need "always facing camera": we must remove the camera rotation from the stack - + glm::vec3 frontAxis(0.0f, 0.0f, 1.0f); if (inHMD) { glm::vec3 camPosition = Application::getInstance()->getCamera()->getPosition(); @@ -680,22 +682,48 @@ void Avatar::renderDisplayName() { glm::quat rotation = Application::getInstance()->getCamera()->getRotation(); frontAxis = glm::rotate(rotation, frontAxis); } - + frontAxis = glm::normalize(glm::vec3(frontAxis.z, 0.0f, -frontAxis.x)); float angle = acos(frontAxis.x) * ((frontAxis.z < 0) ? 1.0f : -1.0f); glRotatef(glm::degrees(angle), 0.0f, 1.0f, 0.0f); - + float scaleFactor = calculateDisplayNameScaleFactor(textPosition, inHMD); glScalef(scaleFactor, -scaleFactor, scaleFactor); // TextRenderer::draw paints the text upside down in y axis - int text_x = -_displayNameBoundingRect.width() / 2; - int text_y = -_displayNameBoundingRect.height() / 2; + // optionally render timing stats for this avatar with the display name + QString renderedDisplayName = _displayName; + QRect nameDynamicRect = _displayNameBoundingRect; + + if (shouldShowReceiveStats) { + float kilobitsPerSecond = getAverageBytesReceivedPerSecond() / (float) BYTES_PER_KILOBIT; + + QString statsFormat = QString("(%1 Kbps, %2 Hz)"); + + if (!renderedDisplayName.isEmpty()) { + statsFormat.prepend(" - "); + } + + QString statsText = statsFormat.arg(QString::number(kilobitsPerSecond, 'f', 2)).arg(getReceiveRate()); + glm::vec2 extent = textRenderer(DISPLAYNAME)->computeExtent(statsText); + + // add the extent required for the stats to whatever was calculated for the display name + nameDynamicRect.setWidth(nameDynamicRect.width() + extent.x); + + if (extent.y > nameDynamicRect.height()) { + nameDynamicRect.setHeight(extent.y); + } + + renderedDisplayName += statsText; + } + + int text_x = -nameDynamicRect.width() / 2; + int text_y = -nameDynamicRect.height() / 2; // draw a gray background - int left = text_x + _displayNameBoundingRect.x(); - int right = left + _displayNameBoundingRect.width(); - int bottom = text_y + _displayNameBoundingRect.y(); - int top = bottom + _displayNameBoundingRect.height(); + int left = text_x + nameDynamicRect.x(); + int right = left + nameDynamicRect.width(); + int bottom = text_y + nameDynamicRect.y(); + int top = bottom + nameDynamicRect.height(); const int border = 8; bottom -= border; left -= border; @@ -708,22 +736,11 @@ void Avatar::renderDisplayName() { DependencyManager::get()->renderBevelCornersRect(left, bottom, right - left, top - bottom, 3, glm::vec4(0.2f, 0.2f, 0.2f, _displayNameAlpha * DISPLAYNAME_BACKGROUND_ALPHA / DISPLAYNAME_ALPHA)); - + glm::vec4 color(0.93f, 0.93f, 0.93f, _displayNameAlpha); - - // optionally render timing stats for this avatar with the display name - QString renderedDisplayName = _displayName; - - if (DependencyManager::get()->shouldShowReceiveStats()) { - float kilobitsPerSecond = getAverageBytesReceivedPerSecond() / (float) BYTES_PER_KILOBIT; - - renderedDisplayName += QString(" - (%1 Kbps, %2 Hz)") - .arg(QString::number(kilobitsPerSecond, 'f', 2)) - .arg(getReceiveRate()); - } - + QByteArray nameUTF8 = renderedDisplayName.toLocal8Bit(); - + glDisable(GL_POLYGON_OFFSET_FILL); textRenderer(DISPLAYNAME)->draw(text_x, text_y, nameUTF8.data(), color); @@ -769,11 +786,11 @@ void Avatar::setSkeletonOffset(const glm::vec3& offset) { } } -glm::vec3 Avatar::getSkeletonPosition() const { - // The avatar is rotated PI about the yAxis, so we have to correct for it +glm::vec3 Avatar::getSkeletonPosition() const { + // The avatar is rotated PI about the yAxis, so we have to correct for it // to get the skeleton offset contribution in the world-frame. const glm::quat FLIP = glm::angleAxis(PI, glm::vec3(0.0f, 1.0f, 0.0f)); - return _position + getOrientation() * FLIP * _skeletonOffset; + return _position + getOrientation() * FLIP * _skeletonOffset; } QVector Avatar::getJointRotations() const { @@ -868,7 +885,7 @@ const float SCRIPT_PRIORITY = DEFAULT_PRIORITY + 1.0f; void Avatar::setJointModelPositionAndOrientation(int index, glm::vec3 position, const glm::quat& rotation) { if (QThread::currentThread() != thread()) { - QMetaObject::invokeMethod(const_cast(this), "setJointModelPositionAndOrientation", + QMetaObject::invokeMethod(const_cast(this), "setJointModelPositionAndOrientation", Qt::AutoConnection, Q_ARG(const int, index), Q_ARG(const glm::vec3, position), Q_ARG(const glm::quat&, rotation)); } else { @@ -878,7 +895,7 @@ void Avatar::setJointModelPositionAndOrientation(int index, glm::vec3 position, void Avatar::setJointModelPositionAndOrientation(const QString& name, glm::vec3 position, const glm::quat& rotation) { if (QThread::currentThread() != thread()) { - QMetaObject::invokeMethod(const_cast(this), "setJointModelPositionAndOrientation", + QMetaObject::invokeMethod(const_cast(this), "setJointModelPositionAndOrientation", Qt::AutoConnection, Q_ARG(const QString&, name), Q_ARG(const glm::vec3, position), Q_ARG(const glm::quat&, rotation)); } else { @@ -919,7 +936,7 @@ void Avatar::setAttachmentData(const QVector& attachmentData) { while (_attachmentModels.size() > attachmentData.size()) { delete _attachmentModels.takeLast(); } - + // update the urls for (int i = 0; i < attachmentData.size(); i++) { _attachmentModels[i]->setURL(attachmentData.at(i).modelURL); @@ -932,12 +949,12 @@ void Avatar::setDisplayName(const QString& displayName) { AvatarData::setDisplayName(displayName); // FIXME is this a sufficient replacement for tightBoundingRect? glm::vec2 extent = textRenderer(DISPLAYNAME)->computeExtent(displayName); - _displayNameBoundingRect = QRect(QPoint(0, 0), QPoint((int)extent.x, (int)extent.y)); + _displayNameBoundingRect = QRect(0, 0, (int)extent.x, (int)extent.y); } void Avatar::setBillboard(const QByteArray& billboard) { AvatarData::setBillboard(billboard); - + // clear out any existing billboard texture _billboardTexture.reset(); } @@ -947,65 +964,65 @@ int Avatar::parseDataAtOffset(const QByteArray& packet, int offset) { // now that we have data for this Avatar we are go for init init(); } - + // change in position implies movement glm::vec3 oldPosition = _position; - + int bytesRead = AvatarData::parseDataAtOffset(packet, offset); - + const float MOVE_DISTANCE_THRESHOLD = 0.001f; _moving = glm::distance(oldPosition, _position) > MOVE_DISTANCE_THRESHOLD; - + return bytesRead; } int Avatar::_jointConesID = GeometryCache::UNKNOWN_ID; // render a makeshift cone section that serves as a body part connecting joint spheres -void Avatar::renderJointConnectingCone(glm::vec3 position1, glm::vec3 position2, +void Avatar::renderJointConnectingCone(glm::vec3 position1, glm::vec3 position2, float radius1, float radius2, const glm::vec4& color) { - + auto geometryCache = DependencyManager::get(); - + if (_jointConesID == GeometryCache::UNKNOWN_ID) { _jointConesID = geometryCache->allocateID(); } - + glm::vec3 axis = position2 - position1; float length = glm::length(axis); - + if (length > 0.0f) { - + axis /= length; - + glm::vec3 perpSin = glm::vec3(1.0f, 0.0f, 0.0f); glm::vec3 perpCos = glm::normalize(glm::cross(axis, perpSin)); perpSin = glm::cross(perpCos, axis); - + float anglea = 0.0f; float angleb = 0.0f; QVector points; - + for (int i = 0; i < NUM_BODY_CONE_SIDES; i ++) { - + // the rectangles that comprise the sides of the cone section are // referenced by "a" and "b" in one dimension, and "1", and "2" in the other dimension. anglea = angleb; angleb = ((float)(i+1) / (float)NUM_BODY_CONE_SIDES) * TWO_PI; - + float sa = sinf(anglea); float sb = sinf(angleb); float ca = cosf(anglea); float cb = cosf(angleb); - + glm::vec3 p1a = position1 + perpSin * sa * radius1 + perpCos * ca * radius1; - glm::vec3 p1b = position1 + perpSin * sb * radius1 + perpCos * cb * radius1; - glm::vec3 p2a = position2 + perpSin * sa * radius2 + perpCos * ca * radius2; - glm::vec3 p2b = position2 + perpSin * sb * radius2 + perpCos * cb * radius2; - + glm::vec3 p1b = position1 + perpSin * sb * radius1 + perpCos * cb * radius1; + glm::vec3 p2a = position2 + perpSin * sa * radius2 + perpCos * ca * radius2; + glm::vec3 p2b = position2 + perpSin * sb * radius2 + perpCos * cb * radius2; + points << p1a << p1b << p2a << p1b << p2a << p2b; } - + // TODO: this is really inefficient constantly recreating these vertices buffers. It would be // better if the avatars cached these buffers for each of the joints they are rendering geometryCache->updateVertices(_jointConesID, points, color); @@ -1052,7 +1069,7 @@ void Avatar::setShowDisplayName(bool showDisplayName) { _displayNameAlpha = 0.0f; return; } - + // For myAvatar, the alpha update is not done (called in simulate for other avatars) if (DependencyManager::get()->getMyAvatar() == this) { if (showDisplayName) { @@ -1060,7 +1077,7 @@ void Avatar::setShowDisplayName(bool showDisplayName) { } else { _displayNameAlpha = 0.0f; } - } + } if (showDisplayName) { _displayNameTargetAlpha = DISPLAYNAME_ALPHA;