From ca9f0c01c1ce1698bdc9d7f506b76fabedfd2b2a Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Thu, 27 Mar 2014 11:08:55 -0700 Subject: [PATCH 1/9] Couple of quick fixes: reset the skeleton joints when space is pressed as a last resort to fix broken IK, don't render display names at the origin if we don't have a skeleton model. --- interface/src/avatar/Avatar.cpp | 10 +++++++--- interface/src/avatar/MyAvatar.cpp | 1 + interface/src/renderer/Model.cpp | 7 +++++++ 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index de9b33d9c7..94e1416e68 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -397,9 +397,13 @@ void Avatar::renderDisplayName() { glPushMatrix(); glm::vec3 textPosition; - getSkeletonModel().getNeckPosition(textPosition); - textPosition += getBodyUpDirection() * getHeadHeight() * 1.1f; - + if (getSkeletonModel().getNeckPosition(textPosition)) { + textPosition += getBodyUpDirection() * getHeadHeight() * 1.1f; + } else { + const float HEAD_PROPORTION = 0.75f; + textPosition = _position + getBodyUpDirection() * (getBillboardSize() * HEAD_PROPORTION); + } + glTranslatef(textPosition.x, textPosition.y, textPosition.z); // we need "always facing camera": we must remove the camera rotation from the stack diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 618cda1199..350b4fb2be 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -79,6 +79,7 @@ void MyAvatar::reset() { // TODO? resurrect headMouse stuff? //_headMouseX = _glWidget->width() / 2; //_headMouseY = _glWidget->height() / 2; + _skeletonModel.reset(); getHead()->reset(); getHand()->reset(); diff --git a/interface/src/renderer/Model.cpp b/interface/src/renderer/Model.cpp index df5560a4b1..36ac6bec99 100644 --- a/interface/src/renderer/Model.cpp +++ b/interface/src/renderer/Model.cpp @@ -130,9 +130,16 @@ void Model::init() { } void Model::reset() { + if (_jointStates.isEmpty()) { + return; + } foreach (Model* attachment, _attachments) { attachment->reset(); } + const FBXGeometry& geometry = _geometry->getFBXGeometry(); + for (int i = 0; i < _jointStates.size(); i++) { + _jointStates[i].rotation = geometry.joints.at(i).rotation; + } } void Model::clearShapes() { From 21eef6c15ee86dd6ce33f96308487724ddfab0c4 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 27 Mar 2014 13:10:03 -0700 Subject: [PATCH 2/9] make sure VoxelServer and ParticleServer are in Agent interest list --- assignment-client/src/Agent.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/assignment-client/src/Agent.cpp b/assignment-client/src/Agent.cpp index 25dcfcdd95..b5199e87e8 100644 --- a/assignment-client/src/Agent.cpp +++ b/assignment-client/src/Agent.cpp @@ -138,7 +138,11 @@ void Agent::run() { ThreadedAssignment::commonInit(AGENT_LOGGING_NAME, NodeType::Agent); NodeList* nodeList = NodeList::getInstance(); - nodeList->addSetOfNodeTypesToNodeInterestSet(NodeSet() << NodeType::AudioMixer << NodeType::AvatarMixer); + nodeList->addSetOfNodeTypesToNodeInterestSet(NodeSet() + << NodeType::AudioMixer + << NodeType::AvatarMixer + << NodeType::VoxelServer + << NodeType::ParticleServer); // figure out the URL for the script for this agent assignment QString scriptURLString("http://%1:8080/assignment/%2"); From 7cc72307456b15e06691b4132be05e441a2a2058 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Thu, 27 Mar 2014 13:18:45 -0700 Subject: [PATCH 3/9] Adjust the shadow shader to prevent seeing shadows on the opposite sides of objects. Closes #2544. --- interface/resources/shaders/shadow_map.frag | 5 ++-- interface/resources/shaders/shadow_map.vert | 30 +++++++++++++++++++++ interface/src/voxels/VoxelSystem.cpp | 15 ++++------- 3 files changed, 38 insertions(+), 12 deletions(-) create mode 100644 interface/resources/shaders/shadow_map.vert diff --git a/interface/resources/shaders/shadow_map.frag b/interface/resources/shaders/shadow_map.frag index 4bd8b8b768..810fc6a947 100644 --- a/interface/resources/shaders/shadow_map.frag +++ b/interface/resources/shaders/shadow_map.frag @@ -10,7 +10,8 @@ uniform sampler2DShadow shadowMap; +varying vec4 shadowColor; + void main(void) { - gl_FragColor = gl_Color * mix(vec4(0.8, 0.8, 0.8, 1.0), vec4(1.0, 1.0, 1.0, 1.0), - shadow2D(shadowMap, gl_TexCoord[0].stp)); + gl_FragColor = gl_Color * mix(shadowColor, vec4(1.0, 1.0, 1.0, 1.0), shadow2D(shadowMap, gl_TexCoord[0].stp)); } diff --git a/interface/resources/shaders/shadow_map.vert b/interface/resources/shaders/shadow_map.vert new file mode 100644 index 0000000000..a15cd070b9 --- /dev/null +++ b/interface/resources/shaders/shadow_map.vert @@ -0,0 +1,30 @@ +#version 120 + +// +// shadow_map.vert +// vertex shader +// +// Created by Andrzej Kapolka on 3/27/14. +// Copyright (c) 2014 High Fidelity, Inc. All rights reserved. +// + +varying vec4 shadowColor; + +void main(void) { + // the shadow color depends on the light product + vec4 normal = normalize(gl_ModelViewMatrix * vec4(gl_Normal, 0.0)); + float lightProduct = dot(normal, gl_LightSource[0].position); + shadowColor = mix(vec4(1.0, 1.0, 1.0, 1.0), vec4(0.8, 0.8, 0.8, 1.0), step(0.0, lightProduct)); + + // standard diffuse lighting + gl_FrontColor = gl_Color * (gl_LightModel.ambient + gl_LightSource[0].ambient + + gl_LightSource[0].diffuse * max(0.0, lightProduct)); + + // generate the shadow texture coordinate using the eye position + vec4 eyePosition = gl_ModelViewMatrix * gl_Vertex; + gl_TexCoord[0] = vec4(dot(gl_EyePlaneS[0], eyePosition), dot(gl_EyePlaneT[0], eyePosition), + dot(gl_EyePlaneR[0], eyePosition), 1.0); + + // use the fixed function transform + gl_Position = ftransform(); +} diff --git a/interface/src/voxels/VoxelSystem.cpp b/interface/src/voxels/VoxelSystem.cpp index 4db5af3c04..5c68485436 100644 --- a/interface/src/voxels/VoxelSystem.cpp +++ b/interface/src/voxels/VoxelSystem.cpp @@ -506,8 +506,10 @@ void VoxelSystem::initVoxelMemory() { _perlinModulateProgram.setUniformValue("permutationNormalTexture", 0); _perlinModulateProgram.release(); - _shadowMapProgram.addShaderFromSourceFile(QGLShader::Fragment, Application::resourcesPath() - + "shaders/shadow_map.frag"); + _shadowMapProgram.addShaderFromSourceFile(QGLShader::Vertex, + Application::resourcesPath() + "shaders/shadow_map.vert"); + _shadowMapProgram.addShaderFromSourceFile(QGLShader::Fragment, + Application::resourcesPath() + "shaders/shadow_map.frag"); _shadowMapProgram.link(); _shadowMapProgram.bind(); @@ -1471,10 +1473,6 @@ void VoxelSystem::applyScaleAndBindProgram(bool texture) { if (Menu::getInstance()->isOptionChecked(MenuOption::Shadows)) { _shadowMapProgram.bind(); glBindTexture(GL_TEXTURE_2D, Application::getInstance()->getTextureCache()->getShadowDepthTextureID()); - glEnable(GL_TEXTURE_GEN_S); - glEnable(GL_TEXTURE_GEN_T); - glEnable(GL_TEXTURE_GEN_R); - glEnable(GL_TEXTURE_2D); glTexGenfv(GL_S, GL_EYE_PLANE, (const GLfloat*)&Application::getInstance()->getShadowMatrix()[0]); glTexGenfv(GL_T, GL_EYE_PLANE, (const GLfloat*)&Application::getInstance()->getShadowMatrix()[1]); @@ -1496,10 +1494,7 @@ void VoxelSystem::removeScaleAndReleaseProgram(bool texture) { if (Menu::getInstance()->isOptionChecked(MenuOption::Shadows)) { _shadowMapProgram.release(); glBindTexture(GL_TEXTURE_2D, 0); - glDisable(GL_TEXTURE_GEN_S); - glDisable(GL_TEXTURE_GEN_T); - glDisable(GL_TEXTURE_GEN_R); - glDisable(GL_TEXTURE_2D); + } else if (texture) { _perlinModulateProgram.release(); From bda96ef935f00abc36ccbb89e3f0a134be61805c Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Thu, 27 Mar 2014 13:35:22 -0700 Subject: [PATCH 4/9] added back use of nodeWithUUID() instead of sharedpointer --- .../src/octree/OctreeSendThread.cpp | 25 ++++++++-- .../src/octree/OctreeSendThread.h | 3 +- assignment-client/src/octree/OctreeServer.cpp | 46 +++++++++++++------ assignment-client/src/octree/OctreeServer.h | 4 ++ libraries/shared/src/NodeList.cpp | 18 ++++++-- libraries/shared/src/NodeList.h | 2 +- 6 files changed, 73 insertions(+), 25 deletions(-) diff --git a/assignment-client/src/octree/OctreeSendThread.cpp b/assignment-client/src/octree/OctreeSendThread.cpp index 382d8aa528..023f3f3d2c 100644 --- a/assignment-client/src/octree/OctreeSendThread.cpp +++ b/assignment-client/src/octree/OctreeSendThread.cpp @@ -21,8 +21,9 @@ quint64 endSceneSleepTime = 0; OctreeSendThread::OctreeSendThread(OctreeServer* myServer, SharedNodePointer node) : _myServer(myServer), - _node(node), + _nodeUUID(node->getUUID()), _packetData(), + _nodeMissingCount(0), _processLock(), _isShuttingDown(false) { @@ -43,7 +44,6 @@ OctreeSendThread::~OctreeSendThread() { } qDebug() << qPrintable(safeServerName) << "server [" << _myServer << "]: client disconnected " "- ending sending thread [" << this << "]"; - _node.clear(); OctreeServer::clientDisconnected(); } @@ -72,13 +72,28 @@ bool OctreeSendThread::process() { // don't do any send processing until the initial load of the octree is complete... if (_myServer->isInitialLoadComplete()) { - if (!_node.isNull()) { - OctreeQueryNode* nodeData = static_cast(_node->getLinkedData()); + SharedNodePointer node = NodeList::getInstance()->nodeWithUUID(_nodeUUID, false); + if (node) { + _nodeMissingCount = 0; + OctreeQueryNode* nodeData = static_cast(node->getLinkedData()); // Sometimes the node data has not yet been linked, in which case we can't really do anything if (nodeData && !nodeData->isShuttingDown()) { bool viewFrustumChanged = nodeData->updateCurrentViewFrustum(); - packetDistributor(_node, nodeData, viewFrustumChanged); + packetDistributor(node, nodeData, viewFrustumChanged); + } + } else { + _nodeMissingCount++; + const int MANY_FAILED_LOCKS = 1; + if (_nodeMissingCount >= MANY_FAILED_LOCKS) { + + QString safeServerName("Octree"); + if (_myServer) { + safeServerName = _myServer->getMyServerName(); + } + + qDebug() << qPrintable(safeServerName) << "server: sending thread [" << this << "]" + << "failed to get nodeWithUUID() " << _nodeUUID <<". Failed:" << _nodeMissingCount << "times"; } } } diff --git a/assignment-client/src/octree/OctreeSendThread.h b/assignment-client/src/octree/OctreeSendThread.h index 4e18ee9b2a..4b1b6d8c92 100644 --- a/assignment-client/src/octree/OctreeSendThread.h +++ b/assignment-client/src/octree/OctreeSendThread.h @@ -38,14 +38,15 @@ protected: virtual bool process(); private: - SharedNodePointer _node; OctreeServer* _myServer; + QUuid _nodeUUID; int handlePacketSend(const SharedNodePointer& node, OctreeQueryNode* nodeData, int& trueBytesSent, int& truePacketsSent); int packetDistributor(const SharedNodePointer& node, OctreeQueryNode* nodeData, bool viewFrustumChanged); OctreePacketData _packetData; + int _nodeMissingCount; QMutex _processLock; // don't allow us to have our nodeData, or our thread to be deleted while we're processing bool _isShuttingDown; }; diff --git a/assignment-client/src/octree/OctreeServer.cpp b/assignment-client/src/octree/OctreeServer.cpp index fa087cced2..fd3f9e6cb7 100644 --- a/assignment-client/src/octree/OctreeServer.cpp +++ b/assignment-client/src/octree/OctreeServer.cpp @@ -1160,7 +1160,6 @@ QString OctreeServer::getStatusLink() { } void OctreeServer::sendStatsPacket() { - // TODO: we have too many stats to fit in a single MTU... so for now, we break it into multiple JSON objects and // send them separately. What we really should do is change the NodeList::sendStatsToDomainServer() to handle the // the following features: @@ -1241,59 +1240,78 @@ QMap OctreeServer::_threadsDidPacketDistributor; QMap OctreeServer::_threadsDidHandlePacketSend; QMap OctreeServer::_threadsDidCallWriteDatagram; +QMutex OctreeServer::_threadsDidProcessMutex; +QMutex OctreeServer::_threadsDidPacketDistributorMutex; +QMutex OctreeServer::_threadsDidHandlePacketSendMutex; +QMutex OctreeServer::_threadsDidCallWriteDatagramMutex; + void OctreeServer::didProcess(OctreeSendThread* thread) { + QMutexLocker locker(&_threadsDidProcessMutex); _threadsDidProcess[thread] = usecTimestampNow(); } void OctreeServer::didPacketDistributor(OctreeSendThread* thread) { + QMutexLocker locker(&_threadsDidPacketDistributorMutex); _threadsDidPacketDistributor[thread] = usecTimestampNow(); } void OctreeServer::didHandlePacketSend(OctreeSendThread* thread) { + QMutexLocker locker(&_threadsDidHandlePacketSendMutex); _threadsDidHandlePacketSend[thread] = usecTimestampNow(); } void OctreeServer::didCallWriteDatagram(OctreeSendThread* thread) { + QMutexLocker locker(&_threadsDidCallWriteDatagramMutex); _threadsDidCallWriteDatagram[thread] = usecTimestampNow(); } void OctreeServer::stopTrackingThread(OctreeSendThread* thread) { + QMutexLocker lockerA(&_threadsDidProcessMutex); + QMutexLocker lockerB(&_threadsDidPacketDistributorMutex); + QMutexLocker lockerC(&_threadsDidHandlePacketSendMutex); + QMutexLocker lockerD(&_threadsDidCallWriteDatagramMutex); + _threadsDidProcess.remove(thread); _threadsDidPacketDistributor.remove(thread); _threadsDidHandlePacketSend.remove(thread); + _threadsDidCallWriteDatagram.remove(thread); } -int howManyThreadsDidSomething(QMap& something, quint64 since) { - if (since == 0) { - return something.size(); - } +int howManyThreadsDidSomething(QMutex& mutex, QMap& something, quint64 since) { int count = 0; - QMap::const_iterator i = something.constBegin(); - while (i != something.constEnd()) { - if (i.value() > since) { - count++; + if (mutex.tryLock()) { + if (since == 0) { + count = something.size(); + } else { + QMap::const_iterator i = something.constBegin(); + while (i != something.constEnd()) { + if (i.value() > since) { + count++; + } + ++i; + } } - ++i; + mutex.unlock(); } return count; } int OctreeServer::howManyThreadsDidProcess(quint64 since) { - return howManyThreadsDidSomething(_threadsDidProcess, since); + return howManyThreadsDidSomething(_threadsDidProcessMutex, _threadsDidProcess, since); } int OctreeServer::howManyThreadsDidPacketDistributor(quint64 since) { - return howManyThreadsDidSomething(_threadsDidPacketDistributor, since); + return howManyThreadsDidSomething(_threadsDidPacketDistributorMutex, _threadsDidPacketDistributor, since); } int OctreeServer::howManyThreadsDidHandlePacketSend(quint64 since) { - return howManyThreadsDidSomething(_threadsDidHandlePacketSend, since); + return howManyThreadsDidSomething(_threadsDidHandlePacketSendMutex, _threadsDidHandlePacketSend, since); } int OctreeServer::howManyThreadsDidCallWriteDatagram(quint64 since) { - return howManyThreadsDidSomething(_threadsDidCallWriteDatagram, since); + return howManyThreadsDidSomething(_threadsDidCallWriteDatagramMutex, _threadsDidCallWriteDatagram, since); } diff --git a/assignment-client/src/octree/OctreeServer.h b/assignment-client/src/octree/OctreeServer.h index 3dac42709f..63d43b6634 100644 --- a/assignment-client/src/octree/OctreeServer.h +++ b/assignment-client/src/octree/OctreeServer.h @@ -211,6 +211,10 @@ protected: static QMap _threadsDidHandlePacketSend; static QMap _threadsDidCallWriteDatagram; + static QMutex _threadsDidProcessMutex; + static QMutex _threadsDidPacketDistributorMutex; + static QMutex _threadsDidHandlePacketSendMutex; + static QMutex _threadsDidCallWriteDatagramMutex; }; #endif // __octree_server__OctreeServer__ diff --git a/libraries/shared/src/NodeList.cpp b/libraries/shared/src/NodeList.cpp index dc5a419295..761ea40d55 100644 --- a/libraries/shared/src/NodeList.cpp +++ b/libraries/shared/src/NodeList.cpp @@ -357,10 +357,20 @@ int NodeList::findNodeAndUpdateWithDataFromPacket(const QByteArray& packet) { return 0; } -SharedNodePointer NodeList::nodeWithUUID(const QUuid& nodeUUID) { - QMutexLocker locker(&_nodeHashMutex); - return _nodeHash.value(nodeUUID); -} +SharedNodePointer NodeList::nodeWithUUID(const QUuid& nodeUUID, bool blockingLock) { + const int WAIT_TIME = 10; // wait up to 10ms in the try lock case + SharedNodePointer node; + // if caller wants us to block and guarantee the correct answer, then honor that request + if (blockingLock) { + // this will block till we can get access + QMutexLocker locker(&_nodeHashMutex); + node = _nodeHash.value(nodeUUID); + } else if (_nodeHashMutex.tryLock(WAIT_TIME)) { // some callers are willing to get wrong answers but not block + node = _nodeHash.value(nodeUUID); + _nodeHashMutex.unlock(); + } + return node; + } SharedNodePointer NodeList::sendingNodeForPacket(const QByteArray& packet) { QUuid nodeUUID = uuidFromPacketHeader(packet); diff --git a/libraries/shared/src/NodeList.h b/libraries/shared/src/NodeList.h index f10e01f3f4..d05d6a2fbc 100644 --- a/libraries/shared/src/NodeList.h +++ b/libraries/shared/src/NodeList.h @@ -103,7 +103,7 @@ public: QByteArray constructPingReplyPacket(const QByteArray& pingPacket); void pingPublicAndLocalSocketsForInactiveNode(const SharedNodePointer& node); - SharedNodePointer nodeWithUUID(const QUuid& nodeUUID); + SharedNodePointer nodeWithUUID(const QUuid& nodeUUID, bool blockingLock = true); SharedNodePointer sendingNodeForPacket(const QByteArray& packet); SharedNodePointer addOrUpdateNode(const QUuid& uuid, char nodeType, From 563725e7810259e49f47a5b6cca883a14bdea5d7 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Thu, 27 Mar 2014 13:44:34 -0700 Subject: [PATCH 5/9] Use the "correct" shadow strategy: include only ambient term when in shadow. --- interface/resources/shaders/shadow_map.frag | 2 +- interface/resources/shaders/shadow_map.vert | 12 +++++------- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/interface/resources/shaders/shadow_map.frag b/interface/resources/shaders/shadow_map.frag index 810fc6a947..b683ed5af2 100644 --- a/interface/resources/shaders/shadow_map.frag +++ b/interface/resources/shaders/shadow_map.frag @@ -13,5 +13,5 @@ uniform sampler2DShadow shadowMap; varying vec4 shadowColor; void main(void) { - gl_FragColor = gl_Color * mix(shadowColor, vec4(1.0, 1.0, 1.0, 1.0), shadow2D(shadowMap, gl_TexCoord[0].stp)); + gl_FragColor = mix(shadowColor, gl_Color, shadow2D(shadowMap, gl_TexCoord[0].stp)); } diff --git a/interface/resources/shaders/shadow_map.vert b/interface/resources/shaders/shadow_map.vert index a15cd070b9..6809ca6e2b 100644 --- a/interface/resources/shaders/shadow_map.vert +++ b/interface/resources/shaders/shadow_map.vert @@ -11,14 +11,12 @@ varying vec4 shadowColor; void main(void) { - // the shadow color depends on the light product - vec4 normal = normalize(gl_ModelViewMatrix * vec4(gl_Normal, 0.0)); - float lightProduct = dot(normal, gl_LightSource[0].position); - shadowColor = mix(vec4(1.0, 1.0, 1.0, 1.0), vec4(0.8, 0.8, 0.8, 1.0), step(0.0, lightProduct)); + // the shadow color includes only the ambient terms + shadowColor = gl_Color * (gl_LightModel.ambient + gl_LightSource[0].ambient); - // standard diffuse lighting - gl_FrontColor = gl_Color * (gl_LightModel.ambient + gl_LightSource[0].ambient + - gl_LightSource[0].diffuse * max(0.0, lightProduct)); + // the normal color includes diffuse + vec4 normal = normalize(gl_ModelViewMatrix * vec4(gl_Normal, 0.0)); + gl_FrontColor = shadowColor + gl_Color * (gl_LightSource[0].diffuse * max(0.0, dot(normal, gl_LightSource[0].position))); // generate the shadow texture coordinate using the eye position vec4 eyePosition = gl_ModelViewMatrix * gl_Vertex; From 9078d9a0f245a4a7218936adc0dc421d1a32a770 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Thu, 27 Mar 2014 14:14:31 -0700 Subject: [PATCH 6/9] Fix for avatar LOD's getting into a degenerate state. Don't persist the avatar LOD, since we adjust it more rapidly. --- interface/interface_en.ts | 8 ++++---- interface/src/Menu.cpp | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/interface/interface_en.ts b/interface/interface_en.ts index da8827d89d..aa7b1951ea 100644 --- a/interface/interface_en.ts +++ b/interface/interface_en.ts @@ -113,18 +113,18 @@ Menu - + Open .ini config file - - + + Text files (*.ini) - + Save .ini config file diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 79b0a23ce5..efb5f04c43 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -393,8 +393,6 @@ void Menu::loadSettings(QSettings* settings) { _maxVoxels = loadSetting(settings, "maxVoxels", DEFAULT_MAX_VOXELS_PER_SYSTEM); _maxVoxelPacketsPerSecond = loadSetting(settings, "maxVoxelsPPS", DEFAULT_MAX_VOXEL_PPS); _voxelSizeScale = loadSetting(settings, "voxelSizeScale", DEFAULT_OCTREE_SIZE_SCALE); - _avatarLODDistanceMultiplier = loadSetting(settings, "avatarLODDistanceMultiplier", - DEFAULT_AVATAR_LOD_DISTANCE_MULTIPLIER); _boundaryLevelAdjust = loadSetting(settings, "boundaryLevelAdjust", 0); settings->beginGroup("View Frustum Offset Camera"); @@ -434,7 +432,6 @@ void Menu::saveSettings(QSettings* settings) { settings->setValue("maxVoxels", _maxVoxels); settings->setValue("maxVoxelsPPS", _maxVoxelPacketsPerSecond); settings->setValue("voxelSizeScale", _voxelSizeScale); - settings->setValue("avatarLODDistanceMultiplier", _avatarLODDistanceMultiplier); settings->setValue("boundaryLevelAdjust", _boundaryLevelAdjust); settings->beginGroup("View Frustum Offset Camera"); settings->setValue("viewFrustumOffsetYaw", _viewFrustumOffset.yaw); @@ -1203,7 +1200,10 @@ void Menu::autoAdjustLOD(float currentFPS) { if (now - _lastAvatarDetailDrop > ADJUST_AVATAR_LOD_DOWN_DELAY) { // attempt to lower the detail in proportion to the fps difference float targetFps = (ADJUST_LOD_DOWN_FPS + ADJUST_LOD_UP_FPS) * 0.5f; - _avatarLODDistanceMultiplier *= (targetFps / _fastFPSAverage.getAverage()); + float averageFps = _fastFPSAverage.getAverage(); + const float MAXIMUM_MULTIPLIER_SCALE = 2.0f; + _avatarLODDistanceMultiplier *= (averageFps < EPSILON) ? MAXIMUM_MULTIPLIER_SCALE : + qMin(MAXIMUM_MULTIPLIER_SCALE, targetFps / averageFps); _lastAvatarDetailDrop = now; } } else if (_fastFPSAverage.getAverage() > ADJUST_LOD_UP_FPS) { From eab4b4bc5d945ed2ef73db745970ea599971c2b0 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Thu, 27 Mar 2014 14:47:46 -0700 Subject: [PATCH 7/9] Make sure we process the model root nodes in alphabetic order. --- interface/src/renderer/FBXReader.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/interface/src/renderer/FBXReader.cpp b/interface/src/renderer/FBXReader.cpp index 53f4e04b0b..6846f79825 100644 --- a/interface/src/renderer/FBXReader.cpp +++ b/interface/src/renderer/FBXReader.cpp @@ -1259,7 +1259,13 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping) remainingModels.insert(model.key()); } while (!remainingModels.isEmpty()) { - QString topID = getTopModelID(parentMap, models, *remainingModels.constBegin()); + QString first = *remainingModels.constBegin(); + foreach (const QString& id, remainingModels) { + if (id < first) { + first = id; + } + } + QString topID = getTopModelID(parentMap, models, first); appendModelIDs(parentMap.value(topID), childMap, models, remainingModels, modelIDs); } From 077df3906fbc491fd740e2bc99674772a2e26037 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Thu, 27 Mar 2014 15:02:26 -0700 Subject: [PATCH 8/9] Make sure we reset our LOD adjustments before we switch back from throttling the renderer. --- interface/interface_en.ts | 4 ++-- interface/src/Application.cpp | 2 ++ interface/src/Menu.cpp | 6 ++++++ interface/src/Menu.h | 1 + 4 files changed, 11 insertions(+), 2 deletions(-) diff --git a/interface/interface_en.ts b/interface/interface_en.ts index aa7b1951ea..8ca6f7d269 100644 --- a/interface/interface_en.ts +++ b/interface/interface_en.ts @@ -14,12 +14,12 @@ - + Open Script - + JavaScript Files (*.js) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index edeb454f11..5b08997b7c 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1636,6 +1636,8 @@ void Application::updateLOD() { // adjust it unless we were asked to disable this feature, or if we're currently in throttleRendering mode if (!Menu::getInstance()->isOptionChecked(MenuOption::DisableAutoAdjustLOD) && !isThrottleRendering()) { Menu::getInstance()->autoAdjustLOD(_fps); + } else { + Menu::getInstance()->resetLODAdjust(); } } diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index efb5f04c43..e1764374ea 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -1249,6 +1249,12 @@ void Menu::autoAdjustLOD(float currentFPS) { } } +void Menu::resetLODAdjust() { + _fpsAverage.reset(); + _fastFPSAverage.reset(); + _lastAvatarDetailDrop = _lastAdjust = usecTimestampNow(); +} + void Menu::setVoxelSizeScale(float sizeScale) { _voxelSizeScale = sizeScale; } diff --git a/interface/src/Menu.h b/interface/src/Menu.h index cab5645304..6b41430eaf 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -85,6 +85,7 @@ public: // User Tweakable LOD Items QString getLODFeedbackText(); void autoAdjustLOD(float currentFPS); + void resetLODAdjust(); void setVoxelSizeScale(float sizeScale); float getVoxelSizeScale() const { return _voxelSizeScale; } float getAvatarLODDistanceMultiplier() const { return _avatarLODDistanceMultiplier; } From c540427b9e25a6aac8185455550daab7052a08f3 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Thu, 27 Mar 2014 15:03:46 -0700 Subject: [PATCH 9/9] tweaks to _processLock to reduce time it's locked --- .../src/octree/OctreeSendThread.cpp | 20 +++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/assignment-client/src/octree/OctreeSendThread.cpp b/assignment-client/src/octree/OctreeSendThread.cpp index 023f3f3d2c..4105b21eb8 100644 --- a/assignment-client/src/octree/OctreeSendThread.cpp +++ b/assignment-client/src/octree/OctreeSendThread.cpp @@ -48,26 +48,28 @@ OctreeSendThread::~OctreeSendThread() { } void OctreeSendThread::setIsShuttingDown() { - QMutexLocker locker(&_processLock); // this will cause us to wait till the process loop is complete _isShuttingDown = true; OctreeServer::stopTrackingThread(this); + + // this will cause us to wait till the process loop is complete, we do this after we change _isShuttingDown + QMutexLocker locker(&_processLock); } bool OctreeSendThread::process() { + if (_isShuttingDown) { + return false; // exit early if we're shutting down + } + OctreeServer::didProcess(this); float lockWaitElapsedUsec = OctreeServer::SKIP_TIME; quint64 lockWaitStart = usecTimestampNow(); - QMutexLocker locker(&_processLock); + _processLock.lock(); quint64 lockWaitEnd = usecTimestampNow(); lockWaitElapsedUsec = (float)(lockWaitEnd - lockWaitStart); OctreeServer::trackProcessWaitTime(lockWaitElapsedUsec); - if (_isShuttingDown) { - return false; // exit early if we're shutting down - } - quint64 start = usecTimestampNow(); // don't do any send processing until the initial load of the octree is complete... @@ -98,6 +100,12 @@ bool OctreeSendThread::process() { } } + _processLock.unlock(); + + if (_isShuttingDown) { + return false; // exit early if we're shutting down + } + // Only sleep if we're still running and we got the lock last time we tried, otherwise try to get the lock asap if (isStillRunning()) { // dynamically sleep until we need to fire off the next set of octree elements