diff --git a/assignment-client/src/audio/AudioMixer.cpp b/assignment-client/src/audio/AudioMixer.cpp index 988bcc1da7..f5bed48a86 100644 --- a/assignment-client/src/audio/AudioMixer.cpp +++ b/assignment-client/src/audio/AudioMixer.cpp @@ -62,7 +62,12 @@ void attachNewBufferToNode(Node *newNode) { } AudioMixer::AudioMixer(const QByteArray& packet) : - ThreadedAssignment(packet) + ThreadedAssignment(packet), + _trailingSleepRatio(1.0f), + _minSourceLoudnessInFrame(1.0f), + _maxSourceLoudnessInFrame(0.0f), + _loudnessCutoffRatio(0.0f), + _minRequiredLoudness(0.0f) { } @@ -301,7 +306,7 @@ void AudioMixer::prepareMixForListeningNode(Node* node) { if ((*otherNode != *node || otherNodeBuffer->shouldLoopbackForNode()) && otherNodeBuffer->willBeAddedToMix() - && otherNodeClientData->getNextOutputLoudness() > 0) { + && otherNodeBuffer->getAverageLoudness() > _minRequiredLoudness) { addBufferToMixForListeningNodeWithBuffer(otherNodeBuffer, nodeRingBuffer); } } @@ -350,12 +355,19 @@ void AudioMixer::run() { char* clientMixBuffer = new char[NETWORK_BUFFER_LENGTH_BYTES_STEREO + numBytesForPacketHeaderGivenPacketType(PacketTypeMixedAudio)]; + + int usecToSleep = BUFFER_SEND_INTERVAL_USECS; while (!_isFinished) { + _minSourceLoudnessInFrame = 1.0f; + _maxSourceLoudnessInFrame = 0.0f; + foreach (const SharedNodePointer& node, nodeList->getNodeHash()) { if (node->getLinkedData()) { - ((AudioMixerClientData*) node->getLinkedData())->checkBuffersBeforeFrameSend(JITTER_BUFFER_SAMPLES); + ((AudioMixerClientData*) node->getLinkedData())->checkBuffersBeforeFrameSend(JITTER_BUFFER_SAMPLES, + _minSourceLoudnessInFrame, + _maxSourceLoudnessInFrame); } } @@ -384,7 +396,7 @@ void AudioMixer::run() { break; } - int usecToSleep = usecTimestamp(&startTime) + (++nextFrame * BUFFER_SEND_INTERVAL_USECS) - usecTimestampNow(); + usecToSleep = usecTimestamp(&startTime) + (++nextFrame * BUFFER_SEND_INTERVAL_USECS) - usecTimestampNow(); if (usecToSleep > 0) { usleep(usecToSleep); diff --git a/assignment-client/src/audio/AudioMixer.h b/assignment-client/src/audio/AudioMixer.h index 5a68b0023f..4ba8cdebd3 100644 --- a/assignment-client/src/audio/AudioMixer.h +++ b/assignment-client/src/audio/AudioMixer.h @@ -37,7 +37,14 @@ private: void prepareMixForListeningNode(Node* node); // client samples capacity is larger than what will be sent to optimize mixing - int16_t _clientSamples[NETWORK_BUFFER_LENGTH_SAMPLES_STEREO + SAMPLE_PHASE_DELAY_AT_90]; + // we are MMX adding 4 samples at a time so we need client samples to have an extra 4 + int16_t _clientSamples[NETWORK_BUFFER_LENGTH_SAMPLES_STEREO + (SAMPLE_PHASE_DELAY_AT_90 * 2)]; + + float _trailingSleepRatio; + float _minSourceLoudnessInFrame; + float _maxSourceLoudnessInFrame; + float _loudnessCutoffRatio; + float _minRequiredLoudness; }; #endif /* defined(__hifi__AudioMixer__) */ diff --git a/assignment-client/src/audio/AudioMixerClientData.cpp b/assignment-client/src/audio/AudioMixerClientData.cpp index b2da0a0aaa..f370a1509f 100644 --- a/assignment-client/src/audio/AudioMixerClientData.cpp +++ b/assignment-client/src/audio/AudioMixerClientData.cpp @@ -6,6 +6,8 @@ // Copyright (c) 2013 HighFidelity, Inc. All rights reserved. // +#include + #include #include @@ -14,8 +16,7 @@ #include "AudioMixerClientData.h" AudioMixerClientData::AudioMixerClientData() : - _ringBuffers(), - _nextOutputLoudness(0) + _ringBuffers() { } @@ -82,16 +83,20 @@ int AudioMixerClientData::parseData(const QByteArray& packet) { return 0; } -void AudioMixerClientData::checkBuffersBeforeFrameSend(int jitterBufferLengthSamples) { +void AudioMixerClientData::checkBuffersBeforeFrameSend(int jitterBufferLengthSamples, + float& currentMinLoudness, + float& currentMaxLoudness) { for (unsigned int i = 0; i < _ringBuffers.size(); i++) { if (_ringBuffers[i]->shouldBeAddedToMix(jitterBufferLengthSamples)) { // this is a ring buffer that is ready to go // set its flag so we know to push its buffer when all is said and done _ringBuffers[i]->setWillBeAddedToMix(true); + // calculate the average loudness for the next NETWORK_BUFFER_LENGTH_SAMPLES_PER_CHANNEL // that would be mixed in - _nextOutputLoudness = _ringBuffers[i]->averageLoudnessForBoundarySamples(NETWORK_BUFFER_LENGTH_SAMPLES_PER_CHANNEL); + _ringBuffers[i]->updateAverageLoudnessForBoundarySamples(NETWORK_BUFFER_LENGTH_SAMPLES_PER_CHANNEL); + } } } diff --git a/assignment-client/src/audio/AudioMixerClientData.h b/assignment-client/src/audio/AudioMixerClientData.h index bb10098e23..d41563bbca 100644 --- a/assignment-client/src/audio/AudioMixerClientData.h +++ b/assignment-client/src/audio/AudioMixerClientData.h @@ -24,14 +24,11 @@ public: const std::vector getRingBuffers() const { return _ringBuffers; } AvatarAudioRingBuffer* getAvatarAudioRingBuffer() const; - float getNextOutputLoudness() const { return _nextOutputLoudness; } - int parseData(const QByteArray& packet); - void checkBuffersBeforeFrameSend(int jitterBufferLengthSamples); + void checkBuffersBeforeFrameSend(int jitterBufferLengthSamples, float& currentMinLoudness, float& currentMaxLoudness); void pushBuffersAfterFrameSend(); private: std::vector _ringBuffers; - float _nextOutputLoudness; }; #endif /* defined(__hifi__AudioMixerClientData__) */ diff --git a/assignment-client/src/octree/OctreeQueryNode.cpp b/assignment-client/src/octree/OctreeQueryNode.cpp index a1922f980d..2ceb9e1040 100644 --- a/assignment-client/src/octree/OctreeQueryNode.cpp +++ b/assignment-client/src/octree/OctreeQueryNode.cpp @@ -36,18 +36,34 @@ OctreeQueryNode::OctreeQueryNode() : _lodChanged(false), _lodInitialized(false), _sequenceNumber(0), - _lastRootTimestamp(0) + _lastRootTimestamp(0), + _myPacketType(PacketTypeUnknown), + _isShuttingDown(false) { } OctreeQueryNode::~OctreeQueryNode() { + _isShuttingDown = true; + const bool extraDebugging = false; + if (extraDebugging) { + qDebug() << "OctreeQueryNode::~OctreeQueryNode()"; + } if (_octreeSendThread) { + if (extraDebugging) { + qDebug() << "OctreeQueryNode::~OctreeQueryNode()... calling _octreeSendThread->terminate()"; + } _octreeSendThread->terminate(); + if (extraDebugging) { + qDebug() << "OctreeQueryNode::~OctreeQueryNode()... calling delete _octreeSendThread"; + } delete _octreeSendThread; } delete[] _octreePacket; delete[] _lastOctreePacket; + if (extraDebugging) { + qDebug() << "OctreeQueryNode::~OctreeQueryNode()... DONE..."; + } } @@ -59,9 +75,13 @@ void OctreeQueryNode::initializeOctreeSendThread(OctreeServer* octreeServer, con } bool OctreeQueryNode::packetIsDuplicate() const { + // if shutting down, return immediately + if (_isShuttingDown) { + return false; + } // since our packets now include header information, like sequence number, and createTime, we can't just do a memcmp // of the entire packet, we need to compare only the packet content... - int numBytesPacketHeader = numBytesForPacketHeaderGivenPacketType(getMyPacketType()); + int numBytesPacketHeader = numBytesForPacketHeaderGivenPacketType(_myPacketType); if (_lastOctreePacketLength == getPacketLength()) { if (memcmp(_lastOctreePacket + (numBytesPacketHeader + OCTREE_PACKET_EXTRA_HEADERS_SIZE), @@ -74,6 +94,11 @@ bool OctreeQueryNode::packetIsDuplicate() const { } bool OctreeQueryNode::shouldSuppressDuplicatePacket() { + // if shutting down, return immediately + if (_isShuttingDown) { + return true; + } + bool shouldSuppress = false; // assume we won't suppress // only consider duplicate packets @@ -107,7 +132,17 @@ bool OctreeQueryNode::shouldSuppressDuplicatePacket() { return shouldSuppress; } +void OctreeQueryNode::init() { + _myPacketType = getMyPacketType(); + resetOctreePacket(true); // don't bump sequence +} + void OctreeQueryNode::resetOctreePacket(bool lastWasSurpressed) { + // if shutting down, return immediately + if (_isShuttingDown) { + return; + } + // Whenever we call this, we will keep a copy of the last packet, so we can determine if the last packet has // changed since we last reset it. Since we know that no two packets can ever be identical without being the same // scene information, (e.g. the root node packet of a static scene), we can use this as a strategy for reducing @@ -128,7 +163,7 @@ void OctreeQueryNode::resetOctreePacket(bool lastWasSurpressed) { } _octreePacketAvailableBytes = MAX_PACKET_SIZE; - int numBytesPacketHeader = populatePacketHeader(reinterpret_cast(_octreePacket), getMyPacketType()); + int numBytesPacketHeader = populatePacketHeader(reinterpret_cast(_octreePacket), _myPacketType); _octreePacketAt = _octreePacket + numBytesPacketHeader; _octreePacketAvailableBytes -= numBytesPacketHeader; @@ -158,6 +193,11 @@ void OctreeQueryNode::resetOctreePacket(bool lastWasSurpressed) { } void OctreeQueryNode::writeToPacket(const unsigned char* buffer, unsigned int bytes) { + // if shutting down, return immediately + if (_isShuttingDown) { + return; + } + // compressed packets include lead bytes which contain compressed size, this allows packing of // multiple compressed portions together if (_currentPacketIsCompressed) { @@ -174,6 +214,11 @@ void OctreeQueryNode::writeToPacket(const unsigned char* buffer, unsigned int by } bool OctreeQueryNode::updateCurrentViewFrustum() { + // if shutting down, return immediately + if (_isShuttingDown) { + return false; + } + bool currentViewFrustumChanged = false; ViewFrustum newestViewFrustum; // get position and orientation details from the camera @@ -233,6 +278,11 @@ void OctreeQueryNode::setViewSent(bool viewSent) { } void OctreeQueryNode::updateLastKnownViewFrustum() { + // if shutting down, return immediately + if (_isShuttingDown) { + return; + } + bool frustumChanges = !_lastKnownViewFrustum.isVerySimilar(_currentViewFrustum); if (frustumChanges) { @@ -247,6 +297,11 @@ void OctreeQueryNode::updateLastKnownViewFrustum() { bool OctreeQueryNode::moveShouldDump() const { + // if shutting down, return immediately + if (_isShuttingDown) { + return false; + } + glm::vec3 oldPosition = _lastKnownViewFrustum.getPosition(); glm::vec3 newPosition = _currentViewFrustum.getPosition(); @@ -259,6 +314,11 @@ bool OctreeQueryNode::moveShouldDump() const { } void OctreeQueryNode::dumpOutOfView() { + // if shutting down, return immediately + if (_isShuttingDown) { + return; + } + int stillInView = 0; int outOfView = 0; OctreeElementBag tempBag; diff --git a/assignment-client/src/octree/OctreeQueryNode.h b/assignment-client/src/octree/OctreeQueryNode.h index 20fa2c5858..eab8cb5d0a 100644 --- a/assignment-client/src/octree/OctreeQueryNode.h +++ b/assignment-client/src/octree/OctreeQueryNode.h @@ -28,6 +28,7 @@ public: OctreeQueryNode(); virtual ~OctreeQueryNode(); + void init(); // called after creation to set up some virtual items virtual PacketType getMyPacketType() const = 0; void resetOctreePacket(bool lastWasSurpressed = false); // resets octree packet to after "V" header @@ -91,6 +92,7 @@ public: unsigned int getlastOctreePacketLength() const { return _lastOctreePacketLength; } int getDuplicatePacketCount() const { return _duplicatePacketCount; } + bool isShuttingDown() const { return _isShuttingDown; } private: OctreeQueryNode(const OctreeQueryNode &); @@ -127,6 +129,9 @@ private: OCTREE_PACKET_SEQUENCE _sequenceNumber; quint64 _lastRootTimestamp; + + PacketType _myPacketType; + bool _isShuttingDown; }; #endif /* defined(__hifi__OctreeQueryNode__) */ diff --git a/assignment-client/src/octree/OctreeSendThread.cpp b/assignment-client/src/octree/OctreeSendThread.cpp index caa729e340..9c04c4a1ad 100644 --- a/assignment-client/src/octree/OctreeSendThread.cpp +++ b/assignment-client/src/octree/OctreeSendThread.cpp @@ -65,7 +65,7 @@ bool OctreeSendThread::process() { nodeData = (OctreeQueryNode*) node->getLinkedData(); // Sometimes the node data has not yet been linked, in which case we can't really do anything - if (nodeData) { + if (nodeData && !nodeData->isShuttingDown()) { bool viewFrustumChanged = nodeData->updateCurrentViewFrustum(); packetDistributor(node, nodeData, viewFrustumChanged); } @@ -99,7 +99,14 @@ quint64 OctreeSendThread::_totalBytes = 0; quint64 OctreeSendThread::_totalWastedBytes = 0; quint64 OctreeSendThread::_totalPackets = 0; -int OctreeSendThread::handlePacketSend(const SharedNodePointer& node, OctreeQueryNode* nodeData, int& trueBytesSent, int& truePacketsSent) { +int OctreeSendThread::handlePacketSend(const SharedNodePointer& node, + OctreeQueryNode* nodeData, int& trueBytesSent, int& truePacketsSent) { + + // if we're shutting down, then exit early + if (nodeData->isShuttingDown()) { + return 0; + } + bool debug = _myServer->wantsDebugSending(); quint64 now = usecTimestampNow(); @@ -136,7 +143,7 @@ int OctreeSendThread::handlePacketSend(const SharedNodePointer& node, OctreeQuer // If we've got a stats message ready to send, then see if we can piggyback them together - if (nodeData->stats.isReadyToSend()) { + if (nodeData->stats.isReadyToSend() && !nodeData->isShuttingDown()) { // Send the stats message to the client unsigned char* statsMessage = nodeData->stats.getStatsMessage(); int statsMessageLength = nodeData->stats.getStatsMessageLength(); @@ -203,7 +210,7 @@ int OctreeSendThread::handlePacketSend(const SharedNodePointer& node, OctreeQuer nodeData->stats.markAsSent(); } else { // If there's actually a packet waiting, then send it. - if (nodeData->isPacketWaiting()) { + if (nodeData->isPacketWaiting() && !nodeData->isShuttingDown()) { // just send the voxel packet NodeList::getInstance()->writeDatagram((char*) nodeData->getPacket(), nodeData->getPacketLength(), SharedNodePointer(node)); @@ -234,6 +241,12 @@ int OctreeSendThread::handlePacketSend(const SharedNodePointer& node, OctreeQuer /// Version of voxel distributor that sends the deepest LOD level at once int OctreeSendThread::packetDistributor(const SharedNodePointer& node, OctreeQueryNode* nodeData, bool viewFrustumChanged) { + + // if shutting down, exit early + if (nodeData->isShuttingDown()) { + return 0; + } + int truePacketsSent = 0; int trueBytesSent = 0; int packetsSentThisInterval = 0; @@ -336,7 +349,7 @@ int OctreeSendThread::packetDistributor(const SharedNodePointer& node, OctreeQue int extraPackingAttempts = 0; bool completedScene = false; - while (somethingToSend && packetsSentThisInterval < maxPacketsPerInterval) { + while (somethingToSend && packetsSentThisInterval < maxPacketsPerInterval && !nodeData->isShuttingDown()) { float lockWaitElapsedUsec = OctreeServer::SKIP_TIME; float encodeElapsedUsec = OctreeServer::SKIP_TIME; float compressAndWriteElapsedUsec = OctreeServer::SKIP_TIME; @@ -503,7 +516,7 @@ int OctreeSendThread::packetDistributor(const SharedNodePointer& node, OctreeQue // Here's where we can/should allow the server to send other data... // send the environment packet // TODO: should we turn this into a while loop to better handle sending multiple special packets - if (_myServer->hasSpecialPacketToSend(node)) { + if (_myServer->hasSpecialPacketToSend(node) && !nodeData->isShuttingDown()) { trueBytesSent += _myServer->sendSpecialPacket(node); truePacketsSent++; packetsSentThisInterval++; diff --git a/assignment-client/src/octree/OctreeServer.cpp b/assignment-client/src/octree/OctreeServer.cpp index 7e6ffe52da..496f9af1a0 100644 --- a/assignment-client/src/octree/OctreeServer.cpp +++ b/assignment-client/src/octree/OctreeServer.cpp @@ -168,7 +168,7 @@ void OctreeServer::trackPacketSendingTime(float time) { void OctreeServer::attachQueryNodeToNode(Node* newNode) { if (!newNode->getLinkedData()) { OctreeQueryNode* newQueryNodeData = _instance->createOctreeQueryNode(); - newQueryNodeData->resetOctreePacket(true); // don't bump sequence + newQueryNodeData->init(); newNode->setLinkedData(newQueryNodeData); } } @@ -784,16 +784,26 @@ void OctreeServer::readPendingDatagrams() { if (packetType == getMyQueryMessageType()) { bool debug = false; if (debug) { - qDebug() << "Got PacketTypeVoxelQuery at" << usecTimestampNow(); + if (matchingNode) { + qDebug() << "Got PacketTypeVoxelQuery at" << usecTimestampNow() << "node:" << *matchingNode; + } else { + qDebug() << "Got PacketTypeVoxelQuery at" << usecTimestampNow() << "node: ??????"; + } } // If we got a PacketType_VOXEL_QUERY, then we're talking to an NodeType_t_AVATAR, and we // need to make sure we have it in our nodeList. if (matchingNode) { + if (debug) { + qDebug() << "calling updateNodeWithDataFromPacket()... node:" << *matchingNode; + } nodeList->updateNodeWithDataFromPacket(matchingNode, receivedPacket); OctreeQueryNode* nodeData = (OctreeQueryNode*) matchingNode->getLinkedData(); if (nodeData && !nodeData->isOctreeSendThreadInitalized()) { + if (debug) { + qDebug() << "calling initializeOctreeSendThread()... node:" << *matchingNode; + } nodeData->initializeOctreeSendThread(this, matchingNode->getUUID()); } } @@ -999,6 +1009,8 @@ void OctreeServer::nodeKilled(SharedNodePointer node) { node->setLinkedData(NULL); // set this first in case another thread comes through and tryes to acces this qDebug() << qPrintable(_safeServerName) << "server deleting Linked Data for node:" << *node; nodeData->deleteLater(); + } else { + qDebug() << qPrintable(_safeServerName) << "server node missing linked data node:" << *node; } } diff --git a/examples/editVoxels.js b/examples/editVoxels.js index 31d483d798..ac0b67407b 100644 --- a/examples/editVoxels.js +++ b/examples/editVoxels.js @@ -20,8 +20,6 @@ var windowDimensions = Controller.getViewportDimensions(); var NEW_VOXEL_SIZE = 1.0; var NEW_VOXEL_DISTANCE_FROM_CAMERA = 3.0; -var ORBIT_RATE_ALTITUDE = 200.0; -var ORBIT_RATE_AZIMUTH = 90.0; var PIXELS_PER_EXTRUDE_VOXEL = 16; var WHEEL_PIXELS_PER_SCALE_CHANGE = 100; var MAX_VOXEL_SCALE = 1.0; @@ -35,21 +33,9 @@ var zFightingSizeAdjust = 0.002; // used to adjust preview voxels to prevent z f var previewLineWidth = 1.5; var oldMode = Camera.getMode(); - +var trackAsOrbitOrPan = false; var isAdding = false; -var isExtruding = false; -var isOrbiting = false; -var isOrbitingFromTouch = false; -var isPanning = false; -var isPanningFromTouch = false; -var touchPointsToOrbit = 2; // you can change these, but be mindful that on some track pads 2 touch points = right click+drag -var touchPointsToPan = 3; -var orbitAzimuth = 0.0; -var orbitAltitude = 0.0; -var orbitCenter = { x: 0, y: 0, z: 0 }; -var orbitPosition = { x: 0, y: 0, z: 0 }; -var torsoToEyeVector = { x: 0, y: 0, z: 0 }; -var orbitRadius = 0.0; +var isExtruding = false; var extrudeDirection = { x: 0, y: 0, z: 0 }; var extrudeScale = 0.0; var lastVoxelPosition = { x: 0, y: 0, z: 0 }; @@ -444,24 +430,11 @@ function getNewVoxelPosition() { return newPosition; } -function fixEulerAngles(eulers) { - var rVal = { x: 0, y: 0, z: eulers.z }; - if (eulers.x >= 90.0) { - rVal.x = 180.0 - eulers.x; - rVal.y = eulers.y - 180.0; - } else if (eulers.x <= -90.0) { - rVal.x = 180.0 - eulers.x; - rVal.y = eulers.y - 180.0; - } - return rVal; -} - var trackLastMouseX = 0; var trackLastMouseY = 0; var trackAsDelete = false; var trackAsRecolor = false; var trackAsEyedropper = false; -var trackAsOrbitOrPan = false; var voxelToolSelected = true; var recolorToolSelected = false; @@ -761,10 +734,6 @@ function trackKeyPressEvent(event) { trackAsEyedropper = true; moveTools(); } - if (event.text == "ALT") { - trackAsOrbitOrPan = true; - moveTools(); - } showPreviewGuides(); } @@ -802,10 +771,6 @@ function trackKeyReleaseEvent(event) { trackAsEyedropper = false; moveTools(); } - if (event.text == "ALT") { - trackAsOrbitOrPan = false; - moveTools(); - } // on F1 toggle the preview mode between cubes and lines if (event.text == "F1") { @@ -816,74 +781,6 @@ function trackKeyReleaseEvent(event) { } } -function startOrbitMode(event) { - mouseX = event.x; - mouseY = event.y; - var pickRay = Camera.computePickRay(event.x, event.y); - var intersection = Voxels.findRayIntersection(pickRay); - - // start orbit camera! - var cameraPosition = Camera.getPosition(); - torsoToEyeVector = Vec3.subtract(cameraPosition, MyAvatar.position); - torsoToEyeVector.x = 0.0; - torsoToEyeVector.z = 0.0; - oldMode = Camera.getMode(); - Camera.setMode("independent"); - Camera.keepLookingAt(intersection.intersection); - // get position for initial azimuth, elevation - orbitCenter = intersection.intersection; - var orbitVector = Vec3.subtract(cameraPosition, orbitCenter); - orbitRadius = Vec3.length(orbitVector); - orbitAzimuth = Math.atan2(orbitVector.z, orbitVector.x); - orbitAltitude = Math.asin(orbitVector.y / Vec3.length(orbitVector)); - - //print("startOrbitMode..."); -} - -function handleOrbitingMove(event) { - var cameraOrientation = Camera.getOrientation(); - var origEulers = Quat.safeEulerAngles(cameraOrientation); - var newEulers = fixEulerAngles(Quat.safeEulerAngles(cameraOrientation)); - var dx = event.x - mouseX; - var dy = event.y - mouseY; - orbitAzimuth += dx / ORBIT_RATE_AZIMUTH; - orbitAltitude += dy / ORBIT_RATE_ALTITUDE; - var orbitVector = { x:(Math.cos(orbitAltitude) * Math.cos(orbitAzimuth)) * orbitRadius, - y:Math.sin(orbitAltitude) * orbitRadius, - z:(Math.cos(orbitAltitude) * Math.sin(orbitAzimuth)) * orbitRadius }; - orbitPosition = Vec3.sum(orbitCenter, orbitVector); - Camera.setPosition(orbitPosition); - - mouseX = event.x; - mouseY = event.y; - //print("handleOrbitingMove..."); -} - -function endOrbitMode(event) { - var cameraOrientation = Camera.getOrientation(); - MyAvatar.position = Vec3.subtract(Camera.getPosition(), torsoToEyeVector); - MyAvatar.headOrientation = cameraOrientation; - Camera.stopLooking(); - Camera.setMode(oldMode); - Camera.setOrientation(cameraOrientation); - //print("endOrbitMode..."); -} - -function startPanMode(event, intersection) { - // start pan camera! - print("handle PAN mode!!!"); -} - -function handlePanMove(event) { - print("PANNING mode!!! "); - //print("isPanning="+isPanning + " inPanningFromTouch="+isPanningFromTouch + " trackAsOrbitOrPan="+trackAsOrbitOrPan); -} - -function endPanMode(event) { - print("ending PAN mode!!!"); -} - - function mousePressEvent(event) { // if our tools are off, then don't do anything @@ -891,36 +788,10 @@ function mousePressEvent(event) { return; } - // Normally, if we're panning or orbiting from touch, ignore these... because our touch takes precedence. - // but In the case of a button="RIGHT" click, we may get some touch messages first, and we actually want to - // cancel any touch mode, and then let the right-click through - if (isOrbitingFromTouch || isPanningFromTouch) { - - // if the user is holding the ALT key AND they are clicking the RIGHT button (or on multi-touch doing a two - // finger touch, then we want to let the new panning behavior take over. - // if it's any other case we still want to bail - if (event.button == "RIGHT" && trackAsOrbitOrPan) { - // cancel our current multitouch operation... - if (isOrbitingFromTouch) { - endOrbitMode(event); - isOrbitingFromTouch = false; - } - if (isPanningFromTouch) { - //print("mousePressEvent... calling endPanMode()"); - endPanMode(event); - isPanningFromTouch = false; - } - // let things fall through - } else { - return; - } - } - // no clicking on overlays while in panning mode if (!trackAsOrbitOrPan) { var clickedOnSomething = false; var clickedOverlay = Overlays.getOverlayAtPoint({x: event.x, y: event.y}); - // If the user clicked on the thumb, handle the slider logic if (clickedOverlay == thumb) { @@ -1008,17 +879,7 @@ function mousePressEvent(event) { calcThumbFromScale(intersection.voxel.s); } - // Note: touch and mouse events can cross paths, so we want to ignore any mouse events that would - // start a pan or orbit if we're already doing a pan or orbit via touch... - if ((event.isAlt || trackAsOrbitOrPan) && !(isOrbitingFromTouch || isPanningFromTouch)) { - if (event.isLeftButton && !event.isRightButton) { - startOrbitMode(event); - isOrbiting = true; - } else if (event.isRightButton && !event.isLeftButton) { - startPanMode(event); - isPanning = true; - } - } else if (trackAsDelete || event.isRightButton && !trackAsEyedropper) { + if (trackAsDelete || event.isRightButton && !trackAsEyedropper) { // Delete voxel voxelDetails = calculateVoxelFromIntersection(intersection,"delete"); Voxels.eraseVoxel(voxelDetails.x, voxelDetails.y, voxelDetails.z, voxelDetails.s); @@ -1209,23 +1070,7 @@ function mouseMoveEvent(event) { return; } - // if we're panning or orbiting from touch, ignore these... because our touch takes precedence. - if (isOrbitingFromTouch || isPanningFromTouch) { - return; - } - // double check that we didn't accidentally miss a pan or orbit click request - if (trackAsOrbitOrPan && !isPanning && !isOrbiting) { - if (event.isLeftButton && !event.isRightButton) { - startOrbitMode(event); - isOrbiting = true; - } - if (!event.isLeftButton && event.isRightButton) { - startPanMode(event); - isPanning = true; - } - } - if (!trackAsOrbitOrPan && isMovingSlider) { thumbX = (event.x - thumbClickOffsetX) - sliderX; if (thumbX < minThumbX) { @@ -1236,10 +1081,6 @@ function mouseMoveEvent(event) { } calcScaleFromThumb(thumbX); - } else if (isOrbiting) { - handleOrbitingMove(event); - } else if (isPanning) { - handlePanMove(event); } else if (!trackAsOrbitOrPan && isAdding) { // Watch the drag direction to tell which way to 'extrude' this voxel if (!isExtruding) { @@ -1293,16 +1134,6 @@ function mouseReleaseEvent(event) { if (isMovingSlider) { isMovingSlider = false; } - - if (isOrbiting) { - endOrbitMode(event); - isOrbiting = false; - } - if (isPanning) { - print("mouseReleaseEvent... calling endPanMode()"); - endPanMode(event); - isPanning = false; - } isAdding = false; isExtruding = false; } @@ -1396,96 +1227,18 @@ function touchBeginEvent(event) { if (!editToolsOn) { return; } - - // if we're already in the middle of orbiting or panning, then ignore these multi-touch events... - if (isOrbiting || isPanning) { - return; - } - - if (event.isAlt || trackAsOrbitOrPan) { - if (event.touchPoints == touchPointsToOrbit) { - // we need to double check that we didn't start an orbit, because the touch events will sometimes - // come in as 2 then 3 touches... - if (isPanningFromTouch) { - print("touchBeginEvent... calling endPanMode()"); - endPanMode(event); - isPanningFromTouch = false; - } - startOrbitMode(event); - isOrbitingFromTouch = true; - } else if (event.touchPoints == touchPointsToPan) { - // we need to double check that we didn't start an orbit, because the touch events will sometimes - // come in as 2 then 3 touches... - if (isOrbitingFromTouch) { - endOrbitMode(event); - isOrbitingFromTouch = false; - } - startPanMode(event); - isPanningFromTouch = true; - } - } } function touchUpdateEvent(event) { if (!editToolsOn) { return; } - - // if we're already in the middle of orbiting or panning, then ignore these multi-touch events... - if (isOrbiting || isPanning) { - return; - } - - if (isOrbitingFromTouch) { - // we need to double check that we didn't start an orbit, because the touch events will sometimes - // come in as 2 then 3 touches... - if (event.touchPoints == touchPointsToPan) { - //print("we now have touchPointsToPan touches... switch to pan..."); - endOrbitMode(event); - isOrbitingFromTouch = false; - startPanMode(event); - isPanningFromTouch = true; - } else { - handleOrbitingMove(event); - } - } - if (isPanningFromTouch) { - //print("touchUpdateEvent... isPanningFromTouch... event.touchPoints=" + event.touchPoints); - // we need to double check that we didn't start an orbit, because the touch events will sometimes - // come in as 2 then 3 touches... - if (event.touchPoints == touchPointsToOrbit) { - //print("we now have touchPointsToOrbit touches... switch to orbit..."); - //print("touchUpdateEvent... calling endPanMode()"); - endPanMode(event); - isPanningFromTouch = false; - startOrbitMode(event); - isOrbitingFromTouch = true; - handleOrbitingMove(event); - } else { - handlePanMove(event); - } - } } function touchEndEvent(event) { if (!editToolsOn) { return; } - - // if we're already in the middle of orbiting or panning, then ignore these multi-touch events... - if (isOrbiting || isPanning) { - return; - } - - if (isOrbitingFromTouch) { - endOrbitMode(event); - isOrbitingFromTouch = false; - } - if (isPanningFromTouch) { - print("touchEndEvent... calling endPanMode()"); - endPanMode(event); - isPanningFromTouch = false; - } } var lastFingerAddVoxel = { x: -1, y: -1, z: -1}; // off of the build-able area diff --git a/examples/inspect.js b/examples/inspect.js new file mode 100644 index 0000000000..9292450784 --- /dev/null +++ b/examples/inspect.js @@ -0,0 +1,240 @@ +// +// inspect.js +// hifi +// +// Created by Clément Brisset on March 20, 2014 +// Copyright (c) 2014 HighFidelity, Inc. All rights reserved. +// +// Allows you to inspect non moving objects (Voxels or Avatars) using Atl, Control (Command on Mac) and Shift +// +// radial mode = hold ALT +// orbit mode = hold ALT + CONTROL +// pan mode = hold ALT + CONTROL + SHIFT +// Once you are in a mode left click on the object to inspect and hold the click +// Dragging the mouse will move your camera according to the mode you are in. +// + +var AZIMUTH_RATE = 90.0; +var ALTITUDE_RATE = 200.0; +var RADIUS_RATE = 20.0; +var PAN_RATE = 50.0; + +var alt = false; +var shift = false; +var control = false; + +var isActive = false; + +var noMode = 0; +var orbitMode = 1; +var radialMode = 2; +var panningMode = 3; + +var mode = noMode; + +var mouseLastX = 0; +var mouseLastY = 0; + + +var center = { x: 0, y: 0, z: 0 }; +var position = { x: 0, y: 0, z: 0 }; +var vector = { x: 0, y: 0, z: 0 }; +var radius = 0.0; +var azimuth = 0.0; +var altitude = 0.0; + + +function handleRadialMode(dx, dy) { + azimuth += dx / AZIMUTH_RATE; + radius += radius * dy / RADIUS_RATE; + if (radius < 1) { + radius = 1; + } + + vector = { x: (Math.cos(altitude) * Math.cos(azimuth)) * radius, + y: Math.sin(altitude) * radius, + z: (Math.cos(altitude) * Math.sin(azimuth)) * radius }; + position = Vec3.sum(center, vector); + Camera.setPosition(position); +} + +function handleOrbitMode(dx, dy) { + azimuth += dx / AZIMUTH_RATE; + altitude += dy / ALTITUDE_RATE; + + vector = { x:(Math.cos(altitude) * Math.cos(azimuth)) * radius, + y:Math.sin(altitude) * radius, + z:(Math.cos(altitude) * Math.sin(azimuth)) * radius }; + position = Vec3.sum(center, vector); + Camera.setPosition(position); +} + + +function handlePanMode(dx, dy) { + var up = Quat.getUp(Camera.getOrientation()); + var right = Quat.getRight(Camera.getOrientation()); + var distance = Vec3.length(vector); + + var dv = Vec3.sum(Vec3.multiply(up, - distance * dy / PAN_RATE), Vec3.multiply(right, distance * dx / PAN_RATE)); + + center = Vec3.sum(center, dv); + position = Vec3.sum(position, dv); + + Camera.setPosition(position); + Camera.keepLookingAt(center); +} + +function saveCameraState() { + oldMode = Camera.getMode(); + var oldPosition = Camera.getPosition(); + Camera.setMode("independent"); + Camera.setPosition(oldPosition); +} + +function restoreCameraState() { + Camera.stopLooking(); + Camera.setMode(oldMode); +} + +function handleModes() { + var newMode = noMode; + if (alt) { + if (control) { + if (shift) { + newMode = panningMode; + } else { + newMode = orbitMode; + } + } else { + newMode = radialMode; + } + } + + // if leaving noMode + if (mode == noMode && newMode != noMode) { + saveCameraState(); + } + // if entering noMode + if (newMode == noMode && mode != noMode) { + restoreCameraState(); + } + + mode = newMode; +} + +function keyPressEvent(event) { + var changed = false; + + if (event.text == "ALT") { + alt = true; + changed = true; + } + if (event.text == "CONTROL") { + control = true; + changed = true; + } + if (event.text == "SHIFT") { + shift = true; + changed = true; + } + + if (changed) { + handleModes(); + } +} + +function keyReleaseEvent(event) { + var changed = false; + + if (event.text == "ALT") { + alt = false; + changed = true; + } + if (event.text == "CONTROL") { + control = false; + changed = true; + } + if (event.text == "SHIFT") { + shift = false; + changed = true; + } + + if (changed) { + handleModes(); + } +} + +function mousePressEvent(event) { + if (alt) { + isActive = true; + mouseLastX = event.x; + mouseLastY = event.y; + + // Compute trajectories related values + var pickRay = Camera.computePickRay(mouseLastX, mouseLastY); + var intersection = Voxels.findRayIntersection(pickRay); + + position = Camera.getPosition(); + + avatarTarget = MyAvatar.getTargetAvatarPosition(); + voxelTarget = intersection.intersection; + if (Vec3.length(Vec3.subtract(avatarTarget, position)) < Vec3.length(Vec3.subtract(voxelTarget, position))) { + if (avatarTarget.x != 0 || avatarTarget.y != 0 || avatarTarget.z != 0) { + center = avatarTarget; + } else { + center = voxelTarget; + } + } else { + if (voxelTarget.x != 0 || voxelTarget.y != 0 || voxelTarget.z != 0) { + center = voxelTarget; + } else { + center = avatarTarget; + } + } + + vector = Vec3.subtract(position, center); + radius = Vec3.length(vector); + azimuth = Math.atan2(vector.z, vector.x); + altitude = Math.asin(vector.y / Vec3.length(vector)); + + Camera.keepLookingAt(center); + } +} + +function mouseReleaseEvent(event) { + if (isActive) { + isActive = false; + } +} + +function mouseMoveEvent(event) { + if (isActive && mode != noMode) { + if (mode == radialMode) { + handleRadialMode(event.x - mouseLastX, event.y - mouseLastY); + } + if (mode == orbitMode) { + handleOrbitMode(event.x - mouseLastX, event.y - mouseLastY); + } + if (mode == panningMode) { + handlePanMode(event.x - mouseLastX, event.y - mouseLastY); + } + + mouseLastX = event.x; + mouseLastY = event.y; + } +} + +function scriptEnding() { + if (mode != noMode) { + restoreCameraState(); + } +} + +Controller.keyPressEvent.connect(keyPressEvent); +Controller.keyReleaseEvent.connect(keyReleaseEvent); + +Controller.mousePressEvent.connect(mousePressEvent); +Controller.mouseReleaseEvent.connect(mouseReleaseEvent); +Controller.mouseMoveEvent.connect(mouseMoveEvent); + +Script.scriptEnding.connect(scriptEnding); \ No newline at end of file diff --git a/interface/resources/visage/tracker.cfg b/interface/resources/visage/tracker.cfg index 10744da6e5..2efb7f3463 100644 --- a/interface/resources/visage/tracker.cfg +++ b/interface/resources/visage/tracker.cfg @@ -40,7 +40,7 @@ detect_strip_roi_width 2 detect_strip_roi_height 4 smoothing_factors - 150 5 -2 100 -1 50 50 0 + 5 25 -2 100 -1 50 25 0 #translation rotation action_units eyebrows mouth gaze eye_closure other process_eyes 1 diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 2243c49ac8..030e3bc6fd 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1690,7 +1690,7 @@ void Application::updateMyAvatarLookAtPosition() { } else { // look in direction of the mouse ray, but use distance from intersection, if any float distance = TREE_SCALE; - if (_myAvatar->getLookAtTargetAvatar()) { + if (_myAvatar->getLookAtTargetAvatar() && _myAvatar != _myAvatar->getLookAtTargetAvatar()) { distance = glm::distance(_mouseRayOrigin, static_cast(_myAvatar->getLookAtTargetAvatar())->getHead()->calculateAverageEyePosition()); } diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index d8a7ce5acb..e62c7e1102 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -138,9 +138,8 @@ Menu::Menu() : this, SLOT(goTo())); - addDisabledActionAndSeparator(fileMenu, "Upload/Browse"); - addActionToQMenuAndActionHash(fileMenu, MenuOption::UploaderAvatarHead, 0, Application::getInstance(), SLOT(uploadFST())); - addActionToQMenuAndActionHash(fileMenu, MenuOption::UploaderAvatarSkeleton, 0, Application::getInstance(), SLOT(uploadFST())); + addDisabledActionAndSeparator(fileMenu, "Upload Avatar Model"); + addActionToQMenuAndActionHash(fileMenu, MenuOption::UploadFST, 0, Application::getInstance(), SLOT(uploadFST())); addDisabledActionAndSeparator(fileMenu, "Settings"); addActionToQMenuAndActionHash(fileMenu, MenuOption::SettingsImport, 0, this, SLOT(importSettings())); @@ -715,7 +714,6 @@ void Menu::editPreferences() { QPushButton headBrowseButton(BROWSE_BUTTON_TEXT); connect(&headBrowseButton, SIGNAL(clicked()), &headBrowser, SLOT(browse())); connect(&headBrowser, SIGNAL(selected(QString)), &headURLEdit, SLOT(setText(QString))); - headURLEdit.setReadOnly(true); headURLEdit.setMinimumWidth(QLINE_MINIMUM_WIDTH); headURLEdit.setPlaceholderText(DEFAULT_HEAD_MODEL_URL.toString()); headModelLayout.addWidget(&headURLEdit); @@ -728,7 +726,6 @@ void Menu::editPreferences() { QPushButton SkeletonBrowseButton(BROWSE_BUTTON_TEXT); connect(&SkeletonBrowseButton, SIGNAL(clicked()), &skeletonBrowser, SLOT(browse())); connect(&skeletonBrowser, SIGNAL(selected(QString)), &skeletonURLEdit, SLOT(setText(QString))); - skeletonURLEdit.setReadOnly(true); skeletonURLEdit.setMinimumWidth(QLINE_MINIMUM_WIDTH); skeletonURLEdit.setPlaceholderText(DEFAULT_BODY_MODEL_URL.toString()); skeletonModelLayout.addWidget(&skeletonURLEdit); diff --git a/interface/src/Menu.h b/interface/src/Menu.h index f99ebe0685..cb0ca4c5c4 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -295,8 +295,7 @@ namespace MenuOption { const QString StopAllScripts = "Stop All Scripts"; const QString TestPing = "Test Ping"; const QString TransmitterDrive = "Transmitter Drive"; - const QString UploaderAvatarHead = "Upload Avatar Head"; - const QString UploaderAvatarSkeleton = "Upload Avatar Skeleton"; + const QString UploadFST = "Upload FST file"; const QString Visage = "Visage"; const QString Quit = "Quit"; const QString Voxels = "Voxels"; diff --git a/interface/src/ModelBrowser.h b/interface/src/ModelBrowser.h index 9eb27f5db6..4628642e89 100644 --- a/interface/src/ModelBrowser.h +++ b/interface/src/ModelBrowser.h @@ -31,7 +31,7 @@ public: ~ModelBrowser(); signals: - void selected(QString); + void selected(QString filename); public slots: void browse(); diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index fb0d704c6a..13e2d3321a 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -582,16 +582,15 @@ void MyAvatar::updateLookAtTargetAvatar() { foreach (const AvatarSharedPointer& avatarPointer, Application::getInstance()->getAvatarManager().getAvatarHash()) { Avatar* avatar = static_cast(avatarPointer.data()); - if (avatar == static_cast(this)) { - continue; - } float distance; if (avatar->findRayIntersection(mouseOrigin, mouseDirection, distance)) { _lookAtTargetAvatar = avatarPointer; + _targetAvatarPosition = avatarPointer->getPosition(); return; } } _lookAtTargetAvatar.clear(); + _targetAvatarPosition = glm::vec3(0, 0, 0); } } diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index 3544fb1a61..d958103fa6 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -76,6 +76,7 @@ public: void orbit(const glm::vec3& position, int deltaX, int deltaY); + Q_INVOKABLE glm::vec3 getTargetAvatarPosition() const { return _targetAvatarPosition; } AvatarData* getLookAtTargetAvatar() const { return _lookAtTargetAvatar.data(); } void updateLookAtTargetAvatar(); void clearLookAtTargetAvatar(); @@ -116,6 +117,7 @@ private: glm::vec3 _moveTarget; int _moveTargetStepCounter; QWeakPointer _lookAtTargetAvatar; + glm::vec3 _targetAvatarPosition; bool _shouldRender; bool _billboardValid; diff --git a/interface/src/renderer/TextureCache.cpp b/interface/src/renderer/TextureCache.cpp index 2b43c89998..b3820abf25 100644 --- a/interface/src/renderer/TextureCache.cpp +++ b/interface/src/renderer/TextureCache.cpp @@ -313,7 +313,7 @@ void ImageReader::run() { int imageArea = image.width() * image.height(); if (opaquePixels == imageArea) { qDebug() << "Image with alpha channel is completely opaque:" << url; - image.convertToFormat(QImage::Format_RGB888); + image = image.convertToFormat(QImage::Format_RGB888); } QMetaObject::invokeMethod(texture.data(), "setImage", Q_ARG(const QImage&, image), Q_ARG(bool, translucentPixels >= imageArea / 2)); diff --git a/libraries/audio/src/AudioRingBuffer.cpp b/libraries/audio/src/AudioRingBuffer.cpp index 0e92ab4eb5..cf35b6a20f 100644 --- a/libraries/audio/src/AudioRingBuffer.cpp +++ b/libraries/audio/src/AudioRingBuffer.cpp @@ -19,7 +19,8 @@ AudioRingBuffer::AudioRingBuffer(int numFrameSamples) : NodeData(), _sampleCapacity(numFrameSamples * RING_BUFFER_LENGTH_FRAMES), _isStarved(true), - _hasStarted(false) + _hasStarted(false), + _averageLoudness(0) { if (numFrameSamples) { _buffer = new int16_t[_sampleCapacity]; @@ -55,18 +56,22 @@ int AudioRingBuffer::parseData(const QByteArray& packet) { return writeData(packet.data() + numBytesPacketHeader, packet.size() - numBytesPacketHeader); } -float AudioRingBuffer::averageLoudnessForBoundarySamples(int numSamples) { +void AudioRingBuffer::updateAverageLoudnessForBoundarySamples(int numSamples) { // ForBoundarySamples means that we expect the number of samples not to roll of the end of the ring buffer - float averageLoudness = 0; + float nextLoudness = 0; for (int i = 0; i < numSamples; ++i) { - averageLoudness += fabsf(_nextOutput[i]); + nextLoudness += fabsf(_nextOutput[i]); } - averageLoudness /= numSamples; - averageLoudness /= MAX_SAMPLE_VALUE; + nextLoudness /= numSamples; + nextLoudness /= MAX_SAMPLE_VALUE; + + const int TRAILING_AVERAGE_FRAMES = 100; + const float CURRENT_FRAME_RATIO = 1.0f / TRAILING_AVERAGE_FRAMES; + const float PREVIOUS_FRAMES_RATIO = 1 - CURRENT_FRAME_RATIO; - return averageLoudness; + _averageLoudness = (_averageLoudness * PREVIOUS_FRAMES_RATIO) + (CURRENT_FRAME_RATIO * nextLoudness); } qint64 AudioRingBuffer::readSamples(int16_t* destination, qint64 maxSamples) { diff --git a/libraries/audio/src/AudioRingBuffer.h b/libraries/audio/src/AudioRingBuffer.h index 0beb45ca18..efc85ac94b 100644 --- a/libraries/audio/src/AudioRingBuffer.h +++ b/libraries/audio/src/AudioRingBuffer.h @@ -50,7 +50,8 @@ public: const int16_t* getNextOutput() { return _nextOutput; } const int16_t* getBuffer() { return _buffer; } - float averageLoudnessForBoundarySamples(int numSamples); + void updateAverageLoudnessForBoundarySamples(int numSamples); + float getAverageLoudness() const { return _averageLoudness; } qint64 readSamples(int16_t* destination, qint64 maxSamples); qint64 writeSamples(const int16_t* source, qint64 maxSamples); @@ -85,6 +86,8 @@ protected: int16_t* _buffer; bool _isStarved; bool _hasStarted; + + float _averageLoudness; }; #endif /* defined(__interface__AudioRingBuffer__) */ diff --git a/libraries/audio/src/PositionalAudioRingBuffer.cpp b/libraries/audio/src/PositionalAudioRingBuffer.cpp index 66a27647d6..8fb3d64e7d 100644 --- a/libraries/audio/src/PositionalAudioRingBuffer.cpp +++ b/libraries/audio/src/PositionalAudioRingBuffer.cpp @@ -50,6 +50,7 @@ int PositionalAudioRingBuffer::parseData(const QByteArray& packet) { int16_t numSilentSamples; memcpy(&numSilentSamples, packet.data() + readBytes, sizeof(int16_t)); + readBytes += sizeof(int16_t); addSilentFrame(numSilentSamples); @@ -79,13 +80,11 @@ int PositionalAudioRingBuffer::parsePositionalData(const QByteArray& positionalB bool PositionalAudioRingBuffer::shouldBeAddedToMix(int numJitterBufferSamples) { if (!isNotStarvedOrHasMinimumSamples(NETWORK_BUFFER_LENGTH_SAMPLES_PER_CHANNEL + numJitterBufferSamples)) { if (_shouldOutputStarveDebug) { - qDebug() << "Starved and do not have minimum samples to start. Buffer held back."; _shouldOutputStarveDebug = false; } return false; } else if (samplesAvailable() < NETWORK_BUFFER_LENGTH_SAMPLES_PER_CHANNEL) { - qDebug() << "Do not have number of samples needed for interval. Buffer starved."; _isStarved = true; // reset our _shouldOutputStarveDebug to true so the next is printed diff --git a/libraries/shared/src/FileDownloader.h b/libraries/shared/src/FileDownloader.h index a2ed0b8ccb..593b39b013 100644 --- a/libraries/shared/src/FileDownloader.h +++ b/libraries/shared/src/FileDownloader.h @@ -28,7 +28,7 @@ public: static QByteArray download(const QUrl dataURL, int timeout = 0); signals: - void done(QNetworkReply::NetworkError); + void done(QNetworkReply::NetworkError error); private slots: void processReply(QNetworkReply* reply); diff --git a/libraries/shared/src/FstReader.cpp b/libraries/shared/src/FstReader.cpp index d82ddf68a3..a803583598 100644 --- a/libraries/shared/src/FstReader.cpp +++ b/libraries/shared/src/FstReader.cpp @@ -24,6 +24,7 @@ static const QString NAME_FIELD = "name"; static const QString FILENAME_FIELD = "filename"; static const QString TEXDIR_FIELD = "texdir"; static const QString LOD_FIELD = "lod"; +static const QString HEAD_SPECIFIC_FIELD = "bs"; static const QString MODEL_URL = "/api/v1/models"; @@ -32,6 +33,7 @@ static const int MAX_SIZE = 10 * 1024 * 1024; // 10 MB FstReader::FstReader() : _lodCount(-1), _texturesCount(-1), + _isHead(false), _readyToSend(false), _dataMultiPart(new QHttpMultiPart(QHttpMultiPart::FormDataType)) { @@ -81,13 +83,15 @@ bool FstReader::zip() { } // according to what is read, we modify the command - if (line.first() == NAME_FIELD) { + if (line[1] == HEAD_SPECIFIC_FIELD) { + _isHead = true; + } else if (line[1] == NAME_FIELD) { QHttpPart textPart; textPart.setHeader(QNetworkRequest::ContentDispositionHeader, "form-data;" " name=\"model_name\""); textPart.setBody(line[1].toUtf8()); _dataMultiPart->append(textPart); - } else if (line.first() == FILENAME_FIELD) { + } else if (line[1] == FILENAME_FIELD) { QFileInfo fbx(QFileInfo(fst).path() + "/" + line[1]); if (!fbx.exists() || !fbx.isFile()) { // Check existence qDebug() << "[ERROR] FBX file " << fbx.absoluteFilePath() << " doesn't exist."; @@ -101,7 +105,7 @@ bool FstReader::zip() { if (!addPart(_zipDir.path() + "/" + line[1], "fbx")) { return false; } - } else if (line.first() == TEXDIR_FIELD) { // Check existence + } else if (line[1] == TEXDIR_FIELD) { // Check existence QFileInfo texdir(QFileInfo(fst).path() + "/" + line[1]); if (!texdir.exists() || !texdir.isDir()) { qDebug() << "[ERROR] Texture directory " << texdir.absolutePath() << " doesn't exist."; @@ -110,7 +114,7 @@ bool FstReader::zip() { if (!addTextures(texdir)) { // Recursive compress and copy return false; } - } else if (line.first() == LOD_FIELD) { + } else if (line[1] == LOD_FIELD) { QFileInfo lod(QFileInfo(fst).path() + "/" + line[1]); if (!lod.exists() || !lod.isFile()) { // Check existence qDebug() << "[ERROR] FBX file " << lod.absoluteFilePath() << " doesn't exist."; @@ -127,6 +131,17 @@ bool FstReader::zip() { } } + + QHttpPart textPart; + textPart.setHeader(QNetworkRequest::ContentDispositionHeader, "form-data;" + " name=\"model_category\""); + if (_isHead) { + textPart.setBody("head"); + } else { + textPart.setBody("skeleton"); + } + _dataMultiPart->append(textPart); + _readyToSend = true; return true; } diff --git a/libraries/shared/src/FstReader.h b/libraries/shared/src/FstReader.h index aab42bd967..1d9da71641 100644 --- a/libraries/shared/src/FstReader.h +++ b/libraries/shared/src/FstReader.h @@ -27,6 +27,7 @@ private: int _lodCount; int _texturesCount; int _totalSize; + bool _isHead; bool _readyToSend; QHttpMultiPart* _dataMultiPart; diff --git a/libraries/shared/src/ThreadedAssignment.cpp b/libraries/shared/src/ThreadedAssignment.cpp index c4282028ae..b3a54b1488 100644 --- a/libraries/shared/src/ThreadedAssignment.cpp +++ b/libraries/shared/src/ThreadedAssignment.cpp @@ -56,6 +56,7 @@ void ThreadedAssignment::commonInit(const QString& targetName, NodeType_t nodeTy void ThreadedAssignment::checkInWithDomainServerOrExit() { if (NodeList::getInstance()->getNumNoReplyDomainCheckIns() == MAX_SILENT_DOMAIN_SERVER_CHECK_INS) { + qDebug() << "NRDC:" << NodeList::getInstance()->getNumNoReplyDomainCheckIns(); setFinished(true); } else { NodeList::getInstance()->sendDomainServerCheckIn();