merge upstream/master into andrew/bispinor

This commit is contained in:
Andrew Meadows 2015-03-20 08:43:36 -07:00
commit e7423caf2b
14 changed files with 234 additions and 83 deletions

View file

@ -21,6 +21,9 @@ if (POLICY CMP0042)
cmake_policy(SET CMP0042 OLD)
endif ()
set_property(GLOBAL PROPERTY USE_FOLDERS ON)
set_property(GLOBAL PROPERTY PREDEFINED_TARGETS_FOLDER "CMakeTargets")
project(hifi)
add_definitions(-DGLM_FORCE_RADIANS)
@ -173,9 +176,13 @@ endif ()
# add subdirectories for all targets
if (NOT ANDROID)
add_subdirectory(assignment-client)
set_target_properties(assignment-client PROPERTIES FOLDER "Apps")
add_subdirectory(domain-server)
set_target_properties(domain-server PROPERTIES FOLDER "Apps")
add_subdirectory(ice-server)
set_target_properties(ice-server PROPERTIES FOLDER "Apps")
add_subdirectory(interface)
set_target_properties(interface PROPERTIES FOLDER "Apps")
add_subdirectory(tests)
add_subdirectory(tools)
endif ()

View file

@ -20,9 +20,9 @@
#include <PacketHeaders.h>
#include <SharedUtil.h>
#include <UUID.h>
#include <TryLocker.h>
#include "AvatarMixerClientData.h"
#include "AvatarMixer.h"
const QString AVATAR_MIXER_LOGGING_NAME = "avatar-mixer";
@ -119,12 +119,25 @@ void AvatarMixer::broadcastAvatarData() {
auto nodeList = DependencyManager::get<NodeList>();
AvatarMixerClientData* nodeData = NULL;
AvatarMixerClientData* otherNodeData = NULL;
nodeList->eachNode([&](const SharedNodePointer& node) {
if (node->getLinkedData() && node->getType() == NodeType::Agent && node->getActiveSocket()
&& (nodeData = reinterpret_cast<AvatarMixerClientData*>(node->getLinkedData()))->getMutex().tryLock()) {
nodeList->eachMatchingNode(
[&](const SharedNodePointer& node)->bool {
if (!node->getLinkedData()) {
return false;
}
if (node->getType() != NodeType::Agent) {
return false;
}
if (!node->getActiveSocket()) {
return false;
}
return true;
},
[&](const SharedNodePointer& node) {
AvatarMixerClientData* nodeData = reinterpret_cast<AvatarMixerClientData*>(node->getLinkedData());
MutexTryLocker lock(nodeData->getMutex());
if (!lock.isLocked()) {
return;
}
++_sumListeners;
// reset packet pointers for this node
@ -132,83 +145,97 @@ void AvatarMixer::broadcastAvatarData() {
AvatarData& avatar = nodeData->getAvatar();
glm::vec3 myPosition = avatar.getPosition();
// TODO use this along with the distance in the calculation of whether to send an update
// about a given otherNode to this node
// FIXME does this mean we should sort the othernodes by distance before iterating
// over them?
float outputBandwidth = node->getOutboundBandwidth();
// this is an AGENT we have received head data from
// send back a packet with other active node data to this node
nodeList->eachNode([&](const SharedNodePointer& otherNode) {
if (otherNode->getLinkedData() && otherNode->getUUID() != node->getUUID()
&& (otherNodeData = reinterpret_cast<AvatarMixerClientData*>(otherNode->getLinkedData()))->getMutex().tryLock()) {
AvatarMixerClientData* otherNodeData = reinterpret_cast<AvatarMixerClientData*>(otherNode->getLinkedData());
nodeList->eachMatchingNode(
[&](const SharedNodePointer& otherNode)->bool {
if (!otherNode->getLinkedData()) {
return false;
}
if (otherNode->getUUID() == node->getUUID()) {
return false;
}
// Check throttling value
if (!(_performanceThrottlingRatio == 0 || randFloat() < (1.0f - _performanceThrottlingRatio))) {
return false;
}
return true;
},
[&](const SharedNodePointer& otherNode) {
AvatarMixerClientData* otherNodeData = otherNodeData = reinterpret_cast<AvatarMixerClientData*>(otherNode->getLinkedData());
MutexTryLocker lock(otherNodeData->getMutex());
if (!lock.isLocked()) {
return;
}
AvatarData& otherAvatar = otherNodeData->getAvatar();
glm::vec3 otherPosition = otherAvatar.getPosition();
float distanceToAvatar = glm::length(myPosition - otherPosition);
// Decide whether to send this avatar's data based on it's distance from us
// The full rate distance is the distance at which EVERY update will be sent for this avatar
// at a distance of twice the full rate distance, there will be a 50% chance of sending this avatar's update
const float FULL_RATE_DISTANCE = 2.0f;
// Decide whether to send this avatar's data based on it's distance from us
if ((_performanceThrottlingRatio == 0 || randFloat() < (1.0f - _performanceThrottlingRatio))
&& (distanceToAvatar == 0.0f || randFloat() < FULL_RATE_DISTANCE / distanceToAvatar)) {
QByteArray avatarByteArray;
avatarByteArray.append(otherNode->getUUID().toRfc4122());
avatarByteArray.append(otherAvatar.toByteArray());
if (avatarByteArray.size() + mixedAvatarByteArray.size() > MAX_PACKET_SIZE) {
nodeList->writeDatagram(mixedAvatarByteArray, node);
// reset the packet
mixedAvatarByteArray.resize(numPacketHeaderBytes);
}
// copy the avatar into the mixedAvatarByteArray packet
mixedAvatarByteArray.append(avatarByteArray);
// if the receiving avatar has just connected make sure we send out the mesh and billboard
// for this avatar (assuming they exist)
bool forceSend = !nodeData->checkAndSetHasReceivedFirstPackets();
// we will also force a send of billboard or identity packet
// if either has changed in the last frame
if (otherNodeData->getBillboardChangeTimestamp() > 0
&& (forceSend
|| otherNodeData->getBillboardChangeTimestamp() > _lastFrameTimestamp
|| randFloat() < BILLBOARD_AND_IDENTITY_SEND_PROBABILITY)) {
QByteArray billboardPacket = byteArrayWithPopulatedHeader(PacketTypeAvatarBillboard);
billboardPacket.append(otherNode->getUUID().toRfc4122());
billboardPacket.append(otherNodeData->getAvatar().getBillboard());
nodeList->writeDatagram(billboardPacket, node);
++_sumBillboardPackets;
}
if (otherNodeData->getIdentityChangeTimestamp() > 0
&& (forceSend
|| otherNodeData->getIdentityChangeTimestamp() > _lastFrameTimestamp
|| randFloat() < BILLBOARD_AND_IDENTITY_SEND_PROBABILITY)) {
QByteArray identityPacket = byteArrayWithPopulatedHeader(PacketTypeAvatarIdentity);
QByteArray individualData = otherNodeData->getAvatar().identityByteArray();
individualData.replace(0, NUM_BYTES_RFC4122_UUID, otherNode->getUUID().toRfc4122());
identityPacket.append(individualData);
nodeList->writeDatagram(identityPacket, node);
++_sumIdentityPackets;
}
glm::vec3 otherPosition = otherAvatar.getPosition();
float distanceToAvatar = glm::length(myPosition - otherPosition);
if (!(distanceToAvatar == 0.0f || randFloat() < FULL_RATE_DISTANCE / distanceToAvatar)) {
return;
}
QByteArray avatarByteArray;
avatarByteArray.append(otherNode->getUUID().toRfc4122());
avatarByteArray.append(otherAvatar.toByteArray());
if (avatarByteArray.size() + mixedAvatarByteArray.size() > MAX_PACKET_SIZE) {
nodeList->writeDatagram(mixedAvatarByteArray, node);
// reset the packet
mixedAvatarByteArray.resize(numPacketHeaderBytes);
}
// copy the avatar into the mixedAvatarByteArray packet
mixedAvatarByteArray.append(avatarByteArray);
// if the receiving avatar has just connected make sure we send out the mesh and billboard
// for this avatar (assuming they exist)
bool forceSend = !nodeData->checkAndSetHasReceivedFirstPackets();
// we will also force a send of billboard or identity packet
// if either has changed in the last frame
if (otherNodeData->getBillboardChangeTimestamp() > 0
&& (forceSend
|| otherNodeData->getBillboardChangeTimestamp() > _lastFrameTimestamp
|| randFloat() < BILLBOARD_AND_IDENTITY_SEND_PROBABILITY)) {
QByteArray billboardPacket = byteArrayWithPopulatedHeader(PacketTypeAvatarBillboard);
billboardPacket.append(otherNode->getUUID().toRfc4122());
billboardPacket.append(otherNodeData->getAvatar().getBillboard());
nodeList->writeDatagram(billboardPacket, node);
++_sumBillboardPackets;
}
if (otherNodeData->getIdentityChangeTimestamp() > 0
&& (forceSend
|| otherNodeData->getIdentityChangeTimestamp() > _lastFrameTimestamp
|| randFloat() < BILLBOARD_AND_IDENTITY_SEND_PROBABILITY)) {
QByteArray identityPacket = byteArrayWithPopulatedHeader(PacketTypeAvatarIdentity);
QByteArray individualData = otherNodeData->getAvatar().identityByteArray();
individualData.replace(0, NUM_BYTES_RFC4122_UUID, otherNode->getUUID().toRfc4122());
identityPacket.append(individualData);
nodeList->writeDatagram(identityPacket, node);
++_sumIdentityPackets;
}
otherNodeData->getMutex().unlock();
}
});
nodeList->writeDatagram(mixedAvatarByteArray, node);
nodeData->getMutex().unlock();
}
});
_lastFrameTimestamp = QDateTime::currentMSecsSinceEpoch();

View file

@ -16,6 +16,7 @@ macro(LINK_HIFI_LIBRARIES)
foreach(HIFI_LIBRARY ${LIBRARIES_TO_LINK})
if (NOT TARGET ${HIFI_LIBRARY})
add_subdirectory("${RELATIVE_LIBRARY_DIR_PATH}/${HIFI_LIBRARY}" "${RELATIVE_LIBRARY_DIR_PATH}/${HIFI_LIBRARY}")
set_target_properties(${HIFI_LIBRARY} PROPERTIES FOLDER "Libraries")
endif ()
include_directories("${HIFI_LIBRARY_DIR}/${HIFI_LIBRARY}/src")

View file

@ -1028,7 +1028,28 @@ void MyAvatar::renderBody(ViewFrustum* renderFrustum, RenderMode renderMode, boo
if (!(_skeletonModel.isRenderable() && getHead()->getFaceModel().isRenderable())) {
return; // wait until both models are loaded
}
Camera *camera = Application::getInstance()->getCamera();
const glm::vec3 cameraPos = camera->getPosition();
// Set near clip distance according to skeleton model dimensions if first person and there is no separate head model.
if (shouldRenderHead(cameraPos, renderMode) || !getHead()->getFaceModel().getURL().isEmpty()) {
renderFrustum->setNearClip(DEFAULT_NEAR_CLIP);
} else {
float clipDistance = _skeletonModel.getHeadClipDistance();
if (OculusManager::isConnected()) {
// If avatar is horizontally in front of camera, increase clip distance by the amount it is in front.
glm::vec3 cameraToAvatar = _position - cameraPos;
cameraToAvatar.y = 0.0f;
glm::vec3 cameraLookAt = camera->getOrientation() * glm::vec3(0.0f, 0.0f, -1.0f);
float headOffset = glm::dot(cameraLookAt, cameraToAvatar);
if (headOffset > 0) {
clipDistance += headOffset;
}
}
renderFrustum->setNearClip(clipDistance);
}
// Render the body's voxels and head
Model::RenderMode modelRenderMode = (renderMode == SHADOW_RENDER_MODE) ?
Model::SHADOW_RENDER_MODE : Model::DEFAULT_RENDER_MODE;
@ -1040,8 +1061,6 @@ void MyAvatar::renderBody(ViewFrustum* renderFrustum, RenderMode renderMode, boo
}
// Render head so long as the camera isn't inside it
const Camera *camera = Application::getInstance()->getCamera();
const glm::vec3 cameraPos = camera->getPosition();
if (shouldRenderHead(cameraPos, renderMode)) {
getHead()->render(1.0f, renderFrustum, modelRenderMode, postLighting);
}
@ -1357,6 +1376,9 @@ void MyAvatar::updatePositionWithPhysics(float deltaTime) {
// rotate back into world-frame
_velocity = rotation * newLocalVelocity;
_velocity += _thrust * deltaTime;
_thrust = glm::vec3(0.0f);
}
void MyAvatar::updateCollisionSound(const glm::vec3 &penetration, float deltaTime, float frequency) {

View file

@ -37,7 +37,8 @@ SkeletonModel::SkeletonModel(Avatar* owningAvatar, QObject* parent) :
_defaultEyeModelPosition(glm::vec3(0.0f, 0.0f, 0.0f)),
_standingFoot(NO_FOOT),
_standingOffset(0.0f),
_clampedFootPosition(0.0f)
_clampedFootPosition(0.0f),
_headClipDistance(DEFAULT_NEAR_CLIP)
{
}
@ -78,6 +79,10 @@ void SkeletonModel::setJointStates(QVector<JointState> states) {
buildShapes();
}
Extents meshExtents = getMeshExtents();
_headClipDistance = -(meshExtents.minimum.z / _scale.z - _defaultEyeModelPosition.z);
_headClipDistance = std::max(_headClipDistance, DEFAULT_NEAR_CLIP);
emit skeletonLoaded();
}

View file

@ -110,6 +110,8 @@ public:
bool hasSkeleton();
float getHeadClipDistance() const { return _headClipDistance; }
signals:
void skeletonLoaded();
@ -160,6 +162,8 @@ private:
int _standingFoot;
glm::vec3 _standingOffset;
glm::vec3 _clampedFootPosition;
float _headClipDistance; // Near clip distance to use if no separate head model
};
#endif // hifi_SkeletonModel_h

View file

@ -17,7 +17,6 @@
#include <QObject>
#include <QElapsedTimer>
#include "DependencyManager.h"
#include "Node.h"
#include "SimpleMovingAverage.h"

View file

@ -263,8 +263,10 @@ qint64 LimitedNodeList::writeDatagram(const QByteArray& datagram,
}
emit dataSent(destinationNode->getType(), datagram.size());
return writeDatagram(datagram, *destinationSockAddr, destinationNode->getConnectionSecret());
auto bytesWritten = writeDatagram(datagram, *destinationSockAddr, destinationNode->getConnectionSecret());
// Keep track of per-destination-node bandwidth
destinationNode->recordBytesSent(bytesWritten);
return bytesWritten;
}
// didn't have a destinationNode to send to, return 0

View file

@ -151,7 +151,18 @@ public:
functor(it->second);
}
}
template<typename PredLambda, typename NodeLambda>
void eachMatchingNode(PredLambda predicate, NodeLambda functor) {
QReadLocker readLock(&_nodeMutex);
for (NodeHash::const_iterator it = _nodeHash.cbegin(); it != _nodeHash.cend(); ++it) {
if (predicate(it->second)) {
functor(it->second);
}
}
}
template<typename BreakableNodeLambda>
void eachNodeBreakable(BreakableNodeLambda functor) {
QReadLocker readLock(&_nodeMutex);

View file

@ -15,6 +15,7 @@
#include <UUID.h>
#include "NetworkPeer.h"
#include "BandwidthRecorder.h"
NetworkPeer::NetworkPeer() :
_uuid(),
@ -96,4 +97,37 @@ QDebug operator<<(QDebug debug, const NetworkPeer &peer) {
<< "- public:" << peer.getPublicSocket()
<< "- local:" << peer.getLocalSocket();
return debug;
}
}
// FIXME this is a temporary implementation to determine if this is the right approach.
// If so, migrate the BandwidthRecorder into the NetworkPeer class
using BandwidthRecorderPtr = QSharedPointer<BandwidthRecorder>;
static QHash<QUuid, BandwidthRecorderPtr> PEER_BANDWIDTH;
BandwidthRecorder& getBandwidthRecorder(const QUuid & uuid) {
if (!PEER_BANDWIDTH.count(uuid)) {
PEER_BANDWIDTH.insert(uuid, BandwidthRecorderPtr(new BandwidthRecorder()));
}
return *PEER_BANDWIDTH[uuid].data();
}
void NetworkPeer::recordBytesSent(int count) {
auto& bw = getBandwidthRecorder(_uuid);
bw.updateOutboundData(0, count);
}
void NetworkPeer::recordBytesReceived(int count) {
auto& bw = getBandwidthRecorder(_uuid);
bw.updateInboundData(0, count);
}
float NetworkPeer::getOutboundBandwidth() {
auto& bw = getBandwidthRecorder(_uuid);
return bw.getAverageOutputKilobitsPerSecond(0);
}
float NetworkPeer::getInboundBandwidth() {
auto& bw = getBandwidthRecorder(_uuid);
return bw.getAverageInputKilobitsPerSecond(0);
}

View file

@ -54,6 +54,12 @@ public:
int getConnectionAttempts() const { return _connectionAttempts; }
void incrementConnectionAttempts() { ++_connectionAttempts; }
void resetConnectionAttemps() { _connectionAttempts = 0; }
void recordBytesSent(int count);
void recordBytesReceived(int count);
float getOutboundBandwidth();
float getInboundBandwidth();
friend QDataStream& operator<<(QDataStream& out, const NetworkPeer& peer);
friend QDataStream& operator>>(QDataStream& in, NetworkPeer& peer);

View file

@ -0,0 +1,28 @@
//
// TryLocker.h
// libraries/shared/src
//
// Created by Brad Davis on 2015/03/16.
// Copyright 2015 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#ifndef hifi_TryLocker_h
#define hifi_TryLocker_h
#include <QMutex>
class MutexTryLocker {
QMutex& _mutex;
bool _locked{ false };
public:
MutexTryLocker(QMutex &m) : _mutex(m), _locked(m.tryLock()) {}
~MutexTryLocker() { if (_locked) _mutex.unlock(); }
bool isLocked() {
return _locked;
}
};
#endif // hifi_TryLocker_h

View file

@ -4,5 +4,6 @@ list(REMOVE_ITEM TEST_SUBDIRS "CMakeFiles")
foreach(DIR ${TEST_SUBDIRS})
if(IS_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/${DIR}")
add_subdirectory(${DIR})
set_target_properties("${DIR}-tests" PROPERTIES FOLDER "Tests")
endif()
endforeach()

View file

@ -1,8 +1,12 @@
# add the tool directories
add_subdirectory(mtc)
set_target_properties(mtc PROPERTIES FOLDER "Tools")
add_subdirectory(scribe)
set_target_properties(scribe PROPERTIES FOLDER "Tools")
find_package(VHACD)
if(VHACD_FOUND)
add_subdirectory(vhacd)
set_target_properties(vhacd PROPERTIES FOLDER "Tools")
endif()