Merge branch 'master' into oculus_old_renderer

This commit is contained in:
Brad Davis 2015-03-19 10:05:22 -07:00
commit 31c7c93aa3
15 changed files with 240 additions and 102 deletions

View file

@ -74,6 +74,14 @@
}
]
},
{
"name": "maximum_user_capacity",
"label": "Maximum User Capacity",
"help": "The limit on how many avatars can be connected at once. 0 means no limit.",
"placeholder": "0",
"default": "0",
"advanced": false
},
{
"name": "allowed_editors",
"type": "table",

View file

@ -44,6 +44,7 @@ const QString ICE_SERVER_DEFAULT_HOSTNAME = "ice.highfidelity.io";
const QString ALLOWED_USERS_SETTINGS_KEYPATH = "security.allowed_users";
const QString MAXIMUM_USER_CAPACITY = "security.maximum_user_capacity";
const QString ALLOWED_EDITORS_SETTINGS_KEYPATH = "security.allowed_editors";
@ -667,9 +668,22 @@ void DomainServer::handleConnectRequest(const QByteArray& packet, const HifiSock
}
unsigned int DomainServer::countConnectedUsers() {
unsigned int result = 0;
auto nodeList = DependencyManager::get<LimitedNodeList>();
nodeList->eachNode([&](const SharedNodePointer& otherNode){
if (otherNode->getType() == NodeType::Agent) {
result++;
}
});
return result;
}
bool DomainServer::shouldAllowConnectionFromNode(const QString& username,
const QByteArray& usernameSignature,
const HifiSockAddr& senderSockAddr) {
const QVariant* allowedUsersVariant = valueForKeyPath(_settingsManager.getSettingsMap(),
ALLOWED_USERS_SETTINGS_KEYPATH);
QStringList allowedUsers = allowedUsersVariant ? allowedUsersVariant->toStringList() : QStringList();
@ -679,6 +693,18 @@ bool DomainServer::shouldAllowConnectionFromNode(const QString& username,
|| senderSockAddr.getAddress() == QHostAddress::LocalHost) {
return true;
}
const QVariant* maximumUserCapacityVariant = valueForKeyPath(_settingsManager.getSettingsMap(), MAXIMUM_USER_CAPACITY);
unsigned int maximumUserCapacity = maximumUserCapacityVariant ? maximumUserCapacityVariant->toUInt() : 0;
if (maximumUserCapacity > 0) {
unsigned int connectedUsers = countConnectedUsers();
if (connectedUsers >= maximumUserCapacity) {
// too many users, deny the new connection.
qDebug() << connectedUsers << "/" << maximumUserCapacity << "users connected, denying new connection.";
return false;
}
qDebug() << connectedUsers << "/" << maximumUserCapacity << "users connected, perhaps allowing new connection.";
}
if (allowedUsers.count() > 0) {
if (allowedUsers.contains(username, Qt::CaseInsensitive)) {

View file

@ -83,6 +83,7 @@ private:
void processDatagram(const QByteArray& receivedPacket, const HifiSockAddr& senderSockAddr);
void handleConnectRequest(const QByteArray& packet, const HifiSockAddr& senderSockAddr);
unsigned int countConnectedUsers();
bool shouldAllowConnectionFromNode(const QString& username, const QByteArray& usernameSignature,
const HifiSockAddr& senderSockAddr);

View file

@ -18,3 +18,4 @@ Script.load("lobby.js");
Script.load("notifications.js");
Script.load("look.js");
Script.load("users.js");
Script.load("utilities/LODWarning.js");

View file

@ -0,0 +1,16 @@
LODManager.LODIncreased.connect(function() {
print("LOD has been increased. You can now see "
+ LODManager.getLODFeedbackText()
+ ", fps:" + LODManager.getFPSAverage()
+ ", fast fps:" + LODManager.getFastFPSAverage()
);
});
LODManager.LODDecreased.connect(function() {
print("LOD has been decreased. You can now see "
+ LODManager.getLODFeedbackText()
+ ", fps:" + LODManager.getFPSAverage()
+ ", fast fps:" + LODManager.getFastFPSAverage()
);
});

View file

@ -13,4 +13,5 @@ Script.load("progress.js");
Script.load("lobby.js");
Script.load("notifications.js");
Script.load("controllers/oculus/goTo.js");
Script.load("utilities/LODWarning.js");
//Script.load("scripts.js"); // Not created yet

View file

@ -0,0 +1,115 @@
// LODWarning.js
// examples
//
// Created by Brad Hefta-Gaub on 3/17/15.
// Copyright 2015 High Fidelity, Inc.
//
// This script will display a warning when the LOD is adjusted to do scene complexity.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
var DISPLAY_WARNING_FOR = 3; // in seconds
var DISTANCE_FROM_CAMERA = 2;
var SHOW_LOD_UP_MESSAGE = false; // By default we only display the LOD message when reducing LOD
var warningIsVisible = false; // initially the warning is hidden
var warningShownAt = 0;
var billboardPosition = Vec3.sum(Camera.getPosition(),
Vec3.multiply(DISTANCE_FROM_CAMERA, Quat.getFront(Camera.getOrientation())));
var warningOverlay = Overlays.addOverlay("text3d", {
position: billboardPosition,
dimensions: { x: 2, y: 1.25 },
width: 2,
height: 1.25,
backgroundColor: { red: 0, green: 0, blue: 0 },
color: { red: 255, green: 255, blue: 255},
topMargin: 0.1,
leftMargin: 0.1,
lineHeight: 0.07,
text: "",
alpha: 0.5,
backgroundAlpha: 0.7,
isFacingAvatar: true,
visible: warningIsVisible,
});
// Handle moving the billboard to remain in front of the camera
var billboardNeedsMoving = false;
Script.update.connect(function() {
if (warningIsVisible) {
var bestBillboardPosition = Vec3.sum(Camera.getPosition(),
Vec3.multiply(DISTANCE_FROM_CAMERA, Quat.getFront(Camera.getOrientation())));
var MAX_DISTANCE = 0.5;
var CLOSE_ENOUGH = 0.01;
if (!billboardNeedsMoving && Vec3.distance(bestBillboardPosition, billboardPosition) > MAX_DISTANCE) {
billboardNeedsMoving = true;
}
if (billboardNeedsMoving && Vec3.distance(bestBillboardPosition, billboardPosition) <= CLOSE_ENOUGH) {
billboardNeedsMoving = false;
}
if (billboardNeedsMoving) {
// slurp the billboard to the best location
moveVector = Vec3.multiply(0.05, Vec3.subtract(bestBillboardPosition, billboardPosition));
billboardPosition = Vec3.sum(billboardPosition, moveVector);
Overlays.editOverlay(warningOverlay, { position: billboardPosition });
}
var now = new Date();
var sinceWarningShown = now - warningShownAt;
if (sinceWarningShown > 1000 * DISPLAY_WARNING_FOR) {
warningIsVisible = false;
Overlays.editOverlay(warningOverlay, { visible: warningIsVisible });
}
}
});
LODManager.LODIncreased.connect(function() {
if (SHOW_LOD_UP_MESSAGE) {
// if the warning wasn't visible, then move it before showing it.
if (!warningIsVisible) {
billboardPosition = Vec3.sum(Camera.getPosition(),
Vec3.multiply(DISTANCE_FROM_CAMERA, Quat.getFront(Camera.getOrientation())));
Overlays.editOverlay(warningOverlay, { position: billboardPosition });
}
warningShownAt = new Date();
warningIsVisible = true;
warningText = "Level of detail has been increased. \n"
+ "You can now see: \n"
+ LODManager.getLODFeedbackText();
Overlays.editOverlay(warningOverlay, { visible: warningIsVisible, text: warningText });
}
});
LODManager.LODDecreased.connect(function() {
// if the warning wasn't visible, then move it before showing it.
if (!warningIsVisible) {
billboardPosition = Vec3.sum(Camera.getPosition(),
Vec3.multiply(DISTANCE_FROM_CAMERA, Quat.getFront(Camera.getOrientation())));
Overlays.editOverlay(warningOverlay, { position: billboardPosition });
}
warningShownAt = new Date();
warningIsVisible = true;
warningText = "\n"
+ "Due to the complexity of the content, the \n"
+ "level of detail has been decreased. \n"
+ "You can now see: \n"
+ LODManager.getLODFeedbackText();
Overlays.editOverlay(warningOverlay, { visible: warningIsVisible, text: warningText });
});
Script.scriptEnding.connect(function() {
Overlays.deleteOverlay(warningOverlay);
});

View file

@ -3561,6 +3561,8 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEngine* scri
scriptEngine->registerGlobalObject("UndoStack", &_undoStackScriptingInterface);
scriptEngine->registerGlobalObject("LODManager", DependencyManager::get<LODManager>().data());
QScriptValue hmdInterface = scriptEngine->registerGlobalObject("HMD", &HMDScriptingInterface::getInstance());
scriptEngine->registerFunction(hmdInterface, "getHUDLookAtPosition2D", HMDScriptingInterface::getHUDLookAtPosition2D, 0);
scriptEngine->registerFunction(hmdInterface, "getHUDLookAtPosition3D", HMDScriptingInterface::getHUDLookAtPosition3D, 0);

View file

@ -75,7 +75,9 @@ void LODManager::autoAdjustLOD(float currentFPS) {
changed = true;
_lastAdjust = now;
qDebug() << "adjusting LOD down... average fps for last approximately 5 seconds=" << _fpsAverage.getAverage()
<< "_octreeSizeScale=" << _octreeSizeScale;
<< "_octreeSizeScale=" << _octreeSizeScale;
emit LODDecreased();
}
if (elapsed > ADJUST_LOD_UP_DELAY && _fpsAverage.getAverage() > ADJUST_LOD_UP_FPS
@ -87,7 +89,9 @@ void LODManager::autoAdjustLOD(float currentFPS) {
changed = true;
_lastAdjust = now;
qDebug() << "adjusting LOD up... average fps for last approximately 5 seconds=" << _fpsAverage.getAverage()
<< "_octreeSizeScale=" << _octreeSizeScale;
<< "_octreeSizeScale=" << _octreeSizeScale;
emit LODIncreased();
}
if (changed) {

View file

@ -21,13 +21,16 @@ const float ADJUST_LOD_DOWN_FPS = 40.0;
const float ADJUST_LOD_UP_FPS = 55.0;
const float DEFAULT_ADJUST_AVATAR_LOD_DOWN_FPS = 30.0f;
const quint64 ADJUST_LOD_DOWN_DELAY = 1000 * 1000 * 5;
const quint64 ADJUST_LOD_DOWN_DELAY = 1000 * 1000 * 0.5; // Consider adjusting LOD down after half a second
const quint64 ADJUST_LOD_UP_DELAY = ADJUST_LOD_DOWN_DELAY * 2;
const float ADJUST_LOD_DOWN_BY = 0.9f;
const float ADJUST_LOD_UP_BY = 1.1f;
const float ADJUST_LOD_MIN_SIZE_SCALE = DEFAULT_OCTREE_SIZE_SCALE * 0.25f;
// This controls how low the auto-adjust LOD will go a value of 1 means it will adjust to a point where you must be 0.25
// meters away from an object of TREE_SCALE before you can see it (which is effectively completely blind). The default value
// DEFAULT_OCTREE_SIZE_SCALE means you can be 400 meters away from a 1 meter object in order to see it (which is ~20:20 vision).
const float ADJUST_LOD_MIN_SIZE_SCALE = 1.0f;
const float ADJUST_LOD_MAX_SIZE_SCALE = DEFAULT_OCTREE_SIZE_SCALE;
const float MINIMUM_AVATAR_LOD_DISTANCE_MULTIPLIER = 0.1f;
@ -38,7 +41,8 @@ const int ONE_SECOND_OF_FRAMES = 60;
const int FIVE_SECONDS_OF_FRAMES = 5 * ONE_SECOND_OF_FRAMES;
class LODManager : public Dependency {
class LODManager : public QObject, public Dependency {
Q_OBJECT
SINGLETON_DEPENDENCY
public:
@ -52,21 +56,27 @@ public:
float getAvatarLODDistanceMultiplier() const { return _avatarLODDistanceMultiplier; }
// User Tweakable LOD Items
QString getLODFeedbackText();
void setOctreeSizeScale(float sizeScale);
float getOctreeSizeScale() const { return _octreeSizeScale; }
Q_INVOKABLE QString getLODFeedbackText();
Q_INVOKABLE void setOctreeSizeScale(float sizeScale);
Q_INVOKABLE float getOctreeSizeScale() const { return _octreeSizeScale; }
void setBoundaryLevelAdjust(int boundaryLevelAdjust);
int getBoundaryLevelAdjust() const { return _boundaryLevelAdjust; }
Q_INVOKABLE void setBoundaryLevelAdjust(int boundaryLevelAdjust);
Q_INVOKABLE int getBoundaryLevelAdjust() const { return _boundaryLevelAdjust; }
void autoAdjustLOD(float currentFPS);
void resetLODAdjust();
Q_INVOKABLE void resetLODAdjust();
Q_INVOKABLE float getFPSAverage() const { return _fpsAverage.getAverage(); }
Q_INVOKABLE float getFastFPSAverage() const { return _fastFPSAverage.getAverage(); }
bool shouldRenderMesh(float largestDimension, float distanceToCamera);
void loadSettings();
void saveSettings();
signals:
void LODIncreased();
void LODDecreased();
private:
LODManager() {}

View file

@ -303,7 +303,7 @@ void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& info) {
_points << mesh.vertices;
}
info.setParams(getShapeType(), 0.5f * getDimensions(), NULL, _collisionModelURL);
info.setParams(getShapeType(), 0.5f * getDimensions(), _collisionModelURL);
info.setConvexHull(_points);
}
}

View file

@ -21,6 +21,8 @@ const int TREE_SCALE = 16384; // ~10 miles.. This is the number of meters of t
// This controls the LOD. Larger number will make smaller voxels visible at greater distance.
const float DEFAULT_OCTREE_SIZE_SCALE = TREE_SCALE * 400.0f;
// This is used in the LOD Tools to translate between the size scale slider and the values used to set the OctreeSizeScale
const float MAX_LOD_SIZE_MULTIPLIER = 2000.0f;
const int NUMBER_OF_CHILDREN = 8;

View file

@ -63,7 +63,6 @@ void PhysicsEngine::addEntityInternal(EntityItem* entity) {
assert(entity);
void* physicsInfo = entity->getPhysicsInfo();
if (!physicsInfo) {
qDebug() << "PhysicsEngine::addEntityInternal(" << entity;
if (entity->isReadyToComputeShape()) {
ShapeInfo shapeInfo;
entity->computeShapeInfo(shapeInfo);
@ -343,7 +342,6 @@ void PhysicsEngine::stepSimulation() {
}
unlock();
_avatarData->unlock();
_entityTree->unlock();
computeCollisionEvents();
@ -506,10 +504,8 @@ void PhysicsEngine::removeObjectFromBullet(ObjectMotionState* motionState) {
btRigidBody* body = motionState->getRigidBody();
if (body) {
const btCollisionShape* shape = body->getCollisionShape();
ShapeInfo shapeInfo;
ShapeInfoUtil::collectInfoFromShape(shape, shapeInfo);
_dynamicsWorld->removeRigidBody(body);
_shapeManager.releaseShape(shapeInfo);
_shapeManager.releaseShape(shape);
// NOTE: setRigidBody() modifies body->m_userPointer so we should clear the MotionState's body BEFORE deleting it.
motionState->setRigidBody(NULL);
delete body;

View file

@ -19,10 +19,9 @@ void ShapeInfo::clear() {
_type = SHAPE_TYPE_NONE;
_halfExtents = glm::vec3(0.0f);
_doubleHashKey.clear();
_externalData = NULL;
}
void ShapeInfo::setParams(ShapeType type, const glm::vec3& halfExtents, QVector<glm::vec3>* data, QString url) {
void ShapeInfo::setParams(ShapeType type, const glm::vec3& halfExtents, QString url) {
_type = type;
switch(type) {
case SHAPE_TYPE_NONE:
@ -45,7 +44,6 @@ void ShapeInfo::setParams(ShapeType type, const glm::vec3& halfExtents, QVector<
_halfExtents = halfExtents;
break;
}
_externalData = data;
}
void ShapeInfo::setBox(const glm::vec3& halfExtents) {
@ -109,91 +107,53 @@ float ShapeInfo::computeVolume() const {
}
const DoubleHashKey& ShapeInfo::getHash() const {
// NOTE: we cache the hash so we only ever need to compute it once for any valid ShapeInfo instance.
// NOTE: we cache the key so we only ever need to compute it once for any valid ShapeInfo instance.
if (_doubleHashKey.isNull() && _type != SHAPE_TYPE_NONE) {
// cast this to non-const pointer so we can do our dirty work
// The key is not yet cached therefore we must compute it! To this end we bypass the const-ness
// of this method by grabbing a non-const pointer to "this" and a non-const reference to _doubleHashKey.
ShapeInfo* thisPtr = const_cast<ShapeInfo*>(this);
DoubleHashKey& key = thisPtr->_doubleHashKey;
// compute hash1
// TODO?: provide lookup table for hash/hash2 of _type rather than recompute?
uint32_t primeIndex = 0;
thisPtr->_doubleHashKey.computeHash((uint32_t)_type, primeIndex++);
key.computeHash((uint32_t)_type, primeIndex++);
const QVector<glm::vec3>* data = getData();
if (data) {
// if externalData exists we use it to continue the hash
// compute hash
uint32_t hash = _doubleHashKey.getHash();
// compute hash1
uint32_t hash = key.getHash();
for (int j = 0; j < 3; ++j) {
// NOTE: 0.49f is used to bump the float up almost half a millimeter
// so the cast to int produces a round() effect rather than a floor()
uint32_t floatHash =
DoubleHashKey::hashFunction((uint32_t)(_halfExtents[j] * MILLIMETERS_PER_METER + copysignf(1.0f, _halfExtents[j]) * 0.49f), primeIndex++);
hash ^= floatHash;
}
key.setHash(hash);
glm::vec3 tmpData;
int numData = data->size();
for (int i = 0; i < numData; ++i) {
tmpData = (*data)[i];
for (int j = 0; j < 3; ++j) {
// NOTE: 0.49f is used to bump the float up almost half a millimeter
// so the cast to int produces a round() effect rather than a floor()
uint32_t floatHash =
DoubleHashKey::hashFunction((uint32_t)(tmpData[j] * MILLIMETERS_PER_METER + copysignf(1.0f, tmpData[j]) * 0.49f), primeIndex++);
hash ^= floatHash;
}
}
thisPtr->_doubleHashKey.setHash(hash);
// compute hash2
// compute hash2
hash = key.getHash2();
for (int j = 0; j < 3; ++j) {
// NOTE: 0.49f is used to bump the float up almost half a millimeter
// so the cast to int produces a round() effect rather than a floor()
uint32_t floatHash =
DoubleHashKey::hashFunction2((uint32_t)(_halfExtents[j] * MILLIMETERS_PER_METER + copysignf(1.0f, _halfExtents[j]) * 0.49f));
hash += ~(floatHash << 17);
hash ^= (floatHash >> 11);
hash += (floatHash << 4);
hash ^= (floatHash >> 7);
hash += ~(floatHash << 10);
hash = (hash << 16) | (hash >> 16);
}
key.setHash2(hash);
QString url = _url.toString();
if (url == "") {
hash = _doubleHashKey.getHash2();
for (int i = 0; i < numData; ++i) {
tmpData = (*data)[i];
for (int j = 0; j < 3; ++j) {
// NOTE: 0.49f is used to bump the float up almost half a millimeter
// so the cast to int produces a round() effect rather than a floor()
uint32_t floatHash =
DoubleHashKey::hashFunction2((uint32_t)(tmpData[j] * MILLIMETERS_PER_METER + copysignf(1.0f, tmpData[j]) * 0.49f));
hash += ~(floatHash << 17);
hash ^= (floatHash >> 11);
hash += (floatHash << 4);
hash ^= (floatHash >> 7);
hash += ~(floatHash << 10);
hash = (hash << 16) | (hash >> 16);
}
}
} else {
QByteArray baUrl = url.toLocal8Bit();
const char *cUrl = baUrl.data();
hash = qChecksum(cUrl, baUrl.count());
}
thisPtr->_doubleHashKey.setHash2(hash);
} else {
// this shape info has no external data so type+extents should be enough to generate a unique hash
// compute hash1
uint32_t hash = _doubleHashKey.getHash();
for (int j = 0; j < 3; ++j) {
// NOTE: 0.49f is used to bump the float up almost half a millimeter
// so the cast to int produces a round() effect rather than a floor()
uint32_t floatHash =
DoubleHashKey::hashFunction((uint32_t)(_halfExtents[j] * MILLIMETERS_PER_METER + copysignf(1.0f, _halfExtents[j]) * 0.49f), primeIndex++);
hash ^= floatHash;
}
thisPtr->_doubleHashKey.setHash(hash);
// compute hash2
hash = _doubleHashKey.getHash2();
for (int j = 0; j < 3; ++j) {
// NOTE: 0.49f is used to bump the float up almost half a millimeter
// so the cast to int produces a round() effect rather than a floor()
uint32_t floatHash =
DoubleHashKey::hashFunction2((uint32_t)(_halfExtents[j] * MILLIMETERS_PER_METER + copysignf(1.0f, _halfExtents[j]) * 0.49f));
hash += ~(floatHash << 17);
hash ^= (floatHash >> 11);
hash += (floatHash << 4);
hash ^= (floatHash >> 7);
hash += ~(floatHash << 10);
hash = (hash << 16) | (hash >> 16);
}
thisPtr->_doubleHashKey.setHash2(hash);
QString url = _url.toString();
if (!url.isEmpty()) {
// fold the urlHash into both parts
QByteArray baUrl = url.toLocal8Bit();
const char *cUrl = baUrl.data();
uint32_t urlHash = qChecksum(cUrl, baUrl.count());
key.setHash(key.getHash() ^ urlHash);
key.setHash2(key.getHash2() ^ urlHash);
}
}
return _doubleHashKey;

View file

@ -40,7 +40,7 @@ class ShapeInfo {
public:
void clear();
void setParams(ShapeType type, const glm::vec3& halfExtents, QVector<glm::vec3>* data = NULL, QString url="");
void setParams(ShapeType type, const glm::vec3& halfExtents, QString url="");
void setBox(const glm::vec3& halfExtents);
void setSphere(float radius);
void setEllipsoid(const glm::vec3& halfExtents);
@ -51,9 +51,6 @@ public:
const glm::vec3& getHalfExtents() const { return _halfExtents; }
void setData(const QVector<glm::vec3>* data) { _externalData = data; }
const QVector<glm::vec3>* getData() const { return _externalData; }
const QVector<glm::vec3>& getPoints() const { return _points; }
void clearPoints () { _points.clear(); }
@ -67,7 +64,6 @@ protected:
ShapeType _type = SHAPE_TYPE_NONE;
glm::vec3 _halfExtents = glm::vec3(0.0f);
DoubleHashKey _doubleHashKey;
const QVector<glm::vec3>* _externalData = NULL;
QVector<glm::vec3> _points; // points for convex collision hull
QUrl _url; // url for model of convex collision hull
};