From ed4ad1cea974b27aaf3bf3b658b0e625ca3c217a Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Thu, 2 Jan 2014 15:37:56 -0800 Subject: [PATCH 01/39] Removed some unused code. --- interface/src/avatar/Avatar.cpp | 4 ---- interface/src/avatar/MyAvatar.cpp | 6 +----- 2 files changed, 1 insertion(+), 9 deletions(-) diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index 02141d3252..d570e112cc 100755 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -190,10 +190,6 @@ void Avatar::simulate(float deltaTime, Transmitter* transmitter) { // update avatar skeleton _skeleton.update(deltaTime, getOrientation(), _position); - - // if this is not my avatar, then hand position comes from transmitted data - _skeleton.joint[ AVATAR_JOINT_RIGHT_FINGERTIPS ].position = _handPosition; - _hand.simulate(deltaTime, false); _skeletonModel.simulate(deltaTime); _head.setBodyRotation(glm::vec3(_bodyPitch, _bodyYaw, _bodyRoll)); diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index cdcaeb2b0e..4040ca63e7 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -751,15 +751,11 @@ void MyAvatar::updateHandMovementAndTouching(float deltaTime, bool enableHandMov glm::vec3 farVector = _mouseRayOrigin + pointDirection * (float)TREE_SCALE - shoulderPosition; const float ARM_RETRACTION = 0.75f; float retractedLength = _skeletonModel.getRightArmLength() * ARM_RETRACTION; - _skeleton.joint[AVATAR_JOINT_RIGHT_FINGERTIPS].position = shoulderPosition + - glm::normalize(farVector) * retractedLength; + setHandPosition(shoulderPosition + glm::normalize(farVector) * retractedLength); pointing = true; } } - //Set right hand position and state to be transmitted, and also tell AvatarTouch about it - setHandPosition(_skeleton.joint[ AVATAR_JOINT_RIGHT_FINGERTIPS ].position); - if (_mousePressed) { _handState = HAND_STATE_GRASPING; } else if (pointing) { From 2814cca9afdf43e6aa4d59797a76e0240da8e23a Mon Sep 17 00:00:00 2001 From: stojce Date: Fri, 3 Jan 2014 21:02:56 +0100 Subject: [PATCH 02/39] #19466 - Add avatar scale to preferences panel --- interface/src/Menu.cpp | 5 +++++ interface/src/avatar/Avatar.h | 2 -- interface/src/ui/LogDialog.h | 1 + libraries/avatars/src/AvatarData.cpp | 10 ++++++++++ libraries/avatars/src/AvatarData.h | 5 ++++- 5 files changed, 20 insertions(+), 3 deletions(-) diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index d4e355d20f..545a765dce 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -824,6 +824,10 @@ void Menu::editPreferences() { leanScale->setValue(applicationInstance->getAvatar()->getLeanScale()); form->addRow("Lean Scale:", leanScale); + QDoubleSpinBox* avatarScale = new QDoubleSpinBox(); + avatarScale->setValue(applicationInstance->getAvatar()->getScale()); + form->addRow("Avatar Scale:", avatarScale); + QSpinBox* audioJitterBufferSamples = new QSpinBox(); audioJitterBufferSamples->setMaximum(10000); audioJitterBufferSamples->setMinimum(-10000); @@ -887,6 +891,7 @@ void Menu::editPreferences() { _maxVoxelPacketsPerSecond = maxVoxelsPPS->value(); applicationInstance->getAvatar()->setLeanScale(leanScale->value()); + applicationInstance->getAvatar()->setNewScale(avatarScale->value()); _audioJitterBufferSamples = audioJitterBufferSamples->value(); diff --git a/interface/src/avatar/Avatar.h b/interface/src/avatar/Avatar.h index 39df62f603..1aa92b98ad 100755 --- a/interface/src/avatar/Avatar.h +++ b/interface/src/avatar/Avatar.h @@ -24,8 +24,6 @@ #include "devices/SerialInterface.h" #include "devices/Transmitter.h" -static const float MAX_SCALE = 1000.f; -static const float MIN_SCALE = .005f; static const float SCALING_RATIO = .05f; static const float SMOOTHING_RATIO = .05f; // 0 < ratio < 1 static const float RESCALING_TOLERANCE = .02f; diff --git a/interface/src/ui/LogDialog.h b/interface/src/ui/LogDialog.h index 9a0481546e..6741012a26 100644 --- a/interface/src/ui/LogDialog.h +++ b/interface/src/ui/LogDialog.h @@ -15,6 +15,7 @@ #include #include #include +#include #include "AbstractLoggerInterface.h" diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index 0a90f4db96..14115ced47 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -295,3 +295,13 @@ int AvatarData::parseData(unsigned char* sourceBuffer, int numBytes) { return sourceBuffer - startPosition; } + +void AvatarData::setNewScale(float newScale) { + if (newScale > MAX_SCALE) { + newScale = MAX_SCALE; + } else if (newScale < MIN_SCALE) { + newScale = MIN_SCALE; + } + _newScale = newScale; + qDebug() << "Changed scale to " << _newScale << "\n"; +} diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index 1dd5dacafe..32acf3583f 100755 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -32,6 +32,9 @@ const int HAND_STATE_START_BIT = 2; // 3rd and 4th bits const int IS_FACESHIFT_CONNECTED = 4; // 5th bit const int IS_CHAT_CIRCLING_ENABLED = 5; +static const float MAX_SCALE = 1000.f; +static const float MIN_SCALE = .005f; + const float MAX_AUDIO_LOUDNESS = 1000.0; // close enough for mouth animation enum KeyState @@ -80,7 +83,7 @@ public: // Scale float getNewScale() const { return _newScale; } - void setNewScale(float newScale) { _newScale = newScale; } + void setNewScale(float); // Hand State void setHandState(char s) { _handState = s; } From aa9fc290a58ea98723bdce96ca348cce39c9263b Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Fri, 3 Jan 2014 16:46:11 -0800 Subject: [PATCH 03/39] Minimum chat circle radius from Ryan, basic avatar/voxel orbit behavior. --- interface/src/Application.cpp | 32 ++++++++++++++++------ interface/src/avatar/Avatar.cpp | 18 ++++++++++++- interface/src/avatar/Avatar.h | 4 ++- interface/src/avatar/Head.h | 1 + interface/src/avatar/MyAvatar.cpp | 15 +++++++++-- interface/src/avatar/MyAvatar.h | 2 ++ interface/src/renderer/Model.cpp | 29 ++++++++++++++++++++ interface/src/renderer/Model.h | 2 ++ libraries/shared/src/GeometryUtil.cpp | 38 +++++++++++++++++++++++++++ libraries/shared/src/GeometryUtil.h | 6 +++++ 10 files changed, 135 insertions(+), 12 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index df09154d46..7d87d3d744 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1105,9 +1105,24 @@ void Application::mouseMoveEvent(QMouseEvent* event) { } if (activeWindow() == _window) { + int deltaX = event->x() - _mouseX; + int deltaY = event->y() - _mouseY; + _mouseX = event->x(); _mouseY = event->y(); + // orbit behavior + if (_mousePressed && !Menu::getInstance()->isVoxelModeActionChecked()) { + if (_lookatTargetAvatar) { + _myAvatar.orbit(_lookatTargetAvatar->getPosition(), deltaX, deltaY); + return; + } + if (_isHoverVoxel) { + _myAvatar.orbit(glm::vec3(_hoverVoxel.x, _hoverVoxel.y, _hoverVoxel.z) * (float)TREE_SCALE, deltaX, deltaY); + return; + } + } + // detect drag glm::vec3 mouseVoxelPos(_mouseVoxel.x, _mouseVoxel.y, _mouseVoxel.z); if (!_justEditedVoxel && mouseVoxelPos != _lastMouseVoxelPos) { @@ -1151,7 +1166,8 @@ void Application::mousePressEvent(QMouseEvent* event) { } if (!_palette.isActive() && (!_isHoverVoxel || _lookatTargetAvatar)) { - _pieMenu.mousePressEvent(_mouseX, _mouseY); + // disable for now + // _pieMenu.mousePressEvent(_mouseX, _mouseY); } if (Menu::getInstance()->isOptionChecked(MenuOption::VoxelSelectMode) && _pasteMode) { @@ -1952,7 +1968,9 @@ void Application::updateLookatTargetAvatar(const glm::vec3& mouseRayOrigin, cons bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings); PerformanceWarning warn(showWarnings, "Application::updateLookatTargetAvatar()"); - _lookatTargetAvatar = findLookatTargetAvatar(mouseRayOrigin, mouseRayDirection, eyePosition, DEFAULT_NODE_ID_REF); + if (!_mousePressed) { + _lookatTargetAvatar = findLookatTargetAvatar(mouseRayOrigin, mouseRayDirection, eyePosition, DEFAULT_NODE_ID_REF); + } } Avatar* Application::findLookatTargetAvatar(const glm::vec3& mouseRayOrigin, const glm::vec3& mouseRayDirection, @@ -1961,17 +1979,15 @@ Avatar* Application::findLookatTargetAvatar(const glm::vec3& mouseRayOrigin, con NodeList* nodeList = NodeList::getInstance(); for (NodeList::iterator node = nodeList->begin(); node != nodeList->end(); node++) { if (node->getLinkedData() != NULL && node->getType() == NODE_TYPE_AGENT) { - Avatar* avatar = (Avatar *) node->getLinkedData(); - glm::vec3 headPosition = avatar->getHead().getPosition(); + Avatar* avatar = (Avatar*)node->getLinkedData(); float distance; - if (rayIntersectsSphere(mouseRayOrigin, mouseRayDirection, headPosition, - HEAD_SPHERE_RADIUS * avatar->getHead().getScale(), distance)) { + if (avatar->findRayIntersection(mouseRayOrigin, mouseRayDirection, distance)) { // rescale to compensate for head embiggening eyePosition = (avatar->getHead().calculateAverageEyePosition() - avatar->getHead().getScalePivot()) * (avatar->getScale() / avatar->getHead().getScale()) + avatar->getHead().getScalePivot(); _lookatIndicatorScale = avatar->getHead().getScale(); - _lookatOtherPosition = headPosition; + _lookatOtherPosition = avatar->getHead().getPosition(); nodeUUID = avatar->getOwningNode()->getUUID(); return avatar; } @@ -2213,7 +2229,7 @@ void Application::updateHoverVoxels(float deltaTime, glm::vec3& mouseRayOrigin, glm::vec4 oldVoxel(_hoverVoxel.x, _hoverVoxel.y, _hoverVoxel.z, _hoverVoxel.s); // only do this work if MAKE_SOUND_ON_VOXEL_HOVER or MAKE_SOUND_ON_VOXEL_CLICK is enabled, // and make sure the tree is not already busy... because otherwise you'll have to wait. - if (!_voxels.treeIsBusy()) { + if (!(_voxels.treeIsBusy() || _mousePressed)) { { PerformanceWarning warn(showWarnings, "Application::updateHoverVoxels() _voxels.findRayIntersection()"); _isHoverVoxel = _voxels.findRayIntersection(mouseRayOrigin, mouseRayDirection, _hoverVoxel, distance, face); diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index d570e112cc..47af6d586c 100755 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -336,8 +336,24 @@ void Avatar::getSkinColors(glm::vec3& lighter, glm::vec3& darker) { } } +bool Avatar::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const { + float minDistance = FLT_MAX; + float modelDistance; + if (_skeletonModel.findRayIntersection(origin, direction, modelDistance)) { + minDistance = qMin(minDistance, modelDistance); + } + if (_head.getFaceModel().findRayIntersection(origin, direction, modelDistance)) { + minDistance = qMin(minDistance, modelDistance); + } + if (minDistance < FLT_MAX) { + distance = minDistance; + return true; + } + return false; +} + bool Avatar::findSpherePenetration(const glm::vec3& penetratorCenter, float penetratorRadius, - glm::vec3& penetration, int skeletonSkipIndex) { + glm::vec3& penetration, int skeletonSkipIndex) const { bool didPenetrate = false; glm::vec3 totalPenetration; glm::vec3 skeletonPenetration; diff --git a/interface/src/avatar/Avatar.h b/interface/src/avatar/Avatar.h index 39df62f603..c74968de9f 100755 --- a/interface/src/avatar/Avatar.h +++ b/interface/src/avatar/Avatar.h @@ -160,6 +160,8 @@ public: void getSkinColors(glm::vec3& lighter, glm::vec3& darker); + bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const; + /// Checks for penetration between the described sphere and the avatar. /// \param penetratorCenter the center of the penetration test sphere /// \param penetratorRadius the radius of the penetration test sphere @@ -167,7 +169,7 @@ public: /// \param skeletonSkipIndex if not -1, the index of a joint to skip (along with its descendents) in the skeleton model /// \return whether or not the sphere penetrated bool findSpherePenetration(const glm::vec3& penetratorCenter, float penetratorRadius, - glm::vec3& penetration, int skeletonSkipIndex = -1); + glm::vec3& penetration, int skeletonSkipIndex = -1) const; virtual int parseData(unsigned char* sourceBuffer, int numBytes); diff --git a/interface/src/avatar/Head.h b/interface/src/avatar/Head.h index 47e998059d..afbf06e68c 100644 --- a/interface/src/avatar/Head.h +++ b/interface/src/avatar/Head.h @@ -78,6 +78,7 @@ public: VideoFace& getVideoFace() { return _videoFace; } FaceModel& getFaceModel() { return _faceModel; } + const FaceModel& getFaceModel() const { return _faceModel; } const bool getReturnToCenter() const { return _returnHeadToCenter; } // Do you want head to try to return to center (depends on interface detected) float getAverageLoudness() const { return _averageLoudness; } diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 4040ca63e7..b42a6ba165 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -542,6 +542,16 @@ void MyAvatar::loadData(QSettings* settings) { settings->endGroup(); } +void MyAvatar::orbit(const glm::vec3& position, int deltaX, int deltaY) { + glm::vec3 vector = getPosition() - position; + glm::quat orientation = getOrientation(); + glm::vec3 up = orientation * IDENTITY_UP; + const float ANGULAR_SCALE = 0.5f; + glm::quat rotation = glm::angleAxis(deltaX * -ANGULAR_SCALE, up); + const float LINEAR_SCALE = 0.01f; + setPosition(position + rotation * vector + up * (deltaY * LINEAR_SCALE * _scale)); + setOrientation(rotation * orientation); +} float MyAvatar::getAbsoluteHeadYaw() const { return glm::yaw(_head.getOrientation()); @@ -909,8 +919,9 @@ void MyAvatar::updateChatCircle(float deltaTime) { // remove members whose accumulated circles are too far away to influence us const float CIRCUMFERENCE_PER_MEMBER = 0.5f; const float CIRCLE_INFLUENCE_SCALE = 2.0f; + const float MIN_RADIUS = 0.3f; for (int i = sortedAvatars.size() - 1; i >= 0; i--) { - float radius = (CIRCUMFERENCE_PER_MEMBER * (i + 2)) / PI_TIMES_TWO; + float radius = qMax(MIN_RADIUS, (CIRCUMFERENCE_PER_MEMBER * (i + 2)) / PI_TIMES_TWO); if (glm::distance(_position, sortedAvatars[i].accumulatedCenter) > radius * CIRCLE_INFLUENCE_SCALE) { sortedAvatars.remove(i); } else { @@ -921,7 +932,7 @@ void MyAvatar::updateChatCircle(float deltaTime) { return; } center = sortedAvatars.last().accumulatedCenter; - float radius = (CIRCUMFERENCE_PER_MEMBER * (sortedAvatars.size() + 1)) / PI_TIMES_TWO; + float radius = qMax(MIN_RADIUS, (CIRCUMFERENCE_PER_MEMBER * (sortedAvatars.size() + 1)) / PI_TIMES_TWO); // compute the average up vector glm::vec3 up = getWorldAlignedOrientation() * IDENTITY_UP; diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index 508f9ce5d2..99cc360d59 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -69,6 +69,8 @@ public: void addThrust(glm::vec3 newThrust) { _thrust += newThrust; }; glm::vec3 getThrust() { return _thrust; }; + void orbit(const glm::vec3& position, int deltaX, int deltaY); + private: bool _mousePressed; float _bodyPitchDelta; diff --git a/interface/src/renderer/Model.cpp b/interface/src/renderer/Model.cpp index e5c23532d8..e7fb6c7c1d 100644 --- a/interface/src/renderer/Model.cpp +++ b/interface/src/renderer/Model.cpp @@ -546,6 +546,35 @@ glm::vec4 Model::computeAverageColor() const { return _geometry ? _geometry->computeAverageColor() : glm::vec4(1.0f, 1.0f, 1.0f, 1.0f); } +bool Model::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const { + const glm::vec3 relativeOrigin = origin - _translation; + const FBXGeometry& geometry = _geometry->getFBXGeometry(); + float minDistance = FLT_MAX; + float radiusScale = extractUniformScale(_scale); + for (int i = 0; i < _jointStates.size(); i++) { + const FBXJoint& joint = geometry.joints[i]; + glm::vec3 end = extractTranslation(_jointStates[i].transform); + float endRadius = joint.boneRadius * radiusScale; + glm::vec3 start = end; + float startRadius = joint.boneRadius * radiusScale; + if (joint.parentIndex != -1) { + start = extractTranslation(_jointStates[joint.parentIndex].transform); + startRadius = geometry.joints[joint.parentIndex].boneRadius * radiusScale; + } + // for now, use average of start and end radii + float capsuleDistance; + if (findRayCapsuleIntersection(relativeOrigin, direction, start, end, + (startRadius + endRadius) / 2.0f, capsuleDistance)) { + minDistance = qMin(minDistance, capsuleDistance); + } + } + if (minDistance < FLT_MAX) { + distance = minDistance; + return true; + } + return false; +} + bool Model::findSpherePenetration(const glm::vec3& penetratorCenter, float penetratorRadius, glm::vec3& penetration, float boneScale, int skipIndex) const { const glm::vec3 relativeCenter = penetratorCenter - _translation; diff --git a/interface/src/renderer/Model.h b/interface/src/renderer/Model.h index 2a97345db0..3b1f66938b 100644 --- a/interface/src/renderer/Model.h +++ b/interface/src/renderer/Model.h @@ -125,6 +125,8 @@ public: /// Returns the average color of all meshes in the geometry. glm::vec4 computeAverageColor() const; + bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const; + bool findSpherePenetration(const glm::vec3& penetratorCenter, float penetratorRadius, glm::vec3& penetration, float boneScale = 1.0f, int skipIndex = -1) const; diff --git a/libraries/shared/src/GeometryUtil.cpp b/libraries/shared/src/GeometryUtil.cpp index 1da05f9fae..2272296d75 100644 --- a/libraries/shared/src/GeometryUtil.cpp +++ b/libraries/shared/src/GeometryUtil.cpp @@ -154,6 +154,44 @@ glm::vec3 addPenetrations(const glm::vec3& currentPenetration, const glm::vec3& newPenetration - (currentDirection * directionalComponent); } +bool findRaySphereIntersection(const glm::vec3& origin, const glm::vec3& direction, + const glm::vec3& center, float radius, float& distance) { + glm::vec3 relativeOrigin = origin - center; + float b = glm::dot(direction, relativeOrigin); + float c = glm::dot(relativeOrigin, relativeOrigin) - radius * radius; + if (c < 0.0f) { + distance = 0.0f; + return true; // starts inside the sphere + } + float radicand = b * b - c; + if (radicand < 0.0f) { + return false; // doesn't hit the sphere + } + float t = -b - sqrtf(radicand); + if (t < 0.0f) { + return false; // doesn't hit the sphere + } + distance = t; + return true; +} + +bool findRayCapsuleIntersection(const glm::vec3& origin, const glm::vec3& direction, + const glm::vec3& start, const glm::vec3& end, float radius, float& distance) { + float minDistance = FLT_MAX; + float sphereDistance; + if (findRaySphereIntersection(origin, direction, start, radius, sphereDistance)) { + minDistance = qMin(minDistance, sphereDistance); + } + if (findRaySphereIntersection(origin, direction, end, radius, sphereDistance)) { + minDistance = qMin(minDistance, sphereDistance); + } + if (minDistance < FLT_MAX) { + distance = minDistance; + return true; + } + return false; +} + // Do line segments (r1p1.x, r1p1.y)--(r1p2.x, r1p2.y) and (r2p1.x, r2p1.y)--(r2p2.x, r2p2.y) intersect? // from: http://ptspts.blogspot.com/2010/06/how-to-determine-if-two-line-segments.html bool doLineSegmentsIntersect(glm::vec2 r1p1, glm::vec2 r1p2, glm::vec2 r2p1, glm::vec2 r2p2) { diff --git a/libraries/shared/src/GeometryUtil.h b/libraries/shared/src/GeometryUtil.h index 8cbb29580a..8044039e55 100644 --- a/libraries/shared/src/GeometryUtil.h +++ b/libraries/shared/src/GeometryUtil.h @@ -49,6 +49,12 @@ bool findCapsulePlanePenetration(const glm::vec3& penetratorStart, const glm::ve glm::vec3 addPenetrations(const glm::vec3& currentPenetration, const glm::vec3& newPenetration); +bool findRaySphereIntersection(const glm::vec3& origin, const glm::vec3& direction, + const glm::vec3& center, float radius, float& distance); + +bool findRayCapsuleIntersection(const glm::vec3& origin, const glm::vec3& direction, + const glm::vec3& start, const glm::vec3& end, float radius, float& distance); + bool doLineSegmentsIntersect(glm::vec2 r1p1, glm::vec2 r1p2, glm::vec2 r2p1, glm::vec2 r2p2); bool isOnSegment(float xi, float yi, float xj, float yj, float xk, float yk); int computeDirection(float xi, float yi, float xj, float yj, float xk, float yk); From 8abd76c0551fcd548fcf4b3587e0cc2ccbbcb82d Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Fri, 3 Jan 2014 17:10:35 -0800 Subject: [PATCH 04/39] fixed issue with voxels not always showing up when changed between clients --- .../octree-server/src/OctreeSendThread.cpp | 106 ++++++++++++------ libraries/octree-server/src/OctreeServer.cpp | 2 +- 2 files changed, 70 insertions(+), 38 deletions(-) diff --git a/libraries/octree-server/src/OctreeSendThread.cpp b/libraries/octree-server/src/OctreeSendThread.cpp index 598db13614..c98b453a9c 100644 --- a/libraries/octree-server/src/OctreeSendThread.cpp +++ b/libraries/octree-server/src/OctreeSendThread.cpp @@ -129,7 +129,8 @@ int OctreeSendThread::handlePacketSend(Node* node, OctreeQueryNode* nodeData, in _totalPackets++; if (debug) { qDebug() << "Adding stats to packet at " << now << " [" << _totalPackets <<"]: sequence: " << sequence << - " size: " << nodeData->getPacketLength() << " [" << _totalBytes << + " statsMessageLength: " << statsMessageLength << + " original size: " << nodeData->getPacketLength() << " [" << _totalBytes << "] wasted bytes:" << thisWastedBytes << " [" << _totalWastedBytes << "]\n"; } @@ -211,6 +212,7 @@ int OctreeSendThread::handlePacketSend(Node* node, OctreeQueryNode* nodeData, in /// Version of voxel distributor that sends the deepest LOD level at once int OctreeSendThread::packetDistributor(Node* node, OctreeQueryNode* nodeData, bool viewFrustumChanged) { + bool forceDebugging = false; int truePacketsSent = 0; int trueBytesSent = 0; @@ -231,16 +233,19 @@ int OctreeSendThread::packetDistributor(Node* node, OctreeQueryNode* nodeData, b // then let's just send that waiting packet. if (!nodeData->getCurrentPacketFormatMatches()) { if (nodeData->isPacketWaiting()) { - if (_myServer->wantsDebugSending() && _myServer->wantsVerboseDebug()) { - printf("wantColor=%s wantCompression=%s SENDING PARTIAL PACKET! currentPacketIsColor=%s currentPacketIsCompressed=%s\n", + if (forceDebugging || (_myServer->wantsDebugSending() && _myServer->wantsVerboseDebug())) { + qDebug("about to call handlePacketSend() .... line: %d -- format change " + "wantColor=%s wantCompression=%s SENDING PARTIAL PACKET! currentPacketIsColor=%s " + "currentPacketIsCompressed=%s\n", + __LINE__, debug::valueOf(wantColor), debug::valueOf(wantCompression), debug::valueOf(nodeData->getCurrentPacketIsColor()), debug::valueOf(nodeData->getCurrentPacketIsCompressed()) ); } packetsSentThisInterval += handlePacketSend(node, nodeData, trueBytesSent, truePacketsSent); } else { - if (_myServer->wantsDebugSending() && _myServer->wantsVerboseDebug()) { - printf("wantColor=%s wantCompression=%s FIXING HEADER! currentPacketIsColor=%s currentPacketIsCompressed=%s\n", + if (forceDebugging || (_myServer->wantsDebugSending() && _myServer->wantsVerboseDebug())) { + qDebug("wantColor=%s wantCompression=%s FIXING HEADER! currentPacketIsColor=%s currentPacketIsCompressed=%s\n", debug::valueOf(wantColor), debug::valueOf(wantCompression), debug::valueOf(nodeData->getCurrentPacketIsColor()), debug::valueOf(nodeData->getCurrentPacketIsCompressed()) ); @@ -252,7 +257,7 @@ int OctreeSendThread::packetDistributor(Node* node, OctreeQueryNode* nodeData, b targetSize = nodeData->getAvailable() - sizeof(OCTREE_PACKET_INTERNAL_SECTION_SIZE); } if (_myServer->wantsDebugSending() && _myServer->wantsVerboseDebug()) { - printf("line:%d _packetData.changeSettings() wantCompression=%s targetSize=%d\n",__LINE__, + qDebug("line:%d _packetData.changeSettings() wantCompression=%s targetSize=%d\n", __LINE__, debug::valueOf(wantCompression), targetSize); } @@ -260,7 +265,7 @@ int OctreeSendThread::packetDistributor(Node* node, OctreeQueryNode* nodeData, b } if (_myServer->wantsDebugSending() && _myServer->wantsVerboseDebug()) { - printf("wantColor/isColor=%s/%s wantCompression/isCompressed=%s/%s viewFrustumChanged=%s, getWantLowResMoving()=%s\n", + qDebug("wantColor/isColor=%s/%s wantCompression/isCompressed=%s/%s viewFrustumChanged=%s, getWantLowResMoving()=%s\n", debug::valueOf(wantColor), debug::valueOf(nodeData->getCurrentPacketIsColor()), debug::valueOf(wantCompression), debug::valueOf(nodeData->getCurrentPacketIsCompressed()), debug::valueOf(viewFrustumChanged), debug::valueOf(nodeData->getWantLowResMoving())); @@ -268,8 +273,8 @@ int OctreeSendThread::packetDistributor(Node* node, OctreeQueryNode* nodeData, b const ViewFrustum* lastViewFrustum = wantDelta ? &nodeData->getLastKnownViewFrustum() : NULL; - if (_myServer->wantsDebugSending() && _myServer->wantsVerboseDebug()) { - printf("packetDistributor() viewFrustumChanged=%s, nodeBag.isEmpty=%s, viewSent=%s\n", + if (forceDebugging || (_myServer->wantsDebugSending() && _myServer->wantsVerboseDebug())) { + qDebug("packetDistributor() viewFrustumChanged=%s, nodeBag.isEmpty=%s, viewSent=%s\n", debug::valueOf(viewFrustumChanged), debug::valueOf(nodeData->nodeBag.isEmpty()), debug::valueOf(nodeData->getViewSent()) ); @@ -279,17 +284,17 @@ int OctreeSendThread::packetDistributor(Node* node, OctreeQueryNode* nodeData, b // the current view frustum for things to send. if (viewFrustumChanged || nodeData->nodeBag.isEmpty()) { uint64_t now = usecTimestampNow(); - if (_myServer->wantsDebugSending() && _myServer->wantsVerboseDebug()) { - printf("(viewFrustumChanged=%s || nodeData->nodeBag.isEmpty() =%s)...\n", + if (forceDebugging || (_myServer->wantsDebugSending() && _myServer->wantsVerboseDebug())) { + qDebug("(viewFrustumChanged=%s || nodeData->nodeBag.isEmpty() =%s)...\n", debug::valueOf(viewFrustumChanged), debug::valueOf(nodeData->nodeBag.isEmpty())); if (nodeData->getLastTimeBagEmpty() > 0) { float elapsedSceneSend = (now - nodeData->getLastTimeBagEmpty()) / 1000000.0f; if (viewFrustumChanged) { - printf("viewFrustumChanged resetting after elapsed time to send scene = %f seconds", elapsedSceneSend); + qDebug("viewFrustumChanged resetting after elapsed time to send scene = %f seconds", elapsedSceneSend); } else { - printf("elapsed time to send scene = %f seconds", elapsedSceneSend); + qDebug("elapsed time to send scene = %f seconds", elapsedSceneSend); } - printf(" [occlusionCulling:%s, wantDelta:%s, wantColor:%s ]\n", + qDebug(" [occlusionCulling:%s, wantDelta:%s, wantColor:%s ]\n", debug::valueOf(nodeData->getWantOcclusionCulling()), debug::valueOf(wantDelta), debug::valueOf(wantColor)); } @@ -316,9 +321,15 @@ int OctreeSendThread::packetDistributor(Node* node, OctreeQueryNode* nodeData, b unsigned long encodeTime = nodeData->stats.getTotalEncodeTime(); unsigned long elapsedTime = nodeData->stats.getElapsedTime(); - packetsSentThisInterval += handlePacketSend(node, nodeData, trueBytesSent, truePacketsSent); - if (_myServer->wantsDebugSending()) { + if (forceDebugging || (_myServer->wantsDebugSending() && _myServer->wantsVerboseDebug())) { + qDebug("about to call handlePacketSend() .... line: %d -- completed scene \n", __LINE__ ); + } + int packetsJustSent = handlePacketSend(node, nodeData, trueBytesSent, truePacketsSent); + packetsSentThisInterval += packetsJustSent; + qDebug("packetsJustSent=%d packetsSentThisInterval=%d\n", packetsJustSent, packetsSentThisInterval); + + if (forceDebugging || _myServer->wantsDebugSending()) { qDebug() << "Scene completed at " << usecTimestampNow() << "encodeTime:" << encodeTime << " sleepTime:" << sleepTime @@ -337,7 +348,7 @@ int OctreeSendThread::packetDistributor(Node* node, OctreeQueryNode* nodeData, b nodeData->nodeBag.deleteAll(); } - if (_myServer->wantsDebugSending()) { + if (forceDebugging || _myServer->wantsDebugSending()) { qDebug() << "Scene started at " << usecTimestampNow() << " Packets:" << _totalPackets << " Bytes:" << _totalBytes @@ -369,15 +380,16 @@ int OctreeSendThread::packetDistributor(Node* node, OctreeQueryNode* nodeData, b int maxPacketsPerInterval = std::min(clientMaxPacketsPerInterval, _myServer->getPacketsPerClientPerInterval()); if (_myServer->wantsDebugSending() && _myServer->wantsVerboseDebug()) { - printf("truePacketsSent=%d packetsSentThisInterval=%d maxPacketsPerInterval=%d server PPI=%d nodePPS=%d nodePPI=%d\n", + qDebug("truePacketsSent=%d packetsSentThisInterval=%d maxPacketsPerInterval=%d server PPI=%d nodePPS=%d nodePPI=%d\n", truePacketsSent, packetsSentThisInterval, maxPacketsPerInterval, _myServer->getPacketsPerClientPerInterval(), nodeData->getMaxOctreePacketsPerSecond(), clientMaxPacketsPerInterval); } int extraPackingAttempts = 0; + bool completedScene = false; while (somethingToSend && packetsSentThisInterval < maxPacketsPerInterval) { if (_myServer->wantsDebugSending() && _myServer->wantsVerboseDebug()) { - printf("truePacketsSent=%d packetsSentThisInterval=%d maxPacketsPerInterval=%d server PPI=%d nodePPS=%d nodePPI=%d\n", + qDebug("truePacketsSent=%d packetsSentThisInterval=%d maxPacketsPerInterval=%d server PPI=%d nodePPS=%d nodePPI=%d\n", truePacketsSent, packetsSentThisInterval, maxPacketsPerInterval, _myServer->getPacketsPerClientPerInterval(), nodeData->getMaxOctreePacketsPerSecond(), clientMaxPacketsPerInterval); } @@ -408,7 +420,12 @@ int OctreeSendThread::packetDistributor(Node* node, OctreeQueryNode* nodeData, b _myServer->getOctree()->lockForRead(); nodeData->stats.encodeStarted(); bytesWritten = _myServer->getOctree()->encodeTreeBitstream(subTree, &_packetData, nodeData->nodeBag, params); - + + // If after calling encodeTreeBitstream() there are no nodes left to send, then we know we've + // sent the entire scene. We want to know this below so we'll actually write this content into + // the packet and send it + completedScene = nodeData->nodeBag.isEmpty(); + // if we're trying to fill a full size packet, then we use this logic to determine if we have a DIDNT_FIT case. if (_packetData.getTargetSize() == MAX_OCTREE_PACKET_DATA_SIZE) { if (_packetData.hasContent() && bytesWritten == 0 && @@ -435,11 +452,12 @@ int OctreeSendThread::packetDistributor(Node* node, OctreeQueryNode* nodeData, b // If the last node didn't fit, but we're in compressed mode, then we actually want to see if we can fit a // little bit more in this packet. To do this we - + + // We only consider sending anything if there is something in the _packetData to send... But // if bytesWritten == 0 it means either the subTree couldn't fit or we had an empty bag... Both cases // mean we should send the previous packet contents and reset it. - if (lastNodeDidntFit) { + if (completedScene || lastNodeDidntFit) { if (_packetData.hasContent()) { // if for some reason the finalized size is greater than our available size, then probably the "compressed" // form actually inflated beyond our padding, and in this case we will send the current packet, then @@ -450,14 +468,17 @@ int OctreeSendThread::packetDistributor(Node* node, OctreeQueryNode* nodeData, b if (writtenSize > nodeData->getAvailable()) { if (_myServer->wantsDebugSending() && _myServer->wantsVerboseDebug()) { - printf("writtenSize[%d] > available[%d] too big, sending packet as is.\n", - writtenSize, nodeData->getAvailable()); } + if (forceDebugging || (_myServer->wantsDebugSending() && _myServer->wantsVerboseDebug())) { + qDebug("about to call handlePacketSend() .... line: %d -- " + "writtenSize[%d] > available[%d] too big, sending packet as is.\n", + __LINE__, writtenSize, nodeData->getAvailable()); + } packetsSentThisInterval += handlePacketSend(node, nodeData, trueBytesSent, truePacketsSent); } - if (_myServer->wantsDebugSending() && _myServer->wantsVerboseDebug()) { - printf("calling writeToPacket() available=%d compressedSize=%d uncompressedSize=%d target=%d\n", + if (forceDebugging || (_myServer->wantsDebugSending() && _myServer->wantsVerboseDebug())) { + qDebug(">>>>>> calling writeToPacket() available=%d compressedSize=%d uncompressedSize=%d target=%d\n", nodeData->getAvailable(), _packetData.getFinalizedSize(), _packetData.getUncompressedSize(), _packetData.getTargetSize()); } @@ -465,7 +486,7 @@ int OctreeSendThread::packetDistributor(Node* node, OctreeQueryNode* nodeData, b extraPackingAttempts = 0; } - // If we're not running compressed, the we know we can just send now. Or if we're running compressed, but + // If we're not running compressed, then we know we can just send now. Or if we're running compressed, but // the packet doesn't have enough space to bother attempting to pack more... bool sendNow = true; @@ -477,6 +498,9 @@ int OctreeSendThread::packetDistributor(Node* node, OctreeQueryNode* nodeData, b int targetSize = MAX_OCTREE_PACKET_DATA_SIZE; if (sendNow) { + if (forceDebugging) { + qDebug("about to call handlePacketSend() .... line: %d -- sendNow = TRUE\n", __LINE__); + } packetsSentThisInterval += handlePacketSend(node, nodeData, trueBytesSent, truePacketsSent); if (wantCompression) { targetSize = nodeData->getAvailable() - sizeof(OCTREE_PACKET_INTERNAL_SECTION_SIZE); @@ -491,7 +515,7 @@ int OctreeSendThread::packetDistributor(Node* node, OctreeQueryNode* nodeData, b targetSize = nodeData->getAvailable() - sizeof(OCTREE_PACKET_INTERNAL_SECTION_SIZE) - COMPRESS_PADDING; } if (_myServer->wantsDebugSending() && _myServer->wantsVerboseDebug()) { - printf("line:%d _packetData.changeSettings() wantCompression=%s targetSize=%d\n",__LINE__, + qDebug("line:%d _packetData.changeSettings() wantCompression=%s targetSize=%d\n",__LINE__, debug::valueOf(nodeData->getWantCompression()), targetSize); } _packetData.changeSettings(nodeData->getWantCompression(), targetSize); // will do reset @@ -521,15 +545,21 @@ int OctreeSendThread::packetDistributor(Node* node, OctreeQueryNode* nodeData, b if (elapsedmsec > 100) { if (elapsedmsec > 1000) { int elapsedsec = (end - start)/1000000; - printf("WARNING! packetLoop() took %d seconds [%d milliseconds %d calls in compress] to generate %d bytes in %d packets %d nodes still to send\n", - elapsedsec, elapsedCompressTimeMsecs, elapsedCompressCalls, trueBytesSent, truePacketsSent, nodeData->nodeBag.count()); + qDebug("WARNING! packetLoop() took %d seconds [%d milliseconds %d calls in compress] " + "to generate %d bytes in %d packets %d nodes still to send\n", + elapsedsec, elapsedCompressTimeMsecs, elapsedCompressCalls, + trueBytesSent, truePacketsSent, nodeData->nodeBag.count()); } else { - printf("WARNING! packetLoop() took %d milliseconds [%d milliseconds %d calls in compress] to generate %d bytes in %d packets, %d nodes still to send\n", - elapsedmsec, elapsedCompressTimeMsecs, elapsedCompressCalls, trueBytesSent, truePacketsSent, nodeData->nodeBag.count()); + qDebug("WARNING! packetLoop() took %d milliseconds [%d milliseconds %d calls in compress] " + "to generate %d bytes in %d packets, %d nodes still to send\n", + elapsedmsec, elapsedCompressTimeMsecs, elapsedCompressCalls, + trueBytesSent, truePacketsSent, nodeData->nodeBag.count()); } } else if (_myServer->wantsDebugSending() && _myServer->wantsVerboseDebug()) { - printf("packetLoop() took %d milliseconds [%d milliseconds %d calls in compress] to generate %d bytes in %d packets, %d nodes still to send\n", - elapsedmsec, elapsedCompressTimeMsecs, elapsedCompressCalls, trueBytesSent, truePacketsSent, nodeData->nodeBag.count()); + qDebug("packetLoop() took %d milliseconds [%d milliseconds %d calls in compress] " + "to generate %d bytes in %d packets, %d nodes still to send\n", + elapsedmsec, elapsedCompressTimeMsecs, elapsedCompressCalls, + trueBytesSent, truePacketsSent, nodeData->nodeBag.count()); } // if after sending packets we've emptied our bag, then we want to remember that we've sent all @@ -544,9 +574,11 @@ int OctreeSendThread::packetDistributor(Node* node, OctreeQueryNode* nodeData, b } if (_myServer->wantsDebugSending() && _myServer->wantsVerboseDebug()) { - printf("truePacketsSent=%d packetsSentThisInterval=%d maxPacketsPerInterval=%d server PPI=%d nodePPS=%d nodePPI=%d\n", - truePacketsSent, packetsSentThisInterval, maxPacketsPerInterval, _myServer->getPacketsPerClientPerInterval(), - nodeData->getMaxOctreePacketsPerSecond(), clientMaxPacketsPerInterval); + qDebug("truePacketsSent=%d packetsSentThisInterval=%d maxPacketsPerInterval=%d " + "server PPI=%d nodePPS=%d nodePPI=%d\n", + truePacketsSent, packetsSentThisInterval, maxPacketsPerInterval, + _myServer->getPacketsPerClientPerInterval(), nodeData->getMaxOctreePacketsPerSecond(), + clientMaxPacketsPerInterval); } } // end if bag wasn't empty, and so we sent stuff... diff --git a/libraries/octree-server/src/OctreeServer.cpp b/libraries/octree-server/src/OctreeServer.cpp index 2ab1c3cfec..2a7f073ca4 100644 --- a/libraries/octree-server/src/OctreeServer.cpp +++ b/libraries/octree-server/src/OctreeServer.cpp @@ -514,7 +514,7 @@ void OctreeServer::processDatagram(const QByteArray& dataByteArray, const HifiSo if (packetType == getMyQueryMessageType()) { bool debug = false; if (debug) { - qDebug() << "Got PACKET_TYPE_VOXEL_QUERY at " << usecTimestampNow() << "\n"; + qDebug() << "Got PACKET_TYPE_VOXEL_QUERY at" << usecTimestampNow() << "\n"; } int numBytesPacketHeader = numBytesForPacketHeader((unsigned char*) dataByteArray.data()); From b72dfbd3a4efac90c71030b61a4a29f02a5597f1 Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Fri, 3 Jan 2014 17:20:54 -0800 Subject: [PATCH 05/39] fix tabs --- .../octree-server/src/OctreeSendThread.cpp | 220 +++++++++--------- 1 file changed, 110 insertions(+), 110 deletions(-) diff --git a/libraries/octree-server/src/OctreeSendThread.cpp b/libraries/octree-server/src/OctreeSendThread.cpp index c98b453a9c..bdf3163f99 100644 --- a/libraries/octree-server/src/OctreeSendThread.cpp +++ b/libraries/octree-server/src/OctreeSendThread.cpp @@ -27,19 +27,19 @@ OctreeSendThread::OctreeSendThread(const QUuid& nodeUUID, OctreeServer* myServer bool OctreeSendThread::process() { uint64_t start = usecTimestampNow(); bool gotLock = false; - + // don't do any send processing until the initial load of the octree is complete... if (_myServer->isInitialLoadComplete()) { Node* node = NodeList::getInstance()->nodeWithUUID(_nodeUUID); - + if (node) { // make sure the node list doesn't kill our node while we're using it if (node->trylock()) { gotLock = true; OctreeQueryNode* nodeData = NULL; - + nodeData = (OctreeQueryNode*) node->getLinkedData(); - + int packetsSent = 0; // Sometimes the node data has not yet been linked, in which case we can't really do anything @@ -50,7 +50,7 @@ bool OctreeSendThread::process() { } packetsSent = packetDistributor(node, nodeData, viewFrustumChanged); } - + node->unlock(); // we're done with this node for now. } } @@ -59,7 +59,7 @@ bool OctreeSendThread::process() { qDebug("OctreeSendThread::process() waiting for isInitialLoadComplete()\n"); } } - + // 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() && gotLock) { // dynamically sleep until we need to fire off the next set of octree elements @@ -99,14 +99,14 @@ int OctreeSendThread::handlePacketSend(Node* node, OctreeQueryNode* nodeData, in nodeData->resetOctreePacket(true); // we still need to reset it though! return packetsSent; // without sending... } - + const unsigned char* messageData = nodeData->getPacket(); int numBytesPacketHeader = numBytesForPacketHeader(messageData); const unsigned char* dataAt = messageData + numBytesPacketHeader; dataAt += sizeof(OCTREE_PACKET_FLAGS); OCTREE_PACKET_SEQUENCE sequence = (*(OCTREE_PACKET_SEQUENCE*)dataAt); dataAt += sizeof(OCTREE_PACKET_SEQUENCE); - + // If we've got a stats message ready to send, then see if we can piggyback them together if (nodeData->stats.isReadyToSend()) { @@ -126,14 +126,14 @@ int OctreeSendThread::handlePacketSend(Node* node, OctreeQueryNode* nodeData, in int thisWastedBytes = 0; _totalWastedBytes += thisWastedBytes; _totalBytes += nodeData->getPacketLength(); - _totalPackets++; + _totalPackets++; if (debug) { - qDebug() << "Adding stats to packet at " << now << " [" << _totalPackets <<"]: sequence: " << sequence << - " statsMessageLength: " << statsMessageLength << + qDebug() << "Adding stats to packet at " << now << " [" << _totalPackets <<"]: sequence: " << sequence << + " statsMessageLength: " << statsMessageLength << " original size: " << nodeData->getPacketLength() << " [" << _totalBytes << "] wasted bytes:" << thisWastedBytes << " [" << _totalWastedBytes << "]\n"; } - + // actually send it NodeList::getInstance()->getNodeSocket().writeDatagram((char*) statsMessage, statsMessageLength, node->getActiveSocket()->getAddress(), @@ -152,7 +152,7 @@ int OctreeSendThread::handlePacketSend(Node* node, OctreeQueryNode* nodeData, in _totalBytes += statsMessageLength; _totalPackets++; if (debug) { - qDebug() << "Sending separate stats packet at " << now << " [" << _totalPackets <<"]: sequence: " << sequence << + qDebug() << "Sending separate stats packet at " << now << " [" << _totalPackets <<"]: sequence: " << sequence << " size: " << statsMessageLength << " [" << _totalBytes << "] wasted bytes:" << thisWastedBytes << " [" << _totalWastedBytes << "]\n"; } @@ -172,7 +172,7 @@ int OctreeSendThread::handlePacketSend(Node* node, OctreeQueryNode* nodeData, in _totalBytes += nodeData->getPacketLength(); _totalPackets++; if (debug) { - qDebug() << "Sending packet at " << now << " [" << _totalPackets <<"]: sequence: " << sequence << + qDebug() << "Sending packet at " << now << " [" << _totalPackets <<"]: sequence: " << sequence << " size: " << nodeData->getPacketLength() << " [" << _totalBytes << "] wasted bytes:" << thisWastedBytes << " [" << _totalWastedBytes << "]\n"; } @@ -192,7 +192,7 @@ int OctreeSendThread::handlePacketSend(Node* node, OctreeQueryNode* nodeData, in _totalBytes += nodeData->getPacketLength(); _totalPackets++; if (debug) { - qDebug() << "Sending packet at " << now << " [" << _totalPackets <<"]: sequence: " << sequence << + qDebug() << "Sending packet at " << now << " [" << _totalPackets <<"]: sequence: " << sequence << " size: " << nodeData->getPacketLength() << " [" << _totalBytes << "] wasted bytes:" << thisWastedBytes << " [" << _totalWastedBytes << "]\n"; } @@ -206,13 +206,13 @@ int OctreeSendThread::handlePacketSend(Node* node, OctreeQueryNode* nodeData, in packetsSent++; nodeData->resetOctreePacket(); } - + return packetsSent; } /// Version of voxel distributor that sends the deepest LOD level at once int OctreeSendThread::packetDistributor(Node* node, OctreeQueryNode* nodeData, bool viewFrustumChanged) { - bool forceDebugging = false; + bool forceDebugging = false; int truePacketsSent = 0; int trueBytesSent = 0; @@ -222,32 +222,32 @@ int OctreeSendThread::packetDistributor(Node* node, OctreeQueryNode* nodeData, b // FOR NOW... node tells us if it wants to receive only view frustum deltas bool wantDelta = viewFrustumChanged && nodeData->getWantDelta(); - // If our packet already has content in it, then we must use the color choice of the waiting packet. - // If we're starting a fresh packet, then... - // If we're moving, and the client asked for low res, then we force monochrome, otherwise, use + // If our packet already has content in it, then we must use the color choice of the waiting packet. + // If we're starting a fresh packet, then... + // If we're moving, and the client asked for low res, then we force monochrome, otherwise, use // the clients requested color state. bool wantColor = nodeData->getWantColor(); bool wantCompression = nodeData->getWantCompression(); // If we have a packet waiting, and our desired want color, doesn't match the current waiting packets color - // then let's just send that waiting packet. + // then let's just send that waiting packet. if (!nodeData->getCurrentPacketFormatMatches()) { if (nodeData->isPacketWaiting()) { if (forceDebugging || (_myServer->wantsDebugSending() && _myServer->wantsVerboseDebug())) { qDebug("about to call handlePacketSend() .... line: %d -- format change " - "wantColor=%s wantCompression=%s SENDING PARTIAL PACKET! currentPacketIsColor=%s " - "currentPacketIsCompressed=%s\n", - __LINE__, + "wantColor=%s wantCompression=%s SENDING PARTIAL PACKET! currentPacketIsColor=%s " + "currentPacketIsCompressed=%s\n", + __LINE__, debug::valueOf(wantColor), debug::valueOf(wantCompression), - debug::valueOf(nodeData->getCurrentPacketIsColor()), + debug::valueOf(nodeData->getCurrentPacketIsColor()), debug::valueOf(nodeData->getCurrentPacketIsCompressed()) ); } packetsSentThisInterval += handlePacketSend(node, nodeData, trueBytesSent, truePacketsSent); } else { if (forceDebugging || (_myServer->wantsDebugSending() && _myServer->wantsVerboseDebug())) { - qDebug("wantColor=%s wantCompression=%s FIXING HEADER! currentPacketIsColor=%s currentPacketIsCompressed=%s\n", + qDebug("wantColor=%s wantCompression=%s FIXING HEADER! currentPacketIsColor=%s currentPacketIsCompressed=%s\n", debug::valueOf(wantColor), debug::valueOf(wantCompression), - debug::valueOf(nodeData->getCurrentPacketIsColor()), + debug::valueOf(nodeData->getCurrentPacketIsColor()), debug::valueOf(nodeData->getCurrentPacketIsCompressed()) ); } nodeData->resetOctreePacket(); @@ -260,13 +260,13 @@ int OctreeSendThread::packetDistributor(Node* node, OctreeQueryNode* nodeData, b qDebug("line:%d _packetData.changeSettings() wantCompression=%s targetSize=%d\n", __LINE__, debug::valueOf(wantCompression), targetSize); } - + _packetData.changeSettings(wantCompression, targetSize); } - + if (_myServer->wantsDebugSending() && _myServer->wantsVerboseDebug()) { - qDebug("wantColor/isColor=%s/%s wantCompression/isCompressed=%s/%s viewFrustumChanged=%s, getWantLowResMoving()=%s\n", - debug::valueOf(wantColor), debug::valueOf(nodeData->getCurrentPacketIsColor()), + qDebug("wantColor/isColor=%s/%s wantCompression/isCompressed=%s/%s viewFrustumChanged=%s, getWantLowResMoving()=%s\n", + debug::valueOf(wantColor), debug::valueOf(nodeData->getCurrentPacketIsColor()), debug::valueOf(wantCompression), debug::valueOf(nodeData->getCurrentPacketIsCompressed()), debug::valueOf(viewFrustumChanged), debug::valueOf(nodeData->getWantLowResMoving())); } @@ -275,16 +275,16 @@ int OctreeSendThread::packetDistributor(Node* node, OctreeQueryNode* nodeData, b if (forceDebugging || (_myServer->wantsDebugSending() && _myServer->wantsVerboseDebug())) { qDebug("packetDistributor() viewFrustumChanged=%s, nodeBag.isEmpty=%s, viewSent=%s\n", - debug::valueOf(viewFrustumChanged), debug::valueOf(nodeData->nodeBag.isEmpty()), + debug::valueOf(viewFrustumChanged), debug::valueOf(nodeData->nodeBag.isEmpty()), debug::valueOf(nodeData->getViewSent()) ); } - - // If the current view frustum has changed OR we have nothing to send, then search against + + // If the current view frustum has changed OR we have nothing to send, then search against // the current view frustum for things to send. if (viewFrustumChanged || nodeData->nodeBag.isEmpty()) { uint64_t now = usecTimestampNow(); - if (forceDebugging || (_myServer->wantsDebugSending() && _myServer->wantsVerboseDebug())) { + if (forceDebugging || (_myServer->wantsDebugSending() && _myServer->wantsVerboseDebug())) { qDebug("(viewFrustumChanged=%s || nodeData->nodeBag.isEmpty() =%s)...\n", debug::valueOf(viewFrustumChanged), debug::valueOf(nodeData->nodeBag.isEmpty())); if (nodeData->getLastTimeBagEmpty() > 0) { @@ -294,20 +294,20 @@ int OctreeSendThread::packetDistributor(Node* node, OctreeQueryNode* nodeData, b } else { qDebug("elapsed time to send scene = %f seconds", elapsedSceneSend); } - qDebug(" [occlusionCulling:%s, wantDelta:%s, wantColor:%s ]\n", - debug::valueOf(nodeData->getWantOcclusionCulling()), debug::valueOf(wantDelta), + qDebug(" [occlusionCulling:%s, wantDelta:%s, wantColor:%s ]\n", + debug::valueOf(nodeData->getWantOcclusionCulling()), debug::valueOf(wantDelta), debug::valueOf(wantColor)); } } - + // if our view has changed, we need to reset these things... if (viewFrustumChanged) { if (nodeData->moveShouldDump() || nodeData->hasLodChanged()) { nodeData->dumpOutOfView(); } nodeData->map.erase(); - } - + } + if (!viewFrustumChanged && !nodeData->getWantDelta()) { // only set our last sent time if we weren't resetting due to frustum change uint64_t now = usecTimestampNow(); @@ -318,40 +318,40 @@ int OctreeSendThread::packetDistributor(Node* node, OctreeQueryNode* nodeData, b nodeData->stats.sceneCompleted(); ::endSceneSleepTime = _usleepTime; unsigned long sleepTime = ::endSceneSleepTime - ::startSceneSleepTime; - + unsigned long encodeTime = nodeData->stats.getTotalEncodeTime(); unsigned long elapsedTime = nodeData->stats.getElapsedTime(); - if (forceDebugging || (_myServer->wantsDebugSending() && _myServer->wantsVerboseDebug())) { - qDebug("about to call handlePacketSend() .... line: %d -- completed scene \n", __LINE__ ); - } - int packetsJustSent = handlePacketSend(node, nodeData, trueBytesSent, truePacketsSent); + if (forceDebugging || (_myServer->wantsDebugSending() && _myServer->wantsVerboseDebug())) { + qDebug("about to call handlePacketSend() .... line: %d -- completed scene \n", __LINE__ ); + } + int packetsJustSent = handlePacketSend(node, nodeData, trueBytesSent, truePacketsSent); packetsSentThisInterval += packetsJustSent; qDebug("packetsJustSent=%d packetsSentThisInterval=%d\n", packetsJustSent, packetsSentThisInterval); if (forceDebugging || _myServer->wantsDebugSending()) { - qDebug() << "Scene completed at " << usecTimestampNow() - << "encodeTime:" << encodeTime - << " sleepTime:" << sleepTime - << " elapsed:" << elapsedTime - << " Packets:" << _totalPackets - << " Bytes:" << _totalBytes + qDebug() << "Scene completed at " << usecTimestampNow() + << "encodeTime:" << encodeTime + << " sleepTime:" << sleepTime + << " elapsed:" << elapsedTime + << " Packets:" << _totalPackets + << " Bytes:" << _totalBytes << " Wasted:" << _totalWastedBytes << "\n"; } - + // start tracking our stats - bool isFullScene = ((!viewFrustumChanged || !nodeData->getWantDelta()) + bool isFullScene = ((!viewFrustumChanged || !nodeData->getWantDelta()) && nodeData->getViewFrustumJustStoppedChanging()) || nodeData->hasLodChanged(); - + // If we're starting a full scene, then definitely we want to empty the nodeBag if (isFullScene) { nodeData->nodeBag.deleteAll(); } if (forceDebugging || _myServer->wantsDebugSending()) { - qDebug() << "Scene started at " << usecTimestampNow() - << " Packets:" << _totalPackets - << " Bytes:" << _totalBytes + qDebug() << "Scene started at " << usecTimestampNow() + << " Packets:" << _totalPackets + << " Bytes:" << _totalBytes << " Wasted:" << _totalWastedBytes << "\n"; } @@ -378,19 +378,19 @@ int OctreeSendThread::packetDistributor(Node* node, OctreeQueryNode* nodeData, b int clientMaxPacketsPerInterval = std::max(1,(nodeData->getMaxOctreePacketsPerSecond() / INTERVALS_PER_SECOND)); int maxPacketsPerInterval = std::min(clientMaxPacketsPerInterval, _myServer->getPacketsPerClientPerInterval()); - + if (_myServer->wantsDebugSending() && _myServer->wantsVerboseDebug()) { - qDebug("truePacketsSent=%d packetsSentThisInterval=%d maxPacketsPerInterval=%d server PPI=%d nodePPS=%d nodePPI=%d\n", - truePacketsSent, packetsSentThisInterval, maxPacketsPerInterval, _myServer->getPacketsPerClientPerInterval(), + qDebug("truePacketsSent=%d packetsSentThisInterval=%d maxPacketsPerInterval=%d server PPI=%d nodePPS=%d nodePPI=%d\n", + truePacketsSent, packetsSentThisInterval, maxPacketsPerInterval, _myServer->getPacketsPerClientPerInterval(), nodeData->getMaxOctreePacketsPerSecond(), clientMaxPacketsPerInterval); } - + int extraPackingAttempts = 0; - bool completedScene = false; + bool completedScene = false; while (somethingToSend && packetsSentThisInterval < maxPacketsPerInterval) { if (_myServer->wantsDebugSending() && _myServer->wantsVerboseDebug()) { - qDebug("truePacketsSent=%d packetsSentThisInterval=%d maxPacketsPerInterval=%d server PPI=%d nodePPS=%d nodePPI=%d\n", - truePacketsSent, packetsSentThisInterval, maxPacketsPerInterval, _myServer->getPacketsPerClientPerInterval(), + qDebug("truePacketsSent=%d packetsSentThisInterval=%d maxPacketsPerInterval=%d server PPI=%d nodePPS=%d nodePPI=%d\n", + truePacketsSent, packetsSentThisInterval, maxPacketsPerInterval, _myServer->getPacketsPerClientPerInterval(), nodeData->getMaxOctreePacketsPerSecond(), clientMaxPacketsPerInterval); } @@ -403,32 +403,32 @@ int OctreeSendThread::packetDistributor(Node* node, OctreeQueryNode* nodeData, b float voxelSizeScale = nodeData->getOctreeSizeScale(); int boundaryLevelAdjustClient = nodeData->getBoundaryLevelAdjust(); - int boundaryLevelAdjust = boundaryLevelAdjustClient + (viewFrustumChanged && nodeData->getWantLowResMoving() + int boundaryLevelAdjust = boundaryLevelAdjustClient + (viewFrustumChanged && nodeData->getWantLowResMoving() ? LOW_RES_MOVING_ADJUST : NO_BOUNDARY_ADJUST); - bool isFullScene = ((!viewFrustumChanged || !nodeData->getWantDelta()) && + bool isFullScene = ((!viewFrustumChanged || !nodeData->getWantDelta()) && nodeData->getViewFrustumJustStoppedChanging()) || nodeData->hasLodChanged(); - - EncodeBitstreamParams params(INT_MAX, &nodeData->getCurrentViewFrustum(), wantColor, + + EncodeBitstreamParams params(INT_MAX, &nodeData->getCurrentViewFrustum(), wantColor, WANT_EXISTS_BITS, DONT_CHOP, wantDelta, lastViewFrustum, wantOcclusionCulling, coverageMap, boundaryLevelAdjust, voxelSizeScale, nodeData->getLastTimeBagEmpty(), isFullScene, &nodeData->stats, _myServer->getJurisdiction()); - + _myServer->getOctree()->lockForRead(); nodeData->stats.encodeStarted(); bytesWritten = _myServer->getOctree()->encodeTreeBitstream(subTree, &_packetData, nodeData->nodeBag, params); - // If after calling encodeTreeBitstream() there are no nodes left to send, then we know we've - // sent the entire scene. We want to know this below so we'll actually write this content into - // the packet and send it - completedScene = nodeData->nodeBag.isEmpty(); + // If after calling encodeTreeBitstream() there are no nodes left to send, then we know we've + // sent the entire scene. We want to know this below so we'll actually write this content into + // the packet and send it + completedScene = nodeData->nodeBag.isEmpty(); // if we're trying to fill a full size packet, then we use this logic to determine if we have a DIDNT_FIT case. if (_packetData.getTargetSize() == MAX_OCTREE_PACKET_DATA_SIZE) { - if (_packetData.hasContent() && bytesWritten == 0 && + if (_packetData.hasContent() && bytesWritten == 0 && params.stopReason == EncodeBitstreamParams::DIDNT_FIT) { lastNodeDidntFit = true; } @@ -449,58 +449,58 @@ int OctreeSendThread::packetDistributor(Node* node, OctreeQueryNode* nodeData, b bytesWritten = 0; somethingToSend = false; // this will cause us to drop out of the loop... } - + // If the last node didn't fit, but we're in compressed mode, then we actually want to see if we can fit a - // little bit more in this packet. To do this we + // little bit more in this packet. To do this we // We only consider sending anything if there is something in the _packetData to send... But // if bytesWritten == 0 it means either the subTree couldn't fit or we had an empty bag... Both cases - // mean we should send the previous packet contents and reset it. + // mean we should send the previous packet contents and reset it. if (completedScene || lastNodeDidntFit) { if (_packetData.hasContent()) { // if for some reason the finalized size is greater than our available size, then probably the "compressed" // form actually inflated beyond our padding, and in this case we will send the current packet, then // write to out new packet... - int writtenSize = _packetData.getFinalizedSize() + int writtenSize = _packetData.getFinalizedSize() + (nodeData->getCurrentPacketIsCompressed() ? sizeof(OCTREE_PACKET_INTERNAL_SECTION_SIZE) : 0); - - + + if (writtenSize > nodeData->getAvailable()) { if (_myServer->wantsDebugSending() && _myServer->wantsVerboseDebug()) { } - if (forceDebugging || (_myServer->wantsDebugSending() && _myServer->wantsVerboseDebug())) { + if (forceDebugging || (_myServer->wantsDebugSending() && _myServer->wantsVerboseDebug())) { qDebug("about to call handlePacketSend() .... line: %d -- " - "writtenSize[%d] > available[%d] too big, sending packet as is.\n", - __LINE__, writtenSize, nodeData->getAvailable()); - } + "writtenSize[%d] > available[%d] too big, sending packet as is.\n", + __LINE__, writtenSize, nodeData->getAvailable()); + } packetsSentThisInterval += handlePacketSend(node, nodeData, trueBytesSent, truePacketsSent); } if (forceDebugging || (_myServer->wantsDebugSending() && _myServer->wantsVerboseDebug())) { qDebug(">>>>>> calling writeToPacket() available=%d compressedSize=%d uncompressedSize=%d target=%d\n", - nodeData->getAvailable(), _packetData.getFinalizedSize(), + nodeData->getAvailable(), _packetData.getFinalizedSize(), _packetData.getUncompressedSize(), _packetData.getTargetSize()); } nodeData->writeToPacket(_packetData.getFinalizedData(), _packetData.getFinalizedSize()); extraPackingAttempts = 0; } - + // If we're not running compressed, then we know we can just send now. Or if we're running compressed, but // the packet doesn't have enough space to bother attempting to pack more... bool sendNow = true; - - if (nodeData->getCurrentPacketIsCompressed() && + + if (nodeData->getCurrentPacketIsCompressed() && nodeData->getAvailable() >= MINIMUM_ATTEMPT_MORE_PACKING && extraPackingAttempts <= REASONABLE_NUMBER_OF_PACKING_ATTEMPTS) { sendNow = false; // try to pack more } - + int targetSize = MAX_OCTREE_PACKET_DATA_SIZE; if (sendNow) { - if (forceDebugging) { - qDebug("about to call handlePacketSend() .... line: %d -- sendNow = TRUE\n", __LINE__); - } + if (forceDebugging) { + qDebug("about to call handlePacketSend() .... line: %d -- sendNow = TRUE\n", __LINE__); + } packetsSentThisInterval += handlePacketSend(node, nodeData, trueBytesSent, truePacketsSent); if (wantCompression) { targetSize = nodeData->getAvailable() - sizeof(OCTREE_PACKET_INTERNAL_SECTION_SIZE); @@ -521,8 +521,8 @@ int OctreeSendThread::packetDistributor(Node* node, OctreeQueryNode* nodeData, b _packetData.changeSettings(nodeData->getWantCompression(), targetSize); // will do reset } } - - + + // Here's where we can/should allow the server to send other data... // send the environment packet if (_myServer->hasSpecialPacketToSend()) { @@ -530,14 +530,14 @@ int OctreeSendThread::packetDistributor(Node* node, OctreeQueryNode* nodeData, b truePacketsSent++; packetsSentThisInterval++; } - - + + uint64_t end = usecTimestampNow(); int elapsedmsec = (end - start)/1000; uint64_t endCompressCalls = OctreePacketData::getCompressContentCalls(); int elapsedCompressCalls = endCompressCalls - startCompressCalls; - + uint64_t endCompressTimeMsecs = OctreePacketData::getCompressContentTime() / 1000; int elapsedCompressTimeMsecs = endCompressTimeMsecs - startCompressTimeMsecs; @@ -546,23 +546,23 @@ int OctreeSendThread::packetDistributor(Node* node, OctreeQueryNode* nodeData, b if (elapsedmsec > 1000) { int elapsedsec = (end - start)/1000000; qDebug("WARNING! packetLoop() took %d seconds [%d milliseconds %d calls in compress] " - "to generate %d bytes in %d packets %d nodes still to send\n", - elapsedsec, elapsedCompressTimeMsecs, elapsedCompressCalls, + "to generate %d bytes in %d packets %d nodes still to send\n", + elapsedsec, elapsedCompressTimeMsecs, elapsedCompressCalls, trueBytesSent, truePacketsSent, nodeData->nodeBag.count()); } else { qDebug("WARNING! packetLoop() took %d milliseconds [%d milliseconds %d calls in compress] " - "to generate %d bytes in %d packets, %d nodes still to send\n", - elapsedmsec, elapsedCompressTimeMsecs, elapsedCompressCalls, + "to generate %d bytes in %d packets, %d nodes still to send\n", + elapsedmsec, elapsedCompressTimeMsecs, elapsedCompressCalls, trueBytesSent, truePacketsSent, nodeData->nodeBag.count()); } } else if (_myServer->wantsDebugSending() && _myServer->wantsVerboseDebug()) { qDebug("packetLoop() took %d milliseconds [%d milliseconds %d calls in compress] " - "to generate %d bytes in %d packets, %d nodes still to send\n", - elapsedmsec, elapsedCompressTimeMsecs, elapsedCompressCalls, + "to generate %d bytes in %d packets, %d nodes still to send\n", + elapsedmsec, elapsedCompressTimeMsecs, elapsedCompressCalls, trueBytesSent, truePacketsSent, nodeData->nodeBag.count()); } - - // if after sending packets we've emptied our bag, then we want to remember that we've sent all + + // if after sending packets we've emptied our bag, then we want to remember that we've sent all // the voxels from the current view frustum if (nodeData->nodeBag.isEmpty()) { nodeData->updateLastKnownViewFrustum(); @@ -575,12 +575,12 @@ int OctreeSendThread::packetDistributor(Node* node, OctreeQueryNode* nodeData, b if (_myServer->wantsDebugSending() && _myServer->wantsVerboseDebug()) { qDebug("truePacketsSent=%d packetsSentThisInterval=%d maxPacketsPerInterval=%d " - "server PPI=%d nodePPS=%d nodePPI=%d\n", - truePacketsSent, packetsSentThisInterval, maxPacketsPerInterval, - _myServer->getPacketsPerClientPerInterval(), nodeData->getMaxOctreePacketsPerSecond(), - clientMaxPacketsPerInterval); + "server PPI=%d nodePPS=%d nodePPI=%d\n", + truePacketsSent, packetsSentThisInterval, maxPacketsPerInterval, + _myServer->getPacketsPerClientPerInterval(), nodeData->getMaxOctreePacketsPerSecond(), + clientMaxPacketsPerInterval); } - + } // end if bag wasn't empty, and so we sent stuff... return truePacketsSent; From 9aa3d31876728369cf76891a2c7c8ae91b72445a Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Fri, 3 Jan 2014 17:24:55 -0800 Subject: [PATCH 06/39] cleanup spacing --- libraries/octree-server/src/OctreeSendThread.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/libraries/octree-server/src/OctreeSendThread.cpp b/libraries/octree-server/src/OctreeSendThread.cpp index bdf3163f99..3ac9ac4911 100644 --- a/libraries/octree-server/src/OctreeSendThread.cpp +++ b/libraries/octree-server/src/OctreeSendThread.cpp @@ -467,9 +467,7 @@ int OctreeSendThread::packetDistributor(Node* node, OctreeQueryNode* nodeData, b if (writtenSize > nodeData->getAvailable()) { - if (_myServer->wantsDebugSending() && _myServer->wantsVerboseDebug()) { - } - if (forceDebugging || (_myServer->wantsDebugSending() && _myServer->wantsVerboseDebug())) { + if (forceDebugging || (_myServer->wantsDebugSending() && _myServer->wantsVerboseDebug())) { qDebug("about to call handlePacketSend() .... line: %d -- " "writtenSize[%d] > available[%d] too big, sending packet as is.\n", __LINE__, writtenSize, nodeData->getAvailable()); From 5144a91d1f97edfb4cd29310f6c618f5414dd90a Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Fri, 3 Jan 2014 17:28:06 -0800 Subject: [PATCH 07/39] cleanup spacing and comments --- libraries/octree-server/src/OctreeSendThread.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/libraries/octree-server/src/OctreeSendThread.cpp b/libraries/octree-server/src/OctreeSendThread.cpp index 3ac9ac4911..fb75a6cee9 100644 --- a/libraries/octree-server/src/OctreeSendThread.cpp +++ b/libraries/octree-server/src/OctreeSendThread.cpp @@ -451,8 +451,8 @@ int OctreeSendThread::packetDistributor(Node* node, OctreeQueryNode* nodeData, b } // If the last node didn't fit, but we're in compressed mode, then we actually want to see if we can fit a - // little bit more in this packet. To do this we - + // little bit more in this packet. To do this we write into the packet, but don't send it yet, we'll + // keep attempting to write in compressed mode to add more compressed segments // We only consider sending anything if there is something in the _packetData to send... But // if bytesWritten == 0 it means either the subTree couldn't fit or we had an empty bag... Both cases @@ -496,9 +496,9 @@ int OctreeSendThread::packetDistributor(Node* node, OctreeQueryNode* nodeData, b int targetSize = MAX_OCTREE_PACKET_DATA_SIZE; if (sendNow) { - if (forceDebugging) { - qDebug("about to call handlePacketSend() .... line: %d -- sendNow = TRUE\n", __LINE__); - } + if (forceDebugging) { + qDebug("about to call handlePacketSend() .... line: %d -- sendNow = TRUE\n", __LINE__); + } packetsSentThisInterval += handlePacketSend(node, nodeData, trueBytesSent, truePacketsSent); if (wantCompression) { targetSize = nodeData->getAvailable() - sizeof(OCTREE_PACKET_INTERNAL_SECTION_SIZE); From c2af9becba0fcf918a23e516bd2b1f4ce24b0c93 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Fri, 3 Jan 2014 17:48:58 -0800 Subject: [PATCH 08/39] Ray/capsule intersection test. --- libraries/shared/src/GeometryUtil.cpp | 56 +++++++++++++++++++++------ 1 file changed, 45 insertions(+), 11 deletions(-) diff --git a/libraries/shared/src/GeometryUtil.cpp b/libraries/shared/src/GeometryUtil.cpp index 2272296d75..b37c36cec5 100644 --- a/libraries/shared/src/GeometryUtil.cpp +++ b/libraries/shared/src/GeometryUtil.cpp @@ -157,12 +157,12 @@ glm::vec3 addPenetrations(const glm::vec3& currentPenetration, const glm::vec3& bool findRaySphereIntersection(const glm::vec3& origin, const glm::vec3& direction, const glm::vec3& center, float radius, float& distance) { glm::vec3 relativeOrigin = origin - center; - float b = glm::dot(direction, relativeOrigin); float c = glm::dot(relativeOrigin, relativeOrigin) - radius * radius; if (c < 0.0f) { distance = 0.0f; return true; // starts inside the sphere } + float b = glm::dot(direction, relativeOrigin); float radicand = b * b - c; if (radicand < 0.0f) { return false; // doesn't hit the sphere @@ -177,19 +177,53 @@ bool findRaySphereIntersection(const glm::vec3& origin, const glm::vec3& directi bool findRayCapsuleIntersection(const glm::vec3& origin, const glm::vec3& direction, const glm::vec3& start, const glm::vec3& end, float radius, float& distance) { - float minDistance = FLT_MAX; - float sphereDistance; - if (findRaySphereIntersection(origin, direction, start, radius, sphereDistance)) { - minDistance = qMin(minDistance, sphereDistance); + if (start == end) { + return findRaySphereIntersection(origin, direction, start, radius, distance); // handle degenerate case } - if (findRaySphereIntersection(origin, direction, end, radius, sphereDistance)) { - minDistance = qMin(minDistance, sphereDistance); + glm::vec3 relativeOrigin = origin - start; + glm::vec3 relativeEnd = end - start; + float capsuleLength = glm::length(relativeEnd); + relativeEnd /= capsuleLength; + float originProjection = glm::dot(relativeEnd, relativeOrigin); + glm::vec3 constant = relativeOrigin - relativeEnd * originProjection; + float c = glm::dot(constant, constant) - radius * radius; + if (c < 0.0f) { // starts inside cylinder + if (originProjection < 0.0f) { // below start + return findRaySphereIntersection(origin, direction, start, radius, distance); + + } else if (originProjection > capsuleLength) { // above end + return findRaySphereIntersection(origin, direction, end, radius, distance); + + } else { // between start and end + distance = 0.0f; + return true; + } } - if (minDistance < FLT_MAX) { - distance = minDistance; - return true; + glm::vec3 coefficient = direction - relativeEnd * glm::dot(relativeEnd, direction); + float a = glm::dot(coefficient, coefficient); + if (a == 0.0f) { + return false; // parallel to enclosing cylinder } - return false; + float b = 2.0f * glm::dot(constant, coefficient); + float divisor = 2.0f * a; + float radicand = b * b - 4.0f * a * c; + if (radicand < 0.0f) { + return false; // doesn't hit the enclosing cylinder + } + float t = (-b - sqrtf(radicand)) / (2.0f * a); + if (t < 0.0f) { + return false; // doesn't hit the enclosing cylinder + } + glm::vec3 intersection = relativeOrigin + direction * t; + float intersectionProjection = glm::dot(relativeEnd, intersection); + if (intersectionProjection < 0.0f) { // below start + return findRaySphereIntersection(origin, direction, start, radius, distance); + + } else if (intersectionProjection > capsuleLength) { // above end + return findRaySphereIntersection(origin, direction, end, radius, distance); + } + distance = t; // between start and end + return true; } // Do line segments (r1p1.x, r1p1.y)--(r1p2.x, r1p2.y) and (r2p1.x, r2p1.y)--(r2p2.x, r2p2.y) intersect? From 1bd069c4e2f305cab0b17f338a3e70444b95a6d2 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Fri, 3 Jan 2014 17:56:55 -0800 Subject: [PATCH 09/39] Removed unused variable. --- libraries/shared/src/GeometryUtil.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/libraries/shared/src/GeometryUtil.cpp b/libraries/shared/src/GeometryUtil.cpp index b37c36cec5..58d6e0476e 100644 --- a/libraries/shared/src/GeometryUtil.cpp +++ b/libraries/shared/src/GeometryUtil.cpp @@ -205,7 +205,6 @@ bool findRayCapsuleIntersection(const glm::vec3& origin, const glm::vec3& direct return false; // parallel to enclosing cylinder } float b = 2.0f * glm::dot(constant, coefficient); - float divisor = 2.0f * a; float radicand = b * b - 4.0f * a * c; if (radicand < 0.0f) { return false; // doesn't hit the enclosing cylinder From a2c33aadbaf3f30f4e3d4a201c464ad65a0dc2a8 Mon Sep 17 00:00:00 2001 From: Philip Rosedale Date: Fri, 3 Jan 2014 17:58:00 -0800 Subject: [PATCH 10/39] two script example files --- examples/clap.js | 52 +++++++++++++++++++++++++++++++++++++++++++ examples/playSound.js | 20 +++++++++++++++++ 2 files changed, 72 insertions(+) create mode 100644 examples/clap.js create mode 100644 examples/playSound.js diff --git a/examples/clap.js b/examples/clap.js new file mode 100644 index 0000000000..3638d3e589 --- /dev/null +++ b/examples/clap.js @@ -0,0 +1,52 @@ +// +// This sample script watches your hydra hands and makes clapping sound +// + +function length(v) { + return Math.sqrt(v.x * v.x + v.y * v.y + v.z * v.z); +} + + +function printVector(v) { + print(v.x + ", " + v.y + ", " + v.z + "\n"); +} + +function vMinus(a, b) { + var rval = { x: a.x - b.x, y: a.y - b.y, z: a.z - b.z }; + return rval; +} + +// First, load the clap sound from a URL +var clap = new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/clap1.raw"); +var clapping = new Array(); +clapping[0] = false; +clapping[1] = false; + +function maybePlaySound() { + // Set the location and other info for the sound to play + var palm1Position = Controller.getSpatialControlPosition(0); + var palm2Position = Controller.getSpatialControlPosition(2); + var distanceBetween = length(vMinus(palm1Position, palm2Position)); + + for (var palm = 0; palm < 2; palm++) { + var palmVelocity = Controller.getSpatialControlVelocity(palm * 2 + 1); + var speed = length(palmVelocity); + + const CLAP_SPEED = 0.2; + const CLAP_DISTANCE = 0.3; + + if (!clapping[palm] && (distanceBetween < CLAP_DISTANCE) && (speed > CLAP_SPEED)) { + var options = new AudioInjectionOptions();
 + options.position = palm1Position; + options.volume = speed / 2.0; + if (options.volume > 1.0) options.volume = 1.0; + Audio.playSound(clap, options); + clapping[palm] = true; + } else if (clapping[palm] && (speed < (CLAP_SPEED / 1.5))) { + clapping[palm] = false; + } + } +} + +// Connect a call back that happens every frame +Agent.willSendVisualDataCallback.connect(maybePlaySound); \ No newline at end of file diff --git a/examples/playSound.js b/examples/playSound.js new file mode 100644 index 0000000000..36e8822b56 --- /dev/null +++ b/examples/playSound.js @@ -0,0 +1,20 @@ +// +// This sample script makes an occassional clapping sound at the location of your palm +// + +// First, load the clap sound from a URL +var clap = new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/clap1.raw"); + +function maybePlaySound() { + if (Math.random() < 0.01) { + // Set the location and other info for the sound to play + var options = new AudioInjectionOptions();
 + var palmPosition = Controller.getSpatialControlPosition(0); + options.position = palmPosition; + options.volume = 0.1; + Audio.playSound(clap, options); + } +} + +// Connect a call back that happens every frame +Agent.willSendVisualDataCallback.connect(maybePlaySound); \ No newline at end of file From e37203d833d0cf790acc7aeec17cf8c4ed271d73 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Fri, 3 Jan 2014 17:59:15 -0800 Subject: [PATCH 11/39] The overloaded function should be const, true. --- libraries/avatars/src/AvatarData.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index 1dd5dacafe..b76d889526 100755 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -115,7 +115,7 @@ public: /// \param skeletonSkipIndex if not -1, the index of a joint to skip (along with its descendents) in the skeleton model /// \return whether or not the sphere penetrated virtual bool findSpherePenetration(const glm::vec3& penetratorCenter, float penetratorRadius, - glm::vec3& penetration, int skeletonSkipIndex = -1) { return false; } + glm::vec3& penetration, int skeletonSkipIndex = -1) const { return false; } protected: QUuid _uuid; From e97e211bc4794d2838b4b2e7108034ff8dbb3700 Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Fri, 3 Jan 2014 18:10:30 -0800 Subject: [PATCH 12/39] quiet down some debugging --- libraries/octree-server/src/OctreeSendThread.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/libraries/octree-server/src/OctreeSendThread.cpp b/libraries/octree-server/src/OctreeSendThread.cpp index fb75a6cee9..ccdf1ed8e2 100644 --- a/libraries/octree-server/src/OctreeSendThread.cpp +++ b/libraries/octree-server/src/OctreeSendThread.cpp @@ -327,7 +327,9 @@ int OctreeSendThread::packetDistributor(Node* node, OctreeQueryNode* nodeData, b } int packetsJustSent = handlePacketSend(node, nodeData, trueBytesSent, truePacketsSent); packetsSentThisInterval += packetsJustSent; - qDebug("packetsJustSent=%d packetsSentThisInterval=%d\n", packetsJustSent, packetsSentThisInterval); + if (forceDebugging) { + qDebug("packetsJustSent=%d packetsSentThisInterval=%d\n", packetsJustSent, packetsSentThisInterval); + } if (forceDebugging || _myServer->wantsDebugSending()) { qDebug() << "Scene completed at " << usecTimestampNow() From 207ea30c6ddbcaa2087fe282861e76905c1cd8aa Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Sat, 4 Jan 2014 01:10:48 -0800 Subject: [PATCH 13/39] fix delete problem with voxels --- libraries/octree/src/OctreeElement.cpp | 160 +++++++++++++------------ 1 file changed, 82 insertions(+), 78 deletions(-) diff --git a/libraries/octree/src/OctreeElement.cpp b/libraries/octree/src/OctreeElement.cpp index f59121877a..0fad79e625 100644 --- a/libraries/octree/src/OctreeElement.cpp +++ b/libraries/octree/src/OctreeElement.cpp @@ -54,7 +54,7 @@ void OctreeElement::init(unsigned char * octalCode) { memcpy(_octalCode.buffer, octalCode, octalCodeLength); delete[] octalCode; } - + // set up the _children union _childBitmask = 0; _childrenExternal = false; @@ -64,7 +64,7 @@ void OctreeElement::init(unsigned char * octalCode) { _singleChildrenCount++; #endif _childrenCount[0]++; - + // default pointers to child nodes to NULL #ifdef HAS_AUDIT_CHILDREN for (int i = 0; i < NUMBER_OF_CHILDREN; i++) { @@ -81,7 +81,7 @@ void OctreeElement::init(unsigned char * octalCode) { #ifdef SIMPLE_EXTERNAL_CHILDREN _children.single = NULL; #endif - + _isDirty = true; _shouldRender = false; _sourceUUIDKey = 0; @@ -100,13 +100,13 @@ OctreeElement::~OctreeElement() { _octcodeMemoryUsage -= bytesRequiredForCodeLength(numberOfThreeBitSectionsInCode(getOctalCode())); delete[] _octalCode.pointer; } - + // delete all of this node's children, this also takes care of all population tracking data deleteAllChildren(); } -void OctreeElement::markWithChangedTime() { - _lastChanged = usecTimestampNow(); +void OctreeElement::markWithChangedTime() { + _lastChanged = usecTimestampNow(); notifyUpdateHooks(); // if the node has changed, notify our hooks } @@ -121,7 +121,7 @@ void OctreeElement::handleSubtreeChanged(Octree* myTree) { if (myTree->getShouldReaverage()) { calculateAverageFromChildren(); } - + markWithChangedTime(); } @@ -184,10 +184,10 @@ void OctreeElement::setShouldRender(bool shouldRender) { void OctreeElement::calculateAABox() { glm::vec3 corner; - + // copy corner into box copyFirstVertexForCode(getOctalCode(),(float*)&corner); - + // this tells you the "size" of the voxel float voxelScale = 1 / powf(2, numberOfThreeBitSectionsInCode(getOctalCode())); _box.setBox(corner,voxelScale); @@ -201,7 +201,7 @@ void OctreeElement::deleteChildAtIndex(int childIndex) { setChildAtIndex(childIndex, NULL); _isDirty = true; markWithChangedTime(); - + // after deleting the child, check to see if we're a leaf if (isLeaf()) { _voxelNodeLeafCount++; @@ -219,13 +219,13 @@ OctreeElement* OctreeElement::removeChildAtIndex(int childIndex) { setChildAtIndex(childIndex, NULL); _isDirty = true; markWithChangedTime(); - + // after removing the child, check to see if we're a leaf if (isLeaf()) { _voxelNodeLeafCount++; } } - + #ifdef HAS_AUDIT_CHILDREN auditChildren("removeChildAtIndex()"); #endif // def HAS_AUDIT_CHILDREN @@ -238,12 +238,12 @@ void OctreeElement::auditChildren(const char* label) const { for (int childIndex = 0; childIndex < NUMBER_OF_CHILDREN; childIndex++) { OctreeElement* testChildNew = getChildAtIndex(childIndex); OctreeElement* testChildOld = _childrenArray[childIndex]; - + if (testChildNew != testChildOld) { auditFailed = true; } } - + const bool alwaysReport = false; // set this to true to get additional debugging if (alwaysReport || auditFailed) { qDebug("%s... auditChildren() %s <<<< \n", label, (auditFailed ? "FAILED" : "PASSED")); @@ -309,7 +309,7 @@ OctreeElement* OctreeElement::getChildAtIndex(int childIndex) const { return NULL; } } break; - + default : { return _children.external[childIndex]; } break; @@ -320,11 +320,11 @@ OctreeElement* OctreeElement::getChildAtIndex(int childIndex) const { PerformanceWarning warn(false,"getChildAtIndex",false,&_getChildAtIndexTime,&_getChildAtIndexCalls); OctreeElement* result = NULL; int childCount = getChildCount(); - + #ifdef HAS_AUDIT_CHILDREN const char* caseStr = NULL; #endif - + switch (childCount) { case 0: #ifdef HAS_AUDIT_CHILDREN @@ -424,7 +424,7 @@ OctreeElement* OctreeElement::getChildAtIndex(int childIndex) const { caseStr, result,_childrenArray[childIndex]); } #endif // def HAS_AUDIT_CHILDREN - return result; + return result; #endif } @@ -435,7 +435,7 @@ void OctreeElement::storeTwoChildren(OctreeElement* childOne, OctreeElement* chi const int64_t minOffset = std::numeric_limits::min(); const int64_t maxOffset = std::numeric_limits::max(); - + bool forceExternal = true; if (!forceExternal && isBetween(offsetOne, maxOffset, minOffset) && isBetween(offsetTwo, maxOffset, minOffset)) { // if previously external, then clean it up... @@ -455,7 +455,7 @@ void OctreeElement::storeTwoChildren(OctreeElement* childOne, OctreeElement* chi _twoChildrenOffsetCount++; } else { // encode in array - + // if not previously external, then allocate appropriately if (!_childrenExternal) { _childrenExternal = true; @@ -516,7 +516,7 @@ void OctreeElement::encodeThreeOffsets(int64_t offsetOne, int64_t offsetTwo, int const uint64_t ENCODE_BITS = 21; const uint64_t ENCODE_MASK = 0xFFFFF; const uint64_t ENCODE_MASK_SIGN = 0x100000; - + uint64_t offsetEncodedOne, offsetEncodedTwo, offsetEncodedThree; if (offsetOne < 0) { offsetEncodedOne = ((-offsetOne & ENCODE_MASK) | ENCODE_MASK_SIGN); @@ -544,13 +544,13 @@ void OctreeElement::storeThreeChildren(OctreeElement* childOne, OctreeElement* c int64_t offsetOne = (uint8_t*)childOne - (uint8_t*)this; int64_t offsetTwo = (uint8_t*)childTwo - (uint8_t*)this; int64_t offsetThree = (uint8_t*)childThree - (uint8_t*)this; - + const int64_t minOffset = -1048576; // what can fit in 20 bits // std::numeric_limits::min(); const int64_t maxOffset = 1048576; // what can fit in 20 bits // std::numeric_limits::max(); - + bool forceExternal = true; if (!forceExternal && - isBetween(offsetOne, maxOffset, minOffset) && + isBetween(offsetOne, maxOffset, minOffset) && isBetween(offsetTwo, maxOffset, minOffset) && isBetween(offsetThree, maxOffset, minOffset)) { // if previously external, then clean it up... @@ -566,7 +566,7 @@ void OctreeElement::storeThreeChildren(OctreeElement* childOne, OctreeElement* c _threeChildrenOffsetCount++; } else { // encode in array - + // if not previously external, then allocate appropriately if (!_childrenExternal) { _childrenExternal = true; @@ -609,13 +609,13 @@ void OctreeElement::checkStoreFourChildren(OctreeElement* childOne, OctreeElemen int64_t offsetTwo = (uint8_t*)childTwo - (uint8_t*)this; int64_t offsetThree = (uint8_t*)childThree - (uint8_t*)this; int64_t offsetFour = (uint8_t*)childFour - (uint8_t*)this; - + const int64_t minOffset = std::numeric_limits::min(); const int64_t maxOffset = std::numeric_limits::max(); - + bool forceExternal = true; if (!forceExternal && - isBetween(offsetOne, maxOffset, minOffset) && + isBetween(offsetOne, maxOffset, minOffset) && isBetween(offsetTwo, maxOffset, minOffset) && isBetween(offsetThree, maxOffset, minOffset) && isBetween(offsetFour, maxOffset, minOffset) @@ -671,10 +671,10 @@ void OctreeElement::deleteAllChildren() { _externalChildrenCount--; _childrenCount[childCount]--; } break; - - + + } - + // If we had externally stored children, clean them too. if (_childrenExternal && _children.external) { delete[] _children.external; @@ -734,7 +734,7 @@ void OctreeElement::setChildAtIndex(int childIndex, OctreeElement* child) { _children.external[childIndex] = child; _externalChildrenMemoryUsage += NUMBER_OF_CHILDREN * sizeof(OctreeElement*); - + } else if (previousChildCount == 2 && newChildCount == 1) { assert(child == NULL); // we are removing a child, so this must be true! OctreeElement* previousFirstChild = _children.external[firstIndex]; @@ -757,7 +757,7 @@ void OctreeElement::setChildAtIndex(int childIndex, OctreeElement* child) { // Here's how we store things... // If we have 0 or 1 children, then we just store them in the _children.single; - // If we have 2 children, + // If we have 2 children, // then if we can we store them as 32 bit signed offsets from our own this pointer, // _children.offsetsTwoChildren[0]-[1] // these are 32 bit offsets @@ -770,7 +770,7 @@ void OctreeElement::setChildAtIndex(int childIndex, OctreeElement* child) { clearAtBit(_childBitmask, childIndex); } int newChildCount = getChildCount(); - + // track our population data if (previousChildCount != newChildCount) { _childrenCount[previousChildCount]--; @@ -781,7 +781,7 @@ void OctreeElement::setChildAtIndex(int childIndex, OctreeElement* child) { if (previousChildCount == 0 && newChildCount == 0) { // nothing to do... } else if ((previousChildCount == 0 || previousChildCount == 1) && newChildCount == 1) { - // If we had 0 children, and we're setting our first child or if we had 1 child, or we're resetting the same child, + // If we had 0 children, and we're setting our first child or if we had 1 child, or we're resetting the same child, // then we can just store it in _children.single _children.single = child; } else if (previousChildCount == 1 && newChildCount == 0) { @@ -803,21 +803,21 @@ void OctreeElement::setChildAtIndex(int childIndex, OctreeElement* child) { } _singleChildrenCount--; - storeTwoChildren(childOne, childTwo); + storeTwoChildren(childOne, childTwo); } else if (previousChildCount == 2 && newChildCount == 1) { // If we had 2 children, and we're removing one, then we know we can go down to single mode //assert(child == NULL); // this is the only logical case - + int indexTwo = getNthBit(previousChildMask, 2); bool keepChildOne = indexTwo == childIndex; OctreeElement* childOne; OctreeElement* childTwo; - retrieveTwoChildren(childOne, childTwo); + retrieveTwoChildren(childOne, childTwo); _singleChildrenCount++; - + if (keepChildOne) { _children.single = childOne; } else { @@ -825,14 +825,14 @@ void OctreeElement::setChildAtIndex(int childIndex, OctreeElement* child) { } } else if (previousChildCount == 2 && newChildCount == 2) { // If we had 2 children, and still have 2, then we know we are resetting one of our existing children - + int indexOne = getNthBit(previousChildMask, 1); bool replaceChildOne = indexOne == childIndex; - // Get the existing two children out of their encoding... + // Get the existing two children out of their encoding... OctreeElement* childOne; OctreeElement* childTwo; - retrieveTwoChildren(childOne, childTwo); + retrieveTwoChildren(childOne, childTwo); if (replaceChildOne) { childOne = child; @@ -841,7 +841,7 @@ void OctreeElement::setChildAtIndex(int childIndex, OctreeElement* child) { } storeTwoChildren(childOne, childTwo); - + } else if (previousChildCount == 2 && newChildCount == 3) { // If we had 2 children, and now have 3, then we know we are going to an external case... @@ -850,8 +850,8 @@ void OctreeElement::setChildAtIndex(int childIndex, OctreeElement* child) { OctreeElement* childTwo; OctreeElement* childThree; - // Get the existing two children out of their encoding... - retrieveTwoChildren(childOne, childTwo); + // Get the existing two children out of their encoding... + retrieveTwoChildren(childOne, childTwo); // determine order of the existing children int indexOne = getNthBit(previousChildMask, 1); @@ -870,7 +870,7 @@ void OctreeElement::setChildAtIndex(int childIndex, OctreeElement* child) { storeThreeChildren(childOne, childTwo, childThree); } else if (previousChildCount == 3 && newChildCount == 2) { // If we had 3 children, and now have 2, then we know we are going from an external case to a potential internal case - + // We need to determine which children we had, and which one we got rid of... int indexOne = getNthBit(previousChildMask, 1); int indexTwo = getNthBit(previousChildMask, 2); @@ -882,8 +882,8 @@ void OctreeElement::setChildAtIndex(int childIndex, OctreeElement* child) { OctreeElement* childTwo; OctreeElement* childThree; - // Get the existing two children out of their encoding... - retrieveThreeChildren(childOne, childTwo, childThree); + // Get the existing two children out of their encoding... + retrieveThreeChildren(childOne, childTwo, childThree); if (removeChildOne) { childOne = childTwo; @@ -894,10 +894,10 @@ void OctreeElement::setChildAtIndex(int childIndex, OctreeElement* child) { // removing child three, nothing to do. } - storeTwoChildren(childOne, childTwo); + storeTwoChildren(childOne, childTwo); } else if (previousChildCount == 3 && newChildCount == 3) { // If we had 3 children, and now have 3, then we need to determine which item we're replacing... - + // We need to determine which children we had, and which one we got rid of... int indexOne = getNthBit(previousChildMask, 1); int indexTwo = getNthBit(previousChildMask, 2); @@ -909,8 +909,8 @@ void OctreeElement::setChildAtIndex(int childIndex, OctreeElement* child) { OctreeElement* childTwo; OctreeElement* childThree; - // Get the existing two children out of their encoding... - retrieveThreeChildren(childOne, childTwo, childThree); + // Get the existing two children out of their encoding... + retrieveThreeChildren(childOne, childTwo, childThree); if (replaceChildOne) { childOne = child; @@ -930,8 +930,8 @@ void OctreeElement::setChildAtIndex(int childIndex, OctreeElement* child) { OctreeElement* childThree; OctreeElement* childFour; - // Get the existing two children out of their encoding... - retrieveThreeChildren(childOne, childTwo, childThree); + // Get the existing two children out of their encoding... + retrieveThreeChildren(childOne, childTwo, childThree); // determine order of the existing children int indexOne = getNthBit(previousChildMask, 1); @@ -959,9 +959,9 @@ void OctreeElement::setChildAtIndex(int childIndex, OctreeElement* child) { const int newChildCount = 4; _children.external = new OctreeElement*[newChildCount]; memset(_children.external, 0, sizeof(OctreeElement*) * newChildCount); - + _externalChildrenMemoryUsage += newChildCount * sizeof(OctreeElement*); - + _children.external[0] = childOne; _children.external[1] = childTwo; _children.external[2] = childThree; @@ -970,7 +970,7 @@ void OctreeElement::setChildAtIndex(int childIndex, OctreeElement* child) { } else if (previousChildCount == 4 && newChildCount == 3) { // If we had 4 children, and now have 3, then we know we are going from an external case to a potential internal case //assert(_children.external && _childrenExternal && previousChildCount == 4); - + // We need to determine which children we had, and which one we got rid of... int indexOne = getNthBit(previousChildMask, 1); int indexTwo = getNthBit(previousChildMask, 2); @@ -1008,7 +1008,7 @@ void OctreeElement::setChildAtIndex(int childIndex, OctreeElement* child) { } else if (previousChildCount == newChildCount) { //assert(_children.external && _childrenExternal && previousChildCount >= 4); //assert(previousChildCount == newChildCount); - + // 4 or more children, one item being replaced, we know we're stored externally, we just need to find the one // that needs to be replaced and replace it. for (int ordinal = 1; ordinal <= 8; ordinal++) { @@ -1024,12 +1024,12 @@ void OctreeElement::setChildAtIndex(int childIndex, OctreeElement* child) { // Growing case... previous must be 4 or greater //assert(_children.external && _childrenExternal && previousChildCount >= 4); //assert(previousChildCount == newChildCount-1); - + // 4 or more children, one item being added, we know we're stored externally, we just figure out where to insert // this child pointer into our external list OctreeElement** newExternalList = new OctreeElement*[newChildCount]; memset(newExternalList, 0, sizeof(OctreeElement*) * newChildCount); - + int copiedCount = 0; for (int ordinal = 1; ordinal <= newChildCount; ordinal++) { int index = getNthBit(previousChildMask, ordinal); @@ -1037,10 +1037,10 @@ void OctreeElement::setChildAtIndex(int childIndex, OctreeElement* child) { newExternalList[ordinal - 1] = _children.external[ordinal - 1]; copiedCount++; } else { - + // insert our new child here... newExternalList[ordinal - 1] = child; - + // if we didn't copy all of our previous children, then we need to if (copiedCount < previousChildCount) { // our child needs to be inserted before this index, and everything else pushed out... @@ -1060,10 +1060,10 @@ void OctreeElement::setChildAtIndex(int childIndex, OctreeElement* child) { //assert(_children.external && _childrenExternal && previousChildCount >= 4); //assert(previousChildCount == newChildCount+1); - // 4 or more children, one item being removed, we know we're stored externally, we just figure out which + // 4 or more children, one item being removed, we know we're stored externally, we just figure out which // item to remove from our external list OctreeElement** newExternalList = new OctreeElement*[newChildCount]; - + for (int ordinal = 1; ordinal <= previousChildCount; ordinal++) { int index = getNthBit(previousChildMask, ordinal); //assert(index != -1); @@ -1090,7 +1090,7 @@ void OctreeElement::setChildAtIndex(int childIndex, OctreeElement* child) { if (getChildCount() == 4 && _childrenExternal && _children.external) { checkStoreFourChildren(_children.external[0], _children.external[1], _children.external[2], _children.external[3]); } - + #ifdef HAS_AUDIT_CHILDREN _childrenArray[childIndex] = child; @@ -1104,11 +1104,11 @@ void OctreeElement::setChildAtIndex(int childIndex, OctreeElement* child) { OctreeElement* OctreeElement::addChildAtIndex(int childIndex) { OctreeElement* childAt = getChildAtIndex(childIndex); if (!childAt) { - // before adding a child, see if we're currently a leaf + // before adding a child, see if we're currently a leaf if (isLeaf()) { _voxelNodeLeafCount--; } - + unsigned char* newChildCode = childOctalCode(getOctalCode(), childIndex); childAt = createNewElement(newChildCode); setChildAtIndex(childIndex, childAt); @@ -1133,11 +1133,15 @@ bool OctreeElement::safeDeepDeleteChildAtIndex(int childIndex, int recursionCoun if (!childToDelete->isLeaf()) { // delete all it's children for (int i = 0; i < NUMBER_OF_CHILDREN; i++) { - deleteApproved = childToDelete->safeDeepDeleteChildAtIndex(i,recursionCount+1); - if (!deleteApproved) { - break; // no point in continuing... + if (childToDelete->getChildAtIndex(i)) { + deleteApproved = childToDelete->safeDeepDeleteChildAtIndex(i,recursionCount+1); + if (!deleteApproved) { + break; // no point in continuing... + } } } + } else { + deleteApproved = true; // because we got here after checking that delete was approved } if (deleteApproved) { deleteChildAtIndex(childIndex); @@ -1155,14 +1159,14 @@ void OctreeElement::printDebugDetails(const char* label) const { for (int i = 0; i < NUMBER_OF_CHILDREN; i++) { OctreeElement* childAt = getChildAtIndex(i); if (childAt) { - setAtBit(childBits,i); + setAtBit(childBits,i); } } qDebug("%s - Voxel at corner=(%f,%f,%f) size=%f\n isLeaf=%s isDirty=%s shouldRender=%s\n children=", label, _box.getCorner().x, _box.getCorner().y, _box.getCorner().z, _box.getScale(), debug::valueOf(isLeaf()), debug::valueOf(isDirty()), debug::valueOf(getShouldRender())); - + outputBits(childBits, false); qDebug("\n octalCode="); printOctalCode(getOctalCode()); @@ -1188,10 +1192,10 @@ ViewFrustum::location OctreeElement::inFrustum(const ViewFrustum& viewFrustum) c // There are two types of nodes for which we want to "render" // 1) Leaves that are in the LOD // 2) Non-leaves are more complicated though... usually you don't want to render them, but if their children -// wouldn't be rendered, then you do want to render them. But sometimes they have some children that ARE +// wouldn't be rendered, then you do want to render them. But sometimes they have some children that ARE // in the LOD, and others that are not. In this case we want to render the parent, and none of the children. // -// Since, if we know the camera position and orientation, we can know which of the corners is the "furthest" +// Since, if we know the camera position and orientation, we can know which of the corners is the "furthest" // corner. We can use we can use this corner as our "voxel position" to do our distance calculations off of. // By doing this, we don't need to test each child voxel's position vs the LOD boundary bool OctreeElement::calculateShouldRender(const ViewFrustum* viewFrustum, float voxelScaleSize, int boundaryLevelAdjust) const { @@ -1285,7 +1289,7 @@ void OctreeElement::notifyUpdateHooks() { } } -bool OctreeElement::findSpherePenetration(const glm::vec3& center, float radius, +bool OctreeElement::findSpherePenetration(const glm::vec3& center, float radius, glm::vec3& penetration, void** penetratedObject) const { return _box.findSpherePenetration(center, radius, penetration); } @@ -1307,7 +1311,7 @@ OctreeElement* OctreeElement::getOrCreateChildElementAt(float x, float y, float } // otherwise, we need to find which of our children we should recurse glm::vec3 ourCenter = _box.calcCenter(); - + int childIndex = CHILD_UNKNOWN; // left half if (x > ourCenter.x) { @@ -1352,13 +1356,13 @@ OctreeElement* OctreeElement::getOrCreateChildElementAt(float x, float y, float } } } - + // Now, check if we have a child at that location child = getChildAtIndex(childIndex); if (!child) { child = addChildAtIndex(childIndex); } - + // Now that we have the child to recurse down, let it answer the original question... return child->getOrCreateChildElementAt(x, y, z, s); } From 0df02618ee0bc4d2f9c2f2ea7edc0252b7b38a03 Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Sat, 4 Jan 2014 03:16:47 -0800 Subject: [PATCH 14/39] use ScriptEngine in Particle::collideWithXXX() methods so that we get all script interfaces --- interface/src/Application.cpp | 904 ++++++++++--------- libraries/particles/src/Particle.cpp | 188 ++-- libraries/particles/src/Particle.h | 63 +- libraries/script-engine/src/ScriptEngine.cpp | 152 ++-- libraries/script-engine/src/ScriptEngine.h | 20 +- 5 files changed, 670 insertions(+), 657 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 635e5c16d7..64a50a5b99 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -44,6 +44,7 @@ #include #include #include +#include #include #include #include @@ -67,14 +68,14 @@ using namespace std; static unsigned STARFIELD_NUM_STARS = 50000; static unsigned STARFIELD_SEED = 1; -static const int BANDWIDTH_METER_CLICK_MAX_DRAG_LENGTH = 6; // farther dragged clicks are ignored +static const int BANDWIDTH_METER_CLICK_MAX_DRAG_LENGTH = 6; // farther dragged clicks are ignored const int IDLE_SIMULATE_MSECS = 16; // How often should call simulate and other stuff // in the idle loop? (60 FPS is default) static QTimer* idleTimer = NULL; const int STARTUP_JITTER_SAMPLES = NETWORK_BUFFER_LENGTH_SAMPLES_PER_CHANNEL / 2; - // Startup optimistically with small jitter buffer that + // Startup optimistically with small jitter buffer that // will start playback on the second received audio packet. const int MIRROR_VIEW_TOP_PADDING = 5; @@ -129,7 +130,7 @@ Application::Application(int& argc, char** argv, timeval &startup_time) : _perfStatsOn(false), _chatEntryOn(false), _audio(&_audioScope, STARTUP_JITTER_SAMPLES), - _stopNetworkReceiveThread(false), + _stopNetworkReceiveThread(false), _voxelProcessor(), _voxelHideShowThread(&_voxels), _voxelEditSender(this), @@ -156,24 +157,24 @@ Application::Application(int& argc, char** argv, timeval &startup_time) : _window->setMenuBar(Menu::getInstance()); qDebug("[VERSION] Build sequence: %i\n", BUILD_VERSION); - + unsigned int listenPort = 0; // bind to an ephemeral port by default const char** constArgv = const_cast(argv); const char* portStr = getCmdOption(argc, constArgv, "--listenPort"); if (portStr) { listenPort = atoi(portStr); } - + NodeList* nodeList = NodeList::createInstance(NODE_TYPE_AGENT, listenPort); - + // put the audio processing on a separate thread QThread* audioThread = new QThread(this); - + _audio.moveToThread(audioThread); connect(audioThread, SIGNAL(started()), &_audio, SLOT(start())); - + audioThread->start(); - + nodeList->addHook(&_voxels); nodeList->addHook(this); nodeList->addDomainListener(this); @@ -184,10 +185,10 @@ Application::Application(int& argc, char** argv, timeval &startup_time) : // read the ApplicationInfo.ini file for Name/Version/Domain information QSettings applicationInfo("resources/info/ApplicationInfo.ini", QSettings::IniFormat); - + // set the associated application properties applicationInfo.beginGroup("INFO"); - + setApplicationName(applicationInfo.value("name").toString()); setApplicationVersion(applicationInfo.value("version").toString()); setOrganizationName(applicationInfo.value("organizationName").toString()); @@ -198,52 +199,55 @@ Application::Application(int& argc, char** argv, timeval &startup_time) : // Check to see if the user passed in a command line option for loading a local // Voxel File. _voxelsFilename = getCmdOption(argc, constArgv, "-i"); - + // the callback for our instance of NodeList is attachNewHeadToNode nodeList->linkedDataCreateCallback = &attachNewHeadToNode; - + #ifdef _WIN32 WSADATA WsaData; int wsaresult = WSAStartup(MAKEWORD(2,2), &WsaData); #endif - + // tell the NodeList instance who to tell the domain server we care about - const char nodeTypesOfInterest[] = {NODE_TYPE_AUDIO_MIXER, NODE_TYPE_AVATAR_MIXER, NODE_TYPE_VOXEL_SERVER, + const char nodeTypesOfInterest[] = {NODE_TYPE_AUDIO_MIXER, NODE_TYPE_AVATAR_MIXER, NODE_TYPE_VOXEL_SERVER, NODE_TYPE_PARTICLE_SERVER}; nodeList->setNodeTypesOfInterest(nodeTypesOfInterest, sizeof(nodeTypesOfInterest)); QTimer* silentNodeTimer = new QTimer(this); connect(silentNodeTimer, SIGNAL(timeout()), nodeList, SLOT(removeSilentNodes())); silentNodeTimer->start(NODE_SILENCE_THRESHOLD_USECS / 1000); - + QString cachePath = QStandardPaths::writableLocation(QStandardPaths::DataLocation); - + _networkAccessManager = new QNetworkAccessManager(this); QNetworkDiskCache* cache = new QNetworkDiskCache(_networkAccessManager); cache->setCacheDirectory(!cachePath.isEmpty() ? cachePath : "interfaceCache"); _networkAccessManager->setCache(cache); - + _window->setCentralWidget(_glWidget); - + restoreSizeAndPosition(); _window->setVisible(true); _glWidget->setFocusPolicy(Qt::StrongFocus); _glWidget->setFocus(); - + // enable mouse tracking; otherwise, we only get drag events _glWidget->setMouseTracking(true); - + // initialization continues in initializeGL when OpenGL context is ready - + // Tell our voxel edit sender about our known jurisdictions _voxelEditSender.setVoxelServerJurisdictions(&_voxelServerJurisdictions); _particleEditSender.setServerJurisdictions(&_particleServerJurisdictions); + Particle::setVoxelEditPacketSender(&_voxelEditSender); + Particle::setParticleEditPacketSender(&_particleEditSender); + // For now we're going to set the PPS for outbound packets to be super high, this is - // probably not the right long term solution. But for now, we're going to do this to + // probably not the right long term solution. But for now, we're going to do this to // allow you to move a particle around in your hand _particleEditSender.setPacketsPerSecond(3000); // super high!! - + // Set the sixense filtering _sixenseManager.setFilter(Menu::getInstance()->isOptionChecked(MenuOption::FilterSixense)); } @@ -251,14 +255,14 @@ Application::Application(int& argc, char** argv, timeval &startup_time) : Application::~Application() { qInstallMessageHandler(NULL); - + // make sure we don't call the idle timer any more delete idleTimer; // ask the audio thread to quit and wait until it is done _audio.thread()->quit(); _audio.thread()->wait(); - + storeSizeAndPosition(); NodeList::getInstance()->removeHook(&_voxels); NodeList::getInstance()->removeHook(this); @@ -278,59 +282,59 @@ Application::~Application() { void Application::restoreSizeAndPosition() { QSettings* settings = new QSettings(this); QRect available = desktop()->availableGeometry(); - + settings->beginGroup("Window"); - + int x = (int)loadSetting(settings, "x", 0); int y = (int)loadSetting(settings, "y", 0); _window->move(x, y); - + int width = (int)loadSetting(settings, "width", available.width()); int height = (int)loadSetting(settings, "height", available.height()); _window->resize(width, height); - + settings->endGroup(); } void Application::storeSizeAndPosition() { QSettings* settings = new QSettings(this); - + settings->beginGroup("Window"); - + settings->setValue("width", _window->rect().width()); settings->setValue("height", _window->rect().height()); - + settings->setValue("x", _window->pos().x()); settings->setValue("y", _window->pos().y()); - + settings->endGroup(); } void Application::initializeGL() { qDebug( "Created Display Window.\n" ); - + // initialize glut for shape drawing; Qt apparently initializes it on OS X #ifndef __APPLE__ int argc = 0; glutInit(&argc, 0); #endif - + // Before we render anything, let's set up our viewFrustumOffsetCamera with a sufficiently large // field of view and near and far clip to make it interesting. //viewFrustumOffsetCamera.setFieldOfView(90.0); _viewFrustumOffsetCamera.setNearClip(0.1); _viewFrustumOffsetCamera.setFarClip(500.0 * TREE_SCALE); - + initDisplay(); qDebug( "Initialized Display.\n" ); - + init(); qDebug( "Init() complete.\n" ); - + // create thread for receipt of data via UDP if (_enableNetworkThread) { pthread_create(&_networkReceiveThread, NULL, networkReceive, NULL); - qDebug("Network receive thread created.\n"); + qDebug("Network receive thread created.\n"); } // create thread for parsing of voxel data independent of the main network and rendering threads @@ -341,21 +345,21 @@ void Application::initializeGL() { if (_enableProcessVoxelsThread) { qDebug("Voxel parsing thread created.\n"); } - + // call terminate before exiting connect(this, SIGNAL(aboutToQuit()), SLOT(terminate())); - + // call our timer function every second QTimer* timer = new QTimer(this); connect(timer, SIGNAL(timeout()), SLOT(timer())); timer->start(1000); - + // call our idle function whenever we can idleTimer = new QTimer(this); connect(idleTimer, SIGNAL(timeout()), SLOT(idle())); idleTimer->start(0); _idleLoopStdev.reset(); - + if (_justStarted) { float startupTime = (usecTimestampNow() - usecTimestamp(&_applicationStartupTime)) / 1000000.0; _justStarted = false; @@ -363,14 +367,14 @@ void Application::initializeGL() { sprintf(title, "Interface: %4.2f seconds\n", startupTime); qDebug("%s", title); const char LOGSTASH_INTERFACE_START_TIME_KEY[] = "interface-start-time"; - + // ask the Logstash class to record the startup time Logging::stashValue(STAT_TYPE_TIMER, LOGSTASH_INTERFACE_START_TIME_KEY, startupTime); } - + // update before the first render update(0.0f); - + InfoView::showFirstTime(); } @@ -378,7 +382,7 @@ void Application::paintGL() { PerformanceWarning::setSuppressShortTimings(Menu::getInstance()->isOptionChecked(MenuOption::SuppressShortTimings)); bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings); PerformanceWarning warn(showWarnings, "Application::paintGL()"); - + glEnable(GL_LINE_SMOOTH); if (OculusManager::isConnected()) { @@ -387,17 +391,17 @@ void Application::paintGL() { _myCamera.setTightness (0.0f); // Camera is directly connected to head without smoothing _myCamera.setTargetPosition(_myAvatar.getHead().calculateAverageEyePosition()); _myCamera.setTargetRotation(_myAvatar.getHead().getOrientation()); - + } else if (_myCamera.getMode() == CAMERA_MODE_FIRST_PERSON) { _myCamera.setTightness(0.0f); // In first person, camera follows head exactly without delay _myCamera.setTargetPosition(_myAvatar.getHead().calculateAverageEyePosition()); _myCamera.setTargetRotation(_myAvatar.getHead().getCameraOrientation()); - + } else if (_myCamera.getMode() == CAMERA_MODE_THIRD_PERSON) { _myCamera.setTightness (0.0f); // Camera is directly connected to head without smoothing _myCamera.setTargetPosition(_myAvatar.getUprightHeadPosition()); _myCamera.setTargetRotation(_myAvatar.getHead().getCameraOrientation()); - + } else if (_myCamera.getMode() == CAMERA_MODE_MIRROR) { _myCamera.setTightness(0.0f); float headHeight = _myAvatar.getHead().calculateAverageEyePosition().y - _myAvatar.getPosition().y; @@ -408,9 +412,9 @@ void Application::paintGL() { // Update camera position _myCamera.update( 1.f/_fps ); - - - // Note: whichCamera is used to pick between the normal camera myCamera for our + + + // Note: whichCamera is used to pick between the normal camera myCamera for our // main camera, vs, an alternate camera. The alternate camera we support right now // is the viewFrustumOffsetCamera. But theoretically, we could use this same mechanism // to add other cameras. @@ -421,7 +425,7 @@ void Application::paintGL() { Camera whichCamera = _myCamera; if (Menu::getInstance()->isOptionChecked(MenuOption::DisplayFrustum)) { - + ViewFrustumOffset viewFrustumOffset = Menu::getInstance()->getViewFrustumOffset(); // set the camera to third-person view but offset so we can see the frustum @@ -433,7 +437,7 @@ void Application::paintGL() { _viewFrustumOffsetCamera.initialize(); // force immediate snap to ideal position and orientation _viewFrustumOffsetCamera.update(1.f/_fps); whichCamera = _viewFrustumOffsetCamera; - } + } if (Menu::getInstance()->isOptionChecked(MenuOption::Shadows)) { updateShadowMap(); @@ -442,22 +446,22 @@ void Application::paintGL() { if (OculusManager::isConnected()) { OculusManager::display(whichCamera); } else if (TV3DManager::isConnected()) { - _glowEffect.prepare(); + _glowEffect.prepare(); TV3DManager::display(whichCamera); _glowEffect.render(); } else { - _glowEffect.prepare(); - + _glowEffect.prepare(); + glMatrixMode(GL_MODELVIEW); glPushMatrix(); glLoadIdentity(); displaySide(whichCamera); glPopMatrix(); - + _glowEffect.render(); - + if (Menu::getInstance()->isOptionChecked(MenuOption::Mirror)) { - + bool eyeRelativeCamera = false; if (_rearMirrorTools->getZoomLevel() == BODY) { _mirrorCamera.setDistance(MIRROR_REARVIEW_BODY_DISTANCE * _myAvatar.getScale()); @@ -469,47 +473,47 @@ void Application::paintGL() { // face/body so that the average eye position lies at the origin eyeRelativeCamera = true; _mirrorCamera.setTargetPosition(glm::vec3()); - + } else { _mirrorCamera.setTargetPosition(_myAvatar.getHead().calculateAverageEyePosition()); } } - + _mirrorCamera.setTargetRotation(_myAvatar.getWorldAlignedOrientation() * glm::quat(glm::vec3(0.0f, PIf, 0.0f))); _mirrorCamera.update(1.0f/_fps); - + // set the bounds of rear mirror view - glViewport(_mirrorViewRect.x(), _glWidget->height() - _mirrorViewRect.y() - _mirrorViewRect.height(), + glViewport(_mirrorViewRect.x(), _glWidget->height() - _mirrorViewRect.y() - _mirrorViewRect.height(), _mirrorViewRect.width(), _mirrorViewRect.height()); - glScissor(_mirrorViewRect.x(), _glWidget->height() - _mirrorViewRect.y() - _mirrorViewRect.height(), + glScissor(_mirrorViewRect.x(), _glWidget->height() - _mirrorViewRect.y() - _mirrorViewRect.height(), _mirrorViewRect.width(), _mirrorViewRect.height()); bool updateViewFrustum = false; updateProjectionMatrix(_mirrorCamera, updateViewFrustum); glEnable(GL_SCISSOR_TEST); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - + // render rear mirror view glPushMatrix(); if (eyeRelativeCamera) { // save absolute translations glm::vec3 absoluteSkeletonTranslation = _myAvatar.getSkeletonModel().getTranslation(); glm::vec3 absoluteFaceTranslation = _myAvatar.getHead().getFaceModel().getTranslation(); - + // get the eye positions relative to the neck and use them to set the face translation glm::vec3 leftEyePosition, rightEyePosition; _myAvatar.getHead().getFaceModel().setTranslation(glm::vec3()); _myAvatar.getHead().getFaceModel().getEyePositions(leftEyePosition, rightEyePosition); _myAvatar.getHead().getFaceModel().setTranslation((leftEyePosition + rightEyePosition) * -0.5f); - + // get the neck position relative to the body and use it to set the skeleton translation glm::vec3 neckPosition; _myAvatar.getSkeletonModel().setTranslation(glm::vec3()); _myAvatar.getSkeletonModel().getNeckPosition(neckPosition); _myAvatar.getSkeletonModel().setTranslation(_myAvatar.getHead().getFaceModel().getTranslation() - neckPosition); - + displaySide(_mirrorCamera, true); - + // restore absolute translations _myAvatar.getSkeletonModel().setTranslation(absoluteSkeletonTranslation); _myAvatar.getHead().getFaceModel().setTranslation(absoluteFaceTranslation); @@ -517,9 +521,9 @@ void Application::paintGL() { displaySide(_mirrorCamera, true); } glPopMatrix(); - + _rearMirrorTools->render(false); - + // reset Viewport and projection matrix glViewport(0, 0, _glWidget->width(), _glWidget->height()); glDisable(GL_SCISSOR_TEST); @@ -527,10 +531,10 @@ void Application::paintGL() { } else if (Menu::getInstance()->isOptionChecked(MenuOption::FullscreenMirror)) { _rearMirrorTools->render(true); } - + displayOverlay(); } - + _frameCount++; } @@ -548,7 +552,7 @@ void Application::resetCamerasOnResizeGL(Camera& camera, int width, int height) void Application::resizeGL(int width, int height) { resetCamerasOnResizeGL(_viewFrustumOffsetCamera, width, height); resetCamerasOnResizeGL(_myCamera, width, height); - + glViewport(0, 0, width, height); // shouldn't this account for the menu??? updateProjectionMatrix(); @@ -565,12 +569,12 @@ void Application::updateProjectionMatrix(Camera& camera, bool updateViewFrustum) float left, right, bottom, top, nearVal, farVal; glm::vec4 nearClipPlane, farClipPlane; - + // Tell our viewFrustum about this change, using the application camera if (updateViewFrustum) { loadViewFrustum(camera, _viewFrustum); computeOffAxisFrustum(left, right, bottom, top, nearVal, farVal, nearClipPlane, farClipPlane); - + // If we're in Display Frustum mode, then we want to use the slightly adjust near/far clip values of the // _viewFrustumOffsetCamera, so that we can see more of the application content in the application's frustum if (Menu::getInstance()->isOptionChecked(MenuOption::DisplayFrustum)) { @@ -583,7 +587,7 @@ void Application::updateProjectionMatrix(Camera& camera, bool updateViewFrustum) tempViewFrustum.computeOffAxisFrustum(left, right, bottom, top, nearVal, farVal, nearClipPlane, farClipPlane); } glFrustum(left, right, bottom, top, nearVal, farVal); - + glMatrixMode(GL_MODELVIEW); } @@ -594,7 +598,7 @@ void Application::resetProfile(const QString& username) { updateWindowTitle(); } -void Application::controlledBroadcastToNodes(unsigned char* broadcastData, size_t dataBytes, +void Application::controlledBroadcastToNodes(unsigned char* broadcastData, size_t dataBytes, const char* nodeTypes, int numNodeTypes) { Application* self = getInstance(); for (int i = 0; i < numNodeTypes; ++i) { @@ -603,7 +607,7 @@ void Application::controlledBroadcastToNodes(unsigned char* broadcastData, size_ if (nodeTypes[i] == NODE_TYPE_VOXEL_SERVER && !Menu::getInstance()->isOptionChecked(MenuOption::Voxels)) { continue; } - + // Perform the broadcast for one type int nReceivingNodes = NodeList::getInstance()->broadcastToNodes(broadcastData, dataBytes, & nodeTypes[i], 1); @@ -620,7 +624,7 @@ void Application::controlledBroadcastToNodes(unsigned char* broadcastData, size_ default: continue; } - self->_bandwidthMeter.outputStream(channel).updateValue(nReceivingNodes * dataBytes); + self->_bandwidthMeter.outputStream(channel).updateValue(nReceivingNodes * dataBytes); } } @@ -631,7 +635,7 @@ void Application::keyPressEvent(QKeyEvent* event) { _myAvatar.setKeyState(event->key() == Qt::Key_Backspace || event->key() == Qt::Key_Delete ? DELETE_KEY_DOWN : INSERT_KEY_DOWN); _myAvatar.setChatMessage(string(_chatEntry.getContents().size(), SOLID_BLOCK_CHAR)); - + } else { _myAvatar.setChatMessage(_chatEntry.getContents()); _chatEntry.clear(); @@ -666,7 +670,7 @@ void Application::keyPressEvent(QKeyEvent* event) { break; case Qt::Key_Apostrophe: _audioScope.inputPaused = !_audioScope.inputPaused; - break; + break; case Qt::Key_L: if (!isShifted && !isMeta) { _displayLevels = !_displayLevels; @@ -676,7 +680,7 @@ void Application::keyPressEvent(QKeyEvent* event) { Menu::getInstance()->triggerOption(MenuOption::Log); } break; - + case Qt::Key_E: if (_nudgeStarted) { _nudgeGuidePosition.y += _mouseVoxel.s; @@ -684,14 +688,14 @@ void Application::keyPressEvent(QKeyEvent* event) { if (!_myAvatar.getDriveKeys(UP)) { _myAvatar.jump(); } - _myAvatar.setDriveKeys(UP, 1); + _myAvatar.setDriveKeys(UP, 1); } break; case Qt::Key_Asterisk: Menu::getInstance()->triggerOption(MenuOption::Stars); break; - + case Qt::Key_C: if (_nudgeStarted) { _nudgeGuidePosition.y -= _mouseVoxel.s; @@ -699,7 +703,7 @@ void Application::keyPressEvent(QKeyEvent* event) { _myAvatar.setDriveKeys(DOWN, 1); } break; - + case Qt::Key_W: if (_nudgeStarted) { if (_lookingAlongX) { @@ -719,7 +723,7 @@ void Application::keyPressEvent(QKeyEvent* event) { _myAvatar.setDriveKeys(FWD, 1); } break; - + case Qt::Key_S: if (isShifted && !isMeta) { _voxels.collectStatsForTreesAndVBOs(); @@ -743,11 +747,11 @@ void Application::keyPressEvent(QKeyEvent* event) { _myAvatar.setDriveKeys(BACK, 1); } break; - + case Qt::Key_Space: resetSensors(); break; - + case Qt::Key_G: if (isShifted) { Menu::getInstance()->triggerOption(MenuOption::Gravity); @@ -755,7 +759,7 @@ void Application::keyPressEvent(QKeyEvent* event) { Menu::getInstance()->triggerOption(MenuOption::VoxelGetColorMode); } break; - + case Qt::Key_A: if (isShifted) { Menu::getInstance()->triggerOption(MenuOption::Atmosphere); @@ -777,7 +781,7 @@ void Application::keyPressEvent(QKeyEvent* event) { _myAvatar.setDriveKeys(ROT_LEFT, 1); } break; - + case Qt::Key_D: if (_nudgeStarted) { if (_lookingAlongX) { @@ -797,7 +801,7 @@ void Application::keyPressEvent(QKeyEvent* event) { _myAvatar.setDriveKeys(ROT_RIGHT, 1); } break; - + case Qt::Key_Return: case Qt::Key_Enter: if (_nudgeStarted) { @@ -809,7 +813,7 @@ void Application::keyPressEvent(QKeyEvent* event) { setMenuShortcutsEnabled(false); } break; - + case Qt::Key_Up: if (_nudgeStarted && !isShifted) { if (_lookingAlongX) { @@ -831,7 +835,7 @@ void Application::keyPressEvent(QKeyEvent* event) { _myAvatar.setDriveKeys(isShifted ? UP : FWD, 1); } break; - + case Qt::Key_Down: if (_nudgeStarted && !isShifted) { if (_lookingAlongX) { @@ -853,7 +857,7 @@ void Application::keyPressEvent(QKeyEvent* event) { _myAvatar.setDriveKeys(isShifted ? DOWN : BACK, 1); } break; - + case Qt::Key_Left: if (_nudgeStarted) { if (_lookingAlongX) { @@ -873,7 +877,7 @@ void Application::keyPressEvent(QKeyEvent* event) { _myAvatar.setDriveKeys(isShifted ? LEFT : ROT_LEFT, 1); } break; - + case Qt::Key_Right: if (_nudgeStarted) { if (_lookingAlongX) { @@ -893,7 +897,7 @@ void Application::keyPressEvent(QKeyEvent* event) { _myAvatar.setDriveKeys(isShifted ? RIGHT : ROT_RIGHT, 1); } break; - + case Qt::Key_I: if (isShifted) { _myCamera.setEyeOffsetOrientation(glm::normalize( @@ -903,7 +907,7 @@ void Application::keyPressEvent(QKeyEvent* event) { } updateProjectionMatrix(); break; - + case Qt::Key_K: if (isShifted) { _myCamera.setEyeOffsetOrientation(glm::normalize( @@ -913,7 +917,7 @@ void Application::keyPressEvent(QKeyEvent* event) { } updateProjectionMatrix(); break; - + case Qt::Key_J: if (isShifted) { _viewFrustum.setFocalLength(_viewFrustum.getFocalLength() - 0.1f); @@ -925,20 +929,20 @@ void Application::keyPressEvent(QKeyEvent* event) { } updateProjectionMatrix(); break; - + case Qt::Key_M: if (isShifted) { _viewFrustum.setFocalLength(_viewFrustum.getFocalLength() + 0.1f); if (TV3DManager::isConnected()) { TV3DManager::configureCamera(_myCamera, _glWidget->width(),_glWidget->height()); } - + } else { _myCamera.setEyeOffsetPosition(_myCamera.getEyeOffsetPosition() + glm::vec3(0.001, 0, 0)); } updateProjectionMatrix(); break; - + case Qt::Key_U: if (isShifted) { _myCamera.setEyeOffsetOrientation(glm::normalize( @@ -948,7 +952,7 @@ void Application::keyPressEvent(QKeyEvent* event) { } updateProjectionMatrix(); break; - + case Qt::Key_Y: if (isShifted) { _myCamera.setEyeOffsetOrientation(glm::normalize( @@ -1040,7 +1044,7 @@ void Application::keyReleaseEvent(QKeyEvent* event) { _myAvatar.setKeyState(NO_KEY_DOWN); return; } - + switch (event->key()) { case Qt::Key_Shift: _pasteMode = false; @@ -1048,47 +1052,47 @@ void Application::keyReleaseEvent(QKeyEvent* event) { case Qt::Key_E: _myAvatar.setDriveKeys(UP, 0); break; - + case Qt::Key_C: _myAvatar.setDriveKeys(DOWN, 0); break; - + case Qt::Key_W: _myAvatar.setDriveKeys(FWD, 0); break; - + case Qt::Key_S: _myAvatar.setDriveKeys(BACK, 0); break; - + case Qt::Key_A: _myAvatar.setDriveKeys(ROT_LEFT, 0); break; - + case Qt::Key_D: _myAvatar.setDriveKeys(ROT_RIGHT, 0); break; - + case Qt::Key_Up: _myAvatar.setDriveKeys(FWD, 0); _myAvatar.setDriveKeys(UP, 0); break; - + case Qt::Key_Down: _myAvatar.setDriveKeys(BACK, 0); _myAvatar.setDriveKeys(DOWN, 0); break; - + case Qt::Key_Left: _myAvatar.setDriveKeys(LEFT, 0); _myAvatar.setDriveKeys(ROT_LEFT, 0); break; - + case Qt::Key_Right: _myAvatar.setDriveKeys(RIGHT, 0); _myAvatar.setDriveKeys(ROT_RIGHT, 0); break; - + default: event->ignore(); break; @@ -1107,7 +1111,7 @@ void Application::mouseMoveEvent(QMouseEvent* event) { if (activeWindow() == _window) { int deltaX = event->x() - _mouseX; int deltaY = event->y() - _mouseY; - + _mouseX = event->x(); _mouseY = event->y(); @@ -1128,7 +1132,7 @@ void Application::mouseMoveEvent(QMouseEvent* event) { if (!_justEditedVoxel && mouseVoxelPos != _lastMouseVoxelPos) { if (event->buttons().testFlag(Qt::LeftButton)) { maybeEditVoxelUnderCursor(); - + } else if (event->buttons().testFlag(Qt::RightButton) && Menu::getInstance()->isVoxelModeActionChecked()) { deleteVoxelUnderCursor(); } @@ -1154,17 +1158,17 @@ void Application::mousePressEvent(QMouseEvent* event) { _mousePressed = true; maybeEditVoxelUnderCursor(); - + if (_audio.mousePressEvent(_mouseX, _mouseY)) { // stop propagation return; } - + if (_rearMirrorTools->mousePressEvent(_mouseX, _mouseY)) { // stop propagation return; } - + if (!_palette.isActive() && (!_isHoverVoxel || _lookatTargetAvatar)) { // disable for now // _pieMenu.mousePressEvent(_mouseX, _mouseY); @@ -1183,18 +1187,18 @@ void Application::mousePressEvent(QMouseEvent* event) { const float GREEN_CLICK_FREQUENCY = 1250.f; const float BLUE_CLICK_FREQUENCY = 1330.f; const float MIDDLE_A_FREQUENCY = 440.f; - float frequency = MIDDLE_A_FREQUENCY + + float frequency = MIDDLE_A_FREQUENCY + (_hoverVoxel.red / 255.f * RED_CLICK_FREQUENCY + _hoverVoxel.green / 255.f * GREEN_CLICK_FREQUENCY + _hoverVoxel.blue / 255.f * BLUE_CLICK_FREQUENCY) / 3.f; - + _audio.startCollisionSound(1.0, frequency, 0.0, HOVER_VOXEL_DECAY, false); _isHoverVoxelSounding = true; - + const float PERCENTAGE_TO_MOVE_TOWARD = 0.90f; glm::vec3 newTarget = getMouseVoxelWorldCoordinates(_hoverVoxel); glm::vec3 myPosition = _myAvatar.getPosition(); - + // If there is not an action tool set (add, delete, color), move to this voxel if (Menu::getInstance()->isOptionChecked(MenuOption::ClickToFly) && !(Menu::getInstance()->isOptionChecked(MenuOption::VoxelAddMode) || @@ -1203,7 +1207,7 @@ void Application::mousePressEvent(QMouseEvent* event) { _myAvatar.setMoveTarget(myPosition + (newTarget - myPosition) * PERCENTAGE_TO_MOVE_TOWARD); } } - + } else if (event->button() == Qt::RightButton && Menu::getInstance()->isVoxelModeActionChecked()) { deleteVoxelUnderCursor(); } @@ -1259,9 +1263,9 @@ void Application::touchEndEvent(QTouchEvent* event) { _isTouchPressed = false; } -const bool USE_MOUSEWHEEL = false; +const bool USE_MOUSEWHEEL = false; void Application::wheelEvent(QWheelEvent* event) { - // Wheel Events disabled for now because they are also activated by touch look pitch up/down. + // Wheel Events disabled for now because they are also activated by touch look pitch up/down. if (USE_MOUSEWHEEL && (activeWindow() == _window)) { if (!Menu::getInstance()->isVoxelModeActionChecked()) { event->ignore(); @@ -1277,7 +1281,7 @@ void Application::wheelEvent(QWheelEvent* event) { void Application::sendPingPackets() { - const char nodesToPing[] = {NODE_TYPE_VOXEL_SERVER, NODE_TYPE_PARTICLE_SERVER, + const char nodesToPing[] = {NODE_TYPE_VOXEL_SERVER, NODE_TYPE_PARTICLE_SERVER, NODE_TYPE_AUDIO_MIXER, NODE_TYPE_AVATAR_MIXER}; unsigned char pingPacket[MAX_PACKET_SIZE]; @@ -1290,24 +1294,24 @@ void Application::sendPingPackets() { void Application::sendAvatarFaceVideoMessage(int frameCount, const QByteArray& data) { unsigned char packet[MAX_PACKET_SIZE]; unsigned char* packetPosition = packet; - + packetPosition += populateTypeAndVersion(packetPosition, PACKET_TYPE_AVATAR_FACE_VIDEO); - + QByteArray rfcUUID = NodeList::getInstance()->getOwnerUUID().toRfc4122(); memcpy(packetPosition, rfcUUID.constData(), rfcUUID.size()); packetPosition += rfcUUID.size(); - + *(uint32_t*)packetPosition = frameCount; packetPosition += sizeof(uint32_t); - + *(uint32_t*)packetPosition = data.size(); packetPosition += sizeof(uint32_t); - + uint32_t* offsetPosition = (uint32_t*)packetPosition; packetPosition += sizeof(uint32_t); - + int headerSize = packetPosition - packet; - + // break the data up into submessages of the maximum size (at least one, for zero-length packets) *offsetPosition = 0; do { @@ -1315,7 +1319,7 @@ void Application::sendAvatarFaceVideoMessage(int frameCount, const QByteArray& d memcpy(packetPosition, data.constData() + *offsetPosition, payloadSize); getInstance()->controlledBroadcastToNodes(packet, headerSize + payloadSize, &NODE_TYPE_AVATAR_MIXER, 1); *offsetPosition += payloadSize; - + } while (*offsetPosition < data.size()); } @@ -1326,24 +1330,24 @@ void Application::timer() { if (Menu::getInstance()->isOptionChecked(MenuOption::TestPing)) { sendPingPackets(); } - + _fps = (float)_frameCount / ((float)diffclock(&_timerStart, &_timerEnd) / 1000.f); _packetsPerSecond = (float)_packetCount / ((float)diffclock(&_timerStart, &_timerEnd) / 1000.f); _bytesPerSecond = (float)_bytesCount / ((float)diffclock(&_timerStart, &_timerEnd) / 1000.f); _frameCount = 0; _packetCount = 0; _bytesCount = 0; - + gettimeofday(&_timerStart, NULL); - + // if we haven't detected gyros, check for them now if (!_serialHeadSensor.isActive()) { _serialHeadSensor.pair(); } - + // ask the node list to check in with the domain server NodeList::getInstance()->sendDomainServerCheckIn(); - + // give the MyAvatar object position to the Profile so it can propagate to the data-server _profile.updatePosition(_myAvatar.getPosition()); } @@ -1352,34 +1356,34 @@ static glm::vec3 getFaceVector(BoxFace face) { switch (face) { case MIN_X_FACE: return glm::vec3(-1, 0, 0); - + case MAX_X_FACE: return glm::vec3(1, 0, 0); - + case MIN_Y_FACE: return glm::vec3(0, -1, 0); - + case MAX_Y_FACE: return glm::vec3(0, 1, 0); - + case MIN_Z_FACE: return glm::vec3(0, 0, -1); - + case MAX_Z_FACE: return glm::vec3(0, 0, 1); } } void Application::idle() { - // Normally we check PipelineWarnings, but since idle will often take more than 10ms we only show these idle timing - // details if we're in ExtraDebugging mode. However, the ::update() and it's subcomponents will show their timing + // Normally we check PipelineWarnings, but since idle will often take more than 10ms we only show these idle timing + // details if we're in ExtraDebugging mode. However, the ::update() and it's subcomponents will show their timing // details normally. bool showWarnings = getLogger()->extraDebugging(); PerformanceWarning warn(showWarnings, "Application::idle()"); - + timeval check; gettimeofday(&check, NULL); - + // Only run simulation code if more than IDLE_SIMULATE_MSECS have passed since last time we ran double timeSinceLastUpdate = diffclock(&_lastTimeUpdated, &check); @@ -1397,7 +1401,7 @@ void Application::idle() { PerformanceWarning warn(showWarnings, "Application::idle()... rest of it"); _lastTimeUpdated = check; _idleLoopStdev.addValue(timeSinceLastUpdate); - + // Record standard deviation and reset counter if needed const int STDEV_SAMPLES = 500; if (_idleLoopStdev.getSamples() > STDEV_SAMPLES) { @@ -1413,7 +1417,7 @@ void Application::idle() { void Application::terminate() { // Close serial port // close(serial_fd); - + LeapManager::terminate(); Menu::getInstance()->saveSettings(); _rearMirrorTools->saveSettings(_settings); @@ -1424,7 +1428,7 @@ void Application::terminate() { if (_enableNetworkThread) { _stopNetworkReceiveThread = true; - pthread_join(_networkReceiveThread, NULL); + pthread_join(_networkReceiveThread, NULL); } printf(""); @@ -1441,18 +1445,18 @@ static Avatar* processAvatarMessageHeader(unsigned char*& packetData, size_t& da if (avatarMixerNode) { avatarMixerNode->recordBytesReceived(dataBytes); } - + // skip the header int numBytesPacketHeader = numBytesForPacketHeader(packetData); packetData += numBytesPacketHeader; dataBytes -= numBytesPacketHeader; - + // read the node id QUuid nodeUUID = QUuid::fromRfc4122(QByteArray((char*) packetData, NUM_BYTES_RFC4122_UUID)); - + packetData += NUM_BYTES_RFC4122_UUID; dataBytes -= NUM_BYTES_RFC4122_UUID; - + // make sure the node exists Node* node = NodeList::getInstance()->nodeWithUUID(nodeUUID); if (!node || !node->getLinkedData()) { @@ -1473,11 +1477,11 @@ void Application::processAvatarURLsMessage(unsigned char* packetData, size_t dat // don't ask over and over again. Instead use this message to // Tell the other avatars that your dataserver data has // changed. - + //QDataStream in(QByteArray((char*)packetData, dataBytes)); //QUrl voxelURL; //in >> voxelURL; - + // use this timing to as the data-server for an updated mesh for this avatar (if we have UUID) DataServerClient::getValuesForKeysAndUUID(QStringList() << DataServerKey::FaceMeshURL << DataServerKey::SkeletonURL, avatar->getUUID()); @@ -1529,7 +1533,7 @@ void Application::removeVoxel(glm::vec3 position, voxel.z = position.z / TREE_SCALE; voxel.s = scale / TREE_SCALE; _voxelEditSender.sendVoxelEditMessage(PACKET_TYPE_VOXEL_ERASE, voxel); - + // delete it locally to see the effect immediately (and in case no voxel server is present) _voxels.deleteVoxelAt(voxel.x, voxel.y, voxel.z, voxel.s); } @@ -1547,21 +1551,25 @@ void Application::shootParticle() { glm::vec3 gravity = DEFAULT_GRAVITY * 0.f; float damping = DEFAULT_DAMPING * 0.01f; QString script( - " function collisionWithVoxel(voxel) { " - " print('collisionWithVoxel(voxel)... '); " - " print('myID=' + Particle.getID() + '\\n'); " - " var voxelColor = voxel.getColor();" - " print('voxelColor=' + voxelColor.red + ', ' + voxelColor.green + ', ' + voxelColor.blue + '\\n'); " - " var myColor = Particle.getColor();" - " print('myColor=' + myColor.red + ', ' + myColor.green + ', ' + myColor.blue + '\\n'); " - " Particle.setColor(voxelColor); " - " } " + " function collisionWithVoxel(voxel) { " + " print('collisionWithVoxel(voxel)... '); " + " print('myID=' + Particle.getID() + '\\n'); " + " var voxelColor = voxel.getColor();" + " print('voxelColor=' + voxelColor.red + ', ' + voxelColor.green + ', ' + voxelColor.blue + '\\n'); " + " var myColor = Particle.getColor();" + " print('myColor=' + myColor.red + ', ' + myColor.green + ', ' + myColor.blue + '\\n'); " + " Particle.setColor(voxelColor); " + " var voxelAt = voxel.getPosition();" + " var voxelScale = voxel.getScale();" + " Voxels.queueVoxelDelete(voxelAt.x, voxelAt.y, voxelAt.z, voxelScale); " + " print('Voxels.queueVoxelDelete(' + voxelAt.x + ', ' + voxelAt.y + ', ' + voxelAt.z + ', ' + voxelScale + ')... \\n'); " + " } " " Particle.collisionWithVoxel.connect(collisionWithVoxel); " ); - - ParticleEditHandle* particleEditHandle = makeParticle(position / (float)TREE_SCALE, radius, color, + + ParticleEditHandle* particleEditHandle = makeParticle(position / (float)TREE_SCALE, radius, color, velocity / (float)TREE_SCALE, gravity, damping, NOT_IN_HAND, script); - + // If we wanted to be able to edit this particle after shooting, then we could store this value // and use it for editing later. But we don't care about that for "shooting" and therefore we just // clean up our memory now. deleting a ParticleEditHandle does not effect the underlying particle, @@ -1576,14 +1584,14 @@ ParticleEditHandle* Application::newParticleEditHandle(uint32_t id) { } // Caller is responsible for managing this EditableParticle -ParticleEditHandle* Application::makeParticle(glm::vec3 position, float radius, xColor color, glm::vec3 velocity, +ParticleEditHandle* Application::makeParticle(glm::vec3 position, float radius, xColor color, glm::vec3 velocity, glm::vec3 gravity, float damping, bool inHand, QString updateScript) { ParticleEditHandle* particleEditHandle = newParticleEditHandle(); particleEditHandle->createParticle(position, radius, color, velocity, gravity, damping, inHand, updateScript); return particleEditHandle; } - + void Application::makeVoxel(glm::vec3 position, float scale, @@ -1601,9 +1609,9 @@ void Application::makeVoxel(glm::vec3 position, voxel.blue = blue; PACKET_TYPE message = isDestructive ? PACKET_TYPE_VOXEL_SET_DESTRUCTIVE : PACKET_TYPE_VOXEL_SET; _voxelEditSender.sendVoxelEditMessage(message, voxel); - + // create the voxel locally so it appears immediately - + _voxels.createVoxel(voxel.x, voxel.y, voxel.z, voxel.s, voxel.red, voxel.green, voxel.blue, isDestructive); @@ -1670,9 +1678,9 @@ bool Application::sendVoxelsOperation(OctreeElement* element, void* extraData) { codeColorBuffer[bytesInCode + RED_INDEX] = voxel->getColor()[RED_INDEX]; codeColorBuffer[bytesInCode + GREEN_INDEX] = voxel->getColor()[GREEN_INDEX]; codeColorBuffer[bytesInCode + BLUE_INDEX] = voxel->getColor()[BLUE_INDEX]; - getInstance()->_voxelEditSender.queueVoxelEditMessage(PACKET_TYPE_VOXEL_SET_DESTRUCTIVE, + getInstance()->_voxelEditSender.queueVoxelEditMessage(PACKET_TYPE_VOXEL_SET_DESTRUCTIVE, codeColorBuffer, codeAndColorLength); - + delete[] codeColorBuffer; } return true; // keep going @@ -1682,7 +1690,7 @@ void Application::exportVoxels() { QString desktopLocation = QStandardPaths::writableLocation(QStandardPaths::DesktopLocation); QString suggestedName = desktopLocation.append("/voxels.svo"); - QString fileNameString = QFileDialog::getSaveFileName(_glWidget, tr("Export Voxels"), suggestedName, + QString fileNameString = QFileDialog::getSaveFileName(_glWidget, tr("Export Voxels"), suggestedName, tr("Sparse Voxel Octree Files (*.svo)")); QByteArray fileNameAscii = fileNameString.toLocal8Bit(); const char* fileName = fileNameAscii.data(); @@ -1729,7 +1737,7 @@ void Application::copyVoxels() { } void Application::pasteVoxelsToOctalCode(const unsigned char* octalCodeDestination) { - // Recurse the clipboard tree, where everything is root relative, and send all the colored voxels to + // Recurse the clipboard tree, where everything is root relative, and send all the colored voxels to // the server as an set voxel message, this will also rebase the voxels to the new location SendVoxelsOperationArgs args; args.newBaseOctCode = octalCodeDestination; @@ -1748,7 +1756,7 @@ void Application::pasteVoxels() { VoxelTreeElement* selectedNode = _voxels.getVoxelAt(_mouseVoxel.x, _mouseVoxel.y, _mouseVoxel.z, _mouseVoxel.s); // we only need the selected voxel to get the newBaseOctCode, which we can actually calculate from the - // voxel size/position details. If we don't have an actual selectedNode then use the mouseVoxel to create a + // voxel size/position details. If we don't have an actual selectedNode then use the mouseVoxel to create a // target octalCode for where the user is pointing. const unsigned char* octalCodeDestination; if (selectedNode) { @@ -1758,7 +1766,7 @@ void Application::pasteVoxels() { } pasteVoxelsToOctalCode(octalCodeDestination); - + if (calculatedOctCode) { delete[] calculatedOctCode; } @@ -1788,7 +1796,7 @@ void Application::nudgeVoxels() { if (!Menu::getInstance()->isOptionChecked(MenuOption::VoxelSelectMode) && selectedNode) { Menu::getInstance()->triggerOption(MenuOption::VoxelSelectMode); } - + if (!_nudgeStarted && selectedNode) { _nudgeVoxel = _mouseVoxel; _nudgeStarted = true; @@ -1839,14 +1847,14 @@ void Application::init() { delete tmpTree; _voxelImporter.init(); - + _environment.init(); _glowEffect.init(); _ambientOcclusionEffect.init(); _voxelShader.init(); _pointShader.init(); - + _headMouseX = _mouseX = _glWidget->width() / 2; _headMouseY = _mouseY = _glWidget->height() / 2; QCursor::setPos(_headMouseX, _headMouseY); @@ -1856,11 +1864,11 @@ void Application::init() { _myCamera.setMode(CAMERA_MODE_FIRST_PERSON); _myCamera.setModeShiftRate(1.0f); _myAvatar.setDisplayingLookatVectors(false); - + _mirrorCamera.setMode(CAMERA_MODE_MIRROR); _mirrorCamera.setAspectRatio((float)MIRROR_VIEW_WIDTH / (float)MIRROR_VIEW_HEIGHT); _mirrorCamera.setFieldOfView(30); - + OculusManager::connect(); if (OculusManager::isConnected()) { QMetaObject::invokeMethod(Menu::getInstance()->getActionForOption(MenuOption::Fullscreen), @@ -1874,9 +1882,9 @@ void Application::init() { "trigger", Qt::QueuedConnection); } - + LeapManager::initialize(); - + gettimeofday(&_timerStart, NULL); gettimeofday(&_lastTimeUpdated, NULL); @@ -1885,14 +1893,14 @@ void Application::init() { _audio.setJitterBufferSamples(Menu::getInstance()->getAudioJitterBufferSamples()); } qDebug("Loaded settings.\n"); - + if (!_profile.getUsername().isEmpty()) { // we have a username for this avatar, ask the data-server for the mesh URL for this avatar DataServerClient::getClientValueForKey(DataServerKey::FaceMeshURL); DataServerClient::getClientValueForKey(DataServerKey::SkeletonURL); } - // Set up VoxelSystem after loading preferences so we can get the desired max voxel count + // Set up VoxelSystem after loading preferences so we can get the desired max voxel count _voxels.setMaxVoxels(Menu::getInstance()->getMaxVoxels()); _voxels.setUseVoxelShader(Menu::getInstance()->isOptionChecked(MenuOption::UseVoxelShader)); _voxels.setVoxelsAsPoints(Menu::getInstance()->isOptionChecked(MenuOption::VoxelsAsPoints)); @@ -1901,11 +1909,11 @@ void Application::init() { _particles.init(); _particles.setViewFrustum(getViewFrustum()); - + _metavoxels.init(); - + _particleCollisionSystem.init(&_particleEditSender, _particles.getTree(), _voxels.getTree(), &_audio, &_myAvatar); - + _palette.init(_glWidget->width(), _glWidget->height()); _palette.addAction(Menu::getInstance()->getActionForOption(MenuOption::VoxelAddMode), 0, 0); _palette.addAction(Menu::getInstance()->getActionForOption(MenuOption::VoxelDeleteMode), 0, 1); @@ -1923,7 +1931,7 @@ void Application::init() { _pieMenu.addAction(_followMode); _audio.init(_glWidget); - + _rearMirrorTools = new RearMirrorTools(_glWidget, _mirrorViewRect, _settings); connect(_rearMirrorTools, SIGNAL(closeView()), SLOT(closeMirrorView())); connect(_rearMirrorTools, SIGNAL(restoreView()), SLOT(restoreMirrorView())); @@ -1941,7 +1949,7 @@ void Application::restoreMirrorView() { if (Menu::getInstance()->isOptionChecked(MenuOption::Mirror)) { Menu::getInstance()->triggerOption(MenuOption::Mirror);; } - + if (!Menu::getInstance()->isOptionChecked(MenuOption::FullscreenMirror)) { Menu::getInstance()->triggerOption(MenuOption::FullscreenMirror); } @@ -1951,7 +1959,7 @@ void Application::shrinkMirrorView() { if (!Menu::getInstance()->isOptionChecked(MenuOption::Mirror)) { Menu::getInstance()->triggerOption(MenuOption::Mirror);; } - + if (Menu::getInstance()->isOptionChecked(MenuOption::FullscreenMirror)) { Menu::getInstance()->triggerOption(MenuOption::FullscreenMirror); } @@ -1967,15 +1975,15 @@ void Application::updateLookatTargetAvatar(const glm::vec3& mouseRayOrigin, cons glm::vec3& eyePosition) { bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings); PerformanceWarning warn(showWarnings, "Application::updateLookatTargetAvatar()"); - + if (!_mousePressed) { _lookatTargetAvatar = findLookatTargetAvatar(mouseRayOrigin, mouseRayDirection, eyePosition, DEFAULT_NODE_ID_REF); } } - + Avatar* Application::findLookatTargetAvatar(const glm::vec3& mouseRayOrigin, const glm::vec3& mouseRayDirection, glm::vec3& eyePosition, QUuid& nodeUUID = DEFAULT_NODE_ID_REF) { - + NodeList* nodeList = NodeList::getInstance(); for (NodeList::iterator node = nodeList->begin(); node != nodeList->end(); node++) { if (node->getLinkedData() != NULL && node->getType() == NODE_TYPE_AGENT) { @@ -1985,7 +1993,7 @@ Avatar* Application::findLookatTargetAvatar(const glm::vec3& mouseRayOrigin, con // rescale to compensate for head embiggening eyePosition = (avatar->getHead().calculateAverageEyePosition() - avatar->getHead().getScalePivot()) * (avatar->getScale() / avatar->getHead().getScale()) + avatar->getHead().getScalePivot(); - + _lookatIndicatorScale = avatar->getHead().getScale(); _lookatOtherPosition = avatar->getHead().getPosition(); nodeUUID = avatar->getOwningNode()->getUUID(); @@ -1999,7 +2007,7 @@ Avatar* Application::findLookatTargetAvatar(const glm::vec3& mouseRayOrigin, con bool Application::isLookingAtMyAvatar(Avatar* avatar) { glm::vec3 theirLookat = avatar->getHead().getLookAtPosition(); glm::vec3 myHeadPosition = _myAvatar.getHead().getPosition(); - + if (pointInSphere(theirLookat, myHeadPosition, HEAD_SPHERE_RADIUS * _myAvatar.getScale())) { return true; } @@ -2100,7 +2108,7 @@ void Application::updateAvatars(float deltaTime, glm::vec3 mouseRayOrigin, glm:: bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings); PerformanceWarning warn(showWarnings, "Application::updateAvatars()"); NodeList* nodeList = NodeList::getInstance(); - + for(NodeList::iterator node = nodeList->begin(); node != nodeList->end(); node++) { node->lock(); if (node->getLinkedData()) { @@ -2113,7 +2121,7 @@ void Application::updateAvatars(float deltaTime, glm::vec3 mouseRayOrigin, glm:: } node->unlock(); } - + // simulate avatar fades for (vector::iterator fade = _avatarFades.begin(); fade != _avatarFades.end(); fade++) { Avatar* avatar = *fade; @@ -2123,7 +2131,7 @@ void Application::updateAvatars(float deltaTime, glm::vec3 mouseRayOrigin, glm:: if (avatar->getNewScale() < MINIMUM_SCALE) { delete avatar; _avatarFades.erase(fade--); - + } else { avatar->simulate(deltaTime, NULL); } @@ -2135,7 +2143,7 @@ void Application::updateMouseRay(float deltaTime, glm::vec3& mouseRayOrigin, glm bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings); PerformanceWarning warn(showWarnings, "Application::updateMouseRay()"); - _viewFrustum.computePickRay(_mouseX / (float)_glWidget->width(), _mouseY / (float)_glWidget->height(), + _viewFrustum.computePickRay(_mouseX / (float)_glWidget->width(), _mouseY / (float)_glWidget->height(), mouseRayOrigin, mouseRayDirection); // adjust for mirroring @@ -2150,7 +2158,7 @@ void Application::updateMouseRay(float deltaTime, glm::vec3& mouseRayOrigin, glm // tell my avatar if the mouse is being pressed... _myAvatar.setMousePressed(_mousePressed); - // tell my avatar the posiion and direction of the ray projected ino the world based on the mouse position + // tell my avatar the posiion and direction of the ray projected ino the world based on the mouse position _myAvatar.setMouseRay(mouseRayOrigin, mouseRayDirection); } @@ -2158,30 +2166,30 @@ void Application::updateFaceshift() { bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings); PerformanceWarning warn(showWarnings, "Application::updateFaceshift()"); - + // Update faceshift _faceshift.update(); - + // Copy angular velocity if measured by faceshift, to the head if (_faceshift.isActive()) { _myAvatar.getHead().setAngularVelocity(_faceshift.getHeadAngularVelocity()); } } -void Application::updateMyAvatarLookAtPosition(glm::vec3& lookAtSpot, glm::vec3& lookAtRayOrigin, +void Application::updateMyAvatarLookAtPosition(glm::vec3& lookAtSpot, glm::vec3& lookAtRayOrigin, glm::vec3& lookAtRayDirection) { bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings); PerformanceWarning warn(showWarnings, "Application::updateMyAvatarLookAtPosition()"); - + if (_myCamera.getMode() == CAMERA_MODE_MIRROR) { lookAtSpot = _myCamera.getPosition(); - + } else if (!_lookatTargetAvatar) { if (_isHoverVoxel) { // Look at the hovered voxel lookAtSpot = getMouseVoxelWorldCoordinates(_hoverVoxel); - + } else { // Just look in direction of the mouse ray const float FAR_AWAY_STARE = TREE_SCALE; @@ -2200,7 +2208,7 @@ void Application::updateMyAvatarLookAtPosition(glm::vec3& lookAtSpot, glm::vec3& _myAvatar.getHead().setLookAtPosition(lookAtSpot); } -void Application::updateHoverVoxels(float deltaTime, glm::vec3& mouseRayOrigin, glm::vec3& mouseRayDirection, +void Application::updateHoverVoxels(float deltaTime, glm::vec3& mouseRayOrigin, glm::vec3& mouseRayDirection, float& distance, BoxFace& face) { bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings); @@ -2222,21 +2230,21 @@ void Application::updateHoverVoxels(float deltaTime, glm::vec3& mouseRayOrigin, } else { // Voxel is not found, clear all _isHoverVoxelSounding = false; - _isHoverVoxel = false; + _isHoverVoxel = false; } } else { // Check for a new hover voxel glm::vec4 oldVoxel(_hoverVoxel.x, _hoverVoxel.y, _hoverVoxel.z, _hoverVoxel.s); - // only do this work if MAKE_SOUND_ON_VOXEL_HOVER or MAKE_SOUND_ON_VOXEL_CLICK is enabled, + // only do this work if MAKE_SOUND_ON_VOXEL_HOVER or MAKE_SOUND_ON_VOXEL_CLICK is enabled, // and make sure the tree is not already busy... because otherwise you'll have to wait. if (!(_voxels.treeIsBusy() || _mousePressed)) { { PerformanceWarning warn(showWarnings, "Application::updateHoverVoxels() _voxels.findRayIntersection()"); _isHoverVoxel = _voxels.findRayIntersection(mouseRayOrigin, mouseRayDirection, _hoverVoxel, distance, face); } - if (MAKE_SOUND_ON_VOXEL_HOVER && _isHoverVoxel && + if (MAKE_SOUND_ON_VOXEL_HOVER && _isHoverVoxel && glm::vec4(_hoverVoxel.x, _hoverVoxel.y, _hoverVoxel.z, _hoverVoxel.s) != oldVoxel) { - + _hoverVoxelOriginalColor[0] = _hoverVoxel.red; _hoverVoxelOriginalColor[1] = _hoverVoxel.green; _hoverVoxelOriginalColor[2] = _hoverVoxel.blue; @@ -2269,7 +2277,7 @@ void Application::updateMouseVoxels(float deltaTime, glm::vec3& mouseRayOrigin, _mouseVoxelScale = _mouseVoxel.s; } _mouseVoxelScaleInitialized = true; - + // find the nearest voxel with the desired scale if (_mouseVoxelScale > _mouseVoxel.s) { // choose the larger voxel that encompasses the one selected @@ -2277,7 +2285,7 @@ void Application::updateMouseVoxels(float deltaTime, glm::vec3& mouseRayOrigin, _mouseVoxel.y = _mouseVoxelScale * floorf(_mouseVoxel.y / _mouseVoxelScale); _mouseVoxel.z = _mouseVoxelScale * floorf(_mouseVoxel.z / _mouseVoxelScale); _mouseVoxel.s = _mouseVoxelScale; - + } else { glm::vec3 faceVector = getFaceVector(face); if (_mouseVoxelScale < _mouseVoxel.s) { @@ -2309,7 +2317,7 @@ void Application::updateMouseVoxels(float deltaTime, glm::vec3& mouseRayOrigin, _mouseVoxel.z = _mouseVoxelScale * floorf(pt.z / worldMouseVoxelScale); _mouseVoxel.s = _mouseVoxelScale; } - + if (Menu::getInstance()->isOptionChecked(MenuOption::VoxelDeleteMode)) { // red indicates deletion _mouseVoxel.red = 255; @@ -2328,7 +2336,7 @@ void Application::updateMouseVoxels(float deltaTime, glm::vec3& mouseRayOrigin, _mouseVoxel.green = paintColor.green(); _mouseVoxel.blue = paintColor.blue(); } - + // if we just edited, use the currently selected voxel as the "last" for drag detection if (_justEditedVoxel) { _lastMouseVoxelPos = glm::vec3(_mouseVoxel.x, _mouseVoxel.y, _mouseVoxel.z); @@ -2364,7 +2372,7 @@ void Application::updateLeap(float deltaTime) { void Application::updateSixense(float deltaTime) { bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings); PerformanceWarning warn(showWarnings, "Application::updateSixense()"); - + _sixenseManager.update(deltaTime); } @@ -2440,7 +2448,7 @@ void Application::updateTransmitter(float deltaTime) { _transmitterPickStart = _myAvatar.getSkeleton().joint[AVATAR_JOINT_CHEST].position; glm::vec3 direction = _myAvatar.getOrientation() * glm::quat(glm::radians(_myTransmitter.getEstimatedRotation())) * IDENTITY_FRONT; - + // check against voxels, avatars const float MAX_PICK_DISTANCE = 100.0f; float minDistance = MAX_PICK_DISTANCE; @@ -2451,7 +2459,7 @@ void Application::updateTransmitter(float deltaTime) { minDistance = min(minDistance, distance); } _transmitterPickEnd = _transmitterPickStart + direction * minDistance; - + } else { _transmitterPickStart = _transmitterPickEnd = glm::vec3(); } @@ -2461,7 +2469,7 @@ void Application::updateCamera(float deltaTime) { bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings); PerformanceWarning warn(showWarnings, "Application::updateCamera()"); - if (!OculusManager::isConnected() && !TV3DManager::isConnected()) { + if (!OculusManager::isConnected() && !TV3DManager::isConnected()) { if (Menu::getInstance()->isOptionChecked(MenuOption::FullscreenMirror)) { if (_myCamera.getMode() != CAMERA_MODE_MIRROR) { _myCamera.setMode(CAMERA_MODE_MIRROR); @@ -2478,15 +2486,15 @@ void Application::updateCamera(float deltaTime) { _myCamera.setModeShiftRate(1.0f); } } - + if (Menu::getInstance()->isOptionChecked(MenuOption::OffAxisProjection)) { float xSign = _myCamera.getMode() == CAMERA_MODE_MIRROR ? 1.0f : -1.0f; if (_faceshift.isActive()) { const float EYE_OFFSET_SCALE = 0.025f; glm::vec3 position = _faceshift.getHeadTranslation() * EYE_OFFSET_SCALE; - _myCamera.setEyeOffsetPosition(glm::vec3(position.x * xSign, position.y, -position.z)); + _myCamera.setEyeOffsetPosition(glm::vec3(position.x * xSign, position.y, -position.z)); updateProjectionMatrix(); - + } else if (_webcam.isActive()) { const float EYE_OFFSET_SCALE = 0.5f; glm::vec3 position = _webcam.getEstimatedPosition() * EYE_OFFSET_SCALE; @@ -2506,7 +2514,7 @@ void Application::updateDialogs(float deltaTime) { if (bandwidthDialog) { bandwidthDialog->update(); } - + VoxelStatsDialog* voxelStatsDialog = Menu::getInstance()->getVoxelStatsDialog(); if (voxelStatsDialog) { voxelStatsDialog->update(); @@ -2549,22 +2557,22 @@ void Application::updateCursor(float deltaTime) { void Application::update(float deltaTime) { bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings); PerformanceWarning warn(showWarnings, "Application::update()"); - + // check what's under the mouse and update the mouse voxel glm::vec3 mouseRayOrigin, mouseRayDirection; updateMouseRay(deltaTime, mouseRayOrigin, mouseRayDirection); - + // Set where I am looking based on my mouse ray (so that other people can see) glm::vec3 lookAtSpot; - + updateFaceshift(); updateLookatTargetAvatar(mouseRayOrigin, mouseRayDirection, lookAtSpot); updateMyAvatarLookAtPosition(lookAtSpot, mouseRayOrigin, mouseRayDirection); - + // Find the voxel we are hovering over, and respond if clicked float distance; BoxFace face; - + updateHoverVoxels(deltaTime, mouseRayOrigin, mouseRayDirection, distance, face); // clicking on voxels and making sounds updateMouseVoxels(deltaTime, mouseRayOrigin, mouseRayDirection, distance, face); // UI/UX related to voxels updateHandAndTouch(deltaTime); // Update state for touch sensors @@ -2582,7 +2590,7 @@ void Application::update(float deltaTime) { updateDialogs(deltaTime); // update various stats dialogs if present updateAudio(deltaTime); // Update audio stats for procedural sounds updateCursor(deltaTime); // Handle cursor updates - + _particles.update(); // update the particles... _particleCollisionSystem.update(); // handle collisions for the particles... } @@ -2590,39 +2598,39 @@ void Application::update(float deltaTime) { void Application::updateAvatar(float deltaTime) { bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings); PerformanceWarning warn(showWarnings, "Application::updateAvatar()"); - + // rotate body yaw for yaw received from multitouch _myAvatar.setOrientation(_myAvatar.getOrientation() * glm::quat(glm::vec3(0, _yawFromTouch, 0))); _yawFromTouch = 0.f; - + // apply pitch from touch _myAvatar.getHead().setMousePitch(_myAvatar.getHead().getMousePitch() + _myAvatar.getHand().getPitchUpdate() + _pitchFromTouch); _myAvatar.getHand().setPitchUpdate(0.f); _pitchFromTouch = 0.0f; - + // Update my avatar's state from gyros and/or webcam _myAvatar.updateFromGyrosAndOrWebcam(Menu::getInstance()->isOptionChecked(MenuOption::TurnWithHead)); - + // Update head mouse from faceshift if active if (_faceshift.isActive()) { glm::vec3 headVelocity = _faceshift.getHeadAngularVelocity(); - + // sets how quickly head angular rotation moves the head mouse const float HEADMOUSE_FACESHIFT_YAW_SCALE = 40.f; const float HEADMOUSE_FACESHIFT_PITCH_SCALE = 30.f; _headMouseX -= headVelocity.y * HEADMOUSE_FACESHIFT_YAW_SCALE; _headMouseY -= headVelocity.x * HEADMOUSE_FACESHIFT_PITCH_SCALE; } - + if (_serialHeadSensor.isActive()) { // Grab latest readings from the gyros float measuredPitchRate = _serialHeadSensor.getLastPitchRate(); float measuredYawRate = _serialHeadSensor.getLastYawRate(); - + // Update gyro-based mouse (X,Y on screen) const float MIN_MOUSE_RATE = 3.0; const float HORIZONTAL_PIXELS_PER_DEGREE = 2880.f / 45.f; @@ -2634,7 +2642,7 @@ void Application::updateAvatar(float deltaTime) { } const float MIDPOINT_OF_SCREEN = 0.5; - + // Only use gyro to set lookAt if mouse hasn't selected an avatar if (!_lookatTargetAvatar) { @@ -2651,7 +2659,7 @@ void Application::updateAvatar(float deltaTime) { } } - + // Constrain head-driven mouse to edges of screen _headMouseX = glm::clamp(_headMouseX, 0, _glWidget->width()); _headMouseY = glm::clamp(_headMouseY, 0, _glWidget->height()); @@ -2659,33 +2667,33 @@ void Application::updateAvatar(float deltaTime) { if (OculusManager::isConnected()) { float yaw, pitch, roll; OculusManager::getEulerAngles(yaw, pitch, roll); - + _myAvatar.getHead().setYaw(yaw); _myAvatar.getHead().setPitch(pitch); _myAvatar.getHead().setRoll(roll); } - + // Get audio loudness data from audio input device _myAvatar.getHead().setAudioLoudness(_audio.getLastInputLoudness()); - + NodeList* nodeList = NodeList::getInstance(); - + // send head/hand data to the avatar mixer and voxel server unsigned char broadcastString[MAX_PACKET_SIZE]; unsigned char* endOfBroadcastStringWrite = broadcastString; - + endOfBroadcastStringWrite += populateTypeAndVersion(endOfBroadcastStringWrite, PACKET_TYPE_HEAD_DATA); - + QByteArray ownerUUID = nodeList->getOwnerUUID().toRfc4122(); memcpy(endOfBroadcastStringWrite, ownerUUID.constData(), ownerUUID.size()); endOfBroadcastStringWrite += ownerUUID.size(); - + endOfBroadcastStringWrite += _myAvatar.getBroadcastData(endOfBroadcastStringWrite); - + const char nodeTypesOfInterest[] = { NODE_TYPE_AVATAR_MIXER }; controlledBroadcastToNodes(broadcastString, endOfBroadcastStringWrite - broadcastString, nodeTypesOfInterest, sizeof(nodeTypesOfInterest)); - + const float AVATAR_URLS_SEND_INTERVAL = 1.0f; if (shouldDo(AVATAR_URLS_SEND_INTERVAL, deltaTime)) { QUrl empty; @@ -2695,10 +2703,10 @@ void Application::updateAvatar(float deltaTime) { // NOTE: we get this from the view frustum, to make it simpler, since the // loadViewFrumstum() method will get the correct details from the camera // We could optimize this to not actually load the viewFrustum, since we don't - // actually need to calculate the view frustum planes to send these details + // actually need to calculate the view frustum planes to send these details // to the server. loadViewFrustum(_myCamera, _viewFrustum); - + // Update my voxel servers with my current voxel query... queryOctree(NODE_TYPE_VOXEL_SERVER, PACKET_TYPE_VOXEL_QUERY, _voxelServerJurisdictions); queryOctree(NODE_TYPE_PARTICLE_SERVER, PACKET_TYPE_PARTICLE_QUERY, _particleServerJurisdictions); @@ -2710,16 +2718,16 @@ void Application::queryOctree(NODE_TYPE serverType, PACKET_TYPE packetType, Node if (!Menu::getInstance()->isOptionChecked(MenuOption::Voxels)) { return; } - + bool wantExtraDebugging = getLogger()->extraDebugging(); - + // These will be the same for all servers, so we can set them up once and then reuse for each server we send to. _voxelQuery.setWantLowResMoving(!Menu::getInstance()->isOptionChecked(MenuOption::DisableLowRes)); _voxelQuery.setWantColor(!Menu::getInstance()->isOptionChecked(MenuOption::DisableColorVoxels)); _voxelQuery.setWantDelta(!Menu::getInstance()->isOptionChecked(MenuOption::DisableDeltaSending)); _voxelQuery.setWantOcclusionCulling(Menu::getInstance()->isOptionChecked(MenuOption::EnableOcclusionCulling)); _voxelQuery.setWantCompression(Menu::getInstance()->isOptionChecked(MenuOption::EnableVoxelPacketCompression)); - + _voxelQuery.setCameraPosition(_viewFrustum.getPosition()); _voxelQuery.setCameraOrientation(_viewFrustum.getOrientation()); _voxelQuery.setCameraFov(_viewFrustum.getFieldOfView()); @@ -2738,7 +2746,7 @@ void Application::queryOctree(NODE_TYPE serverType, PACKET_TYPE packetType, Node int totalServers = 0; int inViewServers = 0; int unknownJurisdictionServers = 0; - + for (NodeList::iterator node = nodeList->begin(); node != nodeList->end(); node++) { // only send to the NodeTypes that are serverType @@ -2747,7 +2755,7 @@ void Application::queryOctree(NODE_TYPE serverType, PACKET_TYPE packetType, Node // get the server bounds for this server QUuid nodeUUID = node->getUUID(); - + // if we haven't heard from this voxel server, go ahead and send it a query, so we // can get the jurisdiction... if (jurisdictions.find(nodeUUID) == jurisdictions.end()) { @@ -2756,7 +2764,7 @@ void Application::queryOctree(NODE_TYPE serverType, PACKET_TYPE packetType, Node const JurisdictionMap& map = (jurisdictions)[nodeUUID]; unsigned char* rootCode = map.getRootOctalCode(); - + if (rootCode) { VoxelPositionSize rootDetails; voxelDetailsForCode(rootCode, rootDetails); @@ -2774,7 +2782,7 @@ void Application::queryOctree(NODE_TYPE serverType, PACKET_TYPE packetType, Node } if (wantExtraDebugging && unknownJurisdictionServers > 0) { - qDebug("Servers: total %d, in view %d, unknown jurisdiction %d \n", + qDebug("Servers: total %d, in view %d, unknown jurisdiction %d \n", totalServers, inViewServers, unknownJurisdictionServers); } @@ -2793,11 +2801,11 @@ void Application::queryOctree(NODE_TYPE serverType, PACKET_TYPE packetType, Node perUnknownServer = (totalPPS / unknownJurisdictionServers); } } - + if (wantExtraDebugging && unknownJurisdictionServers > 0) { qDebug("perServerPPS: %d perUnknownServer: %d\n", perServerPPS, perUnknownServer); } - + for (NodeList::iterator node = nodeList->begin(); node != nodeList->end(); node++) { // only send to the NodeTypes that are serverType if (node->getActiveSocket() != NULL && node->getType() == serverType) { @@ -2808,7 +2816,7 @@ void Application::queryOctree(NODE_TYPE serverType, PACKET_TYPE packetType, Node bool inView = false; bool unknownView = false; - + // if we haven't heard from this voxel server, go ahead and send it a query, so we // can get the jurisdiction... if (jurisdictions.find(nodeUUID) == jurisdictions.end()) { @@ -2820,7 +2828,7 @@ void Application::queryOctree(NODE_TYPE serverType, PACKET_TYPE packetType, Node const JurisdictionMap& map = (jurisdictions)[nodeUUID]; unsigned char* rootCode = map.getRootOctalCode(); - + if (rootCode) { VoxelPositionSize rootDetails; voxelDetailsForCode(rootCode, rootDetails); @@ -2839,17 +2847,17 @@ void Application::queryOctree(NODE_TYPE serverType, PACKET_TYPE packetType, Node } } } - + if (inView) { _voxelQuery.setMaxOctreePacketsPerSecond(perServerPPS); } else if (unknownView) { if (wantExtraDebugging) { - qDebug() << "no known jurisdiction for node " << *node << ", give it budget of " + qDebug() << "no known jurisdiction for node " << *node << ", give it budget of " << perUnknownServer << " to send us jurisdiction.\n"; } - + // set the query's position/orientation to be degenerate in a manner that will get the scene quickly - // If there's only one server, then don't do this, and just let the normal voxel query pass through + // If there's only one server, then don't do this, and just let the normal voxel query pass through // as expected... this way, we will actually get a valid scene if there is one to be seen if (totalServers > 1) { _voxelQuery.setCameraPosition(glm::vec3(-0.1,-0.1,-0.1)); @@ -2880,7 +2888,7 @@ void Application::queryOctree(NODE_TYPE serverType, PACKET_TYPE packetType, Node // encode the query data... endOfVoxelQueryPacket += _voxelQuery.getBroadcastData(endOfVoxelQueryPacket); - + int packetLength = endOfVoxelQueryPacket - voxelQueryPacket; // make sure we still have an active socket @@ -2899,10 +2907,10 @@ void Application::queryOctree(NODE_TYPE serverType, PACKET_TYPE packetType, Node // loadViewFrustum() // // Description: this will load the view frustum bounds for EITHER the head -// or the "myCamera". +// or the "myCamera". // void Application::loadViewFrustum(Camera& camera, ViewFrustum& viewFrustum) { - // We will use these below, from either the camera or head vectors calculated above + // We will use these below, from either the camera or head vectors calculated above glm::vec3 position(camera.getPosition()); float fov = camera.getFieldOfView(); float nearClip = camera.getNearClip(); @@ -2911,10 +2919,10 @@ void Application::loadViewFrustum(Camera& camera, ViewFrustum& viewFrustum) { glm::quat rotation = camera.getRotation(); - // Set the viewFrustum up with the correct position and orientation of the camera + // Set the viewFrustum up with the correct position and orientation of the camera viewFrustum.setPosition(position); viewFrustum.setOrientation(rotation); - + // Also make sure it's got the correct lens details from the camera viewFrustum.setAspectRatio(aspectRatio); viewFrustum.setFieldOfView(fov); @@ -2936,9 +2944,9 @@ void Application::updateShadowMap() { fbo->bind(); glEnable(GL_DEPTH_TEST); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - + glViewport(0, 0, fbo->width(), fbo->height()); - + glm::vec3 lightDirection = -getSunDirection(); glm::quat rotation = glm::inverse(rotationBetween(IDENTITY_FRONT, lightDirection)); glm::vec3 translation = glm::vec3(); @@ -2960,57 +2968,57 @@ void Application::updateShadowMap() { minima = glm::min(minima, points[i]); maxima = glm::max(maxima, points[i]); } - + // stretch out our extents in z so that we get all of the avatars minima.z -= _viewFrustum.getFarClip() * 0.5f; maxima.z += _viewFrustum.getFarClip() * 0.5f; - + // save the combined matrix for rendering _shadowMatrix = glm::transpose(glm::translate(0.5f, 0.5f, 0.5f) * glm::scale(0.5f, 0.5f, 0.5f) * glm::ortho(minima.x, maxima.x, minima.y, maxima.y, -maxima.z, -minima.z) * glm::mat4_cast(rotation) * glm::translate(translation)); - + glMatrixMode(GL_PROJECTION); glPushMatrix(); glLoadIdentity(); glOrtho(minima.x, maxima.x, minima.y, maxima.y, -maxima.z, -minima.z); - + glMatrixMode(GL_MODELVIEW); glPushMatrix(); glLoadIdentity(); glm::vec3 axis = glm::axis(rotation); glRotatef(glm::angle(rotation), axis.x, axis.y, axis.z); - + // store view matrix without translation, which we'll use for precision-sensitive objects glGetFloatv(GL_MODELVIEW_MATRIX, (GLfloat*)&_untranslatedViewMatrix); _viewMatrixTranslation = translation; - + glTranslatef(translation.x, translation.y, translation.z); - + renderAvatars(true); - _particles.render(); - + _particles.render(); + glPopMatrix(); - + glMatrixMode(GL_PROJECTION); glPopMatrix(); - + glMatrixMode(GL_MODELVIEW); - + fbo->release(); - + glViewport(0, 0, _glWidget->width(), _glWidget->height()); } - + const GLfloat WHITE_SPECULAR_COLOR[] = { 1.0f, 1.0f, 1.0f, 1.0f }; const GLfloat NO_SPECULAR_COLOR[] = { 0.0f, 0.0f, 0.0f, 1.0f }; void Application::setupWorldLight() { - + // Setup 3D lights (after the camera transform, so that they are positioned in world space) glEnable(GL_COLOR_MATERIAL); glColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE); - + glm::vec3 sunDirection = getSunDirection(); GLfloat light_position0[] = { sunDirection.x, sunDirection.y, sunDirection.z, 0.0 }; glLightfv(GL_LIGHT0, GL_POSITION, light_position0); @@ -3018,8 +3026,8 @@ void Application::setupWorldLight() { glLightfv(GL_LIGHT0, GL_AMBIENT, ambient_color); GLfloat diffuse_color[] = { 0.8, 0.7, 0.7 }; glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuse_color); - - glLightfv(GL_LIGHT0, GL_SPECULAR, WHITE_SPECULAR_COLOR); + + glLightfv(GL_LIGHT0, GL_SPECULAR, WHITE_SPECULAR_COLOR); glMaterialfv(GL_FRONT, GL_SPECULAR, WHITE_SPECULAR_COLOR); glMateriali(GL_FRONT, GL_SHININESS, 96); } @@ -3032,7 +3040,7 @@ void Application::displaySide(Camera& whichCamera, bool selfAvatarOnly) { if (whichCamera.getMode() == CAMERA_MODE_MIRROR) { glScalef(-1.0f, 1.0f, 1.0f); glFrontFace(GL_CW); - + } else { glFrontFace(GL_CCW); } @@ -3059,9 +3067,9 @@ void Application::displaySide(Camera& whichCamera, bool selfAvatarOnly) { // Setup 3D lights (after the camera transform, so that they are positioned in world space) setupWorldLight(); - + if (!selfAvatarOnly && Menu::getInstance()->isOptionChecked(MenuOption::Stars)) { - PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), + PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), "Application::displaySide() ... stars..."); if (!_stars.isStarsLoaded()) { _stars.generate(STARFIELD_NUM_STARS, STARFIELD_SEED); @@ -3075,7 +3083,7 @@ void Application::displaySide(Camera& whichCamera, bool selfAvatarOnly) { float height = glm::distance(whichCamera.getPosition(), closestData.getAtmosphereCenter()); if (height < closestData.getAtmosphereInnerRadius()) { alpha = 0.0f; - + } else if (height < closestData.getAtmosphereOuterRadius()) { alpha = (height - closestData.getAtmosphereInnerRadius()) / (closestData.getAtmosphereOuterRadius() - closestData.getAtmosphereInnerRadius()); @@ -3088,20 +3096,20 @@ void Application::displaySide(Camera& whichCamera, bool selfAvatarOnly) { // draw the sky dome if (!selfAvatarOnly && Menu::getInstance()->isOptionChecked(MenuOption::Atmosphere)) { - PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), + PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), "Application::displaySide() ... atmosphere..."); _environment.renderAtmospheres(whichCamera); } - + glEnable(GL_LIGHTING); glEnable(GL_DEPTH_TEST); - + // Enable to show line from me to the voxel I am touching //renderLineToTouchedVoxel(); //renderThrustAtVoxel(_voxelThrust); - + if (!selfAvatarOnly) { - // draw a red sphere + // draw a red sphere float sphereRadius = 0.25f; glColor3f(1,0,0); glPushMatrix(); @@ -3117,33 +3125,33 @@ void Application::displaySide(Camera& whichCamera, bool selfAvatarOnly) { } // Draw voxels if (Menu::getInstance()->isOptionChecked(MenuOption::Voxels)) { - PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), + PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), "Application::displaySide() ... voxels..."); if (!Menu::getInstance()->isOptionChecked(MenuOption::DontRenderVoxels)) { _voxels.render(Menu::getInstance()->isOptionChecked(MenuOption::VoxelTextures)); } } - + // also, metavoxels if (Menu::getInstance()->isOptionChecked(MenuOption::Metavoxels)) { - PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), + PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), "Application::displaySide() ... metavoxels..."); _metavoxels.render(); - } + } // render particles... _particles.render(); - + // render the ambient occlusion effect if enabled if (Menu::getInstance()->isOptionChecked(MenuOption::AmbientOcclusion)) { - PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), + PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), "Application::displaySide() ... AmbientOcclusion..."); _ambientOcclusionEffect.render(); } - + // restore default, white specular glMaterialfv(GL_FRONT, GL_SPECULAR, WHITE_SPECULAR_COLOR); - + // Render the highlighted voxel if (_isHighlightVoxel) { renderHighlightVoxel(_highlightVoxel); @@ -3151,7 +3159,7 @@ void Application::displaySide(Camera& whichCamera, bool selfAvatarOnly) { // indicate what we'll be adding/removing in mouse mode, if anything if (_mouseVoxel.s != 0 && whichCamera.getMode() != CAMERA_MODE_MIRROR) { - PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), + PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), "Application::displaySide() ... voxels TOOLS UX..."); glDisable(GL_LIGHTING); @@ -3172,14 +3180,14 @@ void Application::displaySide(Camera& whichCamera, bool selfAvatarOnly) { } else { renderMouseVoxelGrid(_mouseVoxel.x, _mouseVoxel.y, _mouseVoxel.z, _mouseVoxel.s); } - + if (Menu::getInstance()->isOptionChecked(MenuOption::VoxelAddMode)) { // use a contrasting color so that we can see what we're doing glColor3ub(_mouseVoxel.red + 128, _mouseVoxel.green + 128, _mouseVoxel.blue + 128); } else { glColor3ub(_mouseVoxel.red, _mouseVoxel.green, _mouseVoxel.blue); } - + if (_nudgeStarted) { // render nudge guide cube glTranslatef(_nudgeGuidePosition.x + _nudgeVoxel.s*0.5f, @@ -3198,9 +3206,9 @@ void Application::displaySide(Camera& whichCamera, bool selfAvatarOnly) { glPopMatrix(); glEnable(GL_LIGHTING); } - + if (Menu::getInstance()->isOptionChecked(MenuOption::VoxelSelectMode) && _pasteMode && whichCamera.getMode() != CAMERA_MODE_MIRROR) { - PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), + PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), "Application::displaySide() ... PASTE Preview..."); glPushMatrix(); @@ -3223,17 +3231,17 @@ void Application::displaySide(Camera& whichCamera, bool selfAvatarOnly) { if (whichCamera.getMode() != CAMERA_MODE_MIRROR && Menu::getInstance()->isOptionChecked(MenuOption::Stats)) { renderWorldBox(); } - + // brad's frustum for debugging if (Menu::getInstance()->isOptionChecked(MenuOption::DisplayFrustum) && whichCamera.getMode() != CAMERA_MODE_MIRROR) { - PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), + PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), "Application::displaySide() ... renderViewFrustum..."); renderViewFrustum(_viewFrustum); } // render voxel fades if they exist if (_voxelFades.size() > 0) { - PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), + PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), "Application::displaySide() ... voxel fades..."); for(std::vector::iterator fade = _voxelFades.begin(); fade != _voxelFades.end();) { fade->render(); @@ -3245,15 +3253,15 @@ void Application::displaySide(Camera& whichCamera, bool selfAvatarOnly) { } } - { - PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), + { + PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), "Application::displaySide() ... renderFollowIndicator..."); renderFollowIndicator(); } - + // render transmitter pick ray, if non-empty if (_transmitterPickStart != _transmitterPickEnd) { - PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), + PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), "Application::displaySide() ... transmitter pick ray..."); Glower glower; @@ -3265,13 +3273,13 @@ void Application::displaySide(Camera& whichCamera, bool selfAvatarOnly) { glVertex3f(_transmitterPickEnd.x, _transmitterPickEnd.y, _transmitterPickEnd.z); glEnd(); glLineWidth(1.0f); - + glPushMatrix(); glTranslatef(_transmitterPickEnd.x, _transmitterPickEnd.y, _transmitterPickEnd.z); - + const float PICK_END_RADIUS = 0.025f; glutSolidSphere(PICK_END_RADIUS, 8, 8); - + glPopMatrix(); } } @@ -3285,21 +3293,21 @@ void Application::loadTranslatedViewMatrix(const glm::vec3& translation) { void Application::computeOffAxisFrustum(float& left, float& right, float& bottom, float& top, float& near, float& far, glm::vec4& nearClipPlane, glm::vec4& farClipPlane) const { - + _viewFrustum.computeOffAxisFrustum(left, right, bottom, top, near, far, nearClipPlane, farClipPlane); } void Application::displayOverlay() { PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), "Application::displayOverlay()"); - // Render 2D overlay: I/O level bar graphs and text + // Render 2D overlay: I/O level bar graphs and text glMatrixMode(GL_PROJECTION); glPushMatrix(); - glLoadIdentity(); + glLoadIdentity(); gluOrtho2D(0, _glWidget->width(), _glWidget->height(), 0); glDisable(GL_DEPTH_TEST); glDisable(GL_LIGHTING); - + // Display a single screen-size quad to create an alpha blended 'collision' flash if (_audio.getCollisionFlashesScreen()) { float collisionSoundMagnitude = _audio.getCollisionSoundMagnitude(); @@ -3308,7 +3316,7 @@ void Application::displayOverlay() { renderCollisionOverlay(_glWidget->width(), _glWidget->height(), _audio.getCollisionSoundMagnitude()); } } - + if (Menu::getInstance()->isOptionChecked(MenuOption::Stats)) { _audio.render(_glWidget->width(), _glWidget->height()); if (Menu::getInstance()->isOptionChecked(MenuOption::Oscilloscope)) { @@ -3317,7 +3325,7 @@ void Application::displayOverlay() { } //noiseTest(_glWidget->width(), _glWidget->height()); - + if (Menu::getInstance()->isOptionChecked(MenuOption::HeadMouse) && USING_INVENSENSE_MPU9150) { // Display small target box at center or head mouse target that can also be used to measure LOD @@ -3329,7 +3337,7 @@ void Application::displayOverlay() { glVertex2f(_headMouseX + PIXEL_BOX/2, _headMouseY); glVertex2f(_headMouseX, _headMouseY - PIXEL_BOX/2); glVertex2f(_headMouseX, _headMouseY + PIXEL_BOX/2); - glEnd(); + glEnd(); glEnable(GL_LINE_SMOOTH); glColor3f(1.f, 0.f, 0.f); glPointSize(3.0f); @@ -3342,7 +3350,7 @@ void Application::displayOverlay() { const float EYE_TARGET_PIXELS_PER_DEGREE = 40.0; int eyeTargetX = (_glWidget->width() / 2) - _faceshift.getEstimatedEyeYaw() * EYE_TARGET_PIXELS_PER_DEGREE; int eyeTargetY = (_glWidget->height() / 2) - _faceshift.getEstimatedEyePitch() * EYE_TARGET_PIXELS_PER_DEGREE; - + glColor3f(0.0, 1.0, 1.0); glDisable(GL_LINE_SMOOTH); glBegin(GL_LINES); @@ -3354,10 +3362,10 @@ void Application::displayOverlay() { } } - + // Show detected levels from the serial I/O ADC channel sensors if (_displayLevels) _serialHeadSensor.renderLevels(_glWidget->width(), _glWidget->height()); - + // Show hand transmitter data if detected if (_myTransmitter.isConnected()) { _myTransmitter.renderLevels(_glWidget->width(), _glWidget->height()); @@ -3365,21 +3373,21 @@ void Application::displayOverlay() { // Display stats and log text onscreen glLineWidth(1.0f); glPointSize(1.0f); - + if (Menu::getInstance()->isOptionChecked(MenuOption::Stats)) { - // Onscreen text about position, servers, etc + // Onscreen text about position, servers, etc displayStats(); - // Bandwidth meter + // Bandwidth meter if (Menu::getInstance()->isOptionChecked(MenuOption::Bandwidth)) { _bandwidthMeter.render(_glWidget->width(), _glWidget->height()); } // Stats at upper right of screen about who domain server is telling us about glPointSize(1.0f); char nodes[100]; - + NodeList* nodeList = NodeList::getInstance(); int totalAvatars = 0, totalServers = 0; - + for (NodeList::iterator node = nodeList->begin(); node != nodeList->end(); node++) { node->getType() == NODE_TYPE_AGENT ? totalAvatars++ : totalServers++; } @@ -3391,7 +3399,7 @@ void Application::displayOverlay() { if (Menu::getInstance()->isOptionChecked(MenuOption::CoverageMapV2)) { renderCoverageMapV2(); } - + if (Menu::getInstance()->isOptionChecked(MenuOption::CoverageMap)) { renderCoverageMap(); } @@ -3400,7 +3408,7 @@ void Application::displayOverlay() { if (_chatEntryOn) { _chatEntry.render(_glWidget->width(), _glWidget->height()); } - + // Show on-screen msec timer if (Menu::getInstance()->isOptionChecked(MenuOption::FrameTimer)) { char frameTimer[10]; @@ -3410,7 +3418,7 @@ void Application::displayOverlay() { drawtext(_glWidget->width() - 102, _glWidget->height() - 22, 0.30, 0, 1.0, 0, frameTimer, 1, 1, 1); } - + // render the webcam input frame _webcam.renderPreview(_glWidget->width(), _glWidget->height()); @@ -3465,7 +3473,7 @@ void Application::displayOverlay() { if (_pieMenu.isDisplayed()) { _pieMenu.render(); } - + glPopMatrix(); } @@ -3474,7 +3482,7 @@ void Application::displayStats() { const int PELS_PER_LINE = 15; char stats[200]; statsVerticalOffset += PELS_PER_LINE; - sprintf(stats, "%3.0f FPS, %d Pkts/sec, %3.2f Mbps ", + sprintf(stats, "%3.0f FPS, %d Pkts/sec, %3.2f Mbps ", _fps, _packetsPerSecond, (float)_bytesPerSecond * 8.f / 1000000.f); drawtext(10, statsVerticalOffset, 0.10f, 0, 1.0, 0, stats); @@ -3510,7 +3518,7 @@ void Application::displayStats() { sprintf(pingStats, "Ping audio/avatar/voxel: %d / %d / %d avg %d max ", pingAudio, pingAvatar, pingVoxel, pingVoxelMax); drawtext(10, statsVerticalOffset, 0.10f, 0, 1.0, 0, pingStats); } - + char avatarStats[200]; statsVerticalOffset += PELS_PER_LINE; glm::vec3 avatarPos = _myAvatar.getPosition(); @@ -3530,7 +3538,7 @@ void Application::displayStats() { drawtext(10, statsVerticalOffset, 0.10f, 0, 1.0, 0, avatarMixerStats); - // Used for formatting voxel stats details + // Used for formatting voxel stats details statsVerticalOffset += PELS_PER_LINE; // skip a line for voxels QLocale locale(QLocale::English); std::stringstream voxelStats; @@ -3578,7 +3586,7 @@ void Application::displayStats() { // Server Voxels voxelStats.str(""); - voxelStats << + voxelStats << "Server Voxels Total: " << serversTotalString.toLocal8Bit().constData() << " / " << "Internal: " << serversInternalString.toLocal8Bit().constData() << " / " << "Leaves: " << serversLeavesString.toLocal8Bit().constData() << ""; @@ -3594,7 +3602,7 @@ void Application::displayStats() { // Local Voxels voxelStats.str(""); - voxelStats << + voxelStats << "Local Voxels Total: " << localTotalString.toLocal8Bit().constData() << " / " << "Internal: " << localInternalString.toLocal8Bit().constData() << " / " << "Leaves: " << localLeavesString.toLocal8Bit().constData() << ""; @@ -3603,7 +3611,7 @@ void Application::displayStats() { // Local Voxel Memory Usage voxelStats.str(""); - voxelStats << + voxelStats << "Voxels Memory Nodes: " << VoxelTreeElement::getTotalMemoryUsage() / 1000000.f << "MB " "Geometry RAM: " << _voxels.getVoxelMemoryUsageRAM() / 1000000.f << "MB " << "VBO: " << _voxels.getVoxelMemoryUsageVBO() / 1000000.f << "MB "; @@ -3616,8 +3624,8 @@ void Application::displayStats() { // Voxel Rendering voxelStats.str(""); voxelStats.precision(4); - voxelStats << "Voxel Rendering Slots " << - "Max: " << _voxels.getMaxVoxels() / 1000.f << "K " << + voxelStats << "Voxel Rendering Slots " << + "Max: " << _voxels.getMaxVoxels() / 1000.f << "K " << "Drawn: " << _voxels.getVoxelsWritten() / 1000.f << "K " << "Abandoned: " << _voxels.getAbandonedVoxels() / 1000.f << "K "; statsVerticalOffset += PELS_PER_LINE; @@ -3632,9 +3640,9 @@ void Application::displayStats() { int voxelPacketsToProcess = _voxelProcessor.packetsToProcessCount(); QString packetsString = locale.toString((int)voxelPacketsToProcess); QString maxString = locale.toString((int)_recentMaxPackets); - voxelStats << "Voxel Packets to Process: " << packetsString.toLocal8Bit().constData() + voxelStats << "Voxel Packets to Process: " << packetsString.toLocal8Bit().constData() << " [Recent Max: " << maxString.toLocal8Bit().constData() << "]"; - + if (_resetRecentMaxPacketsSoon && voxelPacketsToProcess > 0) { _recentMaxPackets = 0; _resetRecentMaxPacketsSoon = false; @@ -3650,15 +3658,15 @@ void Application::displayStats() { drawtext(10, statsVerticalOffset, 0.10f, 0, 1.0, 0, (char*)voxelStats.str().c_str()); - // Leap data + // Leap data statsVerticalOffset += PELS_PER_LINE; drawtext(10, statsVerticalOffset, 0.10f, 0, 1.0, 0, (char*)LeapManager::statusString().c_str()); - + if (_perfStatsOn) { // Get the PerfStats group details. We need to allocate and array of char* long enough to hold 1+groups char** perfStatLinesArray = new char*[PerfStat::getGroupCount()+1]; int lines = PerfStat::DumpStats(perfStatLinesArray); - + for (int line=0; line < lines; line++) { statsVerticalOffset += PELS_PER_LINE; drawtext(10, statsVerticalOffset, 0.10f, 0, 1.0, 0, perfStatLinesArray[line]); @@ -3699,12 +3707,12 @@ void Application::renderLineToTouchedVoxel() { glm::vec2 Application::getScaledScreenPoint(glm::vec2 projectedPoint) { float horizontalScale = _glWidget->width() / 2.0f; float verticalScale = _glWidget->height() / 2.0f; - - // -1,-1 is 0,windowHeight + + // -1,-1 is 0,windowHeight // 1,1 is windowWidth,0 - + // -1,1 1,1 - // +-----------------------+ + // +-----------------------+ // | | | // | | | // | -1,0 | | @@ -3715,23 +3723,23 @@ glm::vec2 Application::getScaledScreenPoint(glm::vec2 projectedPoint) { // | | | // +-----------------------+ // -1,-1 1,-1 - - glm::vec2 screenPoint((projectedPoint.x + 1.0) * horizontalScale, + + glm::vec2 screenPoint((projectedPoint.x + 1.0) * horizontalScale, ((projectedPoint.y + 1.0) * -verticalScale) + _glWidget->height()); - + return screenPoint; } // render the coverage map on screen void Application::renderCoverageMapV2() { - + //qDebug("renderCoverageMap()\n"); - + glDisable(GL_LIGHTING); glLineWidth(2.0); glBegin(GL_LINES); glColor3f(0,1,1); - + renderCoverageMapsV2Recursively(&_voxels.myCoverageMapV2); glEnd(); @@ -3745,7 +3753,7 @@ void Application::renderCoverageMapsV2Recursively(CoverageMapV2* map) { glm::vec2 firstPoint = getScaledScreenPoint(box.getVertex(0)); glm::vec2 lastPoint(firstPoint); - + for (int i = 1; i < box.getVertexCount(); i++) { glm::vec2 thisPoint = getScaledScreenPoint(box.getVertex(i)); @@ -3769,14 +3777,14 @@ void Application::renderCoverageMapsV2Recursively(CoverageMapV2* map) { // render the coverage map on screen void Application::renderCoverageMap() { - + //qDebug("renderCoverageMap()\n"); - + glDisable(GL_LIGHTING); glLineWidth(2.0); glBegin(GL_LINES); glColor3f(0,0,1); - + renderCoverageMapsRecursively(&_voxels.myCoverageMap); glEnd(); @@ -3785,9 +3793,9 @@ void Application::renderCoverageMap() { void Application::renderCoverageMapsRecursively(CoverageMap* map) { for (int i = 0; i < map->getPolygonCount(); i++) { - + OctreeProjectedPolygon* polygon = map->getPolygon(i); - + if (polygon->getProjectionType() == (PROJECTION_RIGHT | PROJECTION_NEAR | PROJECTION_BOTTOM)) { glColor3f(.5,0,0); // dark red } else if (polygon->getProjectionType() == (PROJECTION_NEAR | PROJECTION_RIGHT)) { @@ -3814,7 +3822,7 @@ void Application::renderCoverageMapsRecursively(CoverageMap* map) { glm::vec2 firstPoint = getScaledScreenPoint(polygon->getVertex(0)); glm::vec2 lastPoint(firstPoint); - + for (int i = 1; i < polygon->getVertexCount(); i++) { glm::vec2 thisPoint = getScaledScreenPoint(polygon->getVertex(i)); @@ -3840,16 +3848,16 @@ void Application::renderAvatars(bool forceRenderHead, bool selfAvatarOnly) { if (!Menu::getInstance()->isOptionChecked(MenuOption::Avatars)) { return; } - PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), + PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), "Application::renderAvatars()"); if (!selfAvatarOnly) { // Render avatars of other nodes NodeList* nodeList = NodeList::getInstance(); - + for (NodeList::iterator node = nodeList->begin(); node != nodeList->end(); node++) { node->lock(); - + if (node->getLinkedData() != NULL && node->getType() == NODE_TYPE_AGENT) { Avatar *avatar = (Avatar *)node->getLinkedData(); if (!avatar->isInitialized()) { @@ -3858,17 +3866,17 @@ void Application::renderAvatars(bool forceRenderHead, bool selfAvatarOnly) { avatar->render(false); avatar->setDisplayingLookatVectors(Menu::getInstance()->isOptionChecked(MenuOption::LookAtVectors)); } - + node->unlock(); } - + // render avatar fades Glower glower; for (vector::iterator fade = _avatarFades.begin(); fade != _avatarFades.end(); fade++) { (*fade)->render(false); } } - + // Render my own Avatar _myAvatar.render(forceRenderHead); _myAvatar.setDisplayingLookatVectors(Menu::getInstance()->isOptionChecked(MenuOption::LookAtVectors)); @@ -3881,7 +3889,7 @@ void Application::renderAvatars(bool forceRenderHead, bool selfAvatarOnly) { // renderViewFrustum() // // Description: this will render the view frustum bounds for EITHER the head -// or the "myCamera". +// or the "myCamera". // // Frustum rendering mode. For debug purposes, we allow drawing the frustum in a couple of different ways. // We can draw it with each of these parts: @@ -3898,12 +3906,12 @@ void Application::renderAvatars(bool forceRenderHead, bool selfAvatarOnly) { void Application::renderViewFrustum(ViewFrustum& viewFrustum) { // Load it with the latest details! loadViewFrustum(_myCamera, viewFrustum); - + glm::vec3 position = viewFrustum.getOffsetPosition(); glm::vec3 direction = viewFrustum.getOffsetDirection(); glm::vec3 up = viewFrustum.getOffsetUp(); glm::vec3 right = viewFrustum.getOffsetRight(); - + // Get ready to draw some lines glDisable(GL_LIGHTING); glColor4f(1.0, 1.0, 1.0, 1.0); @@ -3937,7 +3945,7 @@ void Application::renderViewFrustum(ViewFrustum& viewFrustum) { || Menu::getInstance()->getFrustumDrawMode() == FRUSTUM_DRAW_MODE_PLANES || Menu::getInstance()->getFrustumDrawMode() == FRUSTUM_DRAW_MODE_NEAR_PLANE) { // Drawing the bounds of the frustum - // viewFrustum.getNear plane - bottom edge + // viewFrustum.getNear plane - bottom edge glColor3f(1,0,0); glVertex3f(viewFrustum.getNearBottomLeft().x, viewFrustum.getNearBottomLeft().y, viewFrustum.getNearBottomLeft().z); glVertex3f(viewFrustum.getNearBottomRight().x, viewFrustum.getNearBottomRight().y, viewFrustum.getNearBottomRight().z); @@ -3958,7 +3966,7 @@ void Application::renderViewFrustum(ViewFrustum& viewFrustum) { if (Menu::getInstance()->getFrustumDrawMode() == FRUSTUM_DRAW_MODE_ALL || Menu::getInstance()->getFrustumDrawMode() == FRUSTUM_DRAW_MODE_PLANES || Menu::getInstance()->getFrustumDrawMode() == FRUSTUM_DRAW_MODE_FAR_PLANE) { - // viewFrustum.getFar plane - bottom edge + // viewFrustum.getFar plane - bottom edge glColor3f(0,1,0); glVertex3f(viewFrustum.getFarBottomLeft().x, viewFrustum.getFarBottomLeft().y, viewFrustum.getFarBottomLeft().z); glVertex3f(viewFrustum.getFarBottomRight().x, viewFrustum.getFarBottomRight().y, viewFrustum.getFarBottomRight().z); @@ -3979,7 +3987,7 @@ void Application::renderViewFrustum(ViewFrustum& viewFrustum) { if (Menu::getInstance()->getFrustumDrawMode() == FRUSTUM_DRAW_MODE_ALL || Menu::getInstance()->getFrustumDrawMode() == FRUSTUM_DRAW_MODE_PLANES) { // RIGHT PLANE IS CYAN - // right plane - bottom edge - viewFrustum.getNear to distant + // right plane - bottom edge - viewFrustum.getNear to distant glColor3f(0,1,1); glVertex3f(viewFrustum.getNearBottomRight().x, viewFrustum.getNearBottomRight().y, viewFrustum.getNearBottomRight().z); glVertex3f(viewFrustum.getFarBottomRight().x, viewFrustum.getFarBottomRight().y, viewFrustum.getFarBottomRight().z); @@ -3997,7 +4005,7 @@ void Application::renderViewFrustum(ViewFrustum& viewFrustum) { // left plane - top edge - viewFrustum.getNear to distant glVertex3f(viewFrustum.getNearTopLeft().x, viewFrustum.getNearTopLeft().y, viewFrustum.getNearTopLeft().z); glVertex3f(viewFrustum.getFarTopLeft().x, viewFrustum.getFarTopLeft().y, viewFrustum.getFarTopLeft().z); - + // focal plane - bottom edge glColor3f(1.0f, 0.0f, 1.0f); float focalProportion = (viewFrustum.getFocalLength() - viewFrustum.getNearClip()) / @@ -4051,10 +4059,10 @@ bool Application::maybeEditVoxelUnderCursor() { _mouseVoxel.green, _mouseVoxel.blue, Menu::getInstance()->isOptionChecked(MenuOption::DestructiveAddVoxel)); - + // remember the position for drag detection _justEditedVoxel = true; - + } } else if (Menu::getInstance()->isOptionChecked(MenuOption::VoxelDeleteMode)) { deleteVoxelUnderCursor(); @@ -4066,7 +4074,7 @@ bool Application::maybeEditVoxelUnderCursor() { fade.voxelDetails.z = _mouseVoxel.z - slightlyBigger; fade.voxelDetails.s = _mouseVoxel.s + slightlyBigger + slightlyBigger; _voxelFades.push_back(fade); - + } else if (Menu::getInstance()->isOptionChecked(MenuOption::VoxelGetColorMode)) { eyedropperVoxelUnderCursor(); } else { @@ -4092,8 +4100,8 @@ void Application::deleteVoxelUnderCursor() { void Application::eyedropperVoxelUnderCursor() { VoxelTreeElement* selectedNode = _voxels.getVoxelAt(_mouseVoxel.x, _mouseVoxel.y, _mouseVoxel.z, _mouseVoxel.s); if (selectedNode && selectedNode->isColored()) { - QColor selectedColor(selectedNode->getColor()[RED_INDEX], - selectedNode->getColor()[GREEN_INDEX], + QColor selectedColor(selectedNode->getColor()[RED_INDEX], + selectedNode->getColor()[GREEN_INDEX], selectedNode->getColor()[BLUE_INDEX]); if (selectedColor.isValid()) { @@ -4119,14 +4127,14 @@ void Application::toggleFollowMode() { void Application::resetSensors() { _headMouseX = _mouseX = _glWidget->width() / 2; _headMouseY = _mouseY = _glWidget->height() / 2; - + if (_serialHeadSensor.isActive()) { _serialHeadSensor.resetAverages(); } _webcam.reset(); _faceshift.reset(); LeapManager::reset(); - + if (OculusManager::isConnected()) { OculusManager::reset(); } @@ -4136,7 +4144,7 @@ void Application::resetSensors() { _myTransmitter.resetLevels(); _myAvatar.setVelocity(glm::vec3(0,0,0)); _myAvatar.setThrust(glm::vec3(0,0,0)); - + QMetaObject::invokeMethod(&_audio, "reset", Qt::QueuedConnection); } @@ -4183,12 +4191,12 @@ void Application::updateWindowTitle(){ void Application::domainChanged(QString domain) { // update the user's last domain in their Profile (which will propagate to data-server) _profile.updateDomain(domain); - + updateWindowTitle(); // reset the environment so that we don't erroneously end up with multiple _environment.resetToDefault(); - + // reset our node to stats and node to jurisdiction maps... since these must be changing... _voxelServerJurisdictions.clear(); _octreeServerSceneStats.clear(); @@ -4196,7 +4204,7 @@ void Application::domainChanged(QString domain) { } void Application::nodeAdded(Node* node) { - + } void Application::nodeKilled(Node* node) { @@ -4210,7 +4218,7 @@ void Application::nodeKilled(Node* node) { printf("voxel server going away...... v[%f, %f, %f, %f]\n", rootDetails.x, rootDetails.y, rootDetails.z, rootDetails.s); - + // Add the jurisditionDetails object to the list of "fade outs" if (!Menu::getInstance()->isOptionChecked(MenuOption::DontFadeOnVoxelServerChanges)) { VoxelFade fade(VoxelFade::FADE_OUT, NODE_KILLED_RED, NODE_KILLED_GREEN, NODE_KILLED_BLUE); @@ -4219,18 +4227,18 @@ void Application::nodeKilled(Node* node) { fade.voxelDetails.s = fade.voxelDetails.s * slightly_smaller; _voxelFades.push_back(fade); } - + // If the voxel server is going away, remove it from our jurisdiction map so we don't send voxels to a dead server _voxelServerJurisdictions.erase(nodeUUID); } - + // also clean up scene stats for that server _voxelSceneStatsLock.lockForWrite(); if (_octreeServerSceneStats.find(nodeUUID) != _octreeServerSceneStats.end()) { _octreeServerSceneStats.erase(nodeUUID); } _voxelSceneStatsLock.unlock(); - + } else if (node->getType() == NODE_TYPE_PARTICLE_SERVER) { QUuid nodeUUID = node->getUUID(); // see if this is the first we've heard of this node... @@ -4241,7 +4249,7 @@ void Application::nodeKilled(Node* node) { printf("particle server going away...... v[%f, %f, %f, %f]\n", rootDetails.x, rootDetails.y, rootDetails.z, rootDetails.s); - + // Add the jurisditionDetails object to the list of "fade outs" if (!Menu::getInstance()->isOptionChecked(MenuOption::DontFadeOnVoxelServerChanges)) { VoxelFade fade(VoxelFade::FADE_OUT, NODE_KILLED_RED, NODE_KILLED_GREEN, NODE_KILLED_BLUE); @@ -4250,39 +4258,39 @@ void Application::nodeKilled(Node* node) { fade.voxelDetails.s = fade.voxelDetails.s * slightly_smaller; _voxelFades.push_back(fade); } - + // If the voxel server is going away, remove it from our jurisdiction map so we don't send voxels to a dead server _particleServerJurisdictions.erase(nodeUUID); } - + // also clean up scene stats for that server _voxelSceneStatsLock.lockForWrite(); if (_octreeServerSceneStats.find(nodeUUID) != _octreeServerSceneStats.end()) { _octreeServerSceneStats.erase(nodeUUID); } _voxelSceneStatsLock.unlock(); - + } else if (node->getType() == NODE_TYPE_AGENT) { Avatar* avatar = static_cast(node->getLinkedData()); if (avatar == _lookatTargetAvatar) { _lookatTargetAvatar = NULL; } - + // take over the avatar in order to fade it out node->setLinkedData(NULL); - + _avatarFades.push_back(avatar); } } -void Application::trackIncomingVoxelPacket(unsigned char* messageData, ssize_t messageLength, +void Application::trackIncomingVoxelPacket(unsigned char* messageData, ssize_t messageLength, const HifiSockAddr& senderSockAddr, bool wasStatsPacket) { - + // Attempt to identify the sender from it's address. Node* serverNode = NodeList::getInstance()->nodeWithAddress(senderSockAddr); if (serverNode) { QUuid nodeUUID = serverNode->getUUID(); - + // now that we know the node ID, let's add these stats to the stats for that node... _voxelSceneStatsLock.lockForWrite(); if (_octreeServerSceneStats.find(nodeUUID) != _octreeServerSceneStats.end()) { @@ -4297,16 +4305,16 @@ int Application::parseOctreeStats(unsigned char* messageData, ssize_t messageLen // But, also identify the sender, and keep track of the contained jurisdiction root for this server Node* server = NodeList::getInstance()->nodeWithAddress(senderSockAddr); - - // parse the incoming stats datas stick it in a temporary object for now, while we + + // parse the incoming stats datas stick it in a temporary object for now, while we // determine which server it belongs to VoxelSceneStats temp; int statsMessageLength = temp.unpackFromMessage(messageData, messageLength); - + // quick fix for crash... why would voxelServer be NULL? if (server) { QUuid nodeUUID = server->getUUID(); - + // now that we know the node ID, let's add these stats to the stats for that node... _voxelSceneStatsLock.lockForWrite(); if (_octreeServerSceneStats.find(nodeUUID) != _octreeServerSceneStats.end()) { @@ -4315,10 +4323,10 @@ int Application::parseOctreeStats(unsigned char* messageData, ssize_t messageLen _octreeServerSceneStats[nodeUUID] = temp; } _voxelSceneStatsLock.unlock(); - + VoxelPositionSize rootDetails; voxelDetailsForCode(temp.getJurisdictionRoot(), rootDetails); - + // see if this is the first we've heard of this node... NodeToJurisdictionMap* jurisdiction = NULL; if (server->getType() == NODE_TYPE_VOXEL_SERVER) { @@ -4326,8 +4334,8 @@ int Application::parseOctreeStats(unsigned char* messageData, ssize_t messageLen } else { jurisdiction = &_particleServerJurisdictions; } - - + + if (jurisdiction->find(nodeUUID) == jurisdiction->end()) { printf("stats from new server... v[%f, %f, %f, %f]\n", rootDetails.x, rootDetails.y, rootDetails.z, rootDetails.s); @@ -4354,12 +4362,12 @@ int Application::parseOctreeStats(unsigned char* messageData, ssize_t messageLen // Receive packets from other nodes/servers and decide what to do with them! void* Application::networkReceive(void* args) { - PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), + PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), "Application::networkReceive()"); HifiSockAddr senderSockAddr; ssize_t bytesReceived; - + Application* app = Application::getInstance(); while (!app->_stopNetworkReceiveThread) { if (NodeList::getInstance()->getNodeSocket().hasPendingDatagrams() && @@ -4367,36 +4375,36 @@ void* Application::networkReceive(void* args) { MAX_PACKET_SIZE, senderSockAddr.getAddressPointer(), senderSockAddr.getPortPointer()))) { - + app->_packetCount++; app->_bytesCount += bytesReceived; - + if (packetVersionMatch(app->_incomingPacket)) { // only process this packet if we have a match on the packet version switch (app->_incomingPacket[0]) { case PACKET_TYPE_TRANSMITTER_DATA_V2: // V2 = IOS transmitter app app->_myTransmitter.processIncomingData(app->_incomingPacket, bytesReceived); - + break; case PACKET_TYPE_MIXED_AUDIO: QMetaObject::invokeMethod(&app->_audio, "addReceivedAudioToBuffer", Qt::QueuedConnection, Q_ARG(QByteArray, QByteArray((char*) app->_incomingPacket, bytesReceived))); break; - + case PACKET_TYPE_PARTICLE_ADD_RESPONSE: // look up our ParticleEditHanders.... ParticleEditHandle::handleAddResponse(app->_incomingPacket, bytesReceived); break; - + case PACKET_TYPE_PARTICLE_DATA: case PACKET_TYPE_VOXEL_DATA: case PACKET_TYPE_VOXEL_ERASE: case PACKET_TYPE_OCTREE_STATS: case PACKET_TYPE_ENVIRONMENT_DATA: { - PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), + PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), "Application::networkReceive()... _voxelProcessor.queueReceivedPacket()"); - + bool wantExtraDebugging = app->getLogger()->extraDebugging(); if (wantExtraDebugging && app->_incomingPacket[0] == PACKET_TYPE_VOXEL_DATA) { int numBytesPacketHeader = numBytesForPacketHeader(app->_incomingPacket); @@ -4408,10 +4416,10 @@ void* Application::networkReceive(void* args) { dataAt += sizeof(VOXEL_PACKET_SENT_TIME); VOXEL_PACKET_SENT_TIME arrivedAt = usecTimestampNow(); int flightTime = arrivedAt - sentAt; - + printf("got PACKET_TYPE_VOXEL_DATA, sequence:%d flightTime:%d\n", sequence, flightTime); - } - + } + // add this packet to our list of voxel packets and process them on the voxel processing app->_voxelProcessor.queueReceivedPacket(senderSockAddr, app->_incomingPacket, bytesReceived); break; @@ -4443,15 +4451,15 @@ void* Application::networkReceive(void* args) { break; } } - + if (app->_enableNetworkThread) { - pthread_exit(0); + pthread_exit(0); } - return NULL; + return NULL; } void Application::packetSentNotification(ssize_t length) { - _bandwidthMeter.outputStream(BandwidthMeter::VOXELS).updateValue(length); + _bandwidthMeter.outputStream(BandwidthMeter::VOXELS).updateValue(length); } void Application::loadScript() { @@ -4459,11 +4467,11 @@ void Application::loadScript() { QString desktopLocation = QStandardPaths::writableLocation(QStandardPaths::DesktopLocation); QString suggestedName = desktopLocation.append("/script.js"); - QString fileNameString = QFileDialog::getOpenFileName(_glWidget, tr("Open Script"), suggestedName, + QString fileNameString = QFileDialog::getOpenFileName(_glWidget, tr("Open Script"), suggestedName, tr("JavaScript Files (*.js)")); QByteArray fileNameAscii = fileNameString.toLocal8Bit(); const char* fileName = fileNameAscii.data(); - + printf("fileName:%s\n",fileName); std::ifstream file(fileName, std::ios::in|std::ios::binary|std::ios::ate); @@ -4481,7 +4489,7 @@ void Application::loadScript() { char* entireFile = new char[fileLength+1]; file.read((char*)entireFile, fileLength); file.close(); - + entireFile[fileLength] = 0;// null terminate QString script(entireFile); delete[] entireFile; @@ -4490,10 +4498,10 @@ void Application::loadScript() { bool wantMenuItems = true; // tells the ScriptEngine object to add menu items for itself - ScriptEngine* scriptEngine = new ScriptEngine(script, wantMenuItems, fileName, Menu::getInstance(), + ScriptEngine* scriptEngine = new ScriptEngine(script, wantMenuItems, fileName, Menu::getInstance(), &_controllerScriptingInterface); scriptEngine->setupMenuItems(); - + // setup the packet senders and jurisdiction listeners of the script engine's scripting interfaces so // we can use the same ones from the application. scriptEngine->getVoxelsScriptingInterface()->setPacketSender(&_voxelEditSender); @@ -4501,7 +4509,7 @@ void Application::loadScript() { QThread* workerThread = new QThread(this); - // when the worker thread is started, call our engine's run.. + // when the worker thread is started, call our engine's run.. connect(workerThread, SIGNAL(started()), scriptEngine, SLOT(run())); // when the thread is terminated, add both scriptEngine and thread to the deleteLater queue @@ -4510,9 +4518,9 @@ void Application::loadScript() { // when the application is about to quit, stop our script engine so it unwinds properly connect(this, SIGNAL(aboutToQuit()), scriptEngine, SLOT(stop())); - + scriptEngine->moveToThread(workerThread); - + // Starts an event loop, and emits workerThread->started() workerThread->start(); diff --git a/libraries/particles/src/Particle.cpp b/libraries/particles/src/Particle.cpp index edca3a1fc0..668c12637d 100644 --- a/libraries/particles/src/Particle.cpp +++ b/libraries/particles/src/Particle.cpp @@ -7,39 +7,43 @@ // // -#include #include +#include #include #include // usecTimestampNow() -#include - #include -#include "ParticlesScriptingInterface.h" +// This is not ideal, but adding script-engine as a linked library, will cause a circular reference +// I'm open to other potential solutions. Could we change cmake to allow libraries to reference each others +// headers, but not link to each other, this is essentially what this construct is doing, but would be +// better to add includes to the include path, but not link +#include "../../script-engine/src/ScriptEngine.h" + +#include "ParticlesScriptingInterface.h" #include "Particle.h" uint32_t Particle::_nextID = 0; -VoxelsScriptingInterface* Particle::_voxelsScriptingInterface = NULL; -ParticlesScriptingInterface* Particle::_particlesScriptingInterface = NULL; +VoxelEditPacketSender* Particle::_voxelEditSender = NULL; +ParticleEditPacketSender* Particle::_particleEditSender = NULL; -Particle::Particle(glm::vec3 position, float radius, rgbColor color, glm::vec3 velocity, glm::vec3 gravity, +Particle::Particle(glm::vec3 position, float radius, rgbColor color, glm::vec3 velocity, glm::vec3 gravity, float damping, bool inHand, QString updateScript, uint32_t id) { - + init(position, radius, color, velocity, gravity, damping, inHand, updateScript, id); } Particle::Particle() { rgbColor noColor = { 0, 0, 0 }; - init(glm::vec3(0,0,0), 0, noColor, glm::vec3(0,0,0), + init(glm::vec3(0,0,0), 0, noColor, glm::vec3(0,0,0), DEFAULT_GRAVITY, DEFAULT_DAMPING, NOT_IN_HAND, DEFAULT_SCRIPT, NEW_PARTICLE); } Particle::~Particle() { } -void Particle::init(glm::vec3 position, float radius, rgbColor color, glm::vec3 velocity, glm::vec3 gravity, +void Particle::init(glm::vec3 position, float radius, rgbColor color, glm::vec3 velocity, glm::vec3 gravity, float damping, bool inHand, QString updateScript, uint32_t id) { if (id == NEW_PARTICLE) { _id = _nextID; @@ -51,7 +55,7 @@ void Particle::init(glm::vec3 position, float radius, rgbColor color, glm::vec3 _lastEdited = now; _lastUpdated = now; _created = now; // will get updated as appropriate in setLifetime() - + _position = position; _radius = radius; memcpy(_color, color, sizeof(_color)); @@ -215,7 +219,7 @@ int Particle::readParticleDataFromBuffer(const unsigned char* data, int bytesLef _script = tempString; dataAt += scriptLength; bytesRead += scriptLength; - + //printf("Particle::readParticleDataFromBuffer()... "); debugDump(); } return bytesRead; @@ -227,11 +231,11 @@ Particle Particle::fromEditPacket(unsigned char* data, int length, int& processe unsigned char* dataAt = data; processedBytes = 0; - // the first part of the data is our octcode... + // the first part of the data is our octcode... int octets = numberOfThreeBitSectionsInCode(data); int lengthOfOctcode = bytesRequiredForCodeLength(octets); - - // we don't actually do anything with this octcode... + + // we don't actually do anything with this octcode... dataAt += lengthOfOctcode; processedBytes += lengthOfOctcode; @@ -240,7 +244,7 @@ Particle Particle::fromEditPacket(unsigned char* data, int length, int& processe memcpy(&editID, dataAt, sizeof(editID)); dataAt += sizeof(editID); processedBytes += sizeof(editID); - + // special case for handling "new" particles if (editID == NEW_PARTICLE) { // If this is a NEW_PARTICLE, then we assume that there's an additional uint32_t creatorToken, that @@ -251,19 +255,19 @@ Particle Particle::fromEditPacket(unsigned char* data, int length, int& processe processedBytes += sizeof(creatorTokenID); newParticle.setCreatorTokenID(creatorTokenID); newParticle._newlyCreated = true; - + newParticle.setLifetime(0); // this guy is new! } else { newParticle._id = editID; newParticle._newlyCreated = false; - } + } // lastEdited memcpy(&newParticle._lastEdited, dataAt, sizeof(newParticle._lastEdited)); dataAt += sizeof(newParticle._lastEdited); processedBytes += sizeof(newParticle._lastEdited); - + // radius memcpy(&newParticle._radius, dataAt, sizeof(newParticle._radius)); dataAt += sizeof(newParticle._radius); @@ -283,12 +287,12 @@ Particle Particle::fromEditPacket(unsigned char* data, int length, int& processe memcpy(&newParticle._velocity, dataAt, sizeof(newParticle._velocity)); dataAt += sizeof(newParticle._velocity); processedBytes += sizeof(newParticle._velocity); - + // gravity memcpy(&newParticle._gravity, dataAt, sizeof(newParticle._gravity)); dataAt += sizeof(newParticle._gravity); processedBytes += sizeof(newParticle._gravity); - + // damping memcpy(&newParticle._damping, dataAt, sizeof(newParticle._damping)); dataAt += sizeof(newParticle._damping); @@ -311,11 +315,11 @@ Particle Particle::fromEditPacket(unsigned char* data, int length, int& processe const bool wantDebugging = false; if (wantDebugging) { - printf("Particle::fromEditPacket()...\n"); + printf("Particle::fromEditPacket()...\n"); printf(" Particle id in packet:%u\n", editID); newParticle.debugDump(); } - + return newParticle; } @@ -329,7 +333,7 @@ void Particle::debugDump() const { printf(" color:%d,%d,%d\n", _color[0], _color[1], _color[2]); } -bool Particle::encodeParticleEditMessageDetails(PACKET_TYPE command, int count, const ParticleDetail* details, +bool Particle::encodeParticleEditMessageDetails(PACKET_TYPE command, int count, const ParticleDetail* details, unsigned char* bufferOut, int sizeIn, int& sizeOut) { bool success = true; // assume the best @@ -338,13 +342,13 @@ bool Particle::encodeParticleEditMessageDetails(PACKET_TYPE command, int count, for (int i = 0; i < count && success; i++) { // get the octal code for the particle - unsigned char* octcode = pointToOctalCode(details[i].position.x, details[i].position.y, + unsigned char* octcode = pointToOctalCode(details[i].position.x, details[i].position.y, details[i].position.z, details[i].radius); int octets = numberOfThreeBitSectionsInCode(octcode); int lengthOfOctcode = bytesRequiredForCodeLength(octets); int lenfthOfEditData = lengthOfOctcode + expectedEditMessageBytes(); - + // make sure we have room to copy this particle if (sizeOut + lenfthOfEditData > sizeIn) { success = false; @@ -353,9 +357,9 @@ bool Particle::encodeParticleEditMessageDetails(PACKET_TYPE command, int count, memcpy(copyAt, octcode, lengthOfOctcode); copyAt += lengthOfOctcode; sizeOut += lengthOfOctcode; - + // Now add our edit content details... - + // id memcpy(copyAt, &details[i].id, sizeof(details[i].id)); copyAt += sizeof(details[i].id); @@ -419,7 +423,7 @@ bool Particle::encodeParticleEditMessageDetails(PACKET_TYPE command, int count, sizeOut += scriptLength; bool wantDebugging = false; - if (wantDebugging) { + if (wantDebugging) { printf("encodeParticleEditMessageDetails()....\n"); printf("Particle id :%u\n", details[i].id); printf(" nextID:%u\n", _nextID); @@ -433,7 +437,7 @@ bool Particle::encodeParticleEditMessageDetails(PACKET_TYPE command, int count, } // adjust any internal timestamps to fix clock skew for this server -void Particle::adjustEditPacketForClockSkew(unsigned char* codeColorBuffer, ssize_t length, int clockSkew) { +void Particle::adjustEditPacketForClockSkew(unsigned char* codeColorBuffer, ssize_t length, int clockSkew) { unsigned char* dataAt = codeColorBuffer; int octets = numberOfThreeBitSectionsInCode(dataAt); int lengthOfOctcode = bytesRequiredForCodeLength(octets); @@ -456,7 +460,7 @@ void Particle::adjustEditPacketForClockSkew(unsigned char* codeColorBuffer, ssiz uint64_t lastEditedInServerTime = lastEditedInLocalTime + clockSkew; memcpy(dataAt, &lastEditedInServerTime, sizeof(lastEditedInServerTime)); const bool wantDebug = false; - if (wantDebug) { + if (wantDebug) { qDebug("Particle::adjustEditPacketForClockSkew()...\n"); qDebug() << " lastEditedInLocalTime: " << lastEditedInLocalTime << "\n"; qDebug() << " clockSkew: " << clockSkew << "\n"; @@ -470,7 +474,7 @@ void Particle::update() { float elapsed = static_cast(now - _lastUpdated); _lastUpdated = now; float timeElapsed = elapsed / static_cast(USECS_PER_SECOND); - + // calculate our default shouldDie state... then allow script to change it if it wants... float velocityScalar = glm::length(getVelocity()); const float STILL_MOVING = 0.05f / static_cast(TREE_SCALE); @@ -480,13 +484,13 @@ void Particle::update() { bool isInHand = getInHand(); bool shouldDie = getShouldDie() || (!isInHand && !isStillMoving && isReallyOld); setShouldDie(shouldDie); - + runUpdateScript(); // allow the javascript to alter our state - + // If the ball is in hand, it doesn't move or have gravity effect it if (!isInHand) { _position += _velocity * timeElapsed; - + // handle bounces off the ground... if (_position.y <= 0) { _velocity = _velocity * glm::vec3(1,-1,1); @@ -507,21 +511,21 @@ void Particle::runUpdateScript() { if (!_script.isEmpty()) { QScriptEngine engine; - + // register meta-type for glm::vec3 and rgbColor conversions registerMetaTypes(&engine); - + ParticleScriptObject particleScriptable(this); QScriptValue particleValue = engine.newQObject(&particleScriptable); engine.globalObject().setProperty("Particle", particleValue); - + QScriptValue treeScaleValue = engine.newVariant(QVariant(TREE_SCALE)); engine.globalObject().setProperty("TREE_SCALE", TREE_SCALE); - + QScriptValue result = engine.evaluate(_script); - + particleScriptable.emitUpdate(); - + if (engine.hasUncaughtException()) { int line = engine.uncaughtExceptionLineNumber(); qDebug() << "Uncaught exception at line" << line << ":" << result.toString() << "\n"; @@ -532,45 +536,30 @@ void Particle::runUpdateScript() { void Particle::collisionWithParticle(Particle* other) { if (!_script.isEmpty()) { - QScriptEngine engine; - - // register meta-type for glm::vec3 and rgbColor conversions - registerMetaTypes(&engine); - + ScriptEngine engine(_script); // no menu or controller interface... + + if (_voxelEditSender) { + engine.getVoxelsScriptingInterface()->setPacketSender(_voxelEditSender); + } + if (_particleEditSender) { + engine.getParticlesScriptingInterface()->setPacketSender(_particleEditSender); + } + + // Add the Particle object ParticleScriptObject particleScriptable(this); - QScriptValue particleValue = engine.newQObject(&particleScriptable); - engine.globalObject().setProperty("Particle", particleValue); - - QScriptValue treeScaleValue = engine.newVariant(QVariant(TREE_SCALE)); - engine.globalObject().setProperty("TREE_SCALE", TREE_SCALE); + engine.registerGlobalObject("Particle", &particleScriptable); + // init and evaluate the script, but return so we can emit the collision + engine.evaluate(); - if (getVoxelsScriptingInterface()) { - QScriptValue voxelScripterValue = engine.newQObject(getVoxelsScriptingInterface()); - engine.globalObject().setProperty("Voxels", voxelScripterValue); - } - - if (getParticlesScriptingInterface()) { - QScriptValue particleScripterValue = engine.newQObject(getParticlesScriptingInterface()); - engine.globalObject().setProperty("Particles", particleScripterValue); - } - - QScriptValue result = engine.evaluate(_script); - ParticleScriptObject otherParticleScriptable(other); particleScriptable.emitCollisionWithParticle(&otherParticleScriptable); - if (getVoxelsScriptingInterface()) { - getVoxelsScriptingInterface()->getPacketSender()->releaseQueuedMessages(); + if (_voxelEditSender) { + _voxelEditSender->releaseQueuedMessages(); } - - if (getParticlesScriptingInterface()) { - getParticlesScriptingInterface()->getPacketSender()->releaseQueuedMessages(); - } - - if (engine.hasUncaughtException()) { - int line = engine.uncaughtExceptionLineNumber(); - qDebug() << "Uncaught exception at line" << line << ":" << result.toString() << "\n"; + if (_particleEditSender) { + _particleEditSender->releaseQueuedMessages(); } } } @@ -578,45 +567,32 @@ void Particle::collisionWithParticle(Particle* other) { void Particle::collisionWithVoxel(VoxelDetail* voxelDetails) { if (!_script.isEmpty()) { - QScriptEngine engine; - - // register meta-type for glm::vec3 and rgbColor conversions - registerMetaTypes(&engine); - + ScriptEngine engine(_script); // no menu or controller interface... + + // setup the packet senders and jurisdiction listeners of the script engine's scripting interfaces so + // we can use the same ones as our context. + if (_voxelEditSender) { + engine.getVoxelsScriptingInterface()->setPacketSender(_voxelEditSender); + } + if (_particleEditSender) { + engine.getParticlesScriptingInterface()->setPacketSender(_particleEditSender); + } + + // Add the Particle object ParticleScriptObject particleScriptable(this); - QScriptValue particleValue = engine.newQObject(&particleScriptable); - engine.globalObject().setProperty("Particle", particleValue); - - QScriptValue treeScaleValue = engine.newVariant(QVariant(TREE_SCALE)); - engine.globalObject().setProperty("TREE_SCALE", TREE_SCALE); + engine.registerGlobalObject("Particle", &particleScriptable); + // init and evaluate the script, but return so we can emit the collision + engine.evaluate(); - if (getVoxelsScriptingInterface()) { - QScriptValue voxelScripterValue = engine.newQObject(getVoxelsScriptingInterface()); - engine.globalObject().setProperty("Voxels", voxelScripterValue); - } - - if (getParticlesScriptingInterface()) { - QScriptValue particleScripterValue = engine.newQObject(getParticlesScriptingInterface()); - engine.globalObject().setProperty("Particles", particleScripterValue); - } - - QScriptValue result = engine.evaluate(_script); - VoxelDetailScriptObject voxelDetailsScriptable(voxelDetails); particleScriptable.emitCollisionWithVoxel(&voxelDetailsScriptable); - if (getVoxelsScriptingInterface()) { - getVoxelsScriptingInterface()->getPacketSender()->releaseQueuedMessages(); + if (_voxelEditSender) { + _voxelEditSender->releaseQueuedMessages(); } - - if (getParticlesScriptingInterface()) { - getParticlesScriptingInterface()->getPacketSender()->releaseQueuedMessages(); - } - - if (engine.hasUncaughtException()) { - int line = engine.uncaughtExceptionLineNumber(); - qDebug() << "Uncaught exception at line" << line << ":" << result.toString() << "\n"; + if (_particleEditSender) { + _particleEditSender->releaseQueuedMessages(); } } } @@ -625,7 +601,7 @@ void Particle::collisionWithVoxel(VoxelDetail* voxelDetails) { void Particle::setLifetime(float lifetime) { uint64_t lifetimeInUsecs = lifetime * USECS_PER_SECOND; - _created = usecTimestampNow() - lifetimeInUsecs; + _created = usecTimestampNow() - lifetimeInUsecs; } void Particle::copyChangedProperties(const Particle& other) { diff --git a/libraries/particles/src/Particle.h b/libraries/particles/src/Particle.h index 9c92d0ec48..e93790eab7 100644 --- a/libraries/particles/src/Particle.h +++ b/libraries/particles/src/Particle.h @@ -21,6 +21,8 @@ class VoxelsScriptingInterface; class ParticlesScriptingInterface; +class VoxelEditPacketSender; +class ParticleEditPacketSender; const uint32_t NEW_PARTICLE = 0xFFFFFFFF; @@ -48,19 +50,19 @@ const bool IN_HAND = true; // it's in a hand const bool NOT_IN_HAND = !IN_HAND; // it's not in a hand class Particle { - + public: Particle(); - Particle(glm::vec3 position, float radius, rgbColor color, glm::vec3 velocity, - glm::vec3 gravity = DEFAULT_GRAVITY, float damping = DEFAULT_DAMPING, bool inHand = NOT_IN_HAND, + Particle(glm::vec3 position, float radius, rgbColor color, glm::vec3 velocity, + glm::vec3 gravity = DEFAULT_GRAVITY, float damping = DEFAULT_DAMPING, bool inHand = NOT_IN_HAND, QString updateScript = DEFAULT_SCRIPT, uint32_t id = NEW_PARTICLE); - + /// creates an NEW particle from an PACKET_TYPE_PARTICLE_ADD_OR_EDIT edit data buffer - static Particle fromEditPacket(unsigned char* data, int length, int& processedBytes); - + static Particle fromEditPacket(unsigned char* data, int length, int& processedBytes); + virtual ~Particle(); - virtual void init(glm::vec3 position, float radius, rgbColor color, glm::vec3 velocity, - glm::vec3 gravity = DEFAULT_GRAVITY, float damping = DEFAULT_DAMPING, bool inHand = NOT_IN_HAND, + virtual void init(glm::vec3 position, float radius, rgbColor color, glm::vec3 velocity, + glm::vec3 gravity = DEFAULT_GRAVITY, float damping = DEFAULT_DAMPING, bool inHand = NOT_IN_HAND, QString updateScript = DEFAULT_SCRIPT, uint32_t id = NEW_PARTICLE); const glm::vec3& getPosition() const { return _position; } @@ -71,7 +73,7 @@ public: const glm::vec3& getGravity() const { return _gravity; } bool getInHand() const { return _inHand; } float getDamping() const { return _damping; } - + /// The last updated/simulated time of this particle from the time perspective of the authoritative server/source uint64_t getLastUpdated() const { return _lastUpdated; } @@ -91,9 +93,9 @@ public: void setVelocity(const glm::vec3& value) { _velocity = value; } void setColor(const rgbColor& value) { memcpy(_color, value, sizeof(_color)); } void setColor(const xColor& value) { - _color[RED_INDEX] = value.red; - _color[GREEN_INDEX] = value.green; - _color[BLUE_INDEX] = value.blue; + _color[RED_INDEX] = value.red; + _color[GREEN_INDEX] = value.green; + _color[BLUE_INDEX] = value.blue; } void setRadius(float value) { _radius = value; } void setGravity(const glm::vec3& value) { _gravity = value; } @@ -102,15 +104,15 @@ public: void setShouldDie(bool shouldDie) { _shouldDie = shouldDie; } void setScript(QString updateScript) { _script = updateScript; } void setCreatorTokenID(uint32_t creatorTokenID) { _creatorTokenID = creatorTokenID; } - + bool appendParticleData(OctreePacketData* packetData) const; int readParticleDataFromBuffer(const unsigned char* data, int bytesLeftToRead, ReadBitstreamToTreeParams& args); static int expectedBytes(); static int expectedEditMessageBytes(); - static bool encodeParticleEditMessageDetails(PACKET_TYPE command, int count, const ParticleDetail* details, + static bool encodeParticleEditMessageDetails(PACKET_TYPE command, int count, const ParticleDetail* details, unsigned char* bufferOut, int sizeIn, int& sizeOut); - + static void adjustEditPacketForClockSkew(unsigned char* codeColorBuffer, ssize_t length, int clockSkew); void update(); @@ -118,22 +120,23 @@ public: void collisionWithVoxel(VoxelDetail* voxel); void debugDump() const; - + // similar to assignment/copy, but it handles keeping lifetime accurate void copyChangedProperties(const Particle& other); - - static VoxelsScriptingInterface* getVoxelsScriptingInterface() { return _voxelsScriptingInterface; } - static ParticlesScriptingInterface* getParticlesScriptingInterface() { return _particlesScriptingInterface; } - static void setVoxelsScriptingInterface(VoxelsScriptingInterface* interface) - { _voxelsScriptingInterface = interface; } - - static void setParticlesScriptingInterface(ParticlesScriptingInterface* interface) - { _particlesScriptingInterface = interface; } - + static VoxelEditPacketSender* getVoxelEditPacketSender() { return _voxelEditSender; } + static ParticleEditPacketSender* getParticleEditPacketSender() { return _particleEditSender; } + + static void setVoxelEditPacketSender(VoxelEditPacketSender* interface) + { _voxelEditSender = interface; } + + static void setParticleEditPacketSender(ParticleEditPacketSender* interface) + { _particleEditSender = interface; } + + protected: - static VoxelsScriptingInterface* _voxelsScriptingInterface; - static ParticlesScriptingInterface* _particlesScriptingInterface; + static VoxelEditPacketSender* _voxelEditSender; + static ParticleEditPacketSender* _particleEditSender; void runUpdateScript(); static QScriptValue vec3toScriptValue(QScriptEngine *engine, const glm::vec3 &vec3); @@ -141,8 +144,8 @@ protected: static QScriptValue xColorToScriptValue(QScriptEngine *engine, const xColor& color); static void xColorFromScriptValue(const QScriptValue &object, xColor& color); - void setLifetime(float lifetime); - + void setLifetime(float lifetime); + glm::vec3 _position; rgbColor _color; float _radius; @@ -184,7 +187,7 @@ public slots: float getRadius() const { return _particle->getRadius(); } bool getShouldDie() { return _particle->getShouldDie(); } float getLifetime() const { return _particle->getLifetime(); } - + void setPosition(glm::vec3 value) { _particle->setPosition(value); } void setVelocity(glm::vec3 value) { _particle->setVelocity(value); } void setGravity(glm::vec3 value) { _particle->setGravity(value); } diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index 64bcb93696..86b6e8884e 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -19,11 +19,14 @@ #include #include #include +#include #include #include "ScriptEngine.h" +const unsigned int VISUAL_DATA_CALLBACK_USECS = (1.0 / 60.0) * 1000 * 1000; + int ScriptEngine::_scriptNumber = 1; VoxelsScriptingInterface ScriptEngine::_voxelsScriptingInterface; ParticlesScriptingInterface ScriptEngine::_particlesScriptingInterface; @@ -31,7 +34,7 @@ ParticlesScriptingInterface ScriptEngine::_particlesScriptingInterface; static QScriptValue soundConstructor(QScriptContext* context, QScriptEngine* engine) { QUrl soundURL = QUrl(context->argument(0).toString()); QScriptValue soundScriptValue = engine->newQObject(new Sound(soundURL), QScriptEngine::ScriptOwnership); - + return soundScriptValue; } @@ -41,7 +44,8 @@ ScriptEngine::ScriptEngine(const QString& scriptContents, bool wantMenuItems, _scriptContents = scriptContents; _isFinished = false; _isRunning = false; - + _isInitialized = false; + // some clients will use these menu features _wantMenuItems = wantMenuItems; if (scriptMenuName) { @@ -54,15 +58,6 @@ ScriptEngine::ScriptEngine(const QString& scriptContents, bool wantMenuItems, } _menu = menu; _controllerScriptingInterface = controllerScriptingInterface; - - // hook up our interfaces - if (!Particle::getVoxelsScriptingInterface()) { - Particle::setVoxelsScriptingInterface(getVoxelsScriptingInterface()); - } - - if (!Particle::getParticlesScriptingInterface()) { - Particle::setParticlesScriptingInterface(getParticlesScriptingInterface()); - } } ScriptEngine::~ScriptEngine() { @@ -92,63 +87,89 @@ bool ScriptEngine::setScriptContents(const QString& scriptContents) { Q_SCRIPT_DECLARE_QMETAOBJECT(AudioInjectorOptions, QObject*) -void ScriptEngine::run() { - _isRunning = true; - QScriptEngine engine; - +void ScriptEngine::init() { + if (_isInitialized) { + return; // only initialize once + } + + _isInitialized = true; + _voxelsScriptingInterface.init(); _particlesScriptingInterface.init(); - - // register meta-type for glm::vec3 conversions - registerMetaTypes(&engine); - - QScriptValue agentValue = engine.newQObject(this); - engine.globalObject().setProperty("Agent", agentValue); - - QScriptValue voxelScripterValue = engine.newQObject(&_voxelsScriptingInterface); - engine.globalObject().setProperty("Voxels", voxelScripterValue); - QScriptValue particleScripterValue = engine.newQObject(&_particlesScriptingInterface); - engine.globalObject().setProperty("Particles", particleScripterValue); - - - QScriptValue soundConstructorValue = engine.newFunction(soundConstructor); - QScriptValue soundMetaObject = engine.newQMetaObject(&Sound::staticMetaObject, soundConstructorValue); - engine.globalObject().setProperty("Sound", soundMetaObject); - - QScriptValue injectionOptionValue = engine.scriptValueFromQMetaObject(); - engine.globalObject().setProperty("AudioInjectionOptions", injectionOptionValue); - - QScriptValue audioScriptingInterfaceValue = engine.newQObject(&_audioScriptingInterface); - engine.globalObject().setProperty("Audio", audioScriptingInterfaceValue); - + // register meta-type for glm::vec3 conversions + registerMetaTypes(&_engine); + + QScriptValue agentValue = _engine.newQObject(this); + _engine.globalObject().setProperty("Agent", agentValue); + + QScriptValue voxelScripterValue = _engine.newQObject(&_voxelsScriptingInterface); + _engine.globalObject().setProperty("Voxels", voxelScripterValue); + + QScriptValue particleScripterValue = _engine.newQObject(&_particlesScriptingInterface); + _engine.globalObject().setProperty("Particles", particleScripterValue); + + QScriptValue soundConstructorValue = _engine.newFunction(soundConstructor); + QScriptValue soundMetaObject = _engine.newQMetaObject(&Sound::staticMetaObject, soundConstructorValue); + _engine.globalObject().setProperty("Sound", soundMetaObject); + + QScriptValue injectionOptionValue = _engine.scriptValueFromQMetaObject(); + _engine.globalObject().setProperty("AudioInjectionOptions", injectionOptionValue); + + QScriptValue audioScriptingInterfaceValue = _engine.newQObject(&_audioScriptingInterface); + _engine.globalObject().setProperty("Audio", audioScriptingInterfaceValue); + if (_controllerScriptingInterface) { - QScriptValue controllerScripterValue = engine.newQObject(_controllerScriptingInterface); - engine.globalObject().setProperty("Controller", controllerScripterValue); + QScriptValue controllerScripterValue = _engine.newQObject(_controllerScriptingInterface); + _engine.globalObject().setProperty("Controller", controllerScripterValue); } - - QScriptValue treeScaleValue = engine.newVariant(QVariant(TREE_SCALE)); - engine.globalObject().setProperty("TREE_SCALE", treeScaleValue); - - const unsigned int VISUAL_DATA_CALLBACK_USECS = (1.0 / 60.0) * 1000 * 1000; - + + QScriptValue treeScaleValue = _engine.newVariant(QVariant(TREE_SCALE)); + _engine.globalObject().setProperty("TREE_SCALE", treeScaleValue); + // let the VoxelPacketSender know how frequently we plan to call it _voxelsScriptingInterface.getVoxelPacketSender()->setProcessCallIntervalHint(VISUAL_DATA_CALLBACK_USECS); _particlesScriptingInterface.getParticlePacketSender()->setProcessCallIntervalHint(VISUAL_DATA_CALLBACK_USECS); //qDebug() << "Script:\n" << _scriptContents << "\n"; - - QScriptValue result = engine.evaluate(_scriptContents); +} + +void ScriptEngine::registerGlobalObject(const QString& name, QObject* object) { + QScriptValue value = _engine.newQObject(object); + _engine.globalObject().setProperty(name, value); +} + +void ScriptEngine::evaluate() { + if (!_isInitialized) { + init(); + } + + QScriptValue result = _engine.evaluate(_scriptContents); qDebug() << "Evaluated script.\n"; - - if (engine.hasUncaughtException()) { - int line = engine.uncaughtExceptionLineNumber(); + + if (_engine.hasUncaughtException()) { + int line = _engine.uncaughtExceptionLineNumber(); qDebug() << "Uncaught exception at line" << line << ":" << result.toString() << "\n"; } - +} + +void ScriptEngine::run() { + if (!_isInitialized) { + init(); + } + _isRunning = true; + + QScriptValue result = _engine.evaluate(_scriptContents); + qDebug() << "Evaluated script.\n"; + + if (_engine.hasUncaughtException()) { + int line = _engine.uncaughtExceptionLineNumber(); + qDebug() << "Uncaught exception at line" << line << ":" << result.toString() << "\n"; + } + timeval startTime; gettimeofday(&startTime, NULL); - + int thisFrame = 0; while (!_isFinished) { @@ -166,15 +187,15 @@ void ScriptEngine::run() { if (_isFinished) { break; } - + bool willSendVisualDataCallBack = false; - if (_voxelsScriptingInterface.getVoxelPacketSender()->serversExist()) { + if (_voxelsScriptingInterface.getVoxelPacketSender()->serversExist()) { // allow the scripter's call back to setup visual data willSendVisualDataCallBack = true; - + // release the queue of edit voxel messages. _voxelsScriptingInterface.getVoxelPacketSender()->releaseQueuedMessages(); - + // since we're in non-threaded mode, call process so that the packets are sent if (!_voxelsScriptingInterface.getVoxelPacketSender()->isThreaded()) { _voxelsScriptingInterface.getVoxelPacketSender()->process(); @@ -184,23 +205,23 @@ void ScriptEngine::run() { if (_particlesScriptingInterface.getParticlePacketSender()->serversExist()) { // allow the scripter's call back to setup visual data willSendVisualDataCallBack = true; - + // release the queue of edit voxel messages. _particlesScriptingInterface.getParticlePacketSender()->releaseQueuedMessages(); - + // since we're in non-threaded mode, call process so that the packets are sent if (!_particlesScriptingInterface.getParticlePacketSender()->isThreaded()) { _particlesScriptingInterface.getParticlePacketSender()->process(); } } - + if (willSendVisualDataCallBack) { emit willSendVisualDataCallback(); } - if (engine.hasUncaughtException()) { - int line = engine.uncaughtExceptionLineNumber(); - qDebug() << "Uncaught exception at line" << line << ":" << engine.uncaughtException().toString() << "\n"; + if (_engine.hasUncaughtException()) { + int line = _engine.uncaughtExceptionLineNumber(); + qDebug() << "Uncaught exception at line" << line << ":" << _engine.uncaughtException().toString() << "\n"; } } cleanMenuItems(); @@ -213,9 +234,8 @@ void ScriptEngine::run() { emit finished(); _isRunning = false; } - -void ScriptEngine::stop() { - _isFinished = true; +void ScriptEngine::stop() { + _isFinished = true; } diff --git a/libraries/script-engine/src/ScriptEngine.h b/libraries/script-engine/src/ScriptEngine.h index 49ed913744..842b902fcf 100644 --- a/libraries/script-engine/src/ScriptEngine.h +++ b/libraries/script-engine/src/ScriptEngine.h @@ -17,9 +17,10 @@ #include #include -#include #include +class ParticlesScriptingInterface; + #include "AbstractControllerScriptingInterface.h" const QString NO_SCRIPT(""); @@ -27,12 +28,12 @@ const QString NO_SCRIPT(""); class ScriptEngine : public QObject { Q_OBJECT public: - ScriptEngine(const QString& scriptContents = NO_SCRIPT, bool wantMenuItems = false, + ScriptEngine(const QString& scriptContents = NO_SCRIPT, bool wantMenuItems = false, const char* scriptMenuName = NULL, AbstractMenuInterface* menu = NULL, AbstractControllerScriptingInterface* controllerScriptingInterface = NULL); ~ScriptEngine(); - + /// Access the VoxelsScriptingInterface in order to initialize it with a custom packet sender and jurisdiction listener VoxelsScriptingInterface* getVoxelsScriptingInterface() { return &_voxelsScriptingInterface; } @@ -44,11 +45,15 @@ public: void setupMenuItems(); void cleanMenuItems(); - + + void registerGlobalObject(const QString& name, QObject* object); /// registers a global object by name + public slots: - void run(); + void init(); + void run(); /// runs continuously until Agent.stop() is called void stop(); - + void evaluate(); /// initializes the engine, and evaluates the script, but then returns control to caller + signals: void willSendAudioDataCallback(); void willSendVisualDataCallback(); @@ -57,7 +62,8 @@ protected: QString _scriptContents; bool _isFinished; bool _isRunning; - + bool _isInitialized; + QScriptEngine _engine; private: static VoxelsScriptingInterface _voxelsScriptingInterface; From 0252e7f95e4a6e98a92700e1f0d725364b33f1b7 Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Sat, 4 Jan 2014 03:17:02 -0800 Subject: [PATCH 15/39] use ScriptEngine in Particle::collideWithXXX() methods so that we get all script interfaces --- assignment-client/src/Agent.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/assignment-client/src/Agent.cpp b/assignment-client/src/Agent.cpp index 0a85a81692..2bbd549d4a 100644 --- a/assignment-client/src/Agent.cpp +++ b/assignment-client/src/Agent.cpp @@ -18,6 +18,7 @@ #include #include #include +#include #include "Agent.h" From 7ce774da70b185d116484c6dae367b573d00cdb6 Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Sun, 5 Jan 2014 10:39:58 -0800 Subject: [PATCH 16/39] change particle update to support full script engine --- libraries/particles/src/Particle.cpp | 30 ++++++++-------- .../particles/src/ParticleCollisionSystem.cpp | 36 +++++++++---------- 2 files changed, 34 insertions(+), 32 deletions(-) diff --git a/libraries/particles/src/Particle.cpp b/libraries/particles/src/Particle.cpp index 668c12637d..3323dec88b 100644 --- a/libraries/particles/src/Particle.cpp +++ b/libraries/particles/src/Particle.cpp @@ -509,33 +509,35 @@ void Particle::update() { void Particle::runUpdateScript() { if (!_script.isEmpty()) { + ScriptEngine engine(_script); // no menu or controller interface... - QScriptEngine engine; - - // register meta-type for glm::vec3 and rgbColor conversions - registerMetaTypes(&engine); + if (_voxelEditSender) { + engine.getVoxelsScriptingInterface()->setPacketSender(_voxelEditSender); + } + if (_particleEditSender) { + engine.getParticlesScriptingInterface()->setPacketSender(_particleEditSender); + } + // Add the Particle object ParticleScriptObject particleScriptable(this); - QScriptValue particleValue = engine.newQObject(&particleScriptable); - engine.globalObject().setProperty("Particle", particleValue); + engine.registerGlobalObject("Particle", &particleScriptable); - QScriptValue treeScaleValue = engine.newVariant(QVariant(TREE_SCALE)); - engine.globalObject().setProperty("TREE_SCALE", TREE_SCALE); - - QScriptValue result = engine.evaluate(_script); + // init and evaluate the script, but return so we can emit the collision + engine.evaluate(); particleScriptable.emitUpdate(); - if (engine.hasUncaughtException()) { - int line = engine.uncaughtExceptionLineNumber(); - qDebug() << "Uncaught exception at line" << line << ":" << result.toString() << "\n"; + if (_voxelEditSender) { + _voxelEditSender->releaseQueuedMessages(); + } + if (_particleEditSender) { + _particleEditSender->releaseQueuedMessages(); } } } void Particle::collisionWithParticle(Particle* other) { if (!_script.isEmpty()) { - ScriptEngine engine(_script); // no menu or controller interface... if (_voxelEditSender) { diff --git a/libraries/particles/src/ParticleCollisionSystem.cpp b/libraries/particles/src/ParticleCollisionSystem.cpp index 1e1f3955fe..0760413e7f 100644 --- a/libraries/particles/src/ParticleCollisionSystem.cpp +++ b/libraries/particles/src/ParticleCollisionSystem.cpp @@ -19,12 +19,12 @@ #include "ParticleEditPacketSender.h" #include "ParticleTree.h" -ParticleCollisionSystem::ParticleCollisionSystem(ParticleEditPacketSender* packetSender, +ParticleCollisionSystem::ParticleCollisionSystem(ParticleEditPacketSender* packetSender, ParticleTree* particles, VoxelTree* voxels, AbstractAudioInterface* audio, AvatarData* selfAvatar) { init(packetSender, particles, voxels, audio, selfAvatar); } -void ParticleCollisionSystem::init(ParticleEditPacketSender* packetSender, +void ParticleCollisionSystem::init(ParticleEditPacketSender* packetSender, ParticleTree* particles, VoxelTree* voxels, AbstractAudioInterface* audio, AvatarData* selfAvatar) { _packetSender = packetSender; _particles = particles; @@ -82,7 +82,7 @@ void ParticleCollisionSystem::updateCollisionWithVoxels(Particle* particle) { penetration /= static_cast(TREE_SCALE); updateCollisionSound(particle, penetration, COLLISION_FREQUENCY); applyHardCollision(particle, penetration, ELASTICITY, DAMPING); - + delete voxelDetails; // cleanup returned details } } @@ -96,16 +96,16 @@ void ParticleCollisionSystem::updateCollisionWithParticles(Particle* particle) { glm::vec3 penetration; Particle* penetratedParticle; if (_particles->findSpherePenetration(center, radius, penetration, (void**)&penetratedParticle)) { - + // let the particles run their collision scripts if they have them particle->collisionWithParticle(penetratedParticle); penetratedParticle->collisionWithParticle(particle); penetration /= static_cast(TREE_SCALE); updateCollisionSound(particle, penetration, COLLISION_FREQUENCY); - + // apply a hard collision to both particles of half the penetration each - + float particleShare, penetratedParticleShare; if (particle->getInHand() && penetratedParticle->getInHand()) { particleShare = 0.5f; @@ -140,7 +140,7 @@ void ParticleCollisionSystem::updateCollisionWithAvatars(Particle* particle) { const float COLLISION_FREQUENCY = 0.5f; glm::vec3 penetration; const PalmData* collidingPalm = NULL; - + // first check the selfAvatar if set... if (_selfAvatar) { AvatarData* avatar = (AvatarData*)_selfAvatar; @@ -158,7 +158,7 @@ void ParticleCollisionSystem::updateCollisionWithAvatars(Particle* particle) { // apply a hard collision when ball collides with hand penetration /= static_cast(TREE_SCALE); updateCollisionSound(particle, penetration, COLLISION_FREQUENCY); - + // determine if the palm that collided was moving, if so, then we add that palm velocity as well... glm::vec3 addedVelocity = NO_ADDED_VELOCITY; if (collidingPalm) { @@ -185,7 +185,7 @@ void ParticleCollisionSystem::updateCollisionWithAvatars(Particle* particle) { // TODO: dot collidingPalm and hand velocities and skip collision when they are moving apart. AvatarData* avatar = static_cast(node->getLinkedData()); //printf("updateCollisionWithAvatars()...avatar=%p\n", avatar); - + // check hands... const HandData* handData = avatar->getHandData(); @@ -215,15 +215,15 @@ void ParticleCollisionSystem::updateCollisionWithAvatars(Particle* particle) { } -void ParticleCollisionSystem::applyHardCollision(Particle* particle, const glm::vec3& penetration, +void ParticleCollisionSystem::applyHardCollision(Particle* particle, const glm::vec3& penetration, float elasticity, float damping, const glm::vec3& addedVelocity) { // // Update the particle in response to a hard collision. Position will be reset exactly // to outside the colliding surface. Velocity will be modified according to elasticity. // // if elasticity = 0.0, collision is inelastic (vel normal to collision is lost) - // if elasticity = 1.0, collision is 100% elastic. - // + // if elasticity = 1.0, collision is 100% elastic. + // glm::vec3 position = particle->getPosition(); glm::vec3 velocity = particle->getVelocity(); @@ -249,15 +249,15 @@ void ParticleCollisionSystem::applyHardCollision(Particle* particle, const glm:: printf("ParticleCollisionSystem::applyHardCollision() particle id:%d new velocity:%f,%f,%f inHand:%s\n", particle->getID(), velocity.x, velocity.y, velocity.z, debug::valueOf(particle->getInHand())); } - + ParticleEditHandle particleEditHandle(_packetSender, _particles, particle->getID()); particleEditHandle.updateParticle(position, particle->getRadius(), particle->getXColor(), velocity, particle->getGravity(), particle->getDamping(), particle->getInHand(), particle->getScript()); } - + void ParticleCollisionSystem::updateCollisionSound(Particle* particle, const glm::vec3 &penetration, float frequency) { - + // consider whether to have the collision make a sound const float AUDIBLE_COLLISION_THRESHOLD = 0.1f; const float COLLISION_LOUDNESS = 1.f; @@ -269,16 +269,16 @@ void ParticleCollisionSystem::updateCollisionSound(Particle* particle, const glm // how do we want to handle this?? // glm::vec3 gravity = particle->getGravity() * static_cast(TREE_SCALE); - + if (glm::length(gravity) > EPSILON) { // If gravity is on, remove the effect of gravity on velocity for this - // frame, so that we are not constantly colliding with the surface + // frame, so that we are not constantly colliding with the surface velocity -= _scale * glm::length(gravity) * GRAVITY_EARTH * deltaTime * glm::normalize(gravity); } */ float velocityTowardCollision = glm::dot(velocity, glm::normalize(penetration)); float velocityTangentToCollision = glm::length(velocity) - velocityTowardCollision; - + if (velocityTowardCollision > AUDIBLE_COLLISION_THRESHOLD) { // Volume is proportional to collision velocity // Base frequency is modified upward by the angle of the collision From 5b272bfd6adc0468e29f244e156084c393034c54 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Sun, 5 Jan 2014 17:09:57 -0800 Subject: [PATCH 17/39] fix random UUID creation for injected audio --- libraries/audio/src/AudioInjector.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/audio/src/AudioInjector.cpp b/libraries/audio/src/AudioInjector.cpp index c7ca388139..722c2a6dbd 100644 --- a/libraries/audio/src/AudioInjector.cpp +++ b/libraries/audio/src/AudioInjector.cpp @@ -57,7 +57,7 @@ void AudioInjector::injectAudio() { currentPacketPosition += rfcSessionUUID.size(); // pick a random UUID to use for this stream - QUuid randomStreamUUID; + QUuid randomStreamUUID = QUuid::createUuid(); QByteArray rfcStreamUUID = randomStreamUUID.toRfc4122(); memcpy(currentPacketPosition, rfcStreamUUID, rfcStreamUUID.size()); currentPacketPosition += rfcStreamUUID.size(); From a06f18328ca60327d083fd9732a24f14d4439484 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Sun, 5 Jan 2014 17:40:37 -0800 Subject: [PATCH 18/39] add downsampling so RAW audio for sound assumed to be 48Khz --- libraries/audio/src/Sound.cpp | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/libraries/audio/src/Sound.cpp b/libraries/audio/src/Sound.cpp index 02f8aaef9c..36d2b9563a 100644 --- a/libraries/audio/src/Sound.cpp +++ b/libraries/audio/src/Sound.cpp @@ -27,5 +27,25 @@ Sound::Sound(const QUrl& sampleURL, QObject* parent) : void Sound::replyFinished(QNetworkReply* reply) { // replace our byte array with the downloaded data - _byteArray = reply->readAll(); + QByteArray rawAudioByteArray = reply->readAll(); + + // assume that this was a RAW file and is now an array of samples that are + // signed, 16-bit, 48Khz, mono + + // we want to convert it to the format that the audio-mixer wants + // which is signed, 16-bit, 24Khz, mono + + _byteArray.resize(rawAudioByteArray.size() / 2); + + int numSourceSamples = rawAudioByteArray.size() / sizeof(int16_t); + int16_t* sourceSamples = (int16_t*) rawAudioByteArray.data(); + int16_t* destinationSamples = (int16_t*) _byteArray.data(); + + for (int i = 1; i < numSourceSamples; i += 2) { + if (i + 1 >= numSourceSamples) { + destinationSamples[(i - 1) / 2] = (rawAudioByteArray[i - 1] / 2) + (rawAudioByteArray[i] / 2); + } else { + destinationSamples[(i - 1) / 2] = (sourceSamples[i - 1] / 4) + (sourceSamples[i] / 2) + (sourceSamples[i + 1] / 4); + } + } } \ No newline at end of file From 32be6199fd0f283d447e754e654e116eff31a5ac Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Sun, 5 Jan 2014 17:41:33 -0800 Subject: [PATCH 19/39] fix reference to downloaded byte array samples --- libraries/audio/src/Sound.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/audio/src/Sound.cpp b/libraries/audio/src/Sound.cpp index 36d2b9563a..5f233e2c63 100644 --- a/libraries/audio/src/Sound.cpp +++ b/libraries/audio/src/Sound.cpp @@ -43,7 +43,7 @@ void Sound::replyFinished(QNetworkReply* reply) { for (int i = 1; i < numSourceSamples; i += 2) { if (i + 1 >= numSourceSamples) { - destinationSamples[(i - 1) / 2] = (rawAudioByteArray[i - 1] / 2) + (rawAudioByteArray[i] / 2); + destinationSamples[(i - 1) / 2] = (sourceSamples[i - 1] / 2) + (sourceSamples[i] / 2); } else { destinationSamples[(i - 1) / 2] = (sourceSamples[i - 1] / 4) + (sourceSamples[i] / 2) + (sourceSamples[i + 1] / 4); } From 9eb31b48cc426c49d3e5b9611cb278e16e7aade6 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Mon, 6 Jan 2014 11:09:59 -0800 Subject: [PATCH 20/39] remove the Leap integration --- interface/external/Leap/readme.txt | 11 - interface/external/Leap/stubs/include/Leap.h | 13 - interface/src/Application.cpp | 13 - interface/src/devices/LeapManager.cpp | 352 ------------------- interface/src/devices/LeapManager.h | 44 --- 5 files changed, 433 deletions(-) delete mode 100644 interface/external/Leap/readme.txt delete mode 100755 interface/external/Leap/stubs/include/Leap.h delete mode 100644 interface/src/devices/LeapManager.cpp delete mode 100755 interface/src/devices/LeapManager.h diff --git a/interface/external/Leap/readme.txt b/interface/external/Leap/readme.txt deleted file mode 100644 index 24a79299c0..0000000000 --- a/interface/external/Leap/readme.txt +++ /dev/null @@ -1,11 +0,0 @@ - -Instructions for adding the Leap driver to Interface -Eric Johnston, July 10, 2013 - -NOTE: Without doing step 2, you will crash at program start time. - -1. Copy the Leap sdk folders (lib, include, etc.) into the interface/external/Leap folder. There should be a folder already there called "stub", and this read me.txt should be there as well. - -2. IMPORTANT: Copy the file interface/external/Leap/lib/libc++/libLeap.dylib to /usr/lib - -3. Delete your build directory, run cmake and build, and you should be all set. diff --git a/interface/external/Leap/stubs/include/Leap.h b/interface/external/Leap/stubs/include/Leap.h deleted file mode 100755 index 6d012b9e98..0000000000 --- a/interface/external/Leap/stubs/include/Leap.h +++ /dev/null @@ -1,13 +0,0 @@ - -// This is an empty stub, used as a placeholder for the real Leap.h -// The entire containing Leap folder should be replaced by the one -// from the Leap SDK. - -#define LEAP_STUBS // We're using the stubbed-out Leap header - -namespace Leap { - class Frame {}; - class Controller {}; - class Listener {}; -} - diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 64a50a5b99..5332f20b21 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -55,7 +55,6 @@ #include "Menu.h" #include "Swatch.h" #include "Util.h" -#include "devices/LeapManager.h" #include "devices/OculusManager.h" #include "devices/TV3DManager.h" #include "renderer/ProgramObject.h" @@ -1418,7 +1417,6 @@ void Application::terminate() { // Close serial port // close(serial_fd); - LeapManager::terminate(); Menu::getInstance()->saveSettings(); _rearMirrorTools->saveSettings(_settings); _settings->sync(); @@ -1883,8 +1881,6 @@ void Application::init() { Qt::QueuedConnection); } - LeapManager::initialize(); - gettimeofday(&_timerStart, NULL); gettimeofday(&_lastTimeUpdated, NULL); @@ -2364,9 +2360,6 @@ void Application::updateHandAndTouch(float deltaTime) { void Application::updateLeap(float deltaTime) { bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings); PerformanceWarning warn(showWarnings, "Application::updateLeap()"); - - LeapManager::enableFakeFingers(Menu::getInstance()->isOptionChecked(MenuOption::SimulateLeapHand)); - LeapManager::nextFrame(); } void Application::updateSixense(float deltaTime) { @@ -3657,11 +3650,6 @@ void Application::displayStats() { statsVerticalOffset += PELS_PER_LINE; drawtext(10, statsVerticalOffset, 0.10f, 0, 1.0, 0, (char*)voxelStats.str().c_str()); - - // Leap data - statsVerticalOffset += PELS_PER_LINE; - drawtext(10, statsVerticalOffset, 0.10f, 0, 1.0, 0, (char*)LeapManager::statusString().c_str()); - if (_perfStatsOn) { // Get the PerfStats group details. We need to allocate and array of char* long enough to hold 1+groups char** perfStatLinesArray = new char*[PerfStat::getGroupCount()+1]; @@ -4133,7 +4121,6 @@ void Application::resetSensors() { } _webcam.reset(); _faceshift.reset(); - LeapManager::reset(); if (OculusManager::isConnected()) { OculusManager::reset(); diff --git a/interface/src/devices/LeapManager.cpp b/interface/src/devices/LeapManager.cpp deleted file mode 100644 index b4e745420d..0000000000 --- a/interface/src/devices/LeapManager.cpp +++ /dev/null @@ -1,352 +0,0 @@ -// -// LeapManager.cpp -// hifi -// -// Created by Eric Johnston on 6/26/13. -// Copyright (c) 2013 High Fidelity, Inc. All rights reserved. -// - -#include // needed for RTLD_LAZY - -#include - -#include - -#include "Application.h" -#include "LeapManager.h" -#include "Menu.h" - -// Uncomment the next line to use Leap-smoothed stabilized (slower) data. -//#define USE_STABILIZED_DATA - -bool LeapManager::_libraryExists = false; -bool LeapManager::_doFakeFingers = false; -Leap::Controller* LeapManager::_controller = NULL; -HifiLeapListener* LeapManager::_listener = NULL; -glm::vec3 LeapManager::_baseDrivePosition(0.0f, 150.0f, 0.0f); // experimentally derived - -class HifiLeapListener : public Leap::Listener { -public: - HifiLeapListener() {} - virtual ~HifiLeapListener() {} - - Leap::Frame lastFrame; - virtual void onFrame(const Leap::Controller& controller) { -#ifndef LEAP_STUBS - lastFrame = controller.frame(); -#endif - } - -}; - -void LeapManager::initialize() { -#ifndef LEAP_STUBS - if (dlopen("/usr/lib/libLeap.dylib", RTLD_LAZY)) { - _libraryExists = true; - _controller = new Leap::Controller(); - _listener = new HifiLeapListener(); - } -#endif -} - -static PalmData* getRightmostPalm() { - PalmData* rightmostPalm = NULL; - Hand& hand = Application::getInstance()->getAvatar()->getHand(); - for (int i = 0; i < hand.getNumPalms(); i++) { - PalmData* palm = &hand.getPalms()[i]; - if (palm->isActive() && (rightmostPalm == NULL || palm->getRawPosition().x > rightmostPalm->getRawPosition().x)) { - rightmostPalm = palm; - } - } - return rightmostPalm; -} - -void LeapManager::reset() { - PalmData* rightmostPalm = getRightmostPalm(); - if (rightmostPalm != NULL) { - _baseDrivePosition = rightmostPalm->getRawPosition(); - } -} - -void LeapManager::terminate() { - delete _listener; - delete _controller; - _listener = NULL; - _controller = NULL; -} - -void LeapManager::nextFrame() { - // Apply the frame data directly to the avatar. - MyAvatar* avatar = Application::getInstance()->getAvatar(); - Hand& hand = avatar->getHand(); - - // If we actually get valid Leap data, this will be set to true; - bool gotRealData = false; - - if (controllersExist()) { - _listener->onFrame(*_controller); - } - -#ifndef LEAP_STUBS - if (controllersExist()) { - gotRealData = true; - // First, see which palms and fingers are still valid. - Leap::Frame& frame = _listener->lastFrame; - - // Note that this is O(n^2) at worst, but n is very small. - - // After this many frames of no data, assume the digit is lost. - const int assumeLostAfterFrameCount = 10; - - // Increment our frame data counters - for (size_t i = 0; i < hand.getNumPalms(); ++i) { - PalmData& palm = hand.getPalms()[i]; - palm.incrementFramesWithoutData(); - if (palm.getFramesWithoutData() > assumeLostAfterFrameCount) { - palm.setActive(false); - } - for (size_t f = 0; f < palm.getNumFingers(); ++f) { - FingerData& finger = palm.getFingers()[f]; - finger.incrementFramesWithoutData(); - if (finger.getFramesWithoutData() > assumeLostAfterFrameCount) { - finger.setActive(false); - } - } - } - - size_t numLeapHands = frame.hands().count(); - std::vector palmAssignment(numLeapHands); - - // Look for matches - for (size_t index = 0; index < numLeapHands; ++index) { - PalmData* takeoverCandidate = NULL; - palmAssignment[index] = NULL; - Leap::Hand leapHand = frame.hands()[index]; - int id = leapHand.id(); - if (leapHand.isValid()) { - for (size_t i = 0; i < hand.getNumPalms() && palmAssignment[index] == NULL; ++i) { - PalmData& palm = hand.getPalms()[i]; - if (palm.getLeapID() == id) { - // Found hand with the same ID. We're set! - palmAssignment[index] = &palm; - palm.resetFramesWithoutData(); - } - else if (palm.getFramesWithoutData() > assumeLostAfterFrameCount) { - takeoverCandidate = &palm; - } - } - if (palmAssignment[index] == NULL) { - palmAssignment[index] = takeoverCandidate; - } - if (palmAssignment[index] == NULL) { - palmAssignment[index] = &hand.addNewPalm(); - } - } - } - - // Apply the assignments - for (size_t index = 0; index < numLeapHands; ++index) { - if (palmAssignment[index]) { - Leap::Hand leapHand = frame.hands()[index]; - PalmData& palm = *(palmAssignment[index]); - - palm.resetFramesWithoutData(); - palm.setLeapID(leapHand.id()); - palm.setActive(true); - const Leap::Vector pos = leapHand.palmPosition(); - const Leap::Vector normal = leapHand.palmNormal(); - palm.setRawPosition(glm::vec3(pos.x, pos.y, pos.z)); - palm.setRawNormal(glm::vec3(normal.x, normal.y, normal.z)); - } - } - - // Look for fingers per palm - for (size_t i = 0; i < hand.getNumPalms(); ++i) { - PalmData& palm = hand.getPalms()[i]; - if (palm.isActive()) { - Leap::Hand leapHand = frame.hand(palm.getLeapID()); - if (leapHand.isValid()) { - int numLeapFingers = leapHand.fingers().count(); - std::vector fingerAssignment(numLeapFingers); - - - // Look for matches - for (size_t index = 0; index < numLeapFingers; ++index) { - FingerData* takeoverCandidate = NULL; - fingerAssignment[index] = NULL; - Leap::Finger leapFinger = leapHand.fingers()[index]; - int id = leapFinger.id(); - if (leapFinger.isValid()) { - for (size_t f = 0; f < palm.getNumFingers() && fingerAssignment[index] == NULL; ++f) { - FingerData& finger = palm.getFingers()[f]; - if (finger.getLeapID() == id) { - // Found hand with the same ID. We're set! - fingerAssignment[index] = &finger; - } - else if (finger.getFramesWithoutData() > assumeLostAfterFrameCount) { - takeoverCandidate = &finger; - } - } - // If we didn't find a match, but we found an unused finger, us it. - if (fingerAssignment[index] == NULL) { - fingerAssignment[index] = takeoverCandidate; - } - } - } - - // Apply the assignments - for (size_t index = 0; index < numLeapFingers; ++index) { - if (fingerAssignment[index]) { - Leap::Finger leapFinger = leapHand.fingers()[index]; - FingerData& finger = *(fingerAssignment[index]); - - finger.resetFramesWithoutData(); - finger.setLeapID(leapFinger.id()); - finger.setActive(true); -#ifdef USE_STABILIZED_DATA - const Leap::Vector tip = leapFinger.stabilizedTipPosition(); -#else - const Leap::Vector tip = leapFinger.tipPosition(); -#endif - const Leap::Vector root = tip - leapFinger.direction() * leapFinger.length(); - finger.setRawTipPosition(glm::vec3(tip.x, tip.y, tip.z)); - finger.setRawRootPosition(glm::vec3(root.x, root.y, root.z)); - } - } - } - } - } - } -#endif - if (!gotRealData) { - if (_doFakeFingers) { - // There's no real Leap data and we need to fake it. - for (size_t i = 0; i < hand.getNumPalms(); ++i) { - static const glm::vec3 fakeHandOffsets[] = { - glm::vec3( -250.0f, 50.0f, 50.0f), - glm::vec3( 250.0f, 50.0f, 50.0f) - }; - static const glm::vec3 fakeHandFingerMirrors[] = { - glm::vec3( -1.0f, 1.0f, 1.0f), - glm::vec3( 1.0f, 1.0f, 1.0f) - }; - static const glm::vec3 fakeFingerPositions[] = { - glm::vec3( -60.0f, 0.0f, -40.0f), - glm::vec3( -20.0f, 0.0f, -60.0f), - glm::vec3( 20.0f, 0.0f, -60.0f), - glm::vec3( 60.0f, 0.0f, -40.0f), - glm::vec3( -50.0f, 0.0f, 30.0f) - }; - - PalmData& palm = hand.getPalms()[i]; - palm.setActive(true); - // Simulated data - - palm.setRawPosition(glm::vec3( 0.0f, 0.0f, 0.0f) + fakeHandOffsets[i]); - palm.setRawNormal(glm::vec3(0.0f, -1.0f, 0.0f)); - - for (size_t f = 0; f < palm.getNumFingers(); ++f) { - FingerData& finger = palm.getFingers()[f]; - finger.setActive(true); - const float tipScale = 1.5f; - const float rootScale = 0.75f; - glm::vec3 fingerPos = fakeFingerPositions[f] * fakeHandFingerMirrors[i]; - finger.setRawTipPosition(fingerPos * tipScale + fakeHandOffsets[i]); - finger.setRawRootPosition(fingerPos * rootScale + fakeHandOffsets[i]); - } - } - } - else { - // Just deactivate everything. - for (size_t i = 0; i < hand.getNumPalms(); ++i) { - PalmData& palm = hand.getPalms()[i]; - palm.setActive(false); - for (size_t f = 0; f < palm.getNumFingers(); ++f) { - FingerData& finger = palm.getFingers()[f]; - finger.setActive(false); - } - } - } - } - hand.updateFingerTrails(); - - // if Leap drive is enabled, drive avatar based on Leap input - if (!(Menu::getInstance()->isOptionChecked(MenuOption::LeapDrive) && gotRealData)) { - return; - } - glm::vec3 relativePosition; - glm::vec3 eulerAngles; - PalmData* rightmostPalm = getRightmostPalm(); - if (rightmostPalm != NULL) { - relativePosition = rightmostPalm->getRawPosition() - _baseDrivePosition; - - glm::vec3 directionSum; - int activeFingerCount = 0; - for (int i = 0; i < rightmostPalm->getNumFingers(); i++) { - FingerData& finger = rightmostPalm->getFingers()[i]; - glm::vec3 fingerVector = finger.getTipRawPosition() - rightmostPalm->getRawPosition(); - if (finger.isActive() && glm::length(fingerVector) > EPSILON) { - directionSum += glm::normalize(fingerVector); - activeFingerCount++; - } - } - const int MIN_DIRECTION_FINGER_COUNT = 3; - glm::vec3 right; - if (activeFingerCount >= MIN_DIRECTION_FINGER_COUNT) { - right = glm::normalize(glm::cross(glm::normalize(directionSum), -rightmostPalm->getRawNormal())); - - } else { - right = glm::normalize(glm::cross(IDENTITY_FRONT, -rightmostPalm->getRawNormal())); - } - eulerAngles = safeEulerAngles(glm::quat_cast(glm::mat3(right, -rightmostPalm->getRawNormal(), - glm::cross(right, -rightmostPalm->getRawNormal())))); - } - const float LINEAR_DRIVE_SCALE = 0.01f; - const float LINEAR_DEAD_ZONE = 0.25f; - avatar->setDriveKeys(FWD, glm::clamp(-relativePosition.z * LINEAR_DRIVE_SCALE - LINEAR_DEAD_ZONE, 0.0f, 1.0f)); - avatar->setDriveKeys(BACK, glm::clamp(relativePosition.z * LINEAR_DRIVE_SCALE - LINEAR_DEAD_ZONE, 0.0f, 1.0f)); - avatar->setDriveKeys(LEFT, glm::clamp(-relativePosition.x * LINEAR_DRIVE_SCALE - LINEAR_DEAD_ZONE, 0.0f, 1.0f)); - avatar->setDriveKeys(RIGHT, glm::clamp(relativePosition.x * LINEAR_DRIVE_SCALE - LINEAR_DEAD_ZONE, 0.0f, 1.0f)); - avatar->setDriveKeys(UP, glm::clamp(relativePosition.y * LINEAR_DRIVE_SCALE - LINEAR_DEAD_ZONE, 0.0f, 1.0f)); - avatar->setDriveKeys(DOWN, glm::clamp(-relativePosition.y * LINEAR_DRIVE_SCALE - LINEAR_DEAD_ZONE, 0.0f, 1.0f)); - - const float ANGULAR_DRIVE_SCALE = 0.05f; - const float ANGULAR_DEAD_ZONE = 0.75f; - avatar->setDriveKeys(ROT_LEFT, glm::clamp(glm::max(eulerAngles.y, eulerAngles.z) * ANGULAR_DRIVE_SCALE - - ANGULAR_DEAD_ZONE, 0.0f, 1.0f)); - avatar->setDriveKeys(ROT_RIGHT, glm::clamp(glm::max(-eulerAngles.y, -eulerAngles.z) * ANGULAR_DRIVE_SCALE - - ANGULAR_DEAD_ZONE, 0.0f, 1.0f)); - avatar->setDriveKeys(ROT_UP, glm::clamp(eulerAngles.x * ANGULAR_DRIVE_SCALE - ANGULAR_DEAD_ZONE, 0.0f, 1.0f)); - avatar->setDriveKeys(ROT_DOWN, glm::clamp(-eulerAngles.x * ANGULAR_DRIVE_SCALE - ANGULAR_DEAD_ZONE, 0.0f, 1.0f)); -} - -void LeapManager::enableFakeFingers(bool enable) { - _doFakeFingers = enable; -} - -bool LeapManager::controllersExist() { -#ifdef LEAP_STUBS - return false; -#else - return _listener && _controller && _controller->devices().count() > 0; -#endif -} - -std::string LeapManager::statusString() { - std::stringstream leapString; -#ifndef LEAP_STUBS - if (!_libraryExists) - leapString << "Leap library at /usr/lib/libLeap.dylib does not exist."; - else if (!_controller || !_listener || !_controller->devices().count()) - leapString << "Leap controller is not attached."; - else { - leapString << "Leap pointables: " << _listener->lastFrame.pointables().count(); - if (_listener->lastFrame.pointables().count() > 0) { - Leap::Vector pos = _listener->lastFrame.pointables()[0].tipPosition(); - leapString << " pos: " << pos.x << " " << pos.y << " " << pos.z; - } - } -#endif - return leapString.str(); -} - diff --git a/interface/src/devices/LeapManager.h b/interface/src/devices/LeapManager.h deleted file mode 100755 index 4544d5afcf..0000000000 --- a/interface/src/devices/LeapManager.h +++ /dev/null @@ -1,44 +0,0 @@ -// -// LeapManager.h -// hifi -// -// Created by Eric Johnston on 6/26/13. -// Copyright (c) 2013 High Fidelity, Inc. All rights reserved. -// - -#ifndef __hifi__LeapManager__ -#define __hifi__LeapManager__ - -#include -#include -#include - -class Avatar; -class HifiLeapListener; -namespace Leap { - class Controller; -} - -class LeapManager { -public: - static void nextFrame(); // called once per frame to get new Leap data - static bool controllersExist(); // Returns true if there's at least one active Leap plugged in - static void enableFakeFingers(bool enable); // put fake data in if there's no Leap plugged in - static const std::vector& getFingerTips(); - static const std::vector& getFingerRoots(); - static const std::vector& getHandPositions(); - static const std::vector& getHandNormals(); - static std::string statusString(); - static void initialize(); - static void reset(); - static void terminate(); - -private: - static bool _libraryExists; // The library is present, so we won't crash if we call it. - static bool _doFakeFingers; - static Leap::Controller* _controller; - static HifiLeapListener* _listener; - static glm::vec3 _baseDrivePosition; -}; - -#endif /* defined(__hifi__LeapManager__) */ From 133ba3a23053f07dedc0b08c48a28fd337d01401 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Mon, 6 Jan 2014 11:13:19 -0800 Subject: [PATCH 21/39] remove Leap integration from CMakeLists --- cmake/modules/FindLeap.cmake | 52 ------------------------------------ interface/CMakeLists.txt | 7 ----- 2 files changed, 59 deletions(-) delete mode 100755 cmake/modules/FindLeap.cmake diff --git a/cmake/modules/FindLeap.cmake b/cmake/modules/FindLeap.cmake deleted file mode 100755 index f93499b4d2..0000000000 --- a/cmake/modules/FindLeap.cmake +++ /dev/null @@ -1,52 +0,0 @@ -# - Try to find the Leap library -# -# You must provide a LEAP_ROOT_DIR which contains Lib and Include directories -# -# Once done this will define -# -# LEAP_FOUND - system found Leap -# LEAP_INCLUDE_DIRS - the Leap include directory -# LEAP_LIBRARIES - Link this to use Leap -# -# Created on 6/21/2013 by Eric Johnston, -# adapted from FindLibOVR.cmake by Stephen Birarda -# Copyright (c) 2013 High Fidelity -# - -if (LEAP_LIBRARIES AND LEAP_INCLUDE_DIRS) - # in cache already - set(LEAP_FOUND TRUE) -else (LEAP_LIBRARIES AND LEAP_INCLUDE_DIRS) - find_path(LEAP_INCLUDE_DIRS Leap.h ${LEAP_ROOT_DIR}/include) - - if (LEAP_INCLUDE_DIRS) - ### If we found the real header, get the library as well. - if (APPLE) - find_library(LEAP_LIBRARIES libLeap.dylib ${LEAP_ROOT_DIR}/lib/libc++/) - elseif (WIN32) - find_library(LEAP_LIBRARIES libLeap.dylib ${LEAP_ROOT_DIR}/lib/libc++/) - endif () - else () - ### If we didn't find the real header, just use the stub header, and no library. - find_path(LEAP_INCLUDE_DIRS Leap.h ${LEAP_ROOT_DIR}/stubs/include) - endif () - -# If we're using the Leap stubs, there's only a header, no lib. - if (LEAP_LIBRARIES AND LEAP_INCLUDE_DIRS) - set(LEAP_FOUND TRUE) - endif (LEAP_LIBRARIES AND LEAP_INCLUDE_DIRS) - - if (LEAP_FOUND) - if (NOT Leap_FIND_QUIETLY) - message(STATUS "Found Leap: ${LEAP_LIBRARIES}") - endif (NOT Leap_FIND_QUIETLY) - else (LEAP_FOUND) - if (Leap_FIND_REQUIRED) - message(FATAL_ERROR "Could not find Leap") - endif (Leap_FIND_REQUIRED) - endif (LEAP_FOUND) - - # show the LEAP_INCLUDE_DIRS and LEAP_LIBRARIES variables only in the advanced view - mark_as_advanced(LEAP_INCLUDE_DIRS LEAP_LIBRARIES) - -endif (LEAP_LIBRARIES AND LEAP_INCLUDE_DIRS) \ No newline at end of file diff --git a/interface/CMakeLists.txt b/interface/CMakeLists.txt index ecc6ff79cb..1bbdbde41e 100644 --- a/interface/CMakeLists.txt +++ b/interface/CMakeLists.txt @@ -11,7 +11,6 @@ set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/../cmake set(FACESHIFT_ROOT_DIR ${CMAKE_CURRENT_SOURCE_DIR}/external/faceshift) set(LIBOVR_ROOT_DIR ${CMAKE_CURRENT_SOURCE_DIR}/external/LibOVR) set(LIBVPX_ROOT_DIR ${CMAKE_CURRENT_SOURCE_DIR}/external/LibVPX) -set(LEAP_ROOT_DIR ${CMAKE_CURRENT_SOURCE_DIR}/external/Leap) set(MOTIONDRIVER_ROOT_DIR ${CMAKE_CURRENT_SOURCE_DIR}/external/MotionDriver) set(OPENCV_ROOT_DIR ${CMAKE_CURRENT_SOURCE_DIR}/external/OpenCV) set(SIXENSE_ROOT_DIR ${CMAKE_CURRENT_SOURCE_DIR}/external/Sixense) @@ -104,7 +103,6 @@ find_package(Faceshift) find_package(GLM REQUIRED) find_package(LibOVR) find_package(LibVPX) -find_package(Leap) find_package(MotionDriver) find_package(OpenCV) find_package(OpenNI) @@ -195,11 +193,6 @@ if (APPLE) ${QuartzCore} ${UVCCAMERACONTROL_LIBRARIES} ) - - if (LEAP_FOUND) - target_link_libraries(${TARGET_NAME} ${LEAP_LIBRARIES}) - endif(LEAP_FOUND) - else (APPLE) find_package(OpenGL REQUIRED) find_package(GLUT REQUIRED) From 8b2212b6cf149d3d3fbcf88418c26a035cd612ad Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Mon, 6 Jan 2014 12:08:42 -0800 Subject: [PATCH 22/39] Fix for "red ball" issue with no skeleton: use avatar position for head position. --- interface/src/avatar/Avatar.cpp | 4 +++- interface/src/avatar/Head.cpp | 5 +++-- interface/src/avatar/MyAvatar.cpp | 4 +++- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index 47af6d586c..b3b4793b39 100755 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -194,7 +194,9 @@ void Avatar::simulate(float deltaTime, Transmitter* transmitter) { _skeletonModel.simulate(deltaTime); _head.setBodyRotation(glm::vec3(_bodyPitch, _bodyYaw, _bodyRoll)); glm::vec3 headPosition; - _skeletonModel.getHeadPosition(headPosition); + if (!_skeletonModel.getHeadPosition(headPosition)) { + headPosition = _position; + } _head.setPosition(headPosition); _head.setScale(_scale); _head.setSkinColor(glm::vec3(SKIN_COLOR[0], SKIN_COLOR[1], SKIN_COLOR[2])); diff --git a/interface/src/avatar/Head.cpp b/interface/src/avatar/Head.cpp index 8f285cfd77..5471f8554a 100644 --- a/interface/src/avatar/Head.cpp +++ b/interface/src/avatar/Head.cpp @@ -222,8 +222,9 @@ void Head::simulate(float deltaTime, bool isMine) { calculateGeometry(); // the blend face may have custom eye meshes - _faceModel.getEyePositions(_leftEyePosition, _rightEyePosition); - + if (!_faceModel.getEyePositions(_leftEyePosition, _rightEyePosition)) { + _leftEyePosition = _rightEyePosition = getPosition(); + } } void Head::calculateGeometry() { diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 91b88e427c..e7dba4a4c7 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -282,7 +282,9 @@ void MyAvatar::simulate(float deltaTime, Transmitter* transmitter) { _skeletonModel.simulate(deltaTime); _head.setBodyRotation(glm::vec3(_bodyPitch, _bodyYaw, _bodyRoll)); glm::vec3 headPosition; - _skeletonModel.getHeadPosition(headPosition); + if (!_skeletonModel.getHeadPosition(headPosition)) { + headPosition = _position; + } _head.setPosition(headPosition); _head.setScale(_scale); _head.setSkinColor(glm::vec3(SKIN_COLOR[0], SKIN_COLOR[1], SKIN_COLOR[2])); From 03794f72185f0a78a252e58e6bc87d211887a915 Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Thu, 9 Jan 2014 10:07:51 -0800 Subject: [PATCH 23/39] make casts float match coding standard --- .../particles/src/ParticleCollisionSystem.cpp | 34 +++++++++---------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/libraries/particles/src/ParticleCollisionSystem.cpp b/libraries/particles/src/ParticleCollisionSystem.cpp index 0760413e7f..7538fc7f86 100644 --- a/libraries/particles/src/ParticleCollisionSystem.cpp +++ b/libraries/particles/src/ParticleCollisionSystem.cpp @@ -67,8 +67,8 @@ void ParticleCollisionSystem::checkParticle(Particle* particle) { } void ParticleCollisionSystem::updateCollisionWithVoxels(Particle* particle) { - glm::vec3 center = particle->getPosition() * static_cast(TREE_SCALE); - float radius = particle->getRadius() * static_cast(TREE_SCALE); + glm::vec3 center = particle->getPosition() * (float)(TREE_SCALE); + float radius = particle->getRadius() * (float)(TREE_SCALE); const float ELASTICITY = 0.4f; const float DAMPING = 0.0f; const float COLLISION_FREQUENCY = 0.5f; @@ -79,7 +79,7 @@ void ParticleCollisionSystem::updateCollisionWithVoxels(Particle* particle) { // let the particles run their collision scripts if they have them particle->collisionWithVoxel(voxelDetails); - penetration /= static_cast(TREE_SCALE); + penetration /= (float)(TREE_SCALE); updateCollisionSound(particle, penetration, COLLISION_FREQUENCY); applyHardCollision(particle, penetration, ELASTICITY, DAMPING); @@ -88,8 +88,8 @@ void ParticleCollisionSystem::updateCollisionWithVoxels(Particle* particle) { } void ParticleCollisionSystem::updateCollisionWithParticles(Particle* particle) { - glm::vec3 center = particle->getPosition() * static_cast(TREE_SCALE); - float radius = particle->getRadius() * static_cast(TREE_SCALE); + glm::vec3 center = particle->getPosition() * (float)(TREE_SCALE); + float radius = particle->getRadius() * (float)(TREE_SCALE); const float ELASTICITY = 1.4f; const float DAMPING = 0.0f; const float COLLISION_FREQUENCY = 0.5f; @@ -101,7 +101,7 @@ void ParticleCollisionSystem::updateCollisionWithParticles(Particle* particle) { particle->collisionWithParticle(penetratedParticle); penetratedParticle->collisionWithParticle(particle); - penetration /= static_cast(TREE_SCALE); + penetration /= (float)(TREE_SCALE); updateCollisionSound(particle, penetration, COLLISION_FREQUENCY); // apply a hard collision to both particles of half the penetration each @@ -133,8 +133,8 @@ void ParticleCollisionSystem::updateCollisionWithAvatars(Particle* particle) { } //printf("updateCollisionWithAvatars()...\n"); - glm::vec3 center = particle->getPosition() * static_cast(TREE_SCALE); - float radius = particle->getRadius() * static_cast(TREE_SCALE); + glm::vec3 center = particle->getPosition() * (float)(TREE_SCALE); + float radius = particle->getRadius() * (float)(TREE_SCALE); const float ELASTICITY = 0.4f; const float DAMPING = 0.0f; const float COLLISION_FREQUENCY = 0.5f; @@ -156,13 +156,13 @@ void ParticleCollisionSystem::updateCollisionWithAvatars(Particle* particle) { if (handData->findSpherePenetration(center, radius, penetration, collidingPalm)) { // TODO: dot collidingPalm and hand velocities and skip collision when they are moving apart. // apply a hard collision when ball collides with hand - penetration /= static_cast(TREE_SCALE); + penetration /= (float)(TREE_SCALE); updateCollisionSound(particle, penetration, COLLISION_FREQUENCY); // determine if the palm that collided was moving, if so, then we add that palm velocity as well... glm::vec3 addedVelocity = NO_ADDED_VELOCITY; if (collidingPalm) { - glm::vec3 palmVelocity = collidingPalm->getVelocity() / static_cast(TREE_SCALE); + glm::vec3 palmVelocity = collidingPalm->getVelocity() / (float)(TREE_SCALE); //printf("collidingPalm Velocity=%f,%f,%f\n", palmVelocity.x, palmVelocity.y, palmVelocity.z); addedVelocity = palmVelocity; } @@ -170,7 +170,7 @@ void ParticleCollisionSystem::updateCollisionWithAvatars(Particle* particle) { applyHardCollision(particle, penetration, ELASTICITY, DAMPING, addedVelocity); } else if (avatar->findSpherePenetration(center, radius, penetration)) { // apply hard collision when particle collides with avatar - penetration /= static_cast(TREE_SCALE); + penetration /= (float)(TREE_SCALE); updateCollisionSound(particle, penetration, COLLISION_FREQUENCY); glm::vec3 addedVelocity = avatar->getVelocity(); applyHardCollision(particle, penetration, ELASTICITY, DAMPING, addedVelocity); @@ -191,13 +191,13 @@ void ParticleCollisionSystem::updateCollisionWithAvatars(Particle* particle) { if (handData->findSpherePenetration(center, radius, penetration, collidingPalm)) { // apply a hard collision when ball collides with hand - penetration /= static_cast(TREE_SCALE); + penetration /= (float)(TREE_SCALE); updateCollisionSound(particle, penetration, COLLISION_FREQUENCY); // determine if the palm that collided was moving, if so, then we add that palm velocity as well... glm::vec3 addedVelocity = NO_ADDED_VELOCITY; if (collidingPalm) { - glm::vec3 palmVelocity = collidingPalm->getVelocity() / static_cast(TREE_SCALE); + glm::vec3 palmVelocity = collidingPalm->getVelocity() / (float)(TREE_SCALE); //printf("collidingPalm Velocity=%f,%f,%f\n", palmVelocity.x, palmVelocity.y, palmVelocity.z); addedVelocity = palmVelocity; } @@ -205,7 +205,7 @@ void ParticleCollisionSystem::updateCollisionWithAvatars(Particle* particle) { applyHardCollision(particle, penetration, ELASTICITY, DAMPING, addedVelocity); } else if (avatar->findSpherePenetration(center, radius, penetration)) { - penetration /= static_cast(TREE_SCALE); + penetration /= (float)(TREE_SCALE); updateCollisionSound(particle, penetration, COLLISION_FREQUENCY); glm::vec3 addedVelocity = avatar->getVelocity(); applyHardCollision(particle, penetration, ELASTICITY, DAMPING, addedVelocity); @@ -231,7 +231,7 @@ void ParticleCollisionSystem::applyHardCollision(Particle* particle, const glm:: float velocityDotPenetration = glm::dot(velocity, penetration); if (velocityDotPenetration > EPSILON) { position -= penetration; - static float HALTING_VELOCITY = 0.2f / static_cast(TREE_SCALE); + static float HALTING_VELOCITY = 0.2f / (float)(TREE_SCALE); // cancel out the velocity component in the direction of penetration float penetrationLength = glm::length(penetration); @@ -263,12 +263,12 @@ void ParticleCollisionSystem::updateCollisionSound(Particle* particle, const glm const float COLLISION_LOUDNESS = 1.f; const float DURATION_SCALING = 0.004f; const float NOISE_SCALING = 0.1f; - glm::vec3 velocity = particle->getVelocity() * static_cast(TREE_SCALE); + glm::vec3 velocity = particle->getVelocity() * (float)(TREE_SCALE); /* // how do we want to handle this?? // - glm::vec3 gravity = particle->getGravity() * static_cast(TREE_SCALE); + glm::vec3 gravity = particle->getGravity() * (float)(TREE_SCALE); if (glm::length(gravity) > EPSILON) { // If gravity is on, remove the effect of gravity on velocity for this From c181d0a84929c1504d47ae3f5f43e2875f0ea2ef Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Thu, 9 Jan 2014 12:05:09 -0800 Subject: [PATCH 24/39] first cut at local voxel cache --- interface/src/Application.cpp | 21 +- interface/src/Application.h | 155 +++---- interface/src/VoxelHideShowThread.cpp | 4 +- interface/src/VoxelSystem.cpp | 424 ++++++++++-------- interface/src/VoxelSystem.h | 64 +-- .../src/OctreePersistThread.cpp | 25 +- .../src/OctreePersistThread.h | 12 +- libraries/shared/src/GenericThread.h | 11 +- libraries/voxels/src/VoxelTreeElement.cpp | 44 +- 9 files changed, 416 insertions(+), 344 deletions(-) rename libraries/{octree-server => octree}/src/OctreePersistThread.cpp (94%) rename libraries/{octree-server => octree}/src/OctreePersistThread.h (89%) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 64a50a5b99..572f42bdee 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -86,6 +86,8 @@ const float MIRROR_FULLSCREEN_DISTANCE = 0.35f; const float MIRROR_REARVIEW_DISTANCE = 0.65f; const float MIRROR_REARVIEW_BODY_DISTANCE = 2.3f; +const char* LOCAL_VOXEL_CACHE = "/Users/brad/local_voxel_cache.svo"; + void messageHandler(QtMsgType type, const QMessageLogContext& context, const QString &message) { fprintf(stdout, "%s", message.toLocal8Bit().constData()); @@ -143,7 +145,8 @@ Application::Application(int& argc, char** argv, timeval &startup_time) : _resetRecentMaxPacketsSoon(true), _swatch(NULL), _pasteMode(false), - _logger(new FileLogger()) + _logger(new FileLogger()), + _persistThread(NULL) { _applicationStartupTime = startup_time; @@ -250,6 +253,14 @@ Application::Application(int& argc, char** argv, timeval &startup_time) : // Set the sixense filtering _sixenseManager.setFilter(Menu::getInstance()->isOptionChecked(MenuOption::FilterSixense)); + + _persistThread = new OctreePersistThread(_voxels.getTree(), LOCAL_VOXEL_CACHE); + + if (_persistThread) { + _voxels.beginLoadingLocalVoxelCache(); // while local voxels are importing, don't do individual node VBO updates + connect(_persistThread, SIGNAL(loadCompleted()), &_voxels, SLOT(localVoxelCacheLoaded())); + _persistThread->initialize(true); + } } Application::~Application() { @@ -1436,6 +1447,8 @@ void Application::terminate() { _voxelHideShowThread.terminate(); _voxelEditSender.terminate(); _particleEditSender.terminate(); + _persistThread->terminate(); + _persistThread = NULL; } static Avatar* processAvatarMessageHeader(unsigned char*& packetData, size_t& dataBytes) { @@ -2400,6 +2413,7 @@ void Application::updateThreads(float deltaTime) { _voxelHideShowThread.threadRoutine(); _voxelEditSender.threadRoutine(); _particleEditSender.threadRoutine(); + _persistThread->threadRoutine(); } } @@ -4536,3 +4550,8 @@ void Application::toggleLogDialog() { _logDialog->close(); } } + + +void Application::initAvatarAndViewFrustum() { + updateAvatar(0.f); +} \ No newline at end of file diff --git a/interface/src/Application.h b/interface/src/Application.h index 6f0463bc88..f36ad28a5d 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -10,7 +10,7 @@ #define __interface__Application__ #include -#include +#include #include #include @@ -108,14 +108,14 @@ public: ~Application(); void restoreSizeAndPosition(); - void storeSizeAndPosition(); + void storeSizeAndPosition(); void initializeGL(); void paintGL(); void resizeGL(int width, int height); void keyPressEvent(QKeyEvent* event); void keyReleaseEvent(QKeyEvent* event); - + void mouseMoveEvent(QMouseEvent* event); void mousePressEvent(QMouseEvent* event); void mouseReleaseEvent(QMouseEvent* event); @@ -123,27 +123,27 @@ public: void touchBeginEvent(QTouchEvent* event); void touchEndEvent(QTouchEvent* event); void touchUpdateEvent(QTouchEvent* event); - + void updateWindowTitle(); void wheelEvent(QWheelEvent* event); void shootParticle(); // shoots a particle in the direction you're looking ParticleEditHandle* newParticleEditHandle(uint32_t id = NEW_PARTICLE); - ParticleEditHandle* makeParticle(glm::vec3 position, float radius, xColor color, glm::vec3 velocity, + ParticleEditHandle* makeParticle(glm::vec3 position, float radius, xColor color, glm::vec3 velocity, glm::vec3 gravity, float damping, bool inHand, QString updateScript); - + void makeVoxel(glm::vec3 position, float scale, unsigned char red, unsigned char green, unsigned char blue, bool isDestructive); - + void removeVoxel(glm::vec3 position, float scale); - + const glm::vec3 getMouseVoxelWorldCoordinates(const VoxelDetail _mouseVoxel); - + QGLWidget* getGLWidget() { return _glWidget; } MyAvatar* getAvatar() { return &_myAvatar; } Audio* getAudio() { return &_audio; } @@ -166,24 +166,24 @@ public: NodeToVoxelSceneStats* getOcteeSceneStats() { return &_octreeServerSceneStats; } void lockVoxelSceneStats() { _voxelSceneStatsLock.lockForRead(); } void unlockVoxelSceneStats() { _voxelSceneStatsLock.unlock(); } - + QNetworkAccessManager* getNetworkAccessManager() { return _networkAccessManager; } GeometryCache* getGeometryCache() { return &_geometryCache; } TextureCache* getTextureCache() { return &_textureCache; } GlowEffect* getGlowEffect() { return &_glowEffect; } - + Avatar* getLookatTargetAvatar() const { return _lookatTargetAvatar; } - + Profile* getProfile() { return &_profile; } void resetProfile(const QString& username); - + static void controlledBroadcastToNodes(unsigned char* broadcastData, size_t dataBytes, const char* nodeTypes, int numNodeTypes); - + void setupWorldLight(); void displaySide(Camera& whichCamera, bool selfAvatarOnly = false); - + /// Loads a view matrix that incorporates the specified model translation without the precision issues that can /// result from matrix multiplication at high translation magnitudes. void loadTranslatedViewMatrix(const glm::vec3& translation); @@ -197,9 +197,9 @@ public: virtual void nodeAdded(Node* node); virtual void nodeKilled(Node* node); virtual void packetSentNotification(ssize_t length); - + virtual void domainChanged(QString domain); - + VoxelShader& getVoxelShader() { return _voxelShader; } PointShader& getPointShader() { return _pointShader; } FileLogger* getLogger() { return _logger; } @@ -208,7 +208,7 @@ public: NodeToJurisdictionMap& getVoxelServerJurisdictions() { return _voxelServerJurisdictions; } NodeToJurisdictionMap& getParticleServerJurisdictions() { return _particleServerJurisdictions; } void pasteVoxelsToOctalCode(const unsigned char* octalCodeDestination); - + /// set a voxel which is to be rendered with a highlight void setHighlightVoxel(const VoxelDetail& highlightVoxel) { _highlightVoxel = highlightVoxel; } void setIsHighlightVoxel(bool isHighlightVoxel) { _isHighlightVoxel = isHighlightVoxel; } @@ -222,25 +222,26 @@ public slots: void pasteVoxels(); void nudgeVoxels(); void deleteVoxels(); - + void setRenderVoxels(bool renderVoxels); void doKillLocalVoxels(); void decreaseVoxelSize(); void increaseVoxelSize(); void loadScript(); void toggleLogDialog(); - + void initAvatarAndViewFrustum(); + private slots: - + void timer(); void idle(); void terminate(); - + void setFullscreen(bool fullscreen); - + void renderThrustAtVoxel(const glm::vec3& thrust); void renderLineToTouchedVoxel(); - + void renderCoverageMap(); void renderCoverageMapsRecursively(CoverageMap* map); @@ -250,7 +251,7 @@ private slots: glm::vec2 getScaledScreenPoint(glm::vec2 projectedPoint); void toggleFollowMode(); - + void closeMirrorView(); void restoreMirrorView(); void shrinkMirrorView(); @@ -265,17 +266,17 @@ private: static void processAvatarURLsMessage(unsigned char* packetData, size_t dataBytes); static void processAvatarFaceVideoMessage(unsigned char* packetData, size_t dataBytes); static void sendPingPackets(); - + void initDisplay(); void init(); - + void update(float deltaTime); // Various helper functions called during update() void updateMouseRay(float deltaTime, glm::vec3& mouseRayOrigin, glm::vec3& mouseRayDirection); void updateFaceshift(); void updateMyAvatarLookAtPosition(glm::vec3& lookAtSpot, glm::vec3& lookAtRayOrigin, glm::vec3& lookAtRayDirection); - void updateHoverVoxels(float deltaTime, glm::vec3& mouseRayOrigin, glm::vec3& mouseRayDirection, + void updateHoverVoxels(float deltaTime, glm::vec3& mouseRayOrigin, glm::vec3& mouseRayDirection, float& distance, BoxFace& face); void updateMouseVoxels(float deltaTime, glm::vec3& mouseRayOrigin, glm::vec3& mouseRayDirection, float& distance, BoxFace& face); @@ -298,32 +299,32 @@ private: Avatar* findLookatTargetAvatar(const glm::vec3& mouseRayOrigin, const glm::vec3& mouseRayDirection, glm::vec3& eyePosition, QUuid &nodeUUID); bool isLookingAtMyAvatar(Avatar* avatar); - + void renderLookatIndicator(glm::vec3 pointOfInterest); void renderFollowIndicator(); void renderHighlightVoxel(VoxelDetail voxel); - + void updateAvatar(float deltaTime); void updateAvatars(float deltaTime, glm::vec3 mouseRayOrigin, glm::vec3 mouseRayDirection); void queryOctree(NODE_TYPE serverType, PACKET_TYPE packetType, NodeToJurisdictionMap& jurisdictions); void loadViewFrustum(Camera& camera, ViewFrustum& viewFrustum); - + glm::vec3 getSunDirection(); - + void updateShadowMap(); void displayOverlay(); void displayStats(); void renderAvatars(bool forceRenderHead, bool selfAvatarOnly = false); void renderViewFrustum(ViewFrustum& viewFrustum); - + void checkBandwidthMeterClick(); - + bool maybeEditVoxelUnderCursor(); void deleteVoxelUnderCursor(); void eyedropperVoxelUnderCursor(); - + void setMenuShortcutsEnabled(bool enabled); - + static void attachNewHeadToNode(Node *newNode); static void* networkReceive(void* args); // network receive thread @@ -333,18 +334,18 @@ private: QMainWindow* _window; QGLWidget* _glWidget; - + QAction* _followMode; - + BandwidthMeter _bandwidthMeter; SerialInterface _serialHeadSensor; QNetworkAccessManager* _networkAccessManager; QSettings* _settings; bool _displayLevels; - + glm::vec3 _gravity; - + // Frame Rate Measurement int _frameCount; float _fps; @@ -354,55 +355,55 @@ private: bool _justStarted; Stars _stars; - + Cloud _cloud; - + VoxelSystem _voxels; VoxelTree _clipboard; // if I copy/paste VoxelImporter _voxelImporter; VoxelSystem _sharedVoxelSystem; ViewFrustum _sharedVoxelSystemViewFrustum; - + ParticleTreeRenderer _particles; ParticleCollisionSystem _particleCollisionSystem; QByteArray _voxelsFilename; bool _wantToKillLocalVoxels; - + MetavoxelSystem _metavoxels; - + ViewFrustum _viewFrustum; // current state of view frustum, perspective, orientation, etc. Oscilloscope _audioScope; VoxelQuery _voxelQuery; // NodeData derived class for querying voxels from voxel server - + MyAvatar _myAvatar; // The rendered avatar of oneself Profile _profile; // The data-server linked profile for this user - + Transmitter _myTransmitter; // Gets UDP data from transmitter app used to animate the avatar - + Webcam _webcam; // The webcam interface - + Faceshift _faceshift; - + SixenseManager _sixenseManager; - + Camera _myCamera; // My view onto the world Camera _viewFrustumOffsetCamera; // The camera we use to sometimes show the view frustum from an offset mode Camera _mirrorCamera; // Cammera for mirror view QRect _mirrorViewRect; RearMirrorTools* _rearMirrorTools; - + glm::mat4 _untranslatedViewMatrix; glm::vec3 _viewMatrixTranslation; - + glm::mat4 _shadowMatrix; - + Environment _environment; - + int _headMouseX, _headMouseY; - + int _mouseX; int _mouseY; int _mouseDragStartedX; @@ -420,7 +421,7 @@ private: bool _isTouchPressed; // true if multitouch has been pressed (clear when finished) float _yawFromTouch; float _pitchFromTouch; - + VoxelDetail _mouseVoxelDragging; bool _mousePressed; // true if mouse has been pressed (clear when finished) @@ -428,7 +429,7 @@ private: bool _isHoverVoxel; bool _isHoverVoxelSounding; nodeColor _hoverVoxelOriginalColor; - + VoxelDetail _mouseVoxel; // details of the voxel to be edited float _mouseVoxelScale; // the scale for adding/removing voxels bool _mouseVoxelScaleInitialized; @@ -437,7 +438,7 @@ private: VoxelDetail _highlightVoxel; bool _isHighlightVoxel; - + VoxelDetail _nudgeVoxel; // details of the voxel to be nudged bool _nudgeStarted; bool _lookingAlongX; @@ -447,46 +448,46 @@ private: Avatar* _lookatTargetAvatar; glm::vec3 _lookatOtherPosition; float _lookatIndicatorScale; - + glm::vec3 _transmitterPickStart; glm::vec3 _transmitterPickEnd; - - bool _perfStatsOn; // Do we want to display perfStats? - - ChatEntry _chatEntry; // chat entry field - bool _chatEntryOn; // Whether to show the chat entry - + + bool _perfStatsOn; // Do we want to display perfStats? + + ChatEntry _chatEntry; // chat entry field + bool _chatEntryOn; // Whether to show the chat entry + GeometryCache _geometryCache; TextureCache _textureCache; - + GlowEffect _glowEffect; AmbientOcclusionEffect _ambientOcclusionEffect; VoxelShader _voxelShader; PointShader _pointShader; - + #ifndef _WIN32 Audio _audio; #endif - + bool _enableNetworkThread; pthread_t _networkReceiveThread; bool _stopNetworkReceiveThread; - + bool _enableProcessVoxelsThread; VoxelPacketProcessor _voxelProcessor; VoxelHideShowThread _voxelHideShowThread; VoxelEditPacketSender _voxelEditSender; ParticleEditPacketSender _particleEditSender; - + unsigned char _incomingPacket[MAX_PACKET_SIZE]; int _packetCount; int _packetsPerSecond; int _bytesPerSecond; int _bytesCount; - + int _recentMaxPackets; // recent max incoming voxel packets to process bool _resetRecentMaxPacketsSoon; - + StDev _idleLoopStdev; float _idleLoopMeasuredJitter; @@ -496,22 +497,24 @@ private: bool _pasteMode; PieMenu _pieMenu; - + int parseOctreeStats(unsigned char* messageData, ssize_t messageLength, const HifiSockAddr& senderAddress); void trackIncomingVoxelPacket(unsigned char* messageData, ssize_t messageLength, const HifiSockAddr& senderSockAddr, bool wasStatsPacket); - + NodeToJurisdictionMap _voxelServerJurisdictions; NodeToJurisdictionMap _particleServerJurisdictions; NodeToVoxelSceneStats _octreeServerSceneStats; QReadWriteLock _voxelSceneStatsLock; - + std::vector _voxelFades; std::vector _avatarFades; ControllerScriptingInterface _controllerScriptingInterface; QPointer _logDialog; FileLogger* _logger; + + OctreePersistThread* _persistThread; }; #endif /* defined(__interface__Application__) */ diff --git a/interface/src/VoxelHideShowThread.cpp b/interface/src/VoxelHideShowThread.cpp index 26a27bab52..512d269bd2 100644 --- a/interface/src/VoxelHideShowThread.cpp +++ b/interface/src/VoxelHideShowThread.cpp @@ -35,12 +35,12 @@ bool VoxelHideShowThread::process() { if (showExtraDebugging && elapsed > USECS_PER_FRAME) { qDebug() << "VoxelHideShowThread::process()... checkForCulling took " << elapsed << "\n"; } - + if (isStillRunning()) { if (elapsed < USECS_PER_FRAME) { uint64_t sleepFor = USECS_PER_FRAME - elapsed; usleep(sleepFor); } - } + } return isStillRunning(); // keep running till they terminate us } diff --git a/interface/src/VoxelSystem.cpp b/interface/src/VoxelSystem.cpp index d7c10215f8..e0b2decfaf 100644 --- a/interface/src/VoxelSystem.cpp +++ b/interface/src/VoxelSystem.cpp @@ -35,7 +35,6 @@ const bool VoxelSystem::DONT_BAIL_EARLY = false; - float identityVerticesGlobalNormals[] = { 0,0,0, 1,0,0, 1,1,0, 0,1,0, 0,0,1, 1,0,1, 1,1,1, 0,1,1 }; float identityVertices[] = { 0,0,0, 1,0,0, 1,1,0, 0,1,0, 0,0,1, 1,0,1, 1,1,1, 0,1,1, //0-7 @@ -107,16 +106,18 @@ VoxelSystem::VoxelSystem(float treeScale, int maxVoxels) _inSetupNewVoxelsForDrawing = false; _useFastVoxelPipeline = false; - + _culledOnce = false; _inhideOutOfView = false; _treeIsBusy = false; + } void VoxelSystem::elementDeleted(OctreeElement* element) { VoxelTreeElement* voxel = (VoxelTreeElement*)element; if (voxel->getVoxelSystem() == this) { if (_voxelsInWriteArrays != 0) { +qDebug() << "elementDeleted()... about to call forceRemoveNodeFromArrays()\n"; forceRemoveNodeFromArrays(voxel); } else { if (Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings)) { @@ -148,11 +149,11 @@ void VoxelSystem::elementUpdated(OctreeElement* element) { if (voxel->getShouldRender() != shouldRender) { voxel->setShouldRender(shouldRender); } - + if (!voxel->isLeaf()) { - + // As we check our children, see if any of them went from shouldRender to NOT shouldRender - // then we probably dropped LOD and if we don't have color, we want to average our children + // then we probably dropped LOD and if we don't have color, we want to average our children // for a new color. int childrenGotHiddenCount = 0; for (int i = 0; i < NUMBER_OF_CHILDREN; i++) { @@ -176,7 +177,7 @@ void VoxelSystem::elementUpdated(OctreeElement* element) { _voxelsUpdated++; voxel->clearDirtyBit(); // clear the dirty bit, do this before we potentially delete things. - + setupNewVoxelsForDrawingSingleNode(); } } @@ -219,7 +220,7 @@ void VoxelSystem::freeBufferIndex(glBufferIndex index) { break; } } - } + } if (!inList) { // make the index available for next node that needs to be drawn pthread_mutex_lock(&_freeIndexLock); @@ -308,7 +309,7 @@ void VoxelSystem::setVoxelsAsPoints(bool voxelsAsPoints) { } bool wasInitialized = _initialized; - + // If we're "turning on" Voxels as points, we need to double check that we're in voxel shader mode. // Voxels as points uses the VoxelShader memory model, so if we're not in voxel shader mode, // then set it to voxel shader mode. @@ -318,7 +319,7 @@ void VoxelSystem::setVoxelsAsPoints(bool voxelsAsPoints) { // If enabling this... then do it before checking voxel shader status, that way, if voxel // shader is already enabled, we just start drawing as points. _voxelsAsPoints = true; - + if (!_useVoxelShader) { setUseVoxelShader(true); _voxelShaderModeWhenVoxelsAsPointsEnabled = false; @@ -354,7 +355,7 @@ void VoxelSystem::cleanupVoxelMemory() { delete[] _writeVoxelShaderData; delete[] _readVoxelShaderData; - + _writeVoxelShaderData = _readVoxelShaderData = NULL; } else { @@ -368,7 +369,7 @@ void VoxelSystem::cleanupVoxelMemory() { glDeleteBuffers(1, &_vboIndicesRight); glDeleteBuffers(1, &_vboIndicesFront); glDeleteBuffers(1, &_vboIndicesBack); - + delete[] _readVerticesArray; delete[] _writeVerticesArray; delete[] _readColorsArray; @@ -420,12 +421,12 @@ void VoxelSystem::initVoxelMemory() { _memoryUsageRAM = 0; _memoryUsageVBO = 0; // our VBO allocations as we know them - + // if _voxelsAsPoints then we must have _useVoxelShader if (_voxelsAsPoints && !_useVoxelShader) { _useVoxelShader = true; } - + if (_useVoxelShader) { GLuint* indicesArray = new GLuint[_maxVoxels]; @@ -445,7 +446,7 @@ void VoxelSystem::initVoxelMemory() { glBindBuffer(GL_ARRAY_BUFFER, _vboVoxelsID); glBufferData(GL_ARRAY_BUFFER, _maxVoxels * sizeof(VoxelShaderVBOData), NULL, GL_DYNAMIC_DRAW); _memoryUsageVBO += _maxVoxels * sizeof(VoxelShaderVBOData); - + // delete the indices and normals arrays that are no longer needed delete[] indicesArray; @@ -474,7 +475,7 @@ void VoxelSystem::initVoxelMemory() { setupFaceIndices(_vboIndicesRight, identityIndicesRight); setupFaceIndices(_vboIndicesFront, identityIndicesFront); setupFaceIndices(_vboIndicesBack, identityIndicesBack); - + // Depending on if we're using per vertex normals, we will need more or less vertex points per voxel int vertexPointsPerVoxel = GLOBAL_NORMALS_VERTEX_POINTS_PER_VOXEL; glGenBuffers(1, &_vboVerticesID); @@ -519,10 +520,10 @@ void VoxelSystem::initVoxelMemory() { _perlinModulateProgram.bind(); _perlinModulateProgram.setUniformValue("permutationNormalTexture", 0); _perlinModulateProgram.release(); - + _shadowMapProgram.addShaderFromSourceFile(QGLShader::Fragment, "resources/shaders/shadow_map.frag"); _shadowMapProgram.link(); - + _shadowMapProgram.bind(); _shadowMapProgram.setUniformValue("shadowMap", 0); _shadowMapProgram.release(); @@ -571,26 +572,26 @@ int VoxelSystem::parseData(unsigned char* sourceBuffer, int numBytes) { switch(command) { case PACKET_TYPE_VOXEL_DATA: { PerformanceWarning warn(showTimingDetails, "VoxelSystem::parseData() PACKET_TYPE_VOXEL_DATA part...",showTimingDetails); - + unsigned char* dataAt = sourceBuffer + numBytesPacketHeader; VOXEL_PACKET_FLAGS flags = (*(VOXEL_PACKET_FLAGS*)(dataAt)); dataAt += sizeof(VOXEL_PACKET_FLAGS); VOXEL_PACKET_SEQUENCE sequence = (*(VOXEL_PACKET_SEQUENCE*)dataAt); dataAt += sizeof(VOXEL_PACKET_SEQUENCE); - + VOXEL_PACKET_SENT_TIME sentAt = (*(VOXEL_PACKET_SENT_TIME*)dataAt); dataAt += sizeof(VOXEL_PACKET_SENT_TIME); - + bool packetIsColored = oneAtBit(flags, PACKET_IS_COLOR_BIT); bool packetIsCompressed = oneAtBit(flags, PACKET_IS_COMPRESSED_BIT); - + VOXEL_PACKET_SENT_TIME arrivedAt = usecTimestampNow(); int flightTime = arrivedAt - sentAt; - + VOXEL_PACKET_INTERNAL_SECTION_SIZE sectionLength = 0; int dataBytes = numBytes - VOXEL_PACKET_HEADER_SIZE; - + int subsection = 1; while (dataBytes > 0) { if (packetIsCompressed) { @@ -605,7 +606,7 @@ int VoxelSystem::parseData(unsigned char* sourceBuffer, int numBytes) { } else { sectionLength = dataBytes; } - + if (sectionLength) { // ask the VoxelTree to read the bitstream into the tree ReadBitstreamToTreeParams args(packetIsColored ? WANT_COLOR : NO_COLOR, WANT_EXISTS_BITS, NULL, getDataSourceUUID()); @@ -616,12 +617,12 @@ int VoxelSystem::parseData(unsigned char* sourceBuffer, int numBytes) { qDebug("VoxelSystem::parseData() ... Got Packet Section" " color:%s compressed:%s sequence: %u flight:%d usec size:%d data:%d" " subsection:%d sectionLength:%d uncompressed:%d\n", - debug::valueOf(packetIsColored), debug::valueOf(packetIsCompressed), + debug::valueOf(packetIsColored), debug::valueOf(packetIsCompressed), sequence, flightTime, numBytes, dataBytes, subsection, sectionLength, packetData.getUncompressedSize()); } _tree->readBitstreamToTree(packetData.getUncompressedData(), packetData.getUncompressedSize(), args); unlockTree(); - + dataBytes -= sectionLength; dataAt += sectionLength; } @@ -635,15 +636,13 @@ int VoxelSystem::parseData(unsigned char* sourceBuffer, int numBytes) { } else { setupNewVoxelsForDrawingSingleNode(DONT_BAIL_EARLY); } - + Application::getInstance()->getBandwidthMeter()->inputStream(BandwidthMeter::VOXELS).updateValue(numBytes); - + return numBytes; } void VoxelSystem::setupNewVoxelsForDrawing() { - - PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), "setupNewVoxelsForDrawing()"); @@ -653,7 +652,7 @@ void VoxelSystem::setupNewVoxelsForDrawing() { uint64_t start = usecTimestampNow(); uint64_t sinceLastTime = (start - _setupNewVoxelsForDrawingLastFinished) / 1000; - + bool iAmDebugging = false; // if you're debugging set this to true, so you won't get skipped for slow debugging if (!iAmDebugging && sinceLastTime <= std::max((float) _setupNewVoxelsForDrawingLastElapsed, SIXTY_FPS_IN_MILLISECONDS)) { return; // bail early, it hasn't been long enough since the last time we ran @@ -665,7 +664,7 @@ void VoxelSystem::setupNewVoxelsForDrawing() { if (_tree->isDirty()) { static char buffer[64] = { 0 }; if (Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings)) { - sprintf(buffer, "newTreeToArrays() _writeRenderFullVBO=%s", debug::valueOf(_writeRenderFullVBO)); + sprintf(buffer, "newTreeToArrays() _writeRenderFullVBO=%s", debug::valueOf(_writeRenderFullVBO)); }; PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), buffer); _callsToTreesToArrays++; @@ -678,17 +677,17 @@ void VoxelSystem::setupNewVoxelsForDrawing() { if (_writeRenderFullVBO) { _abandonedVBOSlots = 0; // reset the count of our abandoned slots, why is this here and not earlier???? } - + // since we called treeToArrays, we can assume that our VBO is in sync, and so partial updates to the VBOs are - // ok again, until/unless we call removeOutOfView() - _writeRenderFullVBO = false; + // ok again, until/unless we call removeOutOfView() + _writeRenderFullVBO = false; } else { _voxelsUpdated = 0; } - + // lock on the buffer write lock so we can't modify the data when the GPU is reading it pthread_mutex_lock(&_bufferWriteLock); - + if (_voxelsUpdated) { _voxelsDirty=true; } @@ -703,6 +702,11 @@ void VoxelSystem::setupNewVoxelsForDrawing() { _setupNewVoxelsForDrawingLastFinished = end; _setupNewVoxelsForDrawingLastElapsed = elapsedmsec; _inSetupNewVoxelsForDrawing = false; + + bool extraDebugging = Application::getInstance()->getLogger()->extraDebugging(); + if (extraDebugging) { + qDebug("setupNewVoxelsForDrawing()... _voxelsUpdated=%lu...\n",_voxelsUpdated); + } } void VoxelSystem::setupNewVoxelsForDrawingSingleNode(bool allowBailEarly) { @@ -713,7 +717,7 @@ void VoxelSystem::setupNewVoxelsForDrawingSingleNode(bool allowBailEarly) { uint64_t sinceLastTime = (start - _setupNewVoxelsForDrawingLastFinished) / 1000; bool iAmDebugging = false; // if you're debugging set this to true, so you won't get skipped for slow debugging - if (allowBailEarly && !iAmDebugging && + if (allowBailEarly && !iAmDebugging && sinceLastTime <= std::max((float) _setupNewVoxelsForDrawingLastElapsed, SIXTY_FPS_IN_MILLISECONDS)) { return; // bail early, it hasn't been long enough since the last time we ran } @@ -745,19 +749,17 @@ void VoxelSystem::checkForCulling() { PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), "checkForCulling()"); uint64_t start = usecTimestampNow(); - - + // track how long its been since we were last moving. If we have recently moved then only use delta frustums, if // it's been a long time since we last moved, then go ahead and do a full frustum cull. if (isViewChanging()) { _lastViewIsChanging = start; } uint64_t sinceLastMoving = (start - _lastViewIsChanging) / 1000; - bool enoughTime = (sinceLastMoving >= std::max((float) _lastViewCullingElapsed, VIEW_CULLING_RATE_IN_MILLISECONDS)); // These has changed events will occur before we stop. So we need to remember this for when we finally have stopped - // moving long enough to be enoughTime + // moving long enough to be enoughTime if (hasViewChanged()) { _hasRecentlyChanged = true; } @@ -767,25 +769,25 @@ void VoxelSystem::checkForCulling() { bool forceFullFrustum = enoughTime && _hasRecentlyChanged; // in hide mode, we only track the full frustum culls, because we don't care about the partials. - if (forceFullFrustum) { + if (forceFullFrustum) { _lastViewCulling = start; _hasRecentlyChanged = false; } - + hideOutOfView(forceFullFrustum); - if (forceFullFrustum) { + if (forceFullFrustum) { uint64_t endViewCulling = usecTimestampNow(); _lastViewCullingElapsed = (endViewCulling - start) / 1000; } - + // Once we call cleanupRemovedVoxels() we do need to rebuild our VBOs (if anything was actually removed). So, // we should consider putting this someplace else... as this might be able to occur less frequently, and save us on // VBO reubuilding. Possibly we should do this only if our actual VBO usage crosses some lower boundary. cleanupRemovedVoxels(); uint64_t sinceLastAudit = (start - _lastAudit) / 1000; - + if (Menu::getInstance()->isOptionChecked(MenuOption::AutomaticallyAuditTree)) { if (sinceLastAudit >= std::max((float) _lastViewCullingElapsed, VIEW_CULLING_RATE_IN_MILLISECONDS)) { _lastAudit = start; @@ -812,7 +814,7 @@ void VoxelSystem::cleanupRemovedVoxels() { const float TOO_MANY_ABANDONED_RATIO = 0.5f; if (!_writeRenderFullVBO && (_abandonedVBOSlots > (_voxelsInWriteArrays * TOO_MANY_ABANDONED_RATIO))) { if (Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings)) { - qDebug() << "cleanupRemovedVoxels().. _abandonedVBOSlots [" + qDebug() << "cleanupRemovedVoxels().. _abandonedVBOSlots [" << _abandonedVBOSlots << "] > TOO_MANY_ABANDONED_RATIO \n"; } _writeRenderFullVBO = true; @@ -822,10 +824,10 @@ void VoxelSystem::cleanupRemovedVoxels() { void VoxelSystem::copyWrittenDataToReadArraysFullVBOs() { copyWrittenDataSegmentToReadArrays(0, _voxelsInWriteArrays - 1); _voxelsInReadArrays = _voxelsInWriteArrays; - + // clear our dirty flags memset(_writeVoxelDirtyArray, false, _voxelsInWriteArrays * sizeof(bool)); - + // let the reader know to get the full array _readRenderFullVBO = true; } @@ -851,7 +853,7 @@ void VoxelSystem::copyWrittenDataToReadArraysPartialVBOs() { } } } - + // if we got to the end of the array, and we're in an active dirty segment... if (inSegment) { copyWrittenDataSegmentToReadArrays(segmentStart, _voxelsInWriteArrays - 1); @@ -910,9 +912,9 @@ int VoxelSystem::newTreeToArrays(VoxelTreeElement* voxel) { voxel->setShouldRender(shouldRender); // let children figure out their renderness if (!voxel->isLeaf()) { - + // As we check our children, see if any of them went from shouldRender to NOT shouldRender - // then we probably dropped LOD and if we don't have color, we want to average our children + // then we probably dropped LOD and if we don't have color, we want to average our children // for a new color. int childrenGotHiddenCount = 0; for (int i = 0; i < NUMBER_OF_CHILDREN; i++) { @@ -930,17 +932,23 @@ int VoxelSystem::newTreeToArrays(VoxelTreeElement* voxel) { voxel->calculateAverageFromChildren(); } } - if (_writeRenderFullVBO) { - const bool DONT_REUSE_INDEX = false; - const bool FORCE_REDRAW = true; - voxelsUpdated += updateNodeInArrays(voxel, DONT_REUSE_INDEX, FORCE_REDRAW); - } else { - const bool REUSE_INDEX = true; - const bool DONT_FORCE_REDRAW = false; - voxelsUpdated += updateNodeInArrays(voxel, REUSE_INDEX, DONT_FORCE_REDRAW); + + // for either voxels that should not render, or those that should render and are in view + // update their geometry in the array.if the voxel "should render" but is not in view, then + // it actually doesn't need to be rendered + if (!shouldRender || voxel->isInView(*_viewFrustum)) { + if (_writeRenderFullVBO) { + const bool DONT_REUSE_INDEX = false; + const bool FORCE_REDRAW = true; + voxelsUpdated += updateNodeInArrays(voxel, DONT_REUSE_INDEX, FORCE_REDRAW); + } else { + const bool REUSE_INDEX = true; + const bool DONT_FORCE_REDRAW = false; + voxelsUpdated += updateNodeInArrays(voxel, REUSE_INDEX, DONT_FORCE_REDRAW); + } } voxel->clearDirtyBit(); // clear the dirty bit, do this before we potentially delete things. - + return voxelsUpdated; } @@ -959,7 +967,7 @@ int VoxelSystem::forceRemoveNodeFromArrays(VoxelTreeElement* node) { // If this node has not yet been written to the array, then add it to the end of the array. glBufferIndex nodeIndex = node->getBufferIndex(); node->setBufferIndex(GLBUFFER_INDEX_UNKNOWN); - freeBufferIndex(nodeIndex); // NOTE: This is make the node invisible! + freeBufferIndex(nodeIndex); // NOTE: This will make the node invisible! return 1; // updated! } return 0; // not-updated @@ -982,11 +990,11 @@ int VoxelSystem::updateNodeInArrays(VoxelTreeElement* node, bool reuseIndex, boo if (!_initialized) { return 0; } - + // If we've changed any attributes (our renderness, our color, etc), or we've been told to force a redraw // then update the Arrays... if (forceDraw || node->isDirty()) { - // If we're should render, use our legit location and scale, + // If we're should render, use our legit location and scale, if (node->getShouldRender()) { glm::vec3 startVertex = node->getCorner(); float voxelScale = node->getScale(); @@ -1016,9 +1024,9 @@ int VoxelSystem::updateNodeInArrays(VoxelTreeElement* node, bool reuseIndex, boo void VoxelSystem::updateArraysDetails(glBufferIndex nodeIndex, const glm::vec3& startVertex, float voxelScale, const nodeColor& color) { - if (_initialized) { + if (_initialized && nodeIndex <= _maxVoxels) { _writeVoxelDirtyArray[nodeIndex] = true; - + if (_useVoxelShader) { if (_writeVoxelShaderData) { VoxelShaderVBOData* writeVerticesAt = &_writeVoxelShaderData[nodeIndex]; @@ -1071,15 +1079,17 @@ void VoxelSystem::init() { // VBO for the verticesArray _initialMemoryUsageGPU = getFreeMemoryGPU(); initVoxelMemory(); - + // our own _removedVoxels doesn't need to be notified of voxel deletes VoxelTreeElement::removeDeleteHook(&_removedVoxels); + } void VoxelSystem::changeTree(VoxelTree* newTree) { disconnect(_tree, 0, this, 0); _tree = newTree; + _tree->setDirtyBit(); _tree->getRoot()->setVoxelSystem(this); @@ -1102,7 +1112,7 @@ void VoxelSystem::updateFullVBOs() { PerformanceWarning warn(outputWarning,buffer); updateVBOSegment(0, _voxelsInReadArrays); } - + { PerformanceWarning warn(outputWarning,"updateFullVBOs() : memset(_readVoxelDirtyArray...)"); // consider the _readVoxelDirtyArray[] clean! @@ -1131,7 +1141,7 @@ void VoxelSystem::updatePartialVBOs() { _readVoxelDirtyArray[i] = false; // consider us clean! } } - + // if we got to the end of the array, and we're in an active dirty segment... if (inSegment) { updateVBOSegment(segmentStart, _voxelsInReadArrays - 1); @@ -1195,7 +1205,7 @@ void VoxelSystem::updateVBOSegment(glBufferIndex segmentStart, glBufferIndex seg PerformanceWarning warn(showWarning, "updateVBOSegment() : glBindBuffer(GL_ARRAY_BUFFER, _vboColorsID);"); glBindBuffer(GL_ARRAY_BUFFER, _vboColorsID); } - + { PerformanceWarning warn(showWarning, "updateVBOSegment() : glBufferSubData() _vboColorsID);"); glBufferSubData(GL_ARRAY_BUFFER, segmentStartAt, segmentSizeBytes, readColorsFrom); @@ -1206,14 +1216,14 @@ void VoxelSystem::updateVBOSegment(glBufferIndex segmentStart, glBufferIndex seg void VoxelSystem::render(bool texture) { bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings); PerformanceWarning warn(showWarnings, "render()"); - + // If we got here and we're not initialized then bail! if (!_initialized) { return; } - + updateVBOs(); - + bool dontCallOpenGLDraw = Menu::getInstance()->isOptionChecked(MenuOption::DontCallOpenGLForVoxels); // if not don't... then do... if (_useVoxelShader) { @@ -1253,8 +1263,8 @@ void VoxelSystem::render(bool texture) { glEnableVertexAttribArray(attributeLocation); glVertexAttribPointer(attributeLocation, 1, GL_FLOAT, false, sizeof(VoxelShaderVBOData), BUFFER_OFFSET(3*sizeof(float))); } - - + + glEnableClientState(GL_COLOR_ARRAY); glColorPointer(3, GL_UNSIGNED_BYTE, sizeof(VoxelShaderVBOData), BUFFER_OFFSET(4*sizeof(float)));//The starting point of colors @@ -1285,7 +1295,7 @@ void VoxelSystem::render(bool texture) { { PerformanceWarning warn(showWarnings,"render().. setup before glDrawRangeElementsEXT()..."); - + // tell OpenGL where to find vertex and color information glEnableClientState(GL_VERTEX_ARRAY); glEnableClientState(GL_COLOR_ARRAY); @@ -1306,7 +1316,7 @@ void VoxelSystem::render(bool texture) { if (!dontCallOpenGLDraw) { PerformanceWarning warn(showWarnings, "render().. glDrawRangeElementsEXT()..."); - + glNormal3f(0,1.0f,0); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _vboIndicesTop); glDrawRangeElementsEXT(GL_TRIANGLES, 0, GLOBAL_NORMALS_VERTICES_PER_VOXEL * _voxelsInReadArrays - 1, @@ -1340,7 +1350,7 @@ void VoxelSystem::render(bool texture) { { PerformanceWarning warn(showWarnings, "render().. cleanup after glDrawRangeElementsEXT()..."); - + glDisable(GL_CULL_FACE); removeScaleAndReleaseProgram(texture); @@ -1365,16 +1375,16 @@ void VoxelSystem::applyScaleAndBindProgram(bool texture) { 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]); glTexGenfv(GL_R, GL_EYE_PLANE, (const GLfloat*)&Application::getInstance()->getShadowMatrix()[2]); - + } else if (texture) { _perlinModulateProgram.bind(); glBindTexture(GL_TEXTURE_2D, Application::getInstance()->getTextureCache()->getPermutationNormalTextureID()); } - + glPushMatrix(); glScalef(_treeScale, _treeScale, _treeScale); } @@ -1382,7 +1392,7 @@ void VoxelSystem::applyScaleAndBindProgram(bool texture) { void VoxelSystem::removeScaleAndReleaseProgram(bool texture) { // scale back down to 1 so heads aren't massive glPopMatrix(); - + if (Menu::getInstance()->isOptionChecked(MenuOption::Shadows)) { _shadowMapProgram.release(); glBindTexture(GL_TEXTURE_2D, 0); @@ -1390,7 +1400,7 @@ void VoxelSystem::removeScaleAndReleaseProgram(bool texture) { glDisable(GL_TEXTURE_GEN_T); glDisable(GL_TEXTURE_GEN_R); glDisable(GL_TEXTURE_2D); - + } else if (texture) { _perlinModulateProgram.release(); glBindTexture(GL_TEXTURE_2D, 0); @@ -1403,7 +1413,7 @@ void VoxelSystem::killLocalVoxels() { lockTree(); _tree->eraseAllOctreeElements(); unlockTree(); - clearFreeBufferIndexes(); + clearFreeBufferIndexes(); _voxelsInReadArrays = 0; // do we need to do this? setupNewVoxelsForDrawing(); } @@ -1422,7 +1432,7 @@ bool VoxelSystem::clearAllNodesBufferIndexOperation(OctreeElement* element, void void VoxelSystem::clearAllNodesBufferIndex() { _nodeCount = 0; - lockTree(); + lockTree(); _tree->recurseTreeWithOperation(clearAllNodesBufferIndexOperation); unlockTree(); if (Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings)) { @@ -1584,9 +1594,9 @@ void VoxelSystem::falseColorizeBySource() { const int NUMBER_OF_COLOR_GROUPS = 6; const unsigned char MIN_COLOR = 128; int voxelServerCount = 0; - groupColor groupColors[NUMBER_OF_COLOR_GROUPS] = { - groupColor(255, 0, 0), - groupColor( 0, 255, 0), + groupColor groupColors[NUMBER_OF_COLOR_GROUPS] = { + groupColor(255, 0, 0), + groupColor( 0, 255, 0), groupColor( 0, 0, 255), groupColor(255, 0, 255), groupColor( 0, 255, 255), @@ -1614,7 +1624,7 @@ void VoxelSystem::falseColorizeBySource() { voxelServerCount++; } } - + _tree->recurseTreeWithOperation(falseColorizeBySourceOperation, &args); qDebug("setting false color by source for %d nodes\n", _nodeCount); _tree->setDirtyBit(); @@ -1690,7 +1700,7 @@ public: unsigned long nodesOutside; VoxelTreeElement* insideRoot; VoxelTreeElement* outsideRoot; - + removeOutOfViewArgs(VoxelSystem* voxelSystem, bool widenViewFrustum = true) : thisVoxelSystem(voxelSystem), thisViewFrustum(*voxelSystem->getViewFrustum()), @@ -1730,7 +1740,7 @@ bool VoxelSystem::removeOutOfViewOperation(OctreeElement* element, void* extraDa args->dontRecurseBag.remove(voxel); return false; // stop recursion } - + VoxelSystem* thisVoxelSystem = args->thisVoxelSystem; args->nodesScanned++; // Need to operate on our child nodes, so we can remove them @@ -1748,7 +1758,7 @@ bool VoxelSystem::removeOutOfViewOperation(OctreeElement* element, void* extraDa } break; case ViewFrustum::INSIDE: { // if the child node is fully INSIDE the view, then there's no need to recurse it - // because we know all it's children will also be in the view, so we want to + // because we know all it's children will also be in the view, so we want to // tell the caller to NOT recurse this child args->nodesInside++; args->dontRecurseBag.insert(childNode); @@ -1779,12 +1789,12 @@ bool VoxelSystem::isViewChanging() { bool VoxelSystem::hasViewChanged() { bool result = false; // assume the best - + // If we're still changing, report no change yet. if (isViewChanging()) { return false; } - + // If our viewFrustum has changed since our _lastKnownViewFrustum if (!_lastStableViewFrustum.isVerySimilar(_viewFrustum)) { result = true; @@ -1803,8 +1813,8 @@ void VoxelSystem::removeOutOfView() { } bool showRemoveDebugDetails = false; if (showRemoveDebugDetails) { - qDebug("removeOutOfView() scanned=%ld removed=%ld inside=%ld intersect=%ld outside=%ld _removedVoxels.count()=%d \n", - args.nodesScanned, args.nodesRemoved, args.nodesInside, + qDebug("removeOutOfView() scanned=%ld removed=%ld inside=%ld intersect=%ld outside=%ld _removedVoxels.count()=%d \n", + args.nodesScanned, args.nodesRemoved, args.nodesInside, args.nodesIntersect, args.nodesOutside, _removedVoxels.count() ); } @@ -1816,9 +1826,9 @@ public: VoxelSystem* thisVoxelSystem; ViewFrustum thisViewFrustum; unsigned long nodesScanned; - + showAllLocalVoxelsArgs(VoxelSystem* voxelSystem) : - thisVoxelSystem(voxelSystem), + thisVoxelSystem(voxelSystem), thisViewFrustum(*voxelSystem->getViewFrustum()), nodesScanned(0) { @@ -1880,8 +1890,9 @@ public: unsigned long nodesOutsideInside; unsigned long nodesInsideOutside; unsigned long nodesOutsideOutside; - - hideOutOfViewArgs(VoxelSystem* voxelSystem, VoxelTree* tree, + unsigned long nodesShown; + + hideOutOfViewArgs(VoxelSystem* voxelSystem, VoxelTree* tree, bool culledOnce, bool widenViewFrustum, bool wantDeltaFrustums) : thisVoxelSystem(voxelSystem), tree(tree), @@ -1898,7 +1909,8 @@ public: nodesIntersectInside(0), nodesOutsideInside(0), nodesInsideOutside(0), - nodesOutsideOutside(0) + nodesOutsideOutside(0), + nodesShown(0) { // Widen the FOV for trimming if (widenViewFrustum) { @@ -1916,13 +1928,14 @@ void VoxelSystem::hideOutOfView(bool forceFullFrustum) { if (_inhideOutOfView) { return; } - + _inhideOutOfView = true; bool showDebugDetails = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings); PerformanceWarning warn(showDebugDetails, "hideOutOfView()"); bool widenFrustum = true; + // When using "delta" view frustums and only hide/show items that are in the difference // between the two view frustums. There are some potential problems with this mode. // @@ -1932,7 +1945,7 @@ void VoxelSystem::hideOutOfView(bool forceFullFrustum) { // // 2) Also, voxels will arrive from the network that are OUTSIDE of the view // frustum... these won't get hidden... and so we can't assume they are correctly - // hidden... + // hidden... // // Both these problems are solved by intermittently calling this with forceFullFrustum set // to true. This will essentially clean up the improperly hidden or shown voxels. @@ -1947,13 +1960,13 @@ void VoxelSystem::hideOutOfView(bool forceFullFrustum) { args.lastViewFrustum.printDebugDetails(); } } - + if (!forceFullFrustum && _culledOnce && args.lastViewFrustum.isVerySimilar(args.thisViewFrustum)) { _inhideOutOfView = false; return; } - lockTree(); + lockTree(); _tree->recurseTreeWithOperation(hideOutOfViewOperation,(void*)&args); unlockTree(); _lastCulledViewFrustum = args.thisViewFrustum; // save last stable @@ -1963,16 +1976,19 @@ void VoxelSystem::hideOutOfView(bool forceFullFrustum) { _tree->setDirtyBit(); setupNewVoxelsForDrawingSingleNode(DONT_BAIL_EARLY); } - - bool extraDebugDetails = Application::getInstance()->getLogger()->extraDebugging(); + + bool extraDebugDetails = false; // Application::getInstance()->getLogger()->extraDebugging(); if (extraDebugDetails) { - qDebug("hideOutOfView() scanned=%ld removed=%ld inside=%ld intersect=%ld outside=%ld\n", - args.nodesScanned, args.nodesRemoved, args.nodesInside, + qDebug("hideOutOfView() scanned=%ld removed=%ld show=%ld inside=%ld intersect=%ld outside=%ld\n", + args.nodesScanned, args.nodesRemoved, args.nodesShown, args.nodesInside, args.nodesIntersect, args.nodesOutside ); - qDebug(" inside/inside=%ld intersect/inside=%ld outside/outside=%ld\n", + qDebug(" inside/inside=%ld intersect/inside=%ld outside/outside=%ld\n", args.nodesInsideInside, args.nodesIntersectInside, args.nodesOutsideOutside ); + + qDebug() << "args.thisViewFrustum....\n"; + args.thisViewFrustum.printDebugDetails(); } _inhideOutOfView = false; } @@ -1985,10 +2001,10 @@ bool VoxelSystem::hideAllSubTreeOperation(OctreeElement* element, void* extraDat // how to proceed. If we've never culled, then we just consider all these voxels to be UNKNOWN so that we will not // consider that case. ViewFrustum::location inLastCulledFrustum; - + if (args->culledOnce && args->wantDeltaFrustums) { inLastCulledFrustum = voxel->inFrustum(args->lastViewFrustum); - + // if this node is fully OUTSIDE our last culled view frustum, then we don't need to recurse further if (inLastCulledFrustum == ViewFrustum::OUTSIDE) { args->nodesOutsideOutside++; @@ -2009,7 +2025,7 @@ bool VoxelSystem::hideAllSubTreeOperation(OctreeElement* element, void* extraDat } } - + return true; } @@ -2021,10 +2037,10 @@ bool VoxelSystem::showAllSubTreeOperation(OctreeElement* element, void* extraDat // how to proceed. If we've never culled, then we just consider all these voxels to be UNKNOWN so that we will not // consider that case. ViewFrustum::location inLastCulledFrustum; - + if (args->culledOnce && args->wantDeltaFrustums) { inLastCulledFrustum = voxel->inFrustum(args->lastViewFrustum); - + // if this node is fully inside our last culled view frustum, then we don't need to recurse further if (inLastCulledFrustum == ViewFrustum::INSIDE) { args->nodesInsideInside++; @@ -2047,35 +2063,36 @@ bool VoxelSystem::showAllSubTreeOperation(OctreeElement* element, void* extraDat // These are both needed to force redraw... voxel->setDirtyBit(); voxel->markWithChangedTime(); + args->nodesShown++; } return true; // keep recursing! } -// "hide" voxels in the VBOs that are still in the tree that but not in view. +// "hide" voxels in the VBOs that are still in the tree that but not in view. // We don't remove them from the tree, we don't delete them, we do remove them // from the VBOs and mark them as such in the tree. bool VoxelSystem::hideOutOfViewOperation(OctreeElement* element, void* extraData) { VoxelTreeElement* voxel = (VoxelTreeElement*)element; hideOutOfViewArgs* args = (hideOutOfViewArgs*)extraData; - // If we're still recursing the tree using this operator, then we don't know if we're inside or outside... + // If we're still recursing the tree using this operator, then we don't know if we're inside or outside... // so before we move forward we need to determine our frustum location ViewFrustum::location inFrustum = voxel->inFrustum(args->thisViewFrustum); - + // If we've culled at least once, then we will use the status of this voxel in the last culled frustum to determine // how to proceed. If we've never culled, then we just consider all these voxels to be UNKNOWN so that we will not // consider that case. ViewFrustum::location inLastCulledFrustum; - + if (args->culledOnce && args->wantDeltaFrustums) { inLastCulledFrustum = voxel->inFrustum(args->lastViewFrustum); } - + // ok, now do some processing for this node... switch (inFrustum) { case ViewFrustum::OUTSIDE: { - + // If this node is outside the current view, then we might want to hide it... unless it was previously OUTSIDE, // if it was previously outside, then we can safely assume it's already hidden, and we can also safely assume // that all of it's children are outside both of our views, in which case we can just stop recursing... @@ -2084,17 +2101,17 @@ bool VoxelSystem::hideOutOfViewOperation(OctreeElement* element, void* extraData args->nodesOutsideOutside++; return false; // stop recursing this branch! } - + // if this node is fully OUTSIDE the view, but previously intersected and/or was inside the last view, then - // we need to hide it. Additionally we know that ALL of it's children are also fully OUTSIDE so we can recurse + // we need to hide it. Additionally we know that ALL of it's children are also fully OUTSIDE so we can recurse // the children and simply mark them as hidden args->tree->recurseNodeWithOperation(voxel, hideAllSubTreeOperation, args ); - + return false; - + } break; case ViewFrustum::INSIDE: { - + // If this node is INSIDE the current view, then we might want to show it... unless it was previously INSIDE, // if it was previously INSIDE, then we can safely assume it's already shown, and we can also safely assume // that all of it's children are INSIDE both of our views, in which case we can just stop recursing... @@ -2103,18 +2120,18 @@ bool VoxelSystem::hideOutOfViewOperation(OctreeElement* element, void* extraData args->nodesInsideInside++; return false; // stop recursing this branch! } - + // if this node is fully INSIDE the view, but previously INTERSECTED and/or was OUTSIDE the last view, then - // we need to show it. Additionally we know that ALL of it's children are also fully INSIDE so we can recurse + // we need to show it. Additionally we know that ALL of it's children are also fully INSIDE so we can recurse // the children and simply mark them as visible (as appropriate based on LOD) args->tree->recurseNodeWithOperation(voxel, showAllSubTreeOperation, args); - return false; + return false; } break; case ViewFrustum::INTERSECT: { args->nodesScanned++; - // If this node INTERSECTS the current view, then we might want to show it... unless it was previously INSIDE + // If this node INTERSECTS the current view, then we might want to show it... unless it was previously INSIDE // the last known view, in which case it will already be visible, and we know that all it's children are also // previously INSIDE and visible. So in this case stop recursing if (args->culledOnce && args->wantDeltaFrustums && inLastCulledFrustum == ViewFrustum::INSIDE) { @@ -2125,21 +2142,22 @@ bool VoxelSystem::hideOutOfViewOperation(OctreeElement* element, void* extraData args->nodesIntersect++; // if the child node INTERSECTs the view, then we want to check to see if it thinks it should render - // if it should render but is missing it's VBO index, then we want to flip it on, and we can stop recursing from + // if it should render but is missing it's VBO index, then we want to flip it on, and we can stop recursing from // here because we know will block any children anyway if (voxel->getShouldRender() && !voxel->isKnownBufferIndex()) { voxel->setDirtyBit(); // will this make it draw? + args->nodesShown++; return false; } // If it INTERSECTS but shouldn't be displayed, then it's probably a parent and it is at least partially in view. - // So we DO want to recurse the children because some of them may not be in view... nothing specifically to do, + // So we DO want to recurse the children because some of them may not be in view... nothing specifically to do, // just keep iterating the children - return true; + return true; } break; } // switch - + return true; // keep going! } @@ -2147,7 +2165,7 @@ bool VoxelSystem::hideOutOfViewOperation(OctreeElement* element, void* extraData bool VoxelSystem::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, VoxelDetail& detail, float& distance, BoxFace& face) { - lockTree(); + lockTree(); OctreeElement* element; if (!_tree->findRayIntersection(origin, direction, element, distance, face)) { unlockTree(); @@ -2206,7 +2224,7 @@ bool VoxelSystem::falseColorizeRandomEveryOtherOperation(OctreeElement* element, void VoxelSystem::falseColorizeRandomEveryOther() { falseColorizeRandomEveryOtherArgs args; _tree->recurseTreeWithOperation(falseColorizeRandomEveryOtherOperation,&args); - qDebug("randomized false color for every other node: total %ld, colorable %ld, colored %ld\n", + qDebug("randomized false color for every other node: total %ld, colorable %ld, colored %ld\n", args.totalNodes, args.colorableNodes, args.coloredNodes); _tree->setDirtyBit(); setupNewVoxelsForDrawing(); @@ -2214,9 +2232,9 @@ void VoxelSystem::falseColorizeRandomEveryOther() { class collectStatsForTreesAndVBOsArgs { public: - collectStatsForTreesAndVBOsArgs(int maxVoxels) : - totalNodes(0), - dirtyNodes(0), + collectStatsForTreesAndVBOsArgs(int maxVoxels) : + totalNodes(0), + dirtyNodes(0), shouldRenderNodes(0), coloredNodes(0), nodesInVBO(0), @@ -2228,7 +2246,7 @@ public: hasIndexFound = new bool[maxVoxels]; memset(hasIndexFound, false, maxVoxels * sizeof(bool)); }; - + ~collectStatsForTreesAndVBOsArgs() { delete[] hasIndexFound; } @@ -2244,13 +2262,13 @@ public: unsigned long leafNodes; unsigned long expectedMax; - + bool* hasIndexFound; }; bool VoxelSystem::collectStatsForTreesAndVBOsOperation(OctreeElement* element, void* extraData) { VoxelTreeElement* voxel = (VoxelTreeElement*)element; - + collectStatsForTreesAndVBOsArgs* args = (collectStatsForTreesAndVBOsArgs*)extraData; args->totalNodes++; @@ -2273,17 +2291,17 @@ bool VoxelSystem::collectStatsForTreesAndVBOsOperation(OctreeElement* element, v if (voxel->isKnownBufferIndex()) { args->nodesInVBO++; unsigned long nodeIndex = voxel->getBufferIndex(); - + const bool extraDebugging = false; // enable for extra debugging if (extraDebugging) { - qDebug("node In VBO... [%f,%f,%f] %f ... index=%ld, isDirty=%s, shouldRender=%s \n", - voxel->getCorner().x, voxel->getCorner().y, voxel->getCorner().z, voxel->getScale(), + qDebug("node In VBO... [%f,%f,%f] %f ... index=%ld, isDirty=%s, shouldRender=%s \n", + voxel->getCorner().x, voxel->getCorner().y, voxel->getCorner().z, voxel->getScale(), nodeIndex, debug::valueOf(voxel->isDirty()), debug::valueOf(voxel->getShouldRender())); } if (args->hasIndexFound[nodeIndex]) { args->duplicateVBOIndex++; - qDebug("duplicateVBO found... index=%ld, isDirty=%s, shouldRender=%s \n", nodeIndex, + qDebug("duplicateVBO found... index=%ld, isDirty=%s, shouldRender=%s \n", nodeIndex, debug::valueOf(voxel->isDirty()), debug::valueOf(voxel->getShouldRender())); } else { args->hasIndexFound[nodeIndex] = true; @@ -2291,7 +2309,7 @@ bool VoxelSystem::collectStatsForTreesAndVBOsOperation(OctreeElement* element, v if (nodeIndex > args->expectedMax) { args->nodesInVBOOverExpectedMax++; } - + // if it's in VBO but not-shouldRender, track that also... if (!voxel->getShouldRender()) { args->nodesInVBONotShouldRender++; @@ -2316,9 +2334,9 @@ void VoxelSystem::collectStatsForTreesAndVBOs() { collectStatsForTreesAndVBOsArgs args(_maxVoxels); args.expectedMax = _voxelsInWriteArrays; - + qDebug("CALCULATING Local Voxel Tree Statistics >>>>>>>>>>>>\n"); - + _tree->recurseTreeWithOperation(collectStatsForTreesAndVBOsOperation,&args); qDebug("Local Voxel Tree Statistics:\n total nodes %ld \n leaves %ld \n dirty %ld \n colored %ld \n shouldRender %ld \n", @@ -2327,7 +2345,7 @@ void VoxelSystem::collectStatsForTreesAndVBOs() { qDebug(" _voxelsDirty=%s \n _voxelsInWriteArrays=%ld \n minDirty=%ld \n maxDirty=%ld \n", debug::valueOf(_voxelsDirty), _voxelsInWriteArrays, minDirty, maxDirty); - qDebug(" inVBO %ld \n nodesInVBOOverExpectedMax %ld \n duplicateVBOIndex %ld \n nodesInVBONotShouldRender %ld \n", + qDebug(" inVBO %ld \n nodesInVBOOverExpectedMax %ld \n duplicateVBOIndex %ld \n nodesInVBONotShouldRender %ld \n", args.nodesInVBO, args.nodesInVBOOverExpectedMax, args.duplicateVBOIndex, args.nodesInVBONotShouldRender); glBufferIndex minInVBO = GLBUFFER_INDEX_UNKNOWN; @@ -2340,10 +2358,10 @@ void VoxelSystem::collectStatsForTreesAndVBOs() { } } - qDebug(" minInVBO=%ld \n maxInVBO=%ld \n _voxelsInWriteArrays=%ld \n _voxelsInReadArrays=%ld \n", + qDebug(" minInVBO=%ld \n maxInVBO=%ld \n _voxelsInWriteArrays=%ld \n _voxelsInReadArrays=%ld \n", minInVBO, maxInVBO, _voxelsInWriteArrays, _voxelsInReadArrays); - qDebug(" _freeIndexes.size()=%ld \n", + qDebug(" _freeIndexes.size()=%ld \n", _freeIndexes.size()); qDebug("DONE WITH Local Voxel Tree Statistics >>>>>>>>>>>>\n"); @@ -2354,36 +2372,36 @@ void VoxelSystem::deleteVoxelAt(float x, float y, float z, float s) { lockTree(); _tree->deleteVoxelAt(x, y, z, s); unlockTree(); - + // redraw! setupNewVoxelsForDrawing(); // do we even need to do this? Or will the next network receive kick in? - + }; -VoxelTreeElement* VoxelSystem::getVoxelAt(float x, float y, float z, float s) const { - return _tree->getVoxelAt(x, y, z, s); +VoxelTreeElement* VoxelSystem::getVoxelAt(float x, float y, float z, float s) const { + return _tree->getVoxelAt(x, y, z, s); }; -void VoxelSystem::createVoxel(float x, float y, float z, float s, +void VoxelSystem::createVoxel(float x, float y, float z, float s, unsigned char red, unsigned char green, unsigned char blue, bool destructive) { - + //qDebug("VoxelSystem::createVoxel(%f,%f,%f,%f)\n",x,y,z,s); lockTree(); - _tree->createVoxel(x, y, z, s, red, green, blue, destructive); + _tree->createVoxel(x, y, z, s, red, green, blue, destructive); unlockTree(); - setupNewVoxelsForDrawing(); + setupNewVoxelsForDrawing(); }; -void VoxelSystem::createLine(glm::vec3 point1, glm::vec3 point2, float unitSize, rgbColor color, bool destructive) { - _tree->createLine(point1, point2, unitSize, color, destructive); - setupNewVoxelsForDrawing(); +void VoxelSystem::createLine(glm::vec3 point1, glm::vec3 point2, float unitSize, rgbColor color, bool destructive) { + _tree->createLine(point1, point2, unitSize, color, destructive); + setupNewVoxelsForDrawing(); }; -void VoxelSystem::createSphere(float r,float xc, float yc, float zc, float s, bool solid, - creationMode mode, bool destructive, bool debug) { - _tree->createSphere(r, xc, yc, zc, s, solid, mode, destructive, debug); - setupNewVoxelsForDrawing(); +void VoxelSystem::createSphere(float r,float xc, float yc, float zc, float s, bool solid, + creationMode mode, bool destructive, bool debug) { + _tree->createSphere(r, xc, yc, zc, s, solid, mode, destructive, debug); + setupNewVoxelsForDrawing(); }; void VoxelSystem::copySubTreeIntoNewTree(VoxelTreeElement* startNode, VoxelSystem* destination, bool rebaseToRoot) { @@ -2431,7 +2449,7 @@ bool VoxelSystem::falseColorizeSubTreeOperation(OctreeElement* element, void* ex voxel->setFalseColor(args->color[0], args->color[1], args->color[2]); args->voxelsTouched++; } - return true; + return true; } bool VoxelSystem::falseColorizeOccludedOperation(OctreeElement* element, void* extraData) { @@ -2459,18 +2477,18 @@ bool VoxelSystem::falseColorizeOccludedOperation(OctreeElement* element, void* e if (result == OCCLUDED) { args->nonLeavesOccluded++; delete voxelPolygon; - + FalseColorizeSubTreeOperationArgs subArgs; subArgs.color[0] = 0; subArgs.color[1] = 255; subArgs.color[2] = 0; subArgs.voxelsTouched = 0; - + args->tree->recurseNodeWithOperation(voxel, falseColorizeSubTreeOperation, &subArgs ); - + args->subtreeVoxelsSkipped += (subArgs.voxelsTouched - 1); args->totalVoxels += (subArgs.voxelsTouched - 1); - + return false; } @@ -2509,10 +2527,10 @@ bool VoxelSystem::falseColorizeOccludedOperation(OctreeElement* element, void* e void VoxelSystem::falseColorizeOccluded() { PerformanceWarning warn(true, "falseColorizeOccluded()",true); myCoverageMap.erase(); - + FalseColorizeOccludedArgs args; args.viewFrustum = _viewFrustum; - args.map = &myCoverageMap; + args.map = &myCoverageMap; args.totalVoxels = 0; args.coloredVoxels = 0; args.occludedVoxels = 0; @@ -2527,15 +2545,15 @@ void VoxelSystem::falseColorizeOccluded() { OctreeProjectedPolygon::pointInside_calls = 0; OctreeProjectedPolygon::occludes_calls = 0; OctreeProjectedPolygon::intersects_calls = 0; - + glm::vec3 position = args.viewFrustum->getPosition() * (1.0f/TREE_SCALE); _tree->recurseTreeWithOperationDistanceSorted(falseColorizeOccludedOperation, position, (void*)&args); - qDebug("falseColorizeOccluded()\n position=(%f,%f)\n total=%ld\n colored=%ld\n occluded=%ld\n notOccluded=%ld\n outOfView=%ld\n subtreeVoxelsSkipped=%ld\n nonLeaves=%ld\n nonLeavesOutOfView=%ld\n nonLeavesOccluded=%ld\n pointInside_calls=%ld\n occludes_calls=%ld\n intersects_calls=%ld\n", + qDebug("falseColorizeOccluded()\n position=(%f,%f)\n total=%ld\n colored=%ld\n occluded=%ld\n notOccluded=%ld\n outOfView=%ld\n subtreeVoxelsSkipped=%ld\n nonLeaves=%ld\n nonLeavesOutOfView=%ld\n nonLeavesOccluded=%ld\n pointInside_calls=%ld\n occludes_calls=%ld\n intersects_calls=%ld\n", position.x, position.y, - args.totalVoxels, args.coloredVoxels, args.occludedVoxels, - args.notOccludedVoxels, args.outOfView, args.subtreeVoxelsSkipped, + args.totalVoxels, args.coloredVoxels, args.occludedVoxels, + args.notOccludedVoxels, args.outOfView, args.subtreeVoxelsSkipped, args.nonLeaves, args.nonLeavesOutOfView, args.nonLeavesOccluded, OctreeProjectedPolygon::pointInside_calls, OctreeProjectedPolygon::occludes_calls, @@ -2574,18 +2592,18 @@ bool VoxelSystem::falseColorizeOccludedV2Operation(OctreeElement* element, void* if (result == V2_OCCLUDED) { args->nonLeavesOccluded++; delete voxelPolygon; - + FalseColorizeSubTreeOperationArgs subArgs; subArgs.color[0] = 0; subArgs.color[1] = 255; subArgs.color[2] = 0; subArgs.voxelsTouched = 0; - + args->tree->recurseNodeWithOperation(voxel, falseColorizeSubTreeOperation, &subArgs ); - + args->subtreeVoxelsSkipped += (subArgs.voxelsTouched - 1); args->totalVoxels += (subArgs.voxelsTouched - 1); - + return false; } @@ -2632,10 +2650,10 @@ void VoxelSystem::falseColorizeOccludedV2() { OctreeProjectedPolygon::pointInside_calls = 0; OctreeProjectedPolygon::occludes_calls = 0; OctreeProjectedPolygon::intersects_calls = 0; - + FalseColorizeOccludedArgs args; args.viewFrustum = _viewFrustum; - args.mapV2 = &myCoverageMapV2; + args.mapV2 = &myCoverageMapV2; args.totalVoxels = 0; args.coloredVoxels = 0; args.occludedVoxels = 0; @@ -2646,15 +2664,15 @@ void VoxelSystem::falseColorizeOccludedV2() { args.nonLeavesOutOfView = 0; args.nonLeavesOccluded = 0; args.tree = _tree; - + glm::vec3 position = args.viewFrustum->getPosition() * (1.0f/TREE_SCALE); _tree->recurseTreeWithOperationDistanceSorted(falseColorizeOccludedV2Operation, position, (void*)&args); - qDebug("falseColorizeOccludedV2()\n position=(%f,%f)\n total=%ld\n colored=%ld\n occluded=%ld\n notOccluded=%ld\n outOfView=%ld\n subtreeVoxelsSkipped=%ld\n nonLeaves=%ld\n nonLeavesOutOfView=%ld\n nonLeavesOccluded=%ld\n pointInside_calls=%ld\n occludes_calls=%ld\n intersects_calls=%ld\n", + qDebug("falseColorizeOccludedV2()\n position=(%f,%f)\n total=%ld\n colored=%ld\n occluded=%ld\n notOccluded=%ld\n outOfView=%ld\n subtreeVoxelsSkipped=%ld\n nonLeaves=%ld\n nonLeavesOutOfView=%ld\n nonLeavesOccluded=%ld\n pointInside_calls=%ld\n occludes_calls=%ld\n intersects_calls=%ld\n", position.x, position.y, - args.totalVoxels, args.coloredVoxels, args.occludedVoxels, - args.notOccludedVoxels, args.outOfView, args.subtreeVoxelsSkipped, + args.totalVoxels, args.coloredVoxels, args.occludedVoxels, + args.notOccludedVoxels, args.outOfView, args.subtreeVoxelsSkipped, args.nonLeaves, args.nonLeavesOutOfView, args.nonLeavesOccluded, OctreeProjectedPolygon::pointInside_calls, OctreeProjectedPolygon::occludes_calls, @@ -2717,7 +2735,7 @@ unsigned long VoxelSystem::getFreeMemoryGPU() { const GLenum VBO_FREE_MEMORY_ATI = 0x87FB; glGetIntegerv(VBO_FREE_MEMORY_ATI, &results[0]); GLenum errorATI = glGetError(); - + if (errorATI == GL_NO_ERROR) { _hasMemoryUsageGPU = true; freeMemory = results[0]; @@ -2730,19 +2748,19 @@ unsigned long VoxelSystem::getFreeMemoryGPU() { //const GLenum GPU_MEMORY_INFO_EVICTION_COUNT_NVX = 0x904A; //const GLenum GPU_MEMORY_INFO_EVICTED_MEMORY_NVX = 0x904B; //const GLenum GPU_MEMORY_INFO_TOTAL_AVAILABLE_MEMORY_NVX = 0x9048; - + const GLenum GPU_MEMORY_INFO_CURRENT_AVAILABLE_VIDMEM_NVX = 0x9049; results[0] = 0; glGetIntegerv(GPU_MEMORY_INFO_CURRENT_AVAILABLE_VIDMEM_NVX, &results[0]); - freeMemory += results[0]; + freeMemory += results[0]; GLenum errorNVIDIA = glGetError(); - + if (errorNVIDIA == GL_NO_ERROR) { _hasMemoryUsageGPU = true; freeMemory = results[0]; } } - + const unsigned long BYTES_PER_KBYTE = 1024; return freeMemory * BYTES_PER_KBYTE; // API results in KB, we want it in bytes } @@ -2763,4 +2781,22 @@ void VoxelSystem::unlockTree() { } +void VoxelSystem::localVoxelCacheLoaded() { + qDebug() << "localVoxelCacheLoaded()\n"; + + // Make sure that the application has properly set up the view frustum for our loaded state + Application::getInstance()->initAvatarAndViewFrustum(); + + _tree->setDirtyBit(); + setupNewVoxelsForDrawing(); + _inhideOutOfView = false; // reenable hideOutOfView behavior +} + +void VoxelSystem::beginLoadingLocalVoxelCache() { + qDebug() << "beginLoadingLocalVoxelCache()\n"; + _writeRenderFullVBO = true; // this will disable individual node updates + _inhideOutOfView = true; // this will disable hidOutOfView which we want to do until local cache is loaded +} + + diff --git a/interface/src/VoxelSystem.h b/interface/src/VoxelSystem.h index 28e66d11dc..8173652e55 100644 --- a/interface/src/VoxelSystem.h +++ b/interface/src/VoxelSystem.h @@ -18,6 +18,7 @@ #include #include #include +#include #include "Camera.h" #include "Util.h" @@ -36,7 +37,7 @@ struct VoxelShaderVBOData }; -class VoxelSystem : public NodeData, public OctreeElementDeleteHook, public OctreeElementUpdateHook, +class VoxelSystem : public NodeData, public OctreeElementDeleteHook, public OctreeElementUpdateHook, public NodeListHook, public DomainChangeListener { Q_OBJECT @@ -48,9 +49,9 @@ public: void setDataSourceUUID(const QUuid& dataSourceUUID) { _dataSourceUUID = dataSourceUUID; } const QUuid& getDataSourceUUID() const { return _dataSourceUUID; } - + int parseData(unsigned char* sourceBuffer, int numBytes); - + virtual void init(); void simulate(float deltaTime) { } void render(bool texture); @@ -85,19 +86,19 @@ public: virtual void hideOutOfView(bool forceFullFrustum = false); bool hasViewChanged(); bool isViewChanging(); - + bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, VoxelDetail& detail, float& distance, BoxFace& face); - + bool findSpherePenetration(const glm::vec3& center, float radius, glm::vec3& penetration); bool findCapsulePenetration(const glm::vec3& start, const glm::vec3& end, float radius, glm::vec3& penetration); void deleteVoxelAt(float x, float y, float z, float s); VoxelTreeElement* getVoxelAt(float x, float y, float z, float s) const; - void createVoxel(float x, float y, float z, float s, + void createVoxel(float x, float y, float z, float s, unsigned char red, unsigned char green, unsigned char blue, bool destructive = false); void createLine(glm::vec3 point1, glm::vec3 point2, float unitSize, rgbColor color, bool destructive = false); - void createSphere(float r,float xc, float yc, float zc, float s, bool solid, + void createSphere(float r,float xc, float yc, float zc, float s, bool solid, creationMode mode, bool destructive = false, bool debug = false); void copySubTreeIntoNewTree(VoxelTreeElement* startNode, VoxelSystem* destinationTree, bool rebaseToRoot); @@ -114,18 +115,18 @@ public: virtual void nodeAdded(Node* node); virtual void nodeKilled(Node* node); virtual void domainChanged(QString domain); - + bool treeIsBusy() const { return _treeIsBusy; } - + VoxelTreeElement* getVoxelEnclosing(const glm::vec3& point); - + signals: void importSize(float x, float y, float z); void importProgress(int progress); public slots: void collectStatsForTreesAndVBOs(); - + // Methods that recurse tree void showAllLocalVoxels(); void randomizeVoxelColors(); @@ -141,24 +142,27 @@ public slots: void clearAllNodesBufferIndex(); void cancelImport(); - + void setDisableFastVoxelPipeline(bool disableFastVoxelPipeline); void setUseVoxelShader(bool useVoxelShader); void setVoxelsAsPoints(bool voxelsAsPoints); - + + void localVoxelCacheLoaded(); + void beginLoadingLocalVoxelCache(); + protected: - float _treeScale; - int _maxVoxels; + float _treeScale; + int _maxVoxels; VoxelTree* _tree; void setupNewVoxelsForDrawing(); static const bool DONT_BAIL_EARLY; // by default we will bail early, if you want to force not bailing, then use this void setupNewVoxelsForDrawingSingleNode(bool allowBailEarly = true); void checkForCulling(); - + glm::vec3 computeVoxelVertex(const glm::vec3& startVertex, float voxelScale, int index) const; - + virtual void updateArraysDetails(glBufferIndex nodeIndex, const glm::vec3& startVertex, float voxelScale, const nodeColor& color); virtual void copyWrittenDataSegmentToReadArrays(glBufferIndex segmentStart, glBufferIndex segmentEnd); @@ -170,7 +174,7 @@ private: // disallow copying of VoxelSystem objects VoxelSystem(const VoxelSystem&); VoxelSystem& operator= (const VoxelSystem&); - + bool _initialized; int _callsToTreesToArrays; OctreeElementBag _removedVoxels; @@ -223,10 +227,10 @@ private: unsigned long _voxelsInReadArrays; unsigned long _voxelsInWriteArrays; unsigned long _abandonedVBOSlots; - + bool _writeRenderFullVBO; bool _readRenderFullVBO; - + int _setupNewVoxelsForDrawingLastElapsed; uint64_t _setupNewVoxelsForDrawingLastFinished; uint64_t _lastViewCulling; @@ -234,7 +238,7 @@ private: uint64_t _lastAudit; int _lastViewCullingElapsed; bool _hasRecentlyChanged; - + void initVoxelMemory(); void cleanupVoxelMemory(); @@ -246,7 +250,7 @@ private: GLuint _vboVoxelsIndicesID; /// when using voxel shader, we'll use this VBO for our indexes VoxelShaderVBOData* _writeVoxelShaderData; VoxelShaderVBOData* _readVoxelShaderData; - + GLuint _vboVerticesID; GLuint _vboColorsID; @@ -269,11 +273,11 @@ private: void setupFaceIndices(GLuint& faceVBOID, GLubyte faceIdentityIndices[]); - int newTreeToArrays(VoxelTreeElement *currentNode); + int newTreeToArrays(VoxelTreeElement* currentNode); void cleanupRemovedVoxels(); void copyWrittenDataToReadArrays(bool fullVBOs); - + void updateFullVBOs(); // all voxels in the VBO void updatePartialVBOs(); // multiple segments, only dirty voxels @@ -281,7 +285,7 @@ private: static ProgramObject _perlinModulateProgram; static ProgramObject _shadowMapProgram; - + int _hookID; std::vector _freeIndexes; pthread_mutex_t _freeIndexLock; @@ -289,22 +293,22 @@ private: void freeBufferIndex(glBufferIndex index); void clearFreeBufferIndexes(); glBufferIndex getNextBufferIndex(); - + bool _falseColorizeBySource; QUuid _dataSourceUUID; - + int _voxelServerCount; unsigned long _memoryUsageRAM; unsigned long _memoryUsageVBO; unsigned long _initialMemoryUsageGPU; bool _hasMemoryUsageGPU; - + bool _inSetupNewVoxelsForDrawing; bool _useFastVoxelPipeline; - + bool _inhideOutOfView; bool _treeIsBusy; // is the tree mutex locked? if so, it's busy, and if you can avoid it, don't access the tree - + void lockTree(); void unlockTree(); }; diff --git a/libraries/octree-server/src/OctreePersistThread.cpp b/libraries/octree/src/OctreePersistThread.cpp similarity index 94% rename from libraries/octree-server/src/OctreePersistThread.cpp rename to libraries/octree/src/OctreePersistThread.cpp index 07466f9c43..08a7cffbb8 100644 --- a/libraries/octree-server/src/OctreePersistThread.cpp +++ b/libraries/octree/src/OctreePersistThread.cpp @@ -13,7 +13,6 @@ #include #include "OctreePersistThread.h" -#include "OctreeServer.h" OctreePersistThread::OctreePersistThread(Octree* tree, const char* filename, int persistInterval) : _tree(tree), @@ -41,41 +40,45 @@ bool OctreePersistThread::process() { _loadCompleted = time(0); uint64_t loadDone = usecTimestampNow(); _loadTimeUSecs = loadDone - loadStarted; - + _tree->clearDirtyBit(); // the tree is clean since we just loaded it qDebug("DONE loading Octrees from file... fileRead=%s\n", debug::valueOf(persistantFileRead)); - + unsigned long nodeCount = OctreeElement::getNodeCount(); unsigned long internalNodeCount = OctreeElement::getInternalNodeCount(); unsigned long leafNodeCount = OctreeElement::getLeafNodeCount(); qDebug("Nodes after loading scene %lu nodes %lu internal %lu leaves\n", nodeCount, internalNodeCount, leafNodeCount); double usecPerGet = (double)OctreeElement::getGetChildAtIndexTime() / (double)OctreeElement::getGetChildAtIndexCalls(); - qDebug() << "getChildAtIndexCalls=" << OctreeElement::getGetChildAtIndexCalls() + qDebug() << "getChildAtIndexCalls=" << OctreeElement::getGetChildAtIndexCalls() << " getChildAtIndexTime=" << OctreeElement::getGetChildAtIndexTime() << " perGet=" << usecPerGet << " \n"; - + double usecPerSet = (double)OctreeElement::getSetChildAtIndexTime() / (double)OctreeElement::getSetChildAtIndexCalls(); - qDebug() << "setChildAtIndexCalls=" << OctreeElement::getSetChildAtIndexCalls() + qDebug() << "setChildAtIndexCalls=" << OctreeElement::getSetChildAtIndexCalls() << " setChildAtIndexTime=" << OctreeElement::getSetChildAtIndexTime() << " perset=" << usecPerSet << " \n"; _initialLoadComplete = true; _lastCheck = usecTimestampNow(); // we just loaded, no need to save again + +qDebug() << "about to emit loadCompleted();\n"; + emit loadCompleted(); +qDebug() << "after emit loadCompleted();\n"; } - + if (isStillRunning()) { uint64_t MSECS_TO_USECS = 1000; uint64_t USECS_TO_SLEEP = 10 * MSECS_TO_USECS; // every 10ms usleep(USECS_TO_SLEEP); - + // do our updates then check to save... _tree->lockForWrite(); _tree->update(); _tree->unlock(); - + uint64_t now = usecTimestampNow(); uint64_t sinceLastSave = now - _lastCheck; uint64_t intervalToCheck = _persistInterval * MSECS_TO_USECS; - + if (sinceLastSave > intervalToCheck) { // check the dirty bit and persist here... _lastCheck = usecTimestampNow(); @@ -86,6 +89,6 @@ bool OctreePersistThread::process() { qDebug("DONE saving Octrees to file...\n"); } } - } + } return isStillRunning(); // keep running till they terminate us } diff --git a/libraries/octree-server/src/OctreePersistThread.h b/libraries/octree/src/OctreePersistThread.h similarity index 89% rename from libraries/octree-server/src/OctreePersistThread.h rename to libraries/octree/src/OctreePersistThread.h index e5f261960b..5032bcbf02 100644 --- a/libraries/octree-server/src/OctreePersistThread.h +++ b/libraries/octree/src/OctreePersistThread.h @@ -12,20 +12,24 @@ #define __Octree_server__OctreePersistThread__ #include -#include +#include "Octree.h" -/// Generalized threaded processor for handling received inbound packets. -class OctreePersistThread : public virtual GenericThread { +/// Generalized threaded processor for handling received inbound packets. +class OctreePersistThread : public GenericThread { + Q_OBJECT public: static const int DEFAULT_PERSIST_INTERVAL = 1000 * 30; // every 30 seconds OctreePersistThread(Octree* tree, const char* filename, int persistInterval = DEFAULT_PERSIST_INTERVAL); - + bool isInitialLoadComplete() const { return _initialLoadComplete; } time_t* getLoadCompleted() { return &_loadCompleted; } uint64_t getLoadElapsedTime() const { return _loadTimeUSecs; } +signals: + void loadCompleted(); + protected: /// Implements generic processing behavior for this thread. virtual bool process(); diff --git a/libraries/shared/src/GenericThread.h b/libraries/shared/src/GenericThread.h index 2de9112204..6dea7d8cc8 100644 --- a/libraries/shared/src/GenericThread.h +++ b/libraries/shared/src/GenericThread.h @@ -11,22 +11,25 @@ #ifndef __shared__GenericThread__ #define __shared__GenericThread__ +#include + #include /// A basic generic "thread" class. Handles a single thread of control within the application. Can operate in non-threaded /// mode but caller must regularly call threadRoutine() method. -class GenericThread { +class GenericThread : public QObject { + Q_OBJECT public: GenericThread(); virtual ~GenericThread(); - /// Call to start the thread. + /// Call to start the thread. /// \param bool isThreaded true by default. false for non-threaded mode and caller must call threadRoutine() regularly. void initialize(bool isThreaded = true); /// Call to stop the thread void terminate(); - + /// If you're running in non-threaded mode, you must call this regularly void* threadRoutine(); @@ -42,7 +45,7 @@ protected: /// Unlocks all the resources of the thread. void unlock() { pthread_mutex_unlock(&_mutex); } - + bool isStillRunning() const { return !_stopThread; } private: diff --git a/libraries/voxels/src/VoxelTreeElement.cpp b/libraries/voxels/src/VoxelTreeElement.cpp index 4641cabdf7..e3d0bf3f18 100644 --- a/libraries/voxels/src/VoxelTreeElement.cpp +++ b/libraries/voxels/src/VoxelTreeElement.cpp @@ -14,7 +14,7 @@ #include "VoxelTreeElement.h" #include "VoxelTree.h" -VoxelTreeElement::VoxelTreeElement(unsigned char* octalCode) : OctreeElement() { +VoxelTreeElement::VoxelTreeElement(unsigned char* octalCode) : OctreeElement() { init(octalCode); }; @@ -23,7 +23,7 @@ VoxelTreeElement::~VoxelTreeElement() { } // This will be called primarily on addChildAt(), which means we're adding a child of our -// own type to our own tree. This means we should initialize that child with any tree and type +// own type to our own tree. This means we should initialize that child with any tree and type // specific settings that our children must have. One example is out VoxelSystem, which // we know must match ours. OctreeElement* VoxelTreeElement::createNewElement(unsigned char* octalCode) const { @@ -63,10 +63,10 @@ bool VoxelTreeElement::appendElementData(OctreePacketData* packetData) const { } -int VoxelTreeElement::readElementDataFromBuffer(const unsigned char* data, int bytesLeftToRead, - ReadBitstreamToTreeParams& args) { - const int BYTES_PER_COLOR = 3; - +int VoxelTreeElement::readElementDataFromBuffer(const unsigned char* data, int bytesLeftToRead, + ReadBitstreamToTreeParams& args) { + const int BYTES_PER_COLOR = 3; + // pull the color for this child nodeColor newColor = { 128, 128, 128, 1}; if (args.includeColor) { @@ -82,11 +82,11 @@ uint8_t VoxelTreeElement::_nextIndex = INDEX_FOR_NULL + 1; // start at 1, 0 is r std::map VoxelTreeElement::_mapVoxelSystemPointersToIndex; std::map VoxelTreeElement::_mapIndexToVoxelSystemPointers; -VoxelSystem* VoxelTreeElement::getVoxelSystem() const { +VoxelSystem* VoxelTreeElement::getVoxelSystem() const { if (_voxelSystemIndex > INDEX_FOR_NULL) { if (_mapIndexToVoxelSystemPointers.end() != _mapIndexToVoxelSystemPointers.find(_voxelSystemIndex)) { - VoxelSystem* voxelSystem = _mapIndexToVoxelSystemPointers[_voxelSystemIndex]; + VoxelSystem* voxelSystem = _mapIndexToVoxelSystemPointers[_voxelSystemIndex]; return voxelSystem; } } @@ -129,7 +129,7 @@ void VoxelTreeElement::setFalseColored(bool isFalseColored) { if (_falseColored && !isFalseColored) { memcpy(&_currentColor,&_trueColor,sizeof(nodeColor)); } - _falseColored = isFalseColored; + _falseColored = isFalseColored; _isDirty = true; _density = 1.0f; // If color set, assume leaf, re-averaging will update density if needed. markWithChangedTime(); @@ -167,7 +167,7 @@ void VoxelTreeElement::calculateAverageFromChildren() { density += childAt->getDensity(); } } - density /= (float) NUMBER_OF_CHILDREN; + density /= (float) NUMBER_OF_CHILDREN; // // The VISIBLE_ABOVE_DENSITY sets the density of matter above which an averaged color voxel will // be set. It is an important physical constant in our universe. A number below 0.5 will cause @@ -175,9 +175,9 @@ void VoxelTreeElement::calculateAverageFromChildren() { // less data, which is (probably) going to be preferable because it gives a sense that there is // something out there to go investigate. A number above 0.5 would cause the world to become // more 'empty' at a distance. Exactly 0.5 would match the physical world, at least for materials - // that are not shiny and have equivalent ambient reflectance. + // that are not shiny and have equivalent ambient reflectance. // - const float VISIBLE_ABOVE_DENSITY = 0.10f; + const float VISIBLE_ABOVE_DENSITY = 0.10f; nodeColor newColor = { 0, 0, 0, 0}; if (density > VISIBLE_ABOVE_DENSITY) { // The density of material in the space of the voxel sets whether it is actually colored @@ -188,14 +188,14 @@ void VoxelTreeElement::calculateAverageFromChildren() { // set the alpha to 1 to indicate that this isn't transparent newColor[3] = 1; } - // Set the color from the average of the child colors, and update the density + // Set the color from the average of the child colors, and update the density setColor(newColor); setDensity(density); } // will detect if children are leaves AND the same color // and in that case will delete the children and make this node -// a leaf, returns TRUE if all the leaves are collapsed into a +// a leaf, returns TRUE if all the leaves are collapsed into a // single node bool VoxelTreeElement::collapseChildren() { // scan children, verify that they are ALL present and accounted for @@ -213,15 +213,15 @@ bool VoxelTreeElement::collapseChildren() { red = childAt->getColor()[0]; green = childAt->getColor()[1]; blue = childAt->getColor()[2]; - } else if (red != childAt->getColor()[0] || + } else if (red != childAt->getColor()[0] || green != childAt->getColor()[1] || blue != childAt->getColor()[2]) { allChildrenMatch=false; break; } } } - - + + if (allChildrenMatch) { //qDebug("allChildrenMatch: pruning tree\n"); for (int i = 0; i < NUMBER_OF_CHILDREN; i++) { @@ -230,9 +230,9 @@ bool VoxelTreeElement::collapseChildren() { setChildAtIndex(i, NULL); // set it to NULL } nodeColor collapsedColor; - collapsedColor[0]=red; - collapsedColor[1]=green; - collapsedColor[2]=blue; + collapsedColor[0]=red; + collapsedColor[1]=green; + collapsedColor[2]=blue; collapsedColor[3]=1; // color is set setColor(collapsedColor); } @@ -240,7 +240,7 @@ bool VoxelTreeElement::collapseChildren() { } -bool VoxelTreeElement::findSpherePenetration(const glm::vec3& center, float radius, +bool VoxelTreeElement::findSpherePenetration(const glm::vec3& center, float radius, glm::vec3& penetration, void** penetratedObject) const { if (_box.findSpherePenetration(center, radius, penetration)) { @@ -254,7 +254,7 @@ bool VoxelTreeElement::findSpherePenetration(const glm::vec3& center, float radi voxelDetails->red = getTrueColor()[RED_INDEX]; voxelDetails->green = getTrueColor()[GREEN_INDEX]; voxelDetails->blue = getTrueColor()[BLUE_INDEX]; - + *penetratedObject = (void*)voxelDetails; } return true; From 6e95c52444bb8a195df3a7303a89ffec7b454204 Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Thu, 9 Jan 2014 14:51:04 -0800 Subject: [PATCH 25/39] make local voxel cache properly switch to different cache file when switching domains --- interface/src/Application.cpp | 56 ++++++++++++++++---- interface/src/Application.h | 3 ++ interface/src/VoxelSystem.cpp | 13 +++-- interface/src/VoxelSystem.h | 3 +- libraries/octree/src/OctreePersistThread.cpp | 12 ++--- libraries/octree/src/OctreePersistThread.h | 5 +- 6 files changed, 65 insertions(+), 27 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 572f42bdee..19510693d9 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -86,7 +86,7 @@ const float MIRROR_FULLSCREEN_DISTANCE = 0.35f; const float MIRROR_REARVIEW_DISTANCE = 0.65f; const float MIRROR_REARVIEW_BODY_DISTANCE = 2.3f; -const char* LOCAL_VOXEL_CACHE = "/Users/brad/local_voxel_cache.svo"; +//const char* LOCAL_VOXEL_CACHE = "/Users/brad/local_voxel_cache.svo"; void messageHandler(QtMsgType type, const QMessageLogContext& context, const QString &message) { @@ -181,7 +181,6 @@ Application::Application(int& argc, char** argv, timeval &startup_time) : nodeList->addHook(&_voxels); nodeList->addHook(this); nodeList->addDomainListener(this); - nodeList->addDomainListener(&_voxels); // network receive thread and voxel parsing thread are both controlled by the --nonblocking command line _enableProcessVoxelsThread = _enableNetworkThread = !cmdOptionExists(argc, constArgv, "--nonblocking"); @@ -254,13 +253,6 @@ Application::Application(int& argc, char** argv, timeval &startup_time) : // Set the sixense filtering _sixenseManager.setFilter(Menu::getInstance()->isOptionChecked(MenuOption::FilterSixense)); - _persistThread = new OctreePersistThread(_voxels.getTree(), LOCAL_VOXEL_CACHE); - - if (_persistThread) { - _voxels.beginLoadingLocalVoxelCache(); // while local voxels are importing, don't do individual node VBO updates - connect(_persistThread, SIGNAL(loadCompleted()), &_voxels, SLOT(localVoxelCacheLoaded())); - _persistThread->initialize(true); - } } Application::~Application() { @@ -1950,6 +1942,9 @@ void Application::init() { connect(_rearMirrorTools, SIGNAL(restoreView()), SLOT(restoreMirrorView())); connect(_rearMirrorTools, SIGNAL(shrinkView()), SLOT(shrinkMirrorView())); connect(_rearMirrorTools, SIGNAL(resetView()), SLOT(resetSensors())); + + + updateLocalOctreeCache(true); } void Application::closeMirrorView() { @@ -4215,6 +4210,10 @@ void Application::domainChanged(QString domain) { _voxelServerJurisdictions.clear(); _octreeServerSceneStats.clear(); _particleServerJurisdictions.clear(); + + // reset our persist thread + qDebug() << "domainChanged()... domain=" << domain << " swapping persist cache\n"; + updateLocalOctreeCache(); } void Application::nodeAdded(Node* node) { @@ -4554,4 +4553,43 @@ void Application::toggleLogDialog() { void Application::initAvatarAndViewFrustum() { updateAvatar(0.f); +} + +QString Application::getLocalVoxelCacheFileName() { + QString fileName = QStandardPaths::writableLocation(QStandardPaths::DataLocation); + QDir logDir(fileName); + if (!logDir.exists(fileName)) { + logDir.mkdir(fileName); + } + + fileName.append(QString("/hifi.voxelscache.")); + fileName.append(_profile.getLastDomain()); + fileName.append(QString(".svo")); + + return fileName; +} + + +void Application::updateLocalOctreeCache(bool firstTime) { + // only do this if we've already got a persistThread or we're told this is the first time + if (firstTime || _persistThread) { + + if (_persistThread) { + _persistThread->terminate(); + _persistThread = NULL; + } + + QString localVoxelCacheFileName = getLocalVoxelCacheFileName(); + const int LOCAL_CACHE_PERSIST_INTERVAL = 1000 * 10; // every 10 seconds + _persistThread = new OctreePersistThread(_voxels.getTree(), + localVoxelCacheFileName.toLocal8Bit().constData(),LOCAL_CACHE_PERSIST_INTERVAL); + + qDebug() << "updateLocalOctreeCache()... localVoxelCacheFileName=" << localVoxelCacheFileName << "\n"; + + if (_persistThread) { + _voxels.beginLoadingLocalVoxelCache(); // while local voxels are importing, don't do individual node VBO updates + connect(_persistThread, SIGNAL(loadCompleted()), &_voxels, SLOT(localVoxelCacheLoaded())); + _persistThread->initialize(true); + } + } } \ No newline at end of file diff --git a/interface/src/Application.h b/interface/src/Application.h index f36ad28a5d..19f9a74a3b 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -515,6 +515,9 @@ private: FileLogger* _logger; OctreePersistThread* _persistThread; + + QString getLocalVoxelCacheFileName(); + void updateLocalOctreeCache(bool firstTime = false); }; #endif /* defined(__interface__Application__) */ diff --git a/interface/src/VoxelSystem.cpp b/interface/src/VoxelSystem.cpp index e0b2decfaf..ede9a8e15d 100644 --- a/interface/src/VoxelSystem.cpp +++ b/interface/src/VoxelSystem.cpp @@ -117,7 +117,6 @@ void VoxelSystem::elementDeleted(OctreeElement* element) { VoxelTreeElement* voxel = (VoxelTreeElement*)element; if (voxel->getVoxelSystem() == this) { if (_voxelsInWriteArrays != 0) { -qDebug() << "elementDeleted()... about to call forceRemoveNodeFromArrays()\n"; forceRemoveNodeFromArrays(voxel); } else { if (Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings)) { @@ -706,6 +705,7 @@ void VoxelSystem::setupNewVoxelsForDrawing() { bool extraDebugging = Application::getInstance()->getLogger()->extraDebugging(); if (extraDebugging) { qDebug("setupNewVoxelsForDrawing()... _voxelsUpdated=%lu...\n",_voxelsUpdated); + _viewFrustum->printDebugDetails(); } } @@ -2712,11 +2712,6 @@ void VoxelSystem::nodeKilled(Node* node) { } } -void VoxelSystem::domainChanged(QString domain) { - killLocalVoxels(); -} - - unsigned long VoxelSystem::getFreeMemoryGPU() { // We can't ask all GPUs how much memory they have in use, but we can ask them about how much is free. // So, we can record the free memory before we create our VBOs and the free memory after, and get a basic @@ -2787,7 +2782,9 @@ void VoxelSystem::localVoxelCacheLoaded() { // Make sure that the application has properly set up the view frustum for our loaded state Application::getInstance()->initAvatarAndViewFrustum(); - _tree->setDirtyBit(); + _tree->setDirtyBit(); // make sure the tree thinks it's dirty + _setupNewVoxelsForDrawingLastFinished = 0; // don't allow the setupNewVoxelsForDrawing() shortcuts + _writeRenderFullVBO = true; // this will disable individual node updates, was reset by killLocalVoxels() setupNewVoxelsForDrawing(); _inhideOutOfView = false; // reenable hideOutOfView behavior } @@ -2796,6 +2793,8 @@ void VoxelSystem::beginLoadingLocalVoxelCache() { qDebug() << "beginLoadingLocalVoxelCache()\n"; _writeRenderFullVBO = true; // this will disable individual node updates _inhideOutOfView = true; // this will disable hidOutOfView which we want to do until local cache is loaded + killLocalVoxels(); + qDebug() << "DONE beginLoadingLocalVoxelCache()\n"; } diff --git a/interface/src/VoxelSystem.h b/interface/src/VoxelSystem.h index 8173652e55..e9f0cc47ab 100644 --- a/interface/src/VoxelSystem.h +++ b/interface/src/VoxelSystem.h @@ -38,7 +38,7 @@ struct VoxelShaderVBOData class VoxelSystem : public NodeData, public OctreeElementDeleteHook, public OctreeElementUpdateHook, - public NodeListHook, public DomainChangeListener { + public NodeListHook { Q_OBJECT friend class VoxelHideShowThread; @@ -114,7 +114,6 @@ public: virtual void elementUpdated(OctreeElement* element); virtual void nodeAdded(Node* node); virtual void nodeKilled(Node* node); - virtual void domainChanged(QString domain); bool treeIsBusy() const { return _treeIsBusy; } diff --git a/libraries/octree/src/OctreePersistThread.cpp b/libraries/octree/src/OctreePersistThread.cpp index 08a7cffbb8..b986f457bc 100644 --- a/libraries/octree/src/OctreePersistThread.cpp +++ b/libraries/octree/src/OctreePersistThread.cpp @@ -14,7 +14,7 @@ #include "OctreePersistThread.h" -OctreePersistThread::OctreePersistThread(Octree* tree, const char* filename, int persistInterval) : +OctreePersistThread::OctreePersistThread(Octree* tree, const QString& filename, int persistInterval) : _tree(tree), _filename(filename), _persistInterval(persistInterval), @@ -26,14 +26,14 @@ bool OctreePersistThread::process() { if (!_initialLoadComplete) { uint64_t loadStarted = usecTimestampNow(); - qDebug("loading Octrees from file: %s...\n", _filename); + qDebug() << "loading Octrees from file: " << _filename << "...\n"; bool persistantFileRead; _tree->lockForWrite(); { PerformanceWarning warn(true, "Loading Octree File", true); - persistantFileRead = _tree->readFromSVOFile(_filename); + persistantFileRead = _tree->readFromSVOFile(_filename.toLocal8Bit().constData()); } _tree->unlock(); @@ -60,9 +60,7 @@ bool OctreePersistThread::process() { _initialLoadComplete = true; _lastCheck = usecTimestampNow(); // we just loaded, no need to save again -qDebug() << "about to emit loadCompleted();\n"; emit loadCompleted(); -qDebug() << "after emit loadCompleted();\n"; } if (isStillRunning()) { @@ -83,8 +81,8 @@ qDebug() << "after emit loadCompleted();\n"; // check the dirty bit and persist here... _lastCheck = usecTimestampNow(); if (_tree->isDirty()) { - qDebug("saving Octrees to file %s...\n",_filename); - _tree->writeToSVOFile(_filename); + qDebug() << "saving Octrees to file " << _filename << "...\n"; + _tree->writeToSVOFile(_filename.toLocal8Bit().constData()); _tree->clearDirtyBit(); // tree is clean after saving qDebug("DONE saving Octrees to file...\n"); } diff --git a/libraries/octree/src/OctreePersistThread.h b/libraries/octree/src/OctreePersistThread.h index 5032bcbf02..297387c2fa 100644 --- a/libraries/octree/src/OctreePersistThread.h +++ b/libraries/octree/src/OctreePersistThread.h @@ -11,6 +11,7 @@ #ifndef __Octree_server__OctreePersistThread__ #define __Octree_server__OctreePersistThread__ +#include #include #include "Octree.h" @@ -20,7 +21,7 @@ class OctreePersistThread : public GenericThread { public: static const int DEFAULT_PERSIST_INTERVAL = 1000 * 30; // every 30 seconds - OctreePersistThread(Octree* tree, const char* filename, int persistInterval = DEFAULT_PERSIST_INTERVAL); + OctreePersistThread(Octree* tree, const QString& filename, int persistInterval = DEFAULT_PERSIST_INTERVAL); bool isInitialLoadComplete() const { return _initialLoadComplete; } @@ -35,7 +36,7 @@ protected: virtual bool process(); private: Octree* _tree; - const char* _filename; + QString _filename; int _persistInterval; bool _initialLoadComplete; From 5b14a4eed3cfcc3d1527e63aa4208aa97ba9c0b4 Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Thu, 9 Jan 2014 14:52:40 -0800 Subject: [PATCH 26/39] removed dead code --- interface/src/Application.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 19510693d9..cdcecd33a5 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -86,9 +86,6 @@ const float MIRROR_FULLSCREEN_DISTANCE = 0.35f; const float MIRROR_REARVIEW_DISTANCE = 0.65f; const float MIRROR_REARVIEW_BODY_DISTANCE = 2.3f; -//const char* LOCAL_VOXEL_CACHE = "/Users/brad/local_voxel_cache.svo"; - - void messageHandler(QtMsgType type, const QMessageLogContext& context, const QString &message) { fprintf(stdout, "%s", message.toLocal8Bit().constData()); Application::getInstance()->getLogger()->addMessage(message.toLocal8Bit().constData()); From 5787d3d9e9dd8abf35e49936b43d88a3955300fb Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Thu, 9 Jan 2014 19:00:57 -0800 Subject: [PATCH 27/39] more cast coding standard fixes --- libraries/octree/src/Octree.cpp | 174 ++++++++++++++++---------------- 1 file changed, 87 insertions(+), 87 deletions(-) diff --git a/libraries/octree/src/Octree.cpp b/libraries/octree/src/Octree.cpp index 3f50776443..2113fea5b1 100644 --- a/libraries/octree/src/Octree.cpp +++ b/libraries/octree/src/Octree.cpp @@ -43,7 +43,7 @@ Octree::Octree(bool shouldReaverage) : _shouldReaverage(shouldReaverage), _stopImport(false) { _rootNode = NULL; - + pthread_mutex_init(&_encodeSetLock, NULL); pthread_mutex_init(&_deleteSetLock, NULL); pthread_mutex_init(&_deletePendingSetLock, NULL); @@ -66,13 +66,13 @@ void Octree::recurseTreeWithOperation(RecurseOctreeOperation operation, void* ex } // Recurses voxel node with an operation function -void Octree::recurseNodeWithOperation(OctreeElement* node, RecurseOctreeOperation operation, void* extraData, +void Octree::recurseNodeWithOperation(OctreeElement* node, RecurseOctreeOperation operation, void* extraData, int recursionCount) { if (recursionCount > DANGEROUSLY_DEEP_RECURSION) { qDebug() << "Octree::recurseNodeWithOperation() reached DANGEROUSLY_DEEP_RECURSION, bailing!\n"; return; } - + if (operation(node, extraData)) { for (int i = 0; i < NUMBER_OF_CHILDREN; i++) { OctreeElement* child = node->getChildAtIndex(i); @@ -92,7 +92,7 @@ void Octree::recurseTreeWithOperationDistanceSorted(RecurseOctreeOperation opera } // Recurses voxel node with an operation function -void Octree::recurseNodeWithOperationDistanceSorted(OctreeElement* node, RecurseOctreeOperation operation, +void Octree::recurseNodeWithOperationDistanceSorted(OctreeElement* node, RecurseOctreeOperation operation, const glm::vec3& point, void* extraData, int recursionCount) { if (recursionCount > DANGEROUSLY_DEEP_RECURSION) { @@ -138,7 +138,7 @@ OctreeElement* Octree::nodeForOctalCode(OctreeElement* ancestorNode, if (needleCode == NULL) { return _rootNode; } - + // find the appropriate branch index based on this ancestorNode if (*needleCode > 0) { int branchForNeedle = branchIndexWithDescendant(ancestorNode->getOctalCode(), needleCode); @@ -213,13 +213,13 @@ int Octree::readNodeData(OctreeElement* destinationNode, const unsigned char* no nodeWasDirty = childNodeAt->isDirty(); bytesRead += childNodeAt->readElementDataFromBuffer(nodeData + bytesRead, bytesLeftToRead, args); childNodeAt->setSourceUUID(args.sourceUUID); - + // if we had a local version of the node already, it's possible that we have it already but // with the same color data, so this won't count as a change. To address this we check the following if (!childNodeAt->isDirty() && childNodeAt->getShouldRender() && !childNodeAt->isRendered()) { childNodeAt->setDirtyBit(); // force dirty! } - + nodeIsDirty = childNodeAt->isDirty(); } if (nodeIsDirty) { @@ -299,8 +299,8 @@ void Octree::readBitstreamToTree(const unsigned char * bitstream, unsigned long int octalCodeBytes = bytesRequiredForCodeLength(*bitstreamAt); int theseBytesRead = 0; theseBytesRead += octalCodeBytes; - - theseBytesRead += readNodeData(bitstreamRootNode, bitstreamAt + octalCodeBytes, + + theseBytesRead += readNodeData(bitstreamRootNode, bitstreamAt + octalCodeBytes, bufferSizeBytes - (bytesRead + octalCodeBytes), args); // skip bitstream to new startPoint @@ -341,7 +341,7 @@ void Octree::deleteOctalCodeFromTree(const unsigned char* codeBuffer, bool colla args.pathChanged = false; OctreeElement* node = _rootNode; - + // We can't encode and delete nodes at the same time, so we guard against deleting any node that is actively // being encoded. And we stick that code on our pendingDelete list. if (isEncoding(codeBuffer)) { @@ -380,15 +380,15 @@ void Octree::deleteOctalCodeFromTreeRecursion(OctreeElement* node, void* extraDa OctreeElement* ancestorNode = node; while (true) { int index = branchIndexWithDescendant(ancestorNode->getOctalCode(), args->codeBuffer); - + // we end up with all the children, even the one we want to delete ancestorNode->splitChildren(); - + int lengthOfAncestorNode = numberOfThreeBitSectionsInCode(ancestorNode->getOctalCode()); // If we've reached the parent of the target, then stop breaking up children if (lengthOfAncestorNode == (args->lengthOfCode - 1)) { - + // since we created all the children when we split, we need to delete this target one ancestorNode->deleteChildAtIndex(index); break; @@ -454,21 +454,21 @@ void Octree::processRemoveOctreeElementsBitstream(const unsigned char* bitstream int numBytesPacketHeader = numBytesForPacketHeader(bitstream); unsigned short int sequence = (*((unsigned short int*)(bitstream + numBytesPacketHeader))); uint64_t sentAt = (*((uint64_t*)(bitstream + numBytesPacketHeader + sizeof(sequence)))); - + int atByte = numBytesPacketHeader + sizeof(sequence) + sizeof(sentAt); - + unsigned char* voxelCode = (unsigned char*)&bitstream[atByte]; while (atByte < bufferSizeBytes) { int maxSize = bufferSizeBytes - atByte; int codeLength = numberOfThreeBitSectionsInCode(voxelCode, maxSize); - + if (codeLength == OVERFLOWED_OCTCODE_BUFFER) { printf("WARNING! Got remove voxel bitstream that would overflow buffer in numberOfThreeBitSectionsInCode(), "); printf("bailing processing of packet!\n"); break; } int voxelDataSize = bytesRequiredForCodeLength(codeLength) + SIZE_OF_COLOR_DATA; - + if (atByte + voxelDataSize <= bufferSizeBytes) { deleteOctalCodeFromTree(voxelCode, COLLAPSE_EMPTY_TREE); voxelCode += voxelDataSize; @@ -572,7 +572,7 @@ bool findRayIntersectionOp(OctreeElement* node, void* extraData) { bool Octree::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, OctreeElement*& node, float& distance, BoxFace& face) { - RayArgs args = { origin / static_cast(TREE_SCALE), direction, node, distance, face }; + RayArgs args = { origin / (float)(TREE_SCALE), direction, node, distance, face }; recurseTreeWithOperation(findRayIntersectionOp, &args); return args.found; } @@ -600,21 +600,21 @@ bool findSpherePenetrationOp(OctreeElement* element, void* extraData) { if (element->hasContent()) { glm::vec3 elementPenetration; if (element->findSpherePenetration(args->center, args->radius, elementPenetration, &args->penetratedObject)) { - args->penetration = addPenetrations(args->penetration, elementPenetration * static_cast(TREE_SCALE)); + args->penetration = addPenetrations(args->penetration, elementPenetration * (float)(TREE_SCALE)); args->found = true; } } return false; } -bool Octree::findSpherePenetration(const glm::vec3& center, float radius, glm::vec3& penetration, +bool Octree::findSpherePenetration(const glm::vec3& center, float radius, glm::vec3& penetration, void** penetratedObject) { - - SphereArgs args = { - center / static_cast(TREE_SCALE), - radius / static_cast(TREE_SCALE), - penetration, - false, + + SphereArgs args = { + center / (float)(TREE_SCALE), + radius / (float)(TREE_SCALE), + penetration, + false, NULL }; penetration = glm::vec3(0.0f, 0.0f, 0.0f); recurseTreeWithOperation(findSpherePenetrationOp, &args); @@ -647,7 +647,7 @@ bool findCapsulePenetrationOp(OctreeElement* node, void* extraData) { if (node->hasContent()) { glm::vec3 nodePenetration; if (box.findCapsulePenetration(args->start, args->end, args->radius, nodePenetration)) { - args->penetration = addPenetrations(args->penetration, nodePenetration * static_cast(TREE_SCALE)); + args->penetration = addPenetrations(args->penetration, nodePenetration * (float)(TREE_SCALE)); args->found = true; } } @@ -655,17 +655,17 @@ bool findCapsulePenetrationOp(OctreeElement* node, void* extraData) { } bool Octree::findCapsulePenetration(const glm::vec3& start, const glm::vec3& end, float radius, glm::vec3& penetration) { - CapsuleArgs args = { - start / static_cast(TREE_SCALE), - end / static_cast(TREE_SCALE), - radius / static_cast(TREE_SCALE), + CapsuleArgs args = { + start / (float)(TREE_SCALE), + end / (float)(TREE_SCALE), + radius / (float)(TREE_SCALE), penetration }; penetration = glm::vec3(0.0f, 0.0f, 0.0f); recurseTreeWithOperation(findCapsulePenetrationOp, &args); return args.found; } -int Octree::encodeTreeBitstream(OctreeElement* node, +int Octree::encodeTreeBitstream(OctreeElement* node, OctreePacketData* packetData, OctreeElementBag& bag, EncodeBitstreamParams& params) { @@ -680,7 +680,7 @@ int Octree::encodeTreeBitstream(OctreeElement* node, } startEncoding(node); - + // If we're at a node that is out of view, then we can return, because no nodes below us will be in view! if (params.viewFrustum && !node->isInView(*params.viewFrustum)) { doneEncoding(node); @@ -696,7 +696,7 @@ int Octree::encodeTreeBitstream(OctreeElement* node, roomForOctalCode = packetData->startSubTree(newCode); if (newCode) { - delete newCode; + delete newCode; } else { codeLength = 1; } @@ -712,23 +712,23 @@ int Octree::encodeTreeBitstream(OctreeElement* node, params.stopReason = EncodeBitstreamParams::DIDNT_FIT; return bytesWritten; } - + bytesWritten += codeLength; // keep track of byte count - + int currentEncodeLevel = 0; - - // record some stats, this is the one node that we won't record below in the recursion function, so we need to + + // record some stats, this is the one node that we won't record below in the recursion function, so we need to // track it here if (params.stats) { params.stats->traversed(node); } - + int childBytesWritten = encodeTreeBitstreamRecursion(node, packetData, bag, params, currentEncodeLevel); // if childBytesWritten == 1 then something went wrong... that's not possible assert(childBytesWritten != 1); - // if includeColor and childBytesWritten == 2, then it can only mean that the lower level trees don't exist or for some + // if includeColor and childBytesWritten == 2, then it can only mean that the lower level trees don't exist or for some // reason couldn't be written... so reset them here... This isn't true for the non-color included case if (params.includeColor && childBytesWritten == 2) { childBytesWritten = 0; @@ -743,19 +743,19 @@ int Octree::encodeTreeBitstream(OctreeElement* node, bytesWritten = 0; //params.stopReason = EncodeBitstreamParams::DIDNT_FIT; } - + if (bytesWritten == 0) { packetData->discardSubTree(); } else { packetData->endSubTree(); } - + doneEncoding(node); - + return bytesWritten; } -int Octree::encodeTreeBitstreamRecursion(OctreeElement* node, +int Octree::encodeTreeBitstreamRecursion(OctreeElement* node, OctreePacketData* packetData, OctreeElementBag& bag, EncodeBitstreamParams& params, int& currentEncodeLevel) const { // How many bytes have we written so far at this level; @@ -770,7 +770,7 @@ int Octree::encodeTreeBitstreamRecursion(OctreeElement* node, // Keep track of how deep we've encoded. currentEncodeLevel++; - + params.maxLevelReached = std::max(currentEncodeLevel,params.maxLevelReached); // If we've reached our max Search Level, then stop searching. @@ -788,11 +788,11 @@ int Octree::encodeTreeBitstreamRecursion(OctreeElement* node, return bytesAtThisLevel; } } - + // caller can pass NULL as viewFrustum if they want everything if (params.viewFrustum) { float distance = node->distanceToCamera(*params.viewFrustum); - float boundaryDistance = boundaryDistanceForRenderLevel(node->getLevel() + params.boundaryLevelAdjust, + float boundaryDistance = boundaryDistanceForRenderLevel(node->getLevel() + params.boundaryLevelAdjust, params.octreeElementSizeScale); // If we're too far away for our render level, then just return @@ -814,28 +814,28 @@ int Octree::encodeTreeBitstreamRecursion(OctreeElement* node, params.stopReason = EncodeBitstreamParams::OUT_OF_VIEW; return bytesAtThisLevel; } - + // Ok, we are in view, but if we're in delta mode, then we also want to make sure we weren't already in view // because we don't send nodes from the previously know in view frustum. bool wasInView = false; - + if (params.deltaViewFrustum && params.lastViewFrustum) { ViewFrustum::location location = node->inFrustum(*params.lastViewFrustum); - + // If we're a leaf, then either intersect or inside is considered "formerly in view" if (node->isLeaf()) { wasInView = location != ViewFrustum::OUTSIDE; } else { wasInView = location == ViewFrustum::INSIDE; } - + // If we were in view, double check that we didn't switch LOD visibility... namely, the was in view doesn't // tell us if it was so small we wouldn't have rendered it. Which may be the case. And we may have moved closer // to it, and so therefore it may now be visible from an LOD perspective, in which case we don't consider it // as "was in view"... if (wasInView) { float distance = node->distanceToCamera(*params.lastViewFrustum); - float boundaryDistance = boundaryDistanceForRenderLevel(node->getLevel() + params.boundaryLevelAdjust, + float boundaryDistance = boundaryDistanceForRenderLevel(node->getLevel() + params.boundaryLevelAdjust, params.octreeElementSizeScale); if (distance >= boundaryDistance) { // This would have been invisible... but now should be visible (we wouldn't be here otherwise)... @@ -855,9 +855,9 @@ int Octree::encodeTreeBitstreamRecursion(OctreeElement* node, return bytesAtThisLevel; } - // If we're not in delta sending mode, and we weren't asked to do a force send, and the voxel hasn't changed, + // If we're not in delta sending mode, and we weren't asked to do a force send, and the voxel hasn't changed, // then we can also bail early and save bits - if (!params.forceSendScene && !params.deltaViewFrustum && + if (!params.forceSendScene && !params.deltaViewFrustum && !node->hasChangedSince(params.lastViewFrustumSent - CHANGE_FUDGE)) { if (params.stats) { params.stats->skippedNoChange(node); @@ -896,9 +896,9 @@ int Octree::encodeTreeBitstreamRecursion(OctreeElement* node, bool keepDiggingDeeper = true; // Assuming we're in view we have a great work ethic, we're always ready for more! // At any given point in writing the bitstream, the largest minimum we might need to flesh out the current level - // is 1 byte for child colors + 3*NUMBER_OF_CHILDREN bytes for the actual colors + 1 byte for child trees. - // There could be sub trees below this point, which might take many more bytes, but that's ok, because we can - // always mark our subtrees as not existing and stop the packet at this point, then start up with a new packet + // is 1 byte for child colors + 3*NUMBER_OF_CHILDREN bytes for the actual colors + 1 byte for child trees. + // There could be sub trees below this point, which might take many more bytes, but that's ok, because we can + // always mark our subtrees as not existing and stop the packet at this point, then start up with a new packet // for the remaining sub trees. unsigned char childrenExistInTreeBits = 0; unsigned char childrenExistInPacketBits = 0; @@ -972,7 +972,7 @@ int Octree::encodeTreeBitstreamRecursion(OctreeElement* node, // Before we determine consider this further, let's see if it's in our LOD scope... float distance = distancesToChildren[i]; // params.viewFrustum ? childNode->distanceToCamera(*params.viewFrustum) : 0; float boundaryDistance = !params.viewFrustum ? 1 : - boundaryDistanceForRenderLevel(childNode->getLevel() + params.boundaryLevelAdjust, + boundaryDistanceForRenderLevel(childNode->getLevel() + params.boundaryLevelAdjust, params.octreeElementSizeScale); if (!(distance < boundaryDistance)) { @@ -1024,12 +1024,12 @@ int Octree::encodeTreeBitstreamRecursion(OctreeElement* node, } // wants occlusion culling & isLeaf() - bool shouldRender = !params.viewFrustum - ? true - : childNode->calculateShouldRender(params.viewFrustum, + bool shouldRender = !params.viewFrustum + ? true + : childNode->calculateShouldRender(params.viewFrustum, params.octreeElementSizeScale, params.boundaryLevelAdjust); - - // track some stats + + // track some stats if (params.stats) { // don't need to check childNode here, because we can't get here with no childNode if (!shouldRender && childNode->isLeaf()) { @@ -1040,29 +1040,29 @@ int Octree::encodeTreeBitstreamRecursion(OctreeElement* node, params.stats->skippedOccluded(childNode); } } - + // track children with actual color, only if the child wasn't previously in view! if (shouldRender && !childIsOccluded) { bool childWasInView = false; - + if (childNode && params.deltaViewFrustum && params.lastViewFrustum) { ViewFrustum::location location = childNode->inFrustum(*params.lastViewFrustum); - + // If we're a leaf, then either intersect or inside is considered "formerly in view" if (childNode->isLeaf()) { childWasInView = location != ViewFrustum::OUTSIDE; } else { childWasInView = location == ViewFrustum::INSIDE; } - } + } // If our child wasn't in view (or we're ignoring wasInView) then we add it to our sending items. // Or if we were previously in the view, but this node has changed since it was last sent, then we do // need to send it. - if (!childWasInView || - (params.deltaViewFrustum && + if (!childWasInView || + (params.deltaViewFrustum && childNode->hasChangedSince(params.lastViewFrustumSent - CHANGE_FUDGE))){ - + childrenColoredBits += (1 << (7 - originalIndex)); inViewWithColorCount++; } else { @@ -1080,10 +1080,10 @@ int Octree::encodeTreeBitstreamRecursion(OctreeElement* node, } } } - - bool continueThisLevel = true; + + bool continueThisLevel = true; continueThisLevel = packetData->appendBitMask(childrenColoredBits); - + if (continueThisLevel) { bytesAtThisLevel += sizeof(childrenColoredBits); // keep track of byte count if (params.stats) { @@ -1100,11 +1100,11 @@ int Octree::encodeTreeBitstreamRecursion(OctreeElement* node, int bytesBeforeChild = packetData->getUncompressedSize(); continueThisLevel = childNode->appendElementData(packetData); int bytesAfterChild = packetData->getUncompressedSize(); - + if (!continueThisLevel) { break; // no point in continuing } - + bytesAtThisLevel += (bytesAfterChild - bytesBeforeChild); // keep track of byte count for this child // don't need to check childNode here, because we can't get here with no childNode @@ -1138,7 +1138,7 @@ int Octree::encodeTreeBitstreamRecursion(OctreeElement* node, } } } - + // We only need to keep digging, if there is at least one child that is inView, and not a leaf. keepDiggingDeeper = (inViewNotLeafCount > 0); @@ -1181,7 +1181,7 @@ int Octree::encodeTreeBitstreamRecursion(OctreeElement* node, // XXXBHG - Note, this seems like the correct logic here, if we included the color in this packet, then // the LOD logic determined that the child nodes would not be visible... and if so, we shouldn't recurse // them further. But... for some time now the code has included and recursed into these child nodes, which - // would likely still send the child content, even though the client wouldn't render it. This change is + // would likely still send the child content, even though the client wouldn't render it. This change is // a major savings (~30%) and it seems to work correctly. But I want us to discuss as a group when we do // a voxel protocol review. // @@ -1232,7 +1232,7 @@ int Octree::encodeTreeBitstreamRecursion(OctreeElement* node, // repair the child exists mask continueThisLevel = packetData->updatePriorBitMask(childExistsPlaceHolder, childrenExistInPacketBits); - + // If this is the last of the child exists bits, then we're actually be rolling out the entire tree if (params.stats && childrenExistInPacketBits == 0) { params.stats->childBitsRemoved(params.includeExistsBits, params.includeColor); @@ -1241,7 +1241,7 @@ int Octree::encodeTreeBitstreamRecursion(OctreeElement* node, if (!continueThisLevel) { break; // can't continue... } - + // Note: no need to move the pointer, cause we already stored this } // end if (childTreeBytesOut == 0) } // end if (oneAtBit(childrenExistInPacketBits, originalIndex)) @@ -1282,14 +1282,14 @@ int Octree::encodeTreeBitstreamRecursion(OctreeElement* node, printf("\n"); **/ - // if we were unable to fit this level in our packet, then rewind and add it to the node bag for + // if we were unable to fit this level in our packet, then rewind and add it to the node bag for // sending later... if (continueThisLevel) { continueThisLevel = packetData->endLevel(thisLevelKey); } else { packetData->discardLevel(thisLevelKey); } - + if (!continueThisLevel) { bag.insert(node); @@ -1325,7 +1325,7 @@ bool Octree::readFromSVOFile(const char* fileName) { unsigned char* dataAt = entireFile; unsigned long dataLength = fileLength; - + // before reading the file, check to see if this version of the Octree supports file versions if (getWantSVOfileVersions()) { // if so, read the first byte of the file and see if it matches the expected version code @@ -1368,7 +1368,7 @@ void Octree::writeToSVOFile(const char* fileName, OctreeElement* node) { if(file.is_open()) { qDebug("saving to file %s...\n", fileName); - + // before reading the file, check to see if this version of the Octree supports file versions if (getWantSVOfileVersions()) { // if so, read the first byte of the file and see if it matches the expected version code @@ -1377,7 +1377,7 @@ void Octree::writeToSVOFile(const char* fileName, OctreeElement* node) { file.write(&expectedType, sizeof(expectedType)); file.write(&expectedVersion, sizeof(expectedType)); } - + OctreeElementBag nodeBag; // If we were given a specific node, start from there, otherwise start from root if (node) { @@ -1392,7 +1392,7 @@ void Octree::writeToSVOFile(const char* fileName, OctreeElement* node) { while (!nodeBag.isEmpty()) { OctreeElement* subTree = nodeBag.extract(); - + lockForRead(); // do tree locking down here so that we have shorter slices and less thread contention EncodeBitstreamParams params(INT_MAX, IGNORE_VIEW_FRUSTUM, WANT_COLOR, NO_EXISTS_BITS); bytesWritten = encodeTreeBitstream(subTree, &packetData, nodeBag, params); @@ -1480,7 +1480,7 @@ void Octree::copyFromTreeIntoSubTree(Octree* sourceTree, OctreeElement* destinat while (!nodeBag.isEmpty()) { OctreeElement* subTree = nodeBag.extract(); - + packetData.reset(); // reset between usage // ask our tree to write a bitsteam @@ -1513,7 +1513,7 @@ void Octree::doneEncoding(OctreeElement* node) { pthread_mutex_lock(&_encodeSetLock); _codesBeingEncoded.erase(node->getOctalCode()); pthread_mutex_unlock(&_encodeSetLock); - + // if we have any pending delete codes, then delete them now. emptyDeleteQueue(); } From e908121bcd6a7b1dd32a17e4804774a6c557bfa1 Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Thu, 9 Jan 2014 19:13:17 -0800 Subject: [PATCH 28/39] fix bug in Octree server that wasn't properly surpressing duplicate packets --- .../octree-server/src/OctreeQueryNode.cpp | 34 ++++++++++--------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/libraries/octree-server/src/OctreeQueryNode.cpp b/libraries/octree-server/src/OctreeQueryNode.cpp index 0e84fe2dba..eaf3b75042 100644 --- a/libraries/octree-server/src/OctreeQueryNode.cpp +++ b/libraries/octree-server/src/OctreeQueryNode.cpp @@ -49,20 +49,22 @@ bool OctreeQueryNode::packetIsDuplicate() const { // 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... if (_lastOctreePacketLength == getPacketLength()) { - return memcmp(_lastOctreePacket + OCTREE_PACKET_HEADER_SIZE, - _octreePacket+OCTREE_PACKET_HEADER_SIZE , getPacketLength() - OCTREE_PACKET_HEADER_SIZE == 0); + if (memcmp(_lastOctreePacket + OCTREE_PACKET_HEADER_SIZE, + _octreePacket + OCTREE_PACKET_HEADER_SIZE , getPacketLength() - OCTREE_PACKET_HEADER_SIZE) == 0) { + return true; + } } return false; } bool OctreeQueryNode::shouldSuppressDuplicatePacket() { bool shouldSuppress = false; // assume we won't suppress - + // only consider duplicate packets if (packetIsDuplicate()) { _duplicatePacketCount++; - // If this is the first suppressed packet, remember our time... + // If this is the first suppressed packet, remember our time... if (_duplicatePacketCount == 1) { _firstSuppressedPacket = usecTimestampNow(); } @@ -97,8 +99,8 @@ void OctreeQueryNode::resetOctreePacket(bool lastWasSurpressed) { _lastOctreePacketLength = getPacketLength(); memcpy(_lastOctreePacket, _octreePacket, _lastOctreePacketLength); - // If we're moving, and the client asked for low res, then we force monochrome, otherwise, use - // the clients requested color state. + // If we're moving, and the client asked for low res, then we force monochrome, otherwise, use + // the clients requested color state. _currentPacketIsColor = getWantColor(); _currentPacketIsCompressed = getWantCompression(); OCTREE_PACKET_FLAGS flags = 0; @@ -152,7 +154,7 @@ void OctreeQueryNode::writeToPacket(const unsigned char* buffer, int bytes) { _octreePacketAvailableBytes -= bytes; _octreePacketAt += bytes; _octreePacketWaiting = true; - } + } } OctreeQueryNode::~OctreeQueryNode() { @@ -175,20 +177,20 @@ bool OctreeQueryNode::updateCurrentViewFrustum() { // Also make sure it's got the correct lens details from the camera float originalFOV = getCameraFov(); float wideFOV = originalFOV + VIEW_FRUSTUM_FOV_OVERSEND; - + newestViewFrustum.setFieldOfView(wideFOV); // hack newestViewFrustum.setAspectRatio(getCameraAspectRatio()); newestViewFrustum.setNearClip(getCameraNearClip()); newestViewFrustum.setFarClip(getCameraFarClip()); newestViewFrustum.setEyeOffsetPosition(getCameraEyeOffsetPosition()); - + // if there has been a change, then recalculate if (!newestViewFrustum.isVerySimilar(_currentViewFrustum)) { _currentViewFrustum = newestViewFrustum; _currentViewFrustum.calculate(); currentViewFrustumChanged = true; } - + // Also check for LOD changes from the client if (_lodInitialized) { if (_lastClientBoundaryLevelAdjust != getBoundaryLevelAdjust()) { @@ -205,7 +207,7 @@ bool OctreeQueryNode::updateCurrentViewFrustum() { _lastClientBoundaryLevelAdjust = getBoundaryLevelAdjust(); _lodChanged = false; } - + // When we first detect that the view stopped changing, we record this. // but we don't change it back to false until we've completely sent this // scene. @@ -216,8 +218,8 @@ bool OctreeQueryNode::updateCurrentViewFrustum() { return currentViewFrustumChanged; } -void OctreeQueryNode::setViewSent(bool viewSent) { - _viewSent = viewSent; +void OctreeQueryNode::setViewSent(bool viewSent) { + _viewSent = viewSent; if (viewSent) { _viewFrustumJustStoppedChanging = false; _lodChanged = false; @@ -226,12 +228,12 @@ void OctreeQueryNode::setViewSent(bool viewSent) { void OctreeQueryNode::updateLastKnownViewFrustum() { bool frustumChanges = !_lastKnownViewFrustum.isVerySimilar(_currentViewFrustum); - + if (frustumChanges) { // save our currentViewFrustum into our lastKnownViewFrustum _lastKnownViewFrustum = _currentViewFrustum; } - + // save that we know the view has been sent. uint64_t now = usecTimestampNow(); setLastTimeBagEmpty(now); // is this what we want? poor names @@ -242,7 +244,7 @@ bool OctreeQueryNode::moveShouldDump() const { glm::vec3 oldPosition = _lastKnownViewFrustum.getPosition(); glm::vec3 newPosition = _currentViewFrustum.getPosition(); - // theoretically we could make this slightly larger but relative to avatar scale. + // theoretically we could make this slightly larger but relative to avatar scale. const float MAXIMUM_MOVE_WITHOUT_DUMP = 0.0f; if (glm::distance(newPosition, oldPosition) > MAXIMUM_MOVE_WITHOUT_DUMP) { return true; From 131af0cf16a4108007c1c05cb9c5aea8b8a20833 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Fri, 10 Jan 2014 12:26:11 -0800 Subject: [PATCH 29/39] I believe this should fix the "Joint/model not in model list" errors. --- interface/src/renderer/FBXReader.cpp | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/interface/src/renderer/FBXReader.cpp b/interface/src/renderer/FBXReader.cpp index 63e3afc8cf..1058bd02d9 100644 --- a/interface/src/renderer/FBXReader.cpp +++ b/interface/src/renderer/FBXReader.cpp @@ -548,17 +548,18 @@ public: }; void appendModelIDs(const QString& parentID, const QMultiHash& childMap, - QHash& models, QVector& modelIDs) { - if (models.contains(parentID)) { + QHash& models, QSet& remainingModels, QVector& modelIDs) { + if (remainingModels.contains(parentID)) { modelIDs.append(parentID); + remainingModels.remove(parentID); } int parentIndex = modelIDs.size() - 1; foreach (const QString& childID, childMap.values(parentID)) { - if (models.contains(childID)) { + if (remainingModels.contains(childID)) { FBXModel& model = models[childID]; if (model.parentIndex == -1) { model.parentIndex = parentIndex; - appendModelIDs(childID, childMap, models, modelIDs); + appendModelIDs(childID, childMap, models, remainingModels, modelIDs); } } } @@ -1101,8 +1102,12 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping) // get the list of models in depth-first traversal order QVector modelIDs; - if (!models.isEmpty()) { - QString top = models.constBegin().key(); + QSet remainingModels; + for (QHash::const_iterator model = models.constBegin(); model != models.constEnd(); model++) { + remainingModels.insert(model.key()); + } + while (!remainingModels.isEmpty()) { + QString top = *remainingModels.constBegin(); forever { foreach (const QString& name, parentMap.values(top)) { if (models.contains(name)) { @@ -1115,7 +1120,7 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping) outerContinue: ; } - appendModelIDs(top, childMap, models, modelIDs); + appendModelIDs(top, childMap, models, remainingModels, modelIDs); } // convert the models to joints From 9fa170494c7274f074f2d38a8e4db78d0bb320de Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Fri, 10 Jan 2014 14:04:46 -0800 Subject: [PATCH 30/39] I believe what we really want to decay is the mouse pitch. --- interface/src/avatar/MyAvatar.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index e7dba4a4c7..490dd5febb 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -210,8 +210,8 @@ void MyAvatar::simulate(float deltaTime, Transmitter* transmitter) { // you start moving, but don't do this with an HMD like the Oculus. if (!OculusManager::isConnected()) { if (forwardAcceleration > ACCELERATION_PULL_THRESHOLD) { - _head.setPitch(_head.getPitch() * (1.f - forwardAcceleration * ACCELERATION_PITCH_DECAY * deltaTime)); - _head.setYaw(_head.getYaw() * (1.f - forwardAcceleration * ACCELERATION_YAW_DECAY * deltaTime)); + _head.setMousePitch(_head.getMousePitch() * qMax(0.0f, + (1.f - forwardAcceleration * ACCELERATION_PITCH_DECAY * deltaTime))); } } else if (fabsf(forwardAcceleration) > OCULUS_ACCELERATION_PULL_THRESHOLD && fabs(_head.getYaw()) > OCULUS_YAW_OFFSET_THRESHOLD) { From 4ae5203f38c67899aebf67e45080c95810c1b23b Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Fri, 10 Jan 2014 14:10:33 -0800 Subject: [PATCH 31/39] Remove unused constant. --- interface/src/avatar/MyAvatar.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 490dd5febb..161ea5b9d8 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -200,7 +200,6 @@ void MyAvatar::simulate(float deltaTime, Transmitter* transmitter) { // Compute instantaneous acceleration float forwardAcceleration = glm::length(glm::dot(getBodyFrontDirection(), getVelocity() - oldVelocity)) / deltaTime; const float ACCELERATION_PITCH_DECAY = 0.4f; - const float ACCELERATION_YAW_DECAY = 0.4f; const float ACCELERATION_PULL_THRESHOLD = 0.2f; const float OCULUS_ACCELERATION_PULL_THRESHOLD = 1.0f; const int OCULUS_YAW_OFFSET_THRESHOLD = 10; From 38118fdab039591c7905afaa3acb3c4963c0b5fe Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Fri, 10 Jan 2014 14:42:05 -0800 Subject: [PATCH 32/39] When we start setting the fake coefficients after we set the real ones, make sure all of the other coefficients are zero. --- interface/src/devices/Faceshift.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/interface/src/devices/Faceshift.cpp b/interface/src/devices/Faceshift.cpp index f8600b6e50..347bb82bc4 100644 --- a/interface/src/devices/Faceshift.cpp +++ b/interface/src/devices/Faceshift.cpp @@ -95,6 +95,7 @@ void Faceshift::reset() { void Faceshift::updateFakeCoefficients(float leftBlink, float rightBlink, float browUp, float jawOpen, std::vector& coefficients) const { coefficients.resize(max((int)coefficients.size(), _jawOpenIndex + 1)); + qFill(coefficients.begin(), coefficients.end(), 0.0f); coefficients[_leftBlinkIndex] = leftBlink; coefficients[_rightBlinkIndex] = rightBlink; coefficients[_browUpCenterIndex] = browUp; From 515b40ecc3e09788db6cf08b138a1adc7e87cb66 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Fri, 10 Jan 2014 15:00:14 -0800 Subject: [PATCH 33/39] Attempt to disable Hydra hands after five seconds without movement. --- interface/src/devices/SixenseManager.cpp | 18 +++++++++++++++++- interface/src/devices/SixenseManager.h | 4 ++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/interface/src/devices/SixenseManager.cpp b/interface/src/devices/SixenseManager.cpp index 9c757a1e45..2498eec5ac 100644 --- a/interface/src/devices/SixenseManager.cpp +++ b/interface/src/devices/SixenseManager.cpp @@ -13,7 +13,9 @@ #include "Application.h" #include "SixenseManager.h" -SixenseManager::SixenseManager() { +using namespace std; + +SixenseManager::SixenseManager() : _lastMovement(usecTimestampNow()) { #ifdef HAVE_SIXENSE sixenseInit(); #endif @@ -98,6 +100,12 @@ void SixenseManager::update(float deltaTime) { palm->setRawVelocity(rawVelocity); // meters/sec palm->setRawPosition(position); + // use the velocity to determine whether there's any movement + const float MOVEMENT_SPEED_THRESHOLD = 0.001f; + if (glm::length(rawVelocity) > MOVEMENT_SPEED_THRESHOLD) { + _lastMovement = usecTimestampNow(); + } + // initialize the "finger" based on the direction FingerData finger(palm, &hand); finger.setActive(true); @@ -118,6 +126,14 @@ void SixenseManager::update(float deltaTime) { palm->getFingers().push_back(finger); palm->getFingers().push_back(finger); } + + // if the controllers haven't been moved in a while, disable + const int MOVEMENT_DISABLE_DURATION = 5 * 1000 * 1000; + if (usecTimestampNow() - _lastMovement > MOVEMENT_DISABLE_DURATION) { + for (vector::iterator it = hand.getPalms().begin(); it != hand.getPalms().end(); it++) { + it->setActive(false); + } + } #endif } diff --git a/interface/src/devices/SixenseManager.h b/interface/src/devices/SixenseManager.h index 874f29fc34..863db7852f 100644 --- a/interface/src/devices/SixenseManager.h +++ b/interface/src/devices/SixenseManager.h @@ -22,6 +22,10 @@ public: public slots: void setFilter(bool filter); + +private: + + uint64_t _lastMovement; }; #endif /* defined(__interface__SixenseManager__) */ From a820b2c3b6be9897742e528011646c74182fc988 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Fri, 10 Jan 2014 15:01:39 -0800 Subject: [PATCH 34/39] force 10.9 build to act like 10.8 with Makefiles, closes #1210 --- CMakeLists.txt | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index c533f360f0..a94e3f9bc7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,12 +6,21 @@ set(CMAKE_PREFIX_PATH ${CMAKE_PREFIX_PATH} $ENV{QT_CMAKE_PREFIX_PATH}) # set our Base SDK to 10.8 set(CMAKE_OSX_SYSROOT /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.8.sdk) +set(CMAKE_OSX_DEPLOYMENT_TARGET 10.8) # Find includes in corresponding build directories set(CMAKE_INCLUDE_CURRENT_DIR ON) # Instruct CMake to run moc automatically when needed. set(CMAKE_AUTOMOC ON) +IF (APPLE) + EXEC_PROGRAM(uname ARGS -v OUTPUT_VARIABLE DARWIN_VERSION) + STRING(REGEX MATCH "[0-9]+" DARWIN_VERSION ${DARWIN_VERSION}) + IF (DARWIN_VERSION GREATER 12) + SET(CMAKE_CXX_FLAGS "-stdlib=libstdc++") + ENDIF (DARWIN_VERSION GREATER 12) +ENDIF(APPLE) + add_subdirectory(animation-server) add_subdirectory(assignment-client) add_subdirectory(domain-server) From 1c3259c8cf8a8de8421a3f2e8ce92f8b80bf3d5c Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Fri, 10 Jan 2014 15:33:43 -0800 Subject: [PATCH 35/39] remove the starfield URL loading code, closes #1497 --- assignment-client/CMakeLists.txt | 5 - interface/src/starfield/Config.h | 1 - interface/src/starfield/Loader.h | 183 ----------------- libraries/shared/CMakeLists.txt | 4 - libraries/shared/src/UrlReader.cpp | 172 ---------------- libraries/shared/src/UrlReader.h | 305 ----------------------------- 6 files changed, 670 deletions(-) delete mode 100644 interface/src/starfield/Loader.h delete mode 100644 libraries/shared/src/UrlReader.cpp delete mode 100644 libraries/shared/src/UrlReader.h diff --git a/assignment-client/CMakeLists.txt b/assignment-client/CMakeLists.txt index c7b8ab5732..418523993b 100644 --- a/assignment-client/CMakeLists.txt +++ b/assignment-client/CMakeLists.txt @@ -39,8 +39,3 @@ include_directories(${ROOT_DIR}/externals/civetweb/include) if (UNIX) target_link_libraries(${TARGET_NAME} ${CMAKE_DL_LIBS}) endif (UNIX) - -# link curl for synchronous script downloads -find_package(CURL REQUIRED) -include_directories(${CURL_INCLUDE_DIRS}) -target_link_libraries(${TARGET_NAME} ${CURL_LIBRARY}) diff --git a/interface/src/starfield/Config.h b/interface/src/starfield/Config.h index d2d93be407..564f6f3a1a 100755 --- a/interface/src/starfield/Config.h +++ b/interface/src/starfield/Config.h @@ -36,7 +36,6 @@ #include #include -#include "UrlReader.h" #include "AngleUtil.h" #include "Radix2InplaceSort.h" #include "Radix2IntegerScanner.h" diff --git a/interface/src/starfield/Loader.h b/interface/src/starfield/Loader.h deleted file mode 100644 index e2f6105f33..0000000000 --- a/interface/src/starfield/Loader.h +++ /dev/null @@ -1,183 +0,0 @@ -// -// starfield/Loader.h -// interface -// -// Created by Tobias Schwinger on 3/29/13. -// Copyright (c) 2013 High Fidelity, Inc. All rights reserved. -// - -#ifndef __interface__starfield__Loader__ -#define __interface__starfield__Loader__ - -#ifndef __interface__Starfield_impl__ -#error "This is an implementation file - not intended for direct inclusion." -#endif - -#include - -#include "Config.h" - -#include "starfield/data/InputVertex.h" -#include "starfield/data/BrightnessLevel.h" - -namespace starfield { - - class Loader : UrlReader { - public: - - bool loadVertices( - InputVertices& destination, char const* url, char const* cacheFile, unsigned limit) - { - _vertices = & destination; - _limit = limit; -#if STARFIELD_SAVE_MEMORY - if (_limit == 0 || _limit > 60000u) - _limit = 60000u; -#endif - _urlStr = url; // in case we fail early - - if (! UrlReader::readUrl(url, *this, cacheFile)) - { - qDebug("%s:%d: %s\n", - _urlStr, _lineNo, getError()); - - return false; - } - qDebug("Loaded %u stars.\n", _recordsRead); - - return true; - } - - protected: - friend class UrlReader; - - void begin(char const* url, - char const* type, - int64_t size, - int64_t stardate) { - - _lineNo = 0u; - _urlStr = url; // new value in http redirect - - _recordsRead = 0u; - - _vertices->clear(); - _vertices->reserve(_limit); -// qDebug("Stars.cpp: loader begin %s\n", url); - } - - size_t transfer(char* input, size_t bytes) { - - size_t consumed = 0u; - char const* end = input + bytes; - char* line, * next = input; - - for (;;) { - - // advance to next line - for (; next != end && isspace(*next); ++next); - consumed = next - input; - line = next; - ++_lineNo; - for (; next != end && *next != '\n' && *next != '\r'; ++next); - if (next == end) - return consumed; - *next++ = '\0'; - - // skip comments - if (*line == '\\' || *line == '/' || *line == ';') - continue; - - // parse - float azi, alt; - unsigned c; - setlocale(LC_NUMERIC, "C"); - if (sscanf(line, " %f %f #%x", & azi, & alt, & c) == 3) { - - if (spaceFor( getBrightness(c) )) { - - storeVertex(azi, alt, c); - } - - ++_recordsRead; - - } else { - - qDebug("Stars.cpp:%d: Bad input from %s\n", - _lineNo, _urlStr); - } - - } - return consumed; - } - - void end(bool ok) - { } - - private: - - bool atLimit() { return _limit > 0u && _recordsRead >= _limit; } - - bool spaceFor(BrightnessLevel b) { - - if (! atLimit()) { - return true; - } - - // just reached the limit? -> establish a minimum heap and - // remember the brightness at its top - if (_recordsRead == _limit) { - -// qDebug("Stars.cpp: vertex limit reached -> heap mode\n"); - - make_heap( - _vertices->begin(), _vertices->end(), - GreaterBrightness() ); - - _minBrightness = getBrightness( - _vertices->begin()->getColor() ); - } - - // not interested? say so - if (_minBrightness >= b) - return false; - - // otherwise free up space for the new vertex - pop_heap( - _vertices->begin(), _vertices->end(), - GreaterBrightness() ); - _vertices->pop_back(); - return true; - } - - void storeVertex(float azi, float alt, unsigned color) { - - _vertices->push_back(InputVertex(azi, alt, color)); - - if (atLimit()) { - - push_heap( - _vertices->begin(), _vertices->end(), - GreaterBrightness() ); - - _minBrightness = getBrightness( - _vertices->begin()->getColor() ); - } - } - - // variables - - InputVertices* _vertices; - unsigned _limit; - - unsigned _lineNo; - char const* _urlStr; - - unsigned _recordsRead; - BrightnessLevel _minBrightness; - }; - -} // anonymous namespace - -#endif - diff --git a/libraries/shared/CMakeLists.txt b/libraries/shared/CMakeLists.txt index 1923d906bb..c1fcf2e553 100644 --- a/libraries/shared/CMakeLists.txt +++ b/libraries/shared/CMakeLists.txt @@ -24,10 +24,6 @@ set(EXTERNAL_ROOT_DIR ${CMAKE_CURRENT_SOURCE_DIR}/external) if (WIN32) # include headers for external libraries and InterfaceConfig. include_directories(${EXTERNAL_ROOT_DIR}) -else (WIN32) - find_package(CURL REQUIRED) - include_directories(${CURL_INCLUDE_DIRS}) - target_link_libraries(${TARGET_NAME} ${CURL_LIBRARY}) endif (WIN32) # link required libraries on UNIX diff --git a/libraries/shared/src/UrlReader.cpp b/libraries/shared/src/UrlReader.cpp deleted file mode 100644 index 3f98326726..0000000000 --- a/libraries/shared/src/UrlReader.cpp +++ /dev/null @@ -1,172 +0,0 @@ -// -// UrlReader.cpp -// hifi -// -// Created by Tobias Schwinger on 3/21/13. -// Copyright (c) 2013 High Fidelity, Inc. All rights reserved. -// - -#include - -#include -#include - -#ifndef _WIN32 -// (Windows port is incomplete and the build files do not support CURL, yet) - -#include - -#include "UrlReader.h" - -// -// ATTENTION: A certain part of the implementation lives in inlined code -// (see the bottom of the header file). -// -// Why? Because it allows stream parsing without having to call around a -// lot (one static and one dynamic call per character if the parser just -// reads one character at a time). -// -// Here is an overview of the code structure: -// -// readUrl -// -> transferBegin (sets up state) -// -> perform (starts CURL transfer) -// -> (specialized, type-erased) callback_template -// -> getInfo (fetches HTTP header, eventually initiates caching) -// -> stream.begin (client code - called once) -// -> feedBuffered (the buffering logic) -// -> stream.transfer (client code - called repeatedly) -// -> stream.end (client code - called when the transfer is done) -// -> transferEnd (closes cache file, if used) -// -// "->" means "calls or inlines", here -// - -size_t const UrlReader::max_read_ahead = CURL_MAX_WRITE_SIZE; - -char const* const UrlReader::success = "UrlReader: Success!"; -char const* const UrlReader::success_cached = "UrlReader: Using local file."; -char const* const UrlReader::error_init_failed = "UrlReader: Initialization failed."; -char const* const UrlReader::error_aborted = "UrlReader: Processing error."; -char const* const UrlReader::error_buffer_overflow = "UrlReader: Buffer overflow."; -char const* const UrlReader::error_leftover_input = "UrlReader: Incomplete processing."; - -#define _curlPtr static_cast(_curlHandle) - -UrlReader::UrlReader() - : _curlHandle(0l), _xtraBuffer(0l), _errorStr(0l), _cacheReadBuffer(0l) { - - _xtraBuffer = new(std::nothrow) char[max_read_ahead]; - if (! _xtraBuffer) { _errorStr = error_init_failed; return; } - _curlHandle = curl_easy_init(); - if (! _curlHandle) { _errorStr = error_init_failed; return; } - curl_easy_setopt(_curlPtr, CURLOPT_NOSIGNAL, 1l); - curl_easy_setopt(_curlPtr, CURLOPT_FAILONERROR, 1l); - curl_easy_setopt(_curlPtr, CURLOPT_FILETIME, 1l); - curl_easy_setopt(_curlPtr, CURLOPT_ENCODING, ""); -} - -UrlReader::~UrlReader() { - - delete[] _xtraBuffer; - delete[] _cacheReadBuffer; - if (! _curlHandle) { - return; - } - curl_easy_cleanup(_curlPtr); -} - -void UrlReader::perform(char const* url, transfer_callback* cb) { - - curl_easy_setopt(_curlPtr, CURLOPT_URL, url); - curl_easy_setopt(_curlPtr, CURLOPT_WRITEFUNCTION, cb); - curl_easy_setopt(_curlPtr, CURLOPT_WRITEDATA, this); - - CURLcode rc = curl_easy_perform(_curlPtr); - - if (rc == CURLE_OK) - { - while (_xtraSize > 0 && _errorStr == success) - cb(0l, 0, 0, this); - } - else if (_errorStr == success) - _errorStr = curl_easy_strerror(rc); -} - -void UrlReader::transferBegin(void* stream, char const* cacheFile) { - - _errorStr = success; - _streamPtr = stream; - _cacheFileName = cacheFile; - _cacheFile = 0l; - _cacheMode = no_cache; - _xtraSize = ~size_t(0); -} - -void UrlReader::getInfo(char const*& url, - char const*& type, int64_t& length, int64_t& stardate) { - - // fetch information from HTTP header - double clen; - long time; - curl_easy_getinfo(_curlPtr, CURLINFO_FILETIME, & time); - curl_easy_getinfo(_curlPtr, CURLINFO_EFFECTIVE_URL, & url); - curl_easy_getinfo(_curlPtr, CURLINFO_CONTENT_TYPE, & type); - curl_easy_getinfo(_curlPtr, CURLINFO_CONTENT_LENGTH_DOWNLOAD, & clen); - length = static_cast(clen); - curl_easy_getinfo(_curlPtr, CURLINFO_FILETIME, & time); - stardate = time; - -// printLog("UrlReader: Ready to transfer from URL '%s'\n", url); - - // check caching file time whether we actually want to download anything - if (_cacheFileName != 0l) { - struct stat s; - stat(_cacheFileName, & s); - if (time > s.st_mtime) { - // file on server is newer -> update cache file - _cacheFile = fopen(_cacheFileName, "wb"); -// printLog("UrlReader: Also writing content to cache file '%s'\n", _cacheFileName); - if (_cacheFile != 0l) { - _cacheMode = cache_write; - } - } else { - // file on server is older -> use cache file - if (! _cacheReadBuffer) { - _cacheReadBuffer = new (std::nothrow) char[max_read_ahead]; - if (! _cacheReadBuffer) { - // out of memory, no caching, have CURL catch it - return; - } - } - _cacheFile = fopen(_cacheFileName, "rb"); -// printLog("UrlReader: Delivering cached content from file '%s'\n", _cacheFileName); - if (_cacheFile != 0l) { - _cacheMode = cache_read; - } - // override error code returned by CURL when we abort the download - _errorStr = success_cached; - } - } -} - -void UrlReader::transferEnd() { - - if (_cacheFile != 0l) { - fclose(_cacheFile); - } -} - -#else // no-op version for incomplete Windows build: - -UrlReader::UrlReader() : _curlHandle(0l) { } -UrlReader::~UrlReader() { } -void UrlReader::perform(char const* url, transfer_callback* cb) { } -void UrlReader::transferBegin(void* stream, char const* cacheFile) { } -void UrlReader::getInfo(char const*& url, char const*& type, - int64_t& length, int64_t& stardate) { } -void UrlReader::transferEnd() { } - -#endif - - diff --git a/libraries/shared/src/UrlReader.h b/libraries/shared/src/UrlReader.h deleted file mode 100644 index 8a9084ce32..0000000000 --- a/libraries/shared/src/UrlReader.h +++ /dev/null @@ -1,305 +0,0 @@ -// -// UrlReader.h -// hifi -// -// Created by Tobias Schwinger on 3/21/13. -// Copyright (c) 2013 High Fidelity, Inc. All rights reserved. -// - -#ifndef __hifi__UrlReader__ -#define __hifi__UrlReader__ - -#include -#include -#include -#include - -// -// UrlReader class that encapsulates a context for sequential data retrieval -// via URLs. Use one per thread. -// -class UrlReader { -public: - - // - // Constructor - performs initialization, never throws. - // - UrlReader(); - - // - // Destructor - frees resources, never throws. - // - ~UrlReader(); - - // - // Reads data from an URL and forwards it to the instance of a class - // fulfilling the ContentStream concept. - // - // The call protocol on the ContentStream is detailed as follows: - // - // 1. begin(char const* url, - // char const* content_type, uint64_t bytes, uint64_t stardate) - // - // All information except 'url' is optional; 'content_type' can - // be a null pointer - 'bytes' and 'stardate' can be equal to - // to 'unavailable'. - // - // 2. transfer(char* buffer, size_t bytes) - // - // Called until all data has been received. The number of bytes - // actually processed should be returned. - // Unprocessed data is stored in an extra buffer whose size is - // given by the constant UrlReader::max_read_ahead - it can be - // assumed to be reasonably large for on-the-fly parsing. - // - // 3. end(bool ok) - // - // Called at the end of the transfer. - // - // Returns the same success code - // - template< class ContentStream > - bool readUrl(char const* url, ContentStream& s, char const* cacheFile = 0l); - - // - // Returns a pointer to a static C-string that describes the error - // condition. - // - inline char const* getError() const; - - // - // Can be called by the stream to set a user-defined error string. - // - inline void setError(char const* static_c_string); - - // - // Pointer to the C-string returned by a call to 'readUrl' when no - // error occurred. - // - static char const* const success; - - // - // Pointer to the C-string returned by a call to 'readUrl' when no - // error occurred and a local file has been read instead of the - // network stream. - // - static char const* const success_cached; - - // - // Pointer to the C-string returned by a call to 'readUrl' when the - // initialization has failed. - // - static char const* const error_init_failed; - - // - // Pointer to the C-string returned by a call to 'readUrl' when the - // transfer has been aborted by the client. - // - static char const* const error_aborted; - - // - // Pointer to the C-string returned by a call to 'readUrl' when - // leftover input from incomplete processing caused a buffer - // overflow. - // - static char const* const error_buffer_overflow; - - // - // Pointer to the C-string return by a call to 'readUrl' when the - // input provided was not completely consumed. - // - static char const* const error_leftover_input; - - // - // Constant of the maximum number of bytes that are buffered - // between invocations of 'transfer'. - // - static size_t const max_read_ahead; - - // - // Constant representing absent information in the call to the - // 'begin' member function of the target stream. - // - static int const unavailable = -1; - - // - // Constant for requesting to abort the current transfer when - // returned by the 'transfer' member function of the target stream. - // - static size_t const abort = ~0u; - -private: - // instances of this class shall not be copied - UrlReader(UrlReader const&); // = delete; - UrlReader& operator=(UrlReader const&); // = delete; - - inline bool isSuccess(); - - // entrypoints to compiled code - - typedef size_t transfer_callback(char*, size_t, size_t, void*); - - enum CacheMode { no_cache, cache_write, cache_read }; - - void transferBegin(void* stream, char const* cacheFile); - void transferEnd(); - - void perform(char const* url, transfer_callback* transfer); - - void getInfo(char const*& url, - char const*& type, int64_t& length, int64_t& stardate); - - // synthesized callback - - template< class Stream > static size_t callback_template(char *input, size_t size, - size_t nmemb, void* thiz); - - template< class Stream > size_t feedBuffered(Stream* stream, - char* input, size_t size); - - // state - - void* _curlHandle; - char* _xtraBuffer; - char const* _errorStr; - void* _streamPtr; - char const* _cacheFileName; - FILE* _cacheFile; - char* _cacheReadBuffer; - CacheMode _cacheMode; - size_t _xtraSize; -}; - -// inline functions - -inline char const* UrlReader::getError() const { - - return _errorStr; -} - -bool UrlReader::isSuccess() { - - return _errorStr == success || _errorStr == success_cached; -} - -template< class ContentStream > -bool UrlReader::readUrl(char const* url, ContentStream& s, char const* cacheFile) { - if (! _curlHandle) return false; - - this->transferBegin(& s, cacheFile); - this->perform(url, & callback_template); - this->transferEnd(); - bool ok = isSuccess(); - s.end(ok); - return ok; -} - -inline void UrlReader::setError(char const* staticCstring) { - - if (this->isSuccess()) - this->_errorStr = staticCstring; -} - -template< class Stream > -size_t UrlReader::feedBuffered(Stream* stream, char* input, size_t size) { - - size_t inputOffset = 0u; - - while (true) { - - char* buffer = input + inputOffset; - size_t bytes = size - inputOffset; - - // data in extra buffer? - if (_xtraSize > 0) { - - // fill extra buffer with beginning of input - size_t fill = max_read_ahead - _xtraSize; - if (bytes < fill) fill = bytes; - memcpy(_xtraBuffer + _xtraSize, buffer, fill); - // use extra buffer for next transfer - buffer = _xtraBuffer; - bytes = _xtraSize + fill; - inputOffset += fill; - } - - // call 'transfer' - size_t processed = stream->transfer(buffer, bytes); - if (processed == abort) { - - setError(error_aborted); - return 0u; - - } else if (! processed && ! input) { - - setError(error_leftover_input); - return 0u; - } - size_t unprocessed = bytes - processed; - - // can switch to input buffer, now? - if (buffer == _xtraBuffer && unprocessed <= inputOffset) { - - _xtraSize = 0u; - inputOffset -= unprocessed; - - } else { // no? unprocessed data -> extra buffer - - if (unprocessed > max_read_ahead) { - - setError(error_buffer_overflow); - return 0; - } - _xtraSize = unprocessed; - memmove(_xtraBuffer, buffer + processed, unprocessed); - - if (inputOffset == size || buffer != _xtraBuffer) { - - return size; - } - } - } // while -} - -template< class Stream > -size_t UrlReader::callback_template(char *input, size_t size, size_t nmemb, void* thiz) { - - size_t result = 0u; - UrlReader* me = static_cast(thiz); - Stream* stream = static_cast(me->_streamPtr); - size *= nmemb; - - // first call? - if (me->_xtraSize == ~size_t(0)) { - - me->_xtraSize = 0u; - // extract meta information and call 'begin' - char const* url, * type; - int64_t length, stardate; - me->getInfo(url, type, length, stardate); - if (me->_cacheMode != cache_read) { - stream->begin(url, type, length, stardate); - } - } - do { - // will have to repeat from here when reading a local file - - // read from cache file? - if (me->_cacheMode == cache_read) { - // change input buffer and start - input = me->_cacheReadBuffer; - size = fread(input, 1, max_read_ahead, me->_cacheFile); - nmemb = 1; - } else if (me->_cacheMode == cache_write) { - fwrite(input, 1, size, me->_cacheFile); - } - - result = me->feedBuffered(stream, input, size); - - } while (me->_cacheMode == cache_read && result != 0 && ! feof(me->_cacheFile)); - - return me->_cacheMode != cache_read ? result : 0; -} - -#endif /* defined(__hifi__UrlReader__) */ - From a7e9434c961e68e4546ca5d8a343e246d1ad2794 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Fri, 10 Jan 2014 15:50:14 -0800 Subject: [PATCH 36/39] Tweaked constants. --- interface/src/devices/SixenseManager.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/interface/src/devices/SixenseManager.cpp b/interface/src/devices/SixenseManager.cpp index 2498eec5ac..352cae44ab 100644 --- a/interface/src/devices/SixenseManager.cpp +++ b/interface/src/devices/SixenseManager.cpp @@ -101,7 +101,7 @@ void SixenseManager::update(float deltaTime) { palm->setRawPosition(position); // use the velocity to determine whether there's any movement - const float MOVEMENT_SPEED_THRESHOLD = 0.001f; + const float MOVEMENT_SPEED_THRESHOLD = 0.05f; if (glm::length(rawVelocity) > MOVEMENT_SPEED_THRESHOLD) { _lastMovement = usecTimestampNow(); } @@ -128,7 +128,7 @@ void SixenseManager::update(float deltaTime) { } // if the controllers haven't been moved in a while, disable - const int MOVEMENT_DISABLE_DURATION = 5 * 1000 * 1000; + const int MOVEMENT_DISABLE_DURATION = 30 * 1000 * 1000; if (usecTimestampNow() - _lastMovement > MOVEMENT_DISABLE_DURATION) { for (vector::iterator it = hand.getPalms().begin(); it != hand.getPalms().end(); it++) { it->setActive(false); From 5283e519270e2899d57e44e21b94bd7537402251 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Fri, 10 Jan 2014 15:51:12 -0800 Subject: [PATCH 37/39] Start out disabled. --- interface/src/devices/SixenseManager.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/devices/SixenseManager.cpp b/interface/src/devices/SixenseManager.cpp index 352cae44ab..9d6b011921 100644 --- a/interface/src/devices/SixenseManager.cpp +++ b/interface/src/devices/SixenseManager.cpp @@ -15,7 +15,7 @@ using namespace std; -SixenseManager::SixenseManager() : _lastMovement(usecTimestampNow()) { +SixenseManager::SixenseManager() : _lastMovement(0) { #ifdef HAVE_SIXENSE sixenseInit(); #endif From a7c14f1f17d2a3826c389aa19a0d838e67a5e9c3 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Fri, 10 Jan 2014 15:53:50 -0800 Subject: [PATCH 38/39] Only register movement if the hand isn't new. --- interface/src/devices/SixenseManager.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/interface/src/devices/SixenseManager.cpp b/interface/src/devices/SixenseManager.cpp index 9d6b011921..9ebe4b35d1 100644 --- a/interface/src/devices/SixenseManager.cpp +++ b/interface/src/devices/SixenseManager.cpp @@ -100,9 +100,9 @@ void SixenseManager::update(float deltaTime) { palm->setRawVelocity(rawVelocity); // meters/sec palm->setRawPosition(position); - // use the velocity to determine whether there's any movement + // use the velocity to determine whether there's any movement (if the hand isn't new) const float MOVEMENT_SPEED_THRESHOLD = 0.05f; - if (glm::length(rawVelocity) > MOVEMENT_SPEED_THRESHOLD) { + if (glm::length(rawVelocity) > MOVEMENT_SPEED_THRESHOLD && foundHand) { _lastMovement = usecTimestampNow(); } From d28ed70ca99c6b23fac9063d49ed7a4e9cb3d6e9 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Fri, 10 Jan 2014 15:55:38 -0800 Subject: [PATCH 39/39] merge pull request 1446 --- assignment-client/CMakeLists.txt | 4 +- assignment-client/src/AssignmentFactory.cpp | 14 +- assignment-client/src/audio/AudioMixer.cpp | 21 +- assignment-client/src/avatars/AvatarMixer.cpp | 17 +- .../src/metavoxels/MetavoxelServer.cpp | 156 ++++++++++ .../src/metavoxels/MetavoxelServer.h | 102 ++++++ cmake/macros/AutoMTC.cmake | 21 ++ domain-server/src/DomainServer.cpp | 18 +- domain-server/src/DomainServer.h | 1 + interface/src/Application.cpp | 10 +- interface/src/Application.h | 2 +- interface/src/MetavoxelSystem.cpp | 244 +++++++++++++++ interface/src/MetavoxelSystem.h | 124 ++++++++ interface/src/renderer/MetavoxelSystem.cpp | 122 -------- interface/src/renderer/MetavoxelSystem.h | 61 ---- libraries/metavoxels/CMakeLists.txt | 3 + .../metavoxels/src/AttributeRegistry.cpp | 9 +- libraries/metavoxels/src/AttributeRegistry.h | 51 ++- libraries/metavoxels/src/Bitstream.cpp | 266 +++++++++++++++- libraries/metavoxels/src/Bitstream.h | 293 +++++++++++++++++- .../metavoxels/src/DatagramSequencer.cpp | 179 +++++++++++ libraries/metavoxels/src/DatagramSequencer.h | 109 +++++++ libraries/metavoxels/src/MetavoxelData.cpp | 184 ++++++++++- libraries/metavoxels/src/MetavoxelData.h | 37 ++- libraries/metavoxels/src/MetavoxelMessages.h | 32 ++ libraries/metavoxels/src/MetavoxelUtil.cpp | 29 ++ libraries/metavoxels/src/MetavoxelUtil.h | 23 ++ libraries/shared/src/Assignment.cpp | 4 + libraries/shared/src/Assignment.h | 1 + libraries/shared/src/HifiSockAddr.cpp | 2 + libraries/shared/src/Node.cpp | 3 + libraries/shared/src/NodeList.cpp | 3 +- libraries/shared/src/NodeTypes.h | 1 + libraries/shared/src/PacketHeaders.h | 1 + libraries/shared/src/ThreadedAssignment.cpp | 23 ++ libraries/shared/src/ThreadedAssignment.h | 1 + tools/mtc/CMakeLists.txt | 11 + tools/mtc/src/main.cpp | 180 +++++++++++ 38 files changed, 2099 insertions(+), 263 deletions(-) create mode 100644 assignment-client/src/metavoxels/MetavoxelServer.cpp create mode 100644 assignment-client/src/metavoxels/MetavoxelServer.h create mode 100644 cmake/macros/AutoMTC.cmake create mode 100644 interface/src/MetavoxelSystem.cpp create mode 100644 interface/src/MetavoxelSystem.h delete mode 100644 interface/src/renderer/MetavoxelSystem.cpp delete mode 100644 interface/src/renderer/MetavoxelSystem.h create mode 100644 libraries/metavoxels/src/DatagramSequencer.cpp create mode 100644 libraries/metavoxels/src/DatagramSequencer.h create mode 100644 libraries/metavoxels/src/MetavoxelMessages.h create mode 100644 libraries/metavoxels/src/MetavoxelUtil.cpp create mode 100644 libraries/metavoxels/src/MetavoxelUtil.h create mode 100644 tools/mtc/CMakeLists.txt create mode 100644 tools/mtc/src/main.cpp diff --git a/assignment-client/CMakeLists.txt b/assignment-client/CMakeLists.txt index c7b8ab5732..491485b264 100644 --- a/assignment-client/CMakeLists.txt +++ b/assignment-client/CMakeLists.txt @@ -9,12 +9,13 @@ set(MACRO_DIR ${ROOT_DIR}/cmake/macros) set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/../cmake/modules/") find_package(Qt5Network REQUIRED) +find_package(Qt5Script REQUIRED) find_package(Qt5Widgets REQUIRED) include(${MACRO_DIR}/SetupHifiProject.cmake) setup_hifi_project(${TARGET_NAME} TRUE) -qt5_use_modules(${TARGET_NAME} Network Widgets) +qt5_use_modules(${TARGET_NAME} Network Script Widgets) # include glm include(${MACRO_DIR}/IncludeGLM.cmake) @@ -28,6 +29,7 @@ link_hifi_library(avatars ${TARGET_NAME} ${ROOT_DIR}) link_hifi_library(octree ${TARGET_NAME} ${ROOT_DIR}) link_hifi_library(voxels ${TARGET_NAME} ${ROOT_DIR}) link_hifi_library(particles ${TARGET_NAME} ${ROOT_DIR}) +link_hifi_library(metavoxels ${TARGET_NAME} ${ROOT_DIR}) link_hifi_library(octree-server ${TARGET_NAME} ${ROOT_DIR}) link_hifi_library(particle-server ${TARGET_NAME} ${ROOT_DIR}) link_hifi_library(voxel-server ${TARGET_NAME} ${ROOT_DIR}) diff --git a/assignment-client/src/AssignmentFactory.cpp b/assignment-client/src/AssignmentFactory.cpp index 815186c4f5..b32a909bfe 100644 --- a/assignment-client/src/AssignmentFactory.cpp +++ b/assignment-client/src/AssignmentFactory.cpp @@ -8,13 +8,15 @@ #include -#include "Agent.h" -#include "audio/AudioMixer.h" -#include "avatars/AvatarMixer.h" -#include #include +#include + +#include "Agent.h" #include "AssignmentFactory.h" +#include "audio/AudioMixer.h" +#include "avatars/AvatarMixer.h" +#include "metavoxels/MetavoxelServer.h" ThreadedAssignment* AssignmentFactory::unpackAssignment(const unsigned char* dataBuffer, int numBytes) { int headerBytes = numBytesForPacketHeader(dataBuffer); @@ -33,7 +35,9 @@ ThreadedAssignment* AssignmentFactory::unpackAssignment(const unsigned char* dat return new VoxelServer(dataBuffer, numBytes); case Assignment::ParticleServerType: return new ParticleServer(dataBuffer, numBytes); + case Assignment::MetavoxelServerType: + return new MetavoxelServer(dataBuffer, numBytes); default: return NULL; } -} \ No newline at end of file +} diff --git a/assignment-client/src/audio/AudioMixer.cpp b/assignment-client/src/audio/AudioMixer.cpp index d8e0580cc9..e315d366f8 100644 --- a/assignment-client/src/audio/AudioMixer.cpp +++ b/assignment-client/src/audio/AudioMixer.cpp @@ -239,30 +239,15 @@ void AudioMixer::processDatagram(const QByteArray& dataByteArray, const HifiSock void AudioMixer::run() { + commonInit(AUDIO_MIXER_LOGGING_TARGET_NAME, NODE_TYPE_AUDIO_MIXER); + NodeList* nodeList = NodeList::getInstance(); - // change the logging target name while this is running - Logging::setTargetName(AUDIO_MIXER_LOGGING_TARGET_NAME); - - nodeList->setOwnerType(NODE_TYPE_AUDIO_MIXER); - const char AUDIO_MIXER_NODE_TYPES_OF_INTEREST[2] = { NODE_TYPE_AGENT, NODE_TYPE_AUDIO_INJECTOR }; nodeList->setNodeTypesOfInterest(AUDIO_MIXER_NODE_TYPES_OF_INTEREST, sizeof(AUDIO_MIXER_NODE_TYPES_OF_INTEREST)); nodeList->linkedDataCreateCallback = attachNewBufferToNode; - QTimer* domainServerTimer = new QTimer(this); - connect(domainServerTimer, SIGNAL(timeout()), this, SLOT(checkInWithDomainServerOrExit())); - domainServerTimer->start(DOMAIN_SERVER_CHECK_IN_USECS / 1000); - - QTimer* silentNodeTimer = new QTimer(this); - connect(silentNodeTimer, SIGNAL(timeout()), nodeList, SLOT(removeSilentNodes())); - silentNodeTimer->start(NODE_SILENCE_THRESHOLD_USECS / 1000); - - QTimer* pingNodesTimer = new QTimer(this); - connect(pingNodesTimer, SIGNAL(timeout()), nodeList, SLOT(pingInactiveNodes())); - pingNodesTimer->start(PING_INACTIVE_NODE_INTERVAL_USECS / 1000); - int nextFrame = 0; timeval startTime; @@ -314,4 +299,4 @@ void AudioMixer::run() { } } -} \ No newline at end of file +} diff --git a/assignment-client/src/avatars/AvatarMixer.cpp b/assignment-client/src/avatars/AvatarMixer.cpp index 0f72ae9680..f6096fd18a 100644 --- a/assignment-client/src/avatars/AvatarMixer.cpp +++ b/assignment-client/src/avatars/AvatarMixer.cpp @@ -162,28 +162,13 @@ void AvatarMixer::processDatagram(const QByteArray& dataByteArray, const HifiSoc } void AvatarMixer::run() { - // change the logging target name while AvatarMixer is running - Logging::setTargetName(AVATAR_MIXER_LOGGING_NAME); + commonInit(AVATAR_MIXER_LOGGING_NAME, NODE_TYPE_AVATAR_MIXER); NodeList* nodeList = NodeList::getInstance(); - nodeList->setOwnerType(NODE_TYPE_AVATAR_MIXER); - nodeList->setNodeTypesOfInterest(&NODE_TYPE_AGENT, 1); nodeList->linkedDataCreateCallback = attachAvatarDataToNode; - QTimer* domainServerTimer = new QTimer(this); - connect(domainServerTimer, SIGNAL(timeout()), this, SLOT(checkInWithDomainServerOrExit())); - domainServerTimer->start(DOMAIN_SERVER_CHECK_IN_USECS / 1000); - - QTimer* pingNodesTimer = new QTimer(this); - connect(pingNodesTimer, SIGNAL(timeout()), nodeList, SLOT(pingInactiveNodes())); - pingNodesTimer->start(PING_INACTIVE_NODE_INTERVAL_USECS / 1000); - - QTimer* silentNodeTimer = new QTimer(this); - connect(silentNodeTimer, SIGNAL(timeout()), nodeList, SLOT(removeSilentNodes())); - silentNodeTimer->start(NODE_SILENCE_THRESHOLD_USECS / 1000); - int nextFrame = 0; timeval startTime; diff --git a/assignment-client/src/metavoxels/MetavoxelServer.cpp b/assignment-client/src/metavoxels/MetavoxelServer.cpp new file mode 100644 index 0000000000..fc586599ce --- /dev/null +++ b/assignment-client/src/metavoxels/MetavoxelServer.cpp @@ -0,0 +1,156 @@ +// +// MetavoxelServer.cpp +// hifi +// +// Created by Andrzej Kapolka on 12/18/13. +// Copyright (c) 2013 High Fidelity, Inc. All rights reserved. +// + +#include + +#include + +#include +#include + +#include "MetavoxelServer.h" + +const int SEND_INTERVAL = 50; + +MetavoxelServer::MetavoxelServer(const unsigned char* dataBuffer, int numBytes) : + ThreadedAssignment(dataBuffer, numBytes), + _data(new MetavoxelData()) { + + _sendTimer.setSingleShot(true); + connect(&_sendTimer, SIGNAL(timeout()), SLOT(sendDeltas())); +} + +void MetavoxelServer::removeSession(const QUuid& sessionId) { + delete _sessions.take(sessionId); +} + +void MetavoxelServer::run() { + commonInit("metavoxel-server", NODE_TYPE_METAVOXEL_SERVER); + + _lastSend = QDateTime::currentMSecsSinceEpoch(); + _sendTimer.start(SEND_INTERVAL); +} + +void MetavoxelServer::processDatagram(const QByteArray& dataByteArray, const HifiSockAddr& senderSockAddr) { + switch (dataByteArray.at(0)) { + case PACKET_TYPE_METAVOXEL_DATA: + processData(dataByteArray, senderSockAddr); + break; + + default: + NodeList::getInstance()->processNodeData(senderSockAddr, (unsigned char*)dataByteArray.data(), dataByteArray.size()); + break; + } +} + +void MetavoxelServer::sendDeltas() { + // send deltas for all sessions + foreach (MetavoxelSession* session, _sessions) { + session->sendDelta(); + } + + // restart the send timer + qint64 now = QDateTime::currentMSecsSinceEpoch(); + int elapsed = now - _lastSend; + _lastSend = now; + + _sendTimer.start(qMax(0, 2 * SEND_INTERVAL - elapsed)); +} + +void MetavoxelServer::processData(const QByteArray& data, const HifiSockAddr& sender) { + // read the session id + int headerPlusIDSize; + QUuid sessionID = readSessionID(data, sender, headerPlusIDSize); + if (sessionID.isNull()) { + return; + } + + // forward to session, creating if necessary + MetavoxelSession*& session = _sessions[sessionID]; + if (!session) { + session = new MetavoxelSession(this, sessionID, QByteArray::fromRawData(data.constData(), headerPlusIDSize)); + } + session->receivedData(data, sender); +} + +MetavoxelSession::MetavoxelSession(MetavoxelServer* server, const QUuid& sessionId, const QByteArray& datagramHeader) : + QObject(server), + _server(server), + _sessionId(sessionId), + _sequencer(datagramHeader) { + + const int TIMEOUT_INTERVAL = 30 * 1000; + _timeoutTimer.setInterval(TIMEOUT_INTERVAL); + _timeoutTimer.setSingleShot(true); + connect(&_timeoutTimer, SIGNAL(timeout()), SLOT(timedOut())); + + connect(&_sequencer, SIGNAL(readyToWrite(const QByteArray&)), SLOT(sendData(const QByteArray&))); + connect(&_sequencer, SIGNAL(readyToRead(Bitstream&)), SLOT(readPacket(Bitstream&))); + connect(&_sequencer, SIGNAL(sendAcknowledged(int)), SLOT(clearSendRecordsBefore(int))); + + // insert the baseline send record + SendRecord record = { 0, MetavoxelDataPointer(new MetavoxelData()) }; + _sendRecords.append(record); +} + +void MetavoxelSession::receivedData(const QByteArray& data, const HifiSockAddr& sender) { + // reset the timeout timer + _timeoutTimer.start(); + + // save the most recent sender + _sender = sender; + + // process through sequencer + _sequencer.receivedDatagram(data); +} + +void MetavoxelSession::sendDelta() { + Bitstream& out = _sequencer.startPacket(); + out << QVariant::fromValue(MetavoxelDeltaMessage()); + writeDelta(_server->getData(), _sendRecords.first().data, out); + _sequencer.endPacket(); + + // record the send + SendRecord record = { _sequencer.getOutgoingPacketNumber(), _server->getData() }; + _sendRecords.append(record); +} + +void MetavoxelSession::timedOut() { + qDebug() << "Session timed out [sessionId=" << _sessionId << ", sender=" << _sender << "]\n"; + _server->removeSession(_sessionId); +} + +void MetavoxelSession::sendData(const QByteArray& data) { + NodeList::getInstance()->getNodeSocket().writeDatagram(data, _sender.getAddress(), _sender.getPort()); +} + +void MetavoxelSession::readPacket(Bitstream& in) { + QVariant message; + in >> message; + handleMessage(message, in); +} + +void MetavoxelSession::clearSendRecordsBefore(int index) { + _sendRecords.erase(_sendRecords.begin(), _sendRecords.begin() + index + 1); +} + +void MetavoxelSession::handleMessage(const QVariant& message, Bitstream& in) { + int userType = message.userType(); + if (userType == ClientStateMessage::Type) { + ClientStateMessage state = message.value(); + _position = state.position; + + } else if (userType == MetavoxelDeltaMessage::Type) { + + + } else if (userType == QMetaType::QVariantList) { + foreach (const QVariant& element, message.toList()) { + handleMessage(element, in); + } + } +} diff --git a/assignment-client/src/metavoxels/MetavoxelServer.h b/assignment-client/src/metavoxels/MetavoxelServer.h new file mode 100644 index 0000000000..407c520116 --- /dev/null +++ b/assignment-client/src/metavoxels/MetavoxelServer.h @@ -0,0 +1,102 @@ +// +// MetavoxelServer.h +// hifi +// +// Created by Andrzej Kapolka on 12/18/13. +// Copyright (c) 2013 High Fidelity, Inc. All rights reserved. +// + +#ifndef __hifi__MetavoxelServer__ +#define __hifi__MetavoxelServer__ + +#include +#include +#include +#include + +#include +#include + +#include +#include + +class MetavoxelSession; + +/// Maintains a shared metavoxel system, accepting change requests and broadcasting updates. +class MetavoxelServer : public ThreadedAssignment { + Q_OBJECT + +public: + + MetavoxelServer(const unsigned char* dataBuffer, int numBytes); + + const MetavoxelDataPointer& getData() const { return _data; } + + void removeSession(const QUuid& sessionId); + + virtual void run(); + + virtual void processDatagram(const QByteArray& dataByteArray, const HifiSockAddr& senderSockAddr); + +private slots: + + void sendDeltas(); + +private: + + void processData(const QByteArray& data, const HifiSockAddr& sender); + + QTimer _sendTimer; + qint64 _lastSend; + + QHash _sessions; + + MetavoxelDataPointer _data; +}; + +/// Contains the state of a single client session. +class MetavoxelSession : public QObject { + Q_OBJECT + +public: + + MetavoxelSession(MetavoxelServer* server, const QUuid& sessionId, const QByteArray& datagramHeader); + + void receivedData(const QByteArray& data, const HifiSockAddr& sender); + + void sendDelta(); + +private slots: + + void timedOut(); + + void sendData(const QByteArray& data); + + void readPacket(Bitstream& in); + + void clearSendRecordsBefore(int index); + +private: + + void handleMessage(const QVariant& message, Bitstream& in); + + class SendRecord { + public: + int packetNumber; + MetavoxelDataPointer data; + }; + + MetavoxelServer* _server; + QUuid _sessionId; + + QTimer _timeoutTimer; + DatagramSequencer _sequencer; + + HifiSockAddr _sender; + + glm::vec3 _position; + + QList _sendRecords; +}; + +#endif /* defined(__hifi__MetavoxelServer__) */ diff --git a/cmake/macros/AutoMTC.cmake b/cmake/macros/AutoMTC.cmake new file mode 100644 index 0000000000..fca8170e64 --- /dev/null +++ b/cmake/macros/AutoMTC.cmake @@ -0,0 +1,21 @@ +macro(AUTO_MTC TARGET ROOT_DIR) + if (NOT TARGET mtc) + add_subdirectory(${ROOT_DIR}/tools/mtc ${ROOT_DIR}/tools/mtc) + endif (NOT TARGET mtc) + + file(GLOB INCLUDE_FILES src/*.h) + + add_custom_command(OUTPUT ${TARGET}_automtc.cpp COMMAND mtc -o ${TARGET}_automtc.cpp + ${INCLUDE_FILES} DEPENDS mtc ${INCLUDE_FILES}) + + find_package(Qt5Core REQUIRED) + + add_library(${TARGET}_automtc STATIC ${TARGET}_automtc.cpp) + + qt5_use_modules(${TARGET}_automtc Core) + + target_link_libraries(${TARGET} ${TARGET}_automtc) + +endmacro() + + diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index ee6caaaf9b..ec1875668a 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -37,6 +37,7 @@ DomainServer::DomainServer(int argc, char* argv[]) : _staticAssignmentFile(QString("%1/config.ds").arg(QCoreApplication::applicationDirPath())), _staticAssignmentFileData(NULL), _voxelServerConfig(NULL), + _metavoxelServerConfig(NULL), _hasCompletedRestartHold(false) { DomainServer::setDomainServerInstance(this); @@ -55,6 +56,9 @@ DomainServer::DomainServer(int argc, char* argv[]) : const char PARTICLE_CONFIG_OPTION[] = "--particleServerConfig"; _particleServerConfig = getCmdOption(argc, (const char**) argv, PARTICLE_CONFIG_OPTION); + const char METAVOXEL_CONFIG_OPTION[] = "--metavoxelServerConfig"; + _metavoxelServerConfig = getCmdOption(argc, (const char**)argv, METAVOXEL_CONFIG_OPTION); + // setup the mongoose web server struct mg_callbacks callbacks = {}; @@ -152,10 +156,11 @@ void DomainServer::readAvailableDatagrams() { int numBytesPublicSocket = HifiSockAddr::unpackSockAddr(packetData + packetIndex, nodeLocalAddress); packetIndex += numBytesPublicSocket; - const char STATICALLY_ASSIGNED_NODES[3] = { + const char STATICALLY_ASSIGNED_NODES[] = { NODE_TYPE_AUDIO_MIXER, NODE_TYPE_AVATAR_MIXER, - NODE_TYPE_VOXEL_SERVER + NODE_TYPE_VOXEL_SERVER, + NODE_TYPE_METAVOXEL_SERVER }; Assignment* matchingStaticAssignment = NULL; @@ -612,6 +617,13 @@ void DomainServer::prepopulateStaticAssignmentFile() { freshStaticAssignments[numFreshStaticAssignments++] = rootParticleServerAssignment; } + // handle metavoxel configuration command line argument + Assignment& metavoxelAssignment = (freshStaticAssignments[numFreshStaticAssignments++] = + Assignment(Assignment::CreateCommand, Assignment::MetavoxelServerType)); + if (_metavoxelServerConfig) { + metavoxelAssignment.setPayload((const unsigned char*)_metavoxelServerConfig, strlen(_metavoxelServerConfig)); + } + qDebug() << "Adding" << numFreshStaticAssignments << "static assignments to fresh file.\n"; _staticAssignmentFile.open(QIODevice::WriteOnly); @@ -787,4 +799,4 @@ void DomainServer::addStaticAssignmentsBackToQueueAfterRestart() { void DomainServer::cleanup() { _staticAssignmentFile.unmap(_staticAssignmentFileData); _staticAssignmentFile.close(); -} \ No newline at end of file +} diff --git a/domain-server/src/DomainServer.h b/domain-server/src/DomainServer.h index e6047fed9a..10cb58b786 100644 --- a/domain-server/src/DomainServer.h +++ b/domain-server/src/DomainServer.h @@ -62,6 +62,7 @@ private: const char* _voxelServerConfig; const char* _particleServerConfig; + const char* _metavoxelServerConfig; bool _hasCompletedRestartHold; private slots: diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 5332f20b21..f24366f268 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -208,8 +208,8 @@ Application::Application(int& argc, char** argv, timeval &startup_time) : #endif // tell the NodeList instance who to tell the domain server we care about - const char nodeTypesOfInterest[] = {NODE_TYPE_AUDIO_MIXER, NODE_TYPE_AVATAR_MIXER, NODE_TYPE_VOXEL_SERVER, - NODE_TYPE_PARTICLE_SERVER}; + const char nodeTypesOfInterest[] = {NODE_TYPE_AUDIO_MIXER, NODE_TYPE_AVATAR_MIXER, NODE_TYPE_VOXEL_SERVER, + NODE_TYPE_PARTICLE_SERVER, NODE_TYPE_METAVOXEL_SERVER}; nodeList->setNodeTypesOfInterest(nodeTypesOfInterest, sizeof(nodeTypesOfInterest)); QTimer* silentNodeTimer = new QTimer(this); @@ -1281,7 +1281,7 @@ void Application::wheelEvent(QWheelEvent* event) { void Application::sendPingPackets() { const char nodesToPing[] = {NODE_TYPE_VOXEL_SERVER, NODE_TYPE_PARTICLE_SERVER, - NODE_TYPE_AUDIO_MIXER, NODE_TYPE_AVATAR_MIXER}; + NODE_TYPE_AUDIO_MIXER, NODE_TYPE_AVATAR_MIXER, NODE_TYPE_METAVOXEL_SERVER}; unsigned char pingPacket[MAX_PACKET_SIZE]; int length = NodeList::getInstance()->fillPingPacket(pingPacket); @@ -4411,6 +4411,10 @@ void* Application::networkReceive(void* args) { app->_voxelProcessor.queueReceivedPacket(senderSockAddr, app->_incomingPacket, bytesReceived); break; } + case PACKET_TYPE_METAVOXEL_DATA: + app->_metavoxels.processData(QByteArray((const char*)app->_incomingPacket, bytesReceived), + senderSockAddr); + break; case PACKET_TYPE_BULK_AVATAR_DATA: NodeList::getInstance()->processBulkNodeData(senderSockAddr, app->_incomingPacket, diff --git a/interface/src/Application.h b/interface/src/Application.h index 6f0463bc88..f8aea3f808 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -37,6 +37,7 @@ #include "Cloud.h" #include "Environment.h" #include "GLCanvas.h" +#include "MetavoxelSystem.h" #include "PacketHeaders.h" #include "PieMenu.h" #include "Stars.h" @@ -59,7 +60,6 @@ #include "renderer/AmbientOcclusionEffect.h" #include "renderer/GeometryCache.h" #include "renderer/GlowEffect.h" -#include "renderer/MetavoxelSystem.h" #include "renderer/PointShader.h" #include "renderer/TextureCache.h" #include "renderer/VoxelShader.h" diff --git a/interface/src/MetavoxelSystem.cpp b/interface/src/MetavoxelSystem.cpp new file mode 100644 index 0000000000..355b027e93 --- /dev/null +++ b/interface/src/MetavoxelSystem.cpp @@ -0,0 +1,244 @@ +// +// MetavoxelSystem.cpp +// interface +// +// Created by Andrzej Kapolka on 12/10/13. +// Copyright (c) 2013 High Fidelity, Inc. All rights reserved. +// + +#include +#include +#include + +#include + +#include +#include + +#include "Application.h" +#include "MetavoxelSystem.h" + +ProgramObject MetavoxelSystem::_program; +int MetavoxelSystem::_pointScaleLocation; + +MetavoxelSystem::MetavoxelSystem() : + _pointVisitor(_points), + _buffer(QOpenGLBuffer::VertexBuffer) { +} + +MetavoxelSystem::~MetavoxelSystem() { + NodeList::getInstance()->removeHook(this); +} + +void MetavoxelSystem::init() { + if (!_program.isLinked()) { + switchToResourcesParentIfRequired(); + _program.addShaderFromSourceFile(QGLShader::Vertex, "resources/shaders/metavoxel_point.vert"); + _program.link(); + + _pointScaleLocation = _program.uniformLocation("pointScale"); + } + + NodeList::getInstance()->addHook(this); + + AttributeRegistry::getInstance()->configureScriptEngine(&_scriptEngine); + + QFile scriptFile("resources/scripts/sphere.js"); + scriptFile.open(QIODevice::ReadOnly); + QScriptValue guideFunction = _scriptEngine.evaluate(QTextStream(&scriptFile).readAll()); + _data.setAttributeValue(MetavoxelPath(), AttributeValue(AttributeRegistry::getInstance()->getGuideAttribute(), + encodeInline(PolymorphicDataPointer(new ScriptedMetavoxelGuide(guideFunction))))); + + _buffer.setUsagePattern(QOpenGLBuffer::DynamicDraw); + _buffer.create(); +} + +void MetavoxelSystem::processData(const QByteArray& data, const HifiSockAddr& sender) { + QMetaObject::invokeMethod(this, "receivedData", Q_ARG(const QByteArray&, data), Q_ARG(const HifiSockAddr&, sender)); +} + +void MetavoxelSystem::simulate(float deltaTime) { + // simulate the clients + foreach (MetavoxelClient* client, _clients) { + client->simulate(deltaTime); + } + + _points.clear(); + _data.guide(_pointVisitor); + + _buffer.bind(); + int bytes = _points.size() * sizeof(Point); + if (_buffer.size() < bytes) { + _buffer.allocate(_points.constData(), bytes); + } else { + _buffer.write(0, _points.constData(), bytes); + } + _buffer.release(); +} + +void MetavoxelSystem::render() { + int viewport[4]; + glGetIntegerv(GL_VIEWPORT, viewport); + const int VIEWPORT_WIDTH_INDEX = 2; + const int VIEWPORT_HEIGHT_INDEX = 3; + float viewportWidth = viewport[VIEWPORT_WIDTH_INDEX]; + float viewportHeight = viewport[VIEWPORT_HEIGHT_INDEX]; + float viewportDiagonal = sqrtf(viewportWidth*viewportWidth + viewportHeight*viewportHeight); + float worldDiagonal = glm::distance(Application::getInstance()->getViewFrustum()->getNearBottomLeft(), + Application::getInstance()->getViewFrustum()->getNearTopRight()); + + _program.bind(); + _program.setUniformValue(_pointScaleLocation, viewportDiagonal * + Application::getInstance()->getViewFrustum()->getNearClip() / worldDiagonal); + + _buffer.bind(); + + Point* pt = 0; + glVertexPointer(4, GL_FLOAT, sizeof(Point), &pt->vertex); + glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(Point), &pt->color); + glNormalPointer(GL_BYTE, sizeof(Point), &pt->normal); + + glEnableClientState(GL_VERTEX_ARRAY); + glEnableClientState(GL_COLOR_ARRAY); + glEnableClientState(GL_NORMAL_ARRAY); + + glEnable(GL_VERTEX_PROGRAM_POINT_SIZE_ARB); + + glDrawArrays(GL_POINTS, 0, _points.size()); + + glDisable(GL_VERTEX_PROGRAM_POINT_SIZE_ARB); + + glDisableClientState(GL_VERTEX_ARRAY); + glDisableClientState(GL_COLOR_ARRAY); + glDisableClientState(GL_NORMAL_ARRAY); + + _buffer.release(); + + _program.release(); +} + +void MetavoxelSystem::nodeAdded(Node* node) { + if (node->getType() == NODE_TYPE_METAVOXEL_SERVER) { + QMetaObject::invokeMethod(this, "addClient", Q_ARG(const QUuid&, node->getUUID()), + Q_ARG(const HifiSockAddr&, node->getLocalSocket())); + } +} + +void MetavoxelSystem::nodeKilled(Node* node) { + if (node->getType() == NODE_TYPE_METAVOXEL_SERVER) { + QMetaObject::invokeMethod(this, "removeClient", Q_ARG(const QUuid&, node->getUUID())); + } +} + +void MetavoxelSystem::addClient(const QUuid& uuid, const HifiSockAddr& address) { + MetavoxelClient* client = new MetavoxelClient(address); + _clients.insert(uuid, client); + _clientsBySessionID.insert(client->getSessionID(), client); +} + +void MetavoxelSystem::removeClient(const QUuid& uuid) { + MetavoxelClient* client = _clients.take(uuid); + _clientsBySessionID.remove(client->getSessionID()); + delete client; +} + +void MetavoxelSystem::receivedData(const QByteArray& data, const HifiSockAddr& sender) { + int headerPlusIDSize; + QUuid sessionID = readSessionID(data, sender, headerPlusIDSize); + if (sessionID.isNull()) { + return; + } + MetavoxelClient* client = _clientsBySessionID.value(sessionID); + if (client) { + client->receivedData(data, sender); + } +} + +MetavoxelSystem::PointVisitor::PointVisitor(QVector& points) : + MetavoxelVisitor(QVector() << + AttributeRegistry::getInstance()->getColorAttribute() << + AttributeRegistry::getInstance()->getNormalAttribute()), + _points(points) { +} + +bool MetavoxelSystem::PointVisitor::visit(const MetavoxelInfo& info) { + if (!info.isLeaf) { + return true; + } + QRgb color = info.attributeValues.at(0).getInlineValue(); + QRgb normal = info.attributeValues.at(1).getInlineValue(); + int alpha = qAlpha(color); + if (alpha > 0) { + Point point = { glm::vec4(info.minimum + glm::vec3(info.size, info.size, info.size) * 0.5f, info.size), + { qRed(color), qGreen(color), qBlue(color), alpha }, { qRed(normal), qGreen(normal), qBlue(normal) } }; + _points.append(point); + } + return false; +} + +static QByteArray createDatagramHeader(const QUuid& sessionID) { + QByteArray header(MAX_PACKET_HEADER_BYTES, 0); + populateTypeAndVersion(reinterpret_cast(header.data()), PACKET_TYPE_METAVOXEL_DATA); + header += sessionID.toRfc4122(); + return header; +} + +MetavoxelClient::MetavoxelClient(const HifiSockAddr& address) : + _address(address), + _sessionID(QUuid::createUuid()), + _sequencer(createDatagramHeader(_sessionID)), + _data(new MetavoxelData()) { + + connect(&_sequencer, SIGNAL(readyToWrite(const QByteArray&)), SLOT(sendData(const QByteArray&))); + connect(&_sequencer, SIGNAL(readyToRead(Bitstream&)), SLOT(readPacket(Bitstream&))); + connect(&_sequencer, SIGNAL(receiveAcknowledged(int)), SLOT(clearReceiveRecordsBefore(int))); + + // insert the baseline receive record + ReceiveRecord record = { 0, _data }; + _receiveRecords.append(record); +} + +void MetavoxelClient::simulate(float deltaTime) { + Bitstream& out = _sequencer.startPacket(); + ClientStateMessage state = { Application::getInstance()->getCamera()->getPosition() }; + out << QVariant::fromValue(state); + _sequencer.endPacket(); +} + +void MetavoxelClient::receivedData(const QByteArray& data, const HifiSockAddr& sender) { + // save the most recent sender + _address = sender; + + // process through sequencer + _sequencer.receivedDatagram(data); +} + +void MetavoxelClient::sendData(const QByteArray& data) { + NodeList::getInstance()->getNodeSocket().writeDatagram(data, _address.getAddress(), _address.getPort()); +} + +void MetavoxelClient::readPacket(Bitstream& in) { + QVariant message; + in >> message; + handleMessage(message, in); + + // record the receipt + ReceiveRecord record = { _sequencer.getIncomingPacketNumber(), _data }; + _receiveRecords.append(record); +} + +void MetavoxelClient::clearReceiveRecordsBefore(int index) { + _receiveRecords.erase(_receiveRecords.begin(), _receiveRecords.begin() + index + 1); +} + +void MetavoxelClient::handleMessage(const QVariant& message, Bitstream& in) { + int userType = message.userType(); + if (userType == MetavoxelDeltaMessage::Type) { + readDelta(_data, _receiveRecords.first().data, in); + + } else if (userType == QMetaType::QVariantList) { + foreach (const QVariant& element, message.toList()) { + handleMessage(element, in); + } + } +} diff --git a/interface/src/MetavoxelSystem.h b/interface/src/MetavoxelSystem.h new file mode 100644 index 0000000000..ddb69644d5 --- /dev/null +++ b/interface/src/MetavoxelSystem.h @@ -0,0 +1,124 @@ +// +// MetavoxelSystem.h +// interface +// +// Created by Andrzej Kapolka on 12/10/13. +// Copyright (c) 2013 High Fidelity, Inc. All rights reserved. +// + +#ifndef __interface__MetavoxelSystem__ +#define __interface__MetavoxelSystem__ + +#include +#include +#include +#include + +#include + +#include + +#include +#include + +#include "renderer/ProgramObject.h" + +class MetavoxelClient; + +/// Renders a metavoxel tree. +class MetavoxelSystem : public QObject, public NodeListHook { + Q_OBJECT + +public: + + MetavoxelSystem(); + ~MetavoxelSystem(); + + void init(); + + void processData(const QByteArray& data, const HifiSockAddr& sender); + + void simulate(float deltaTime); + void render(); + + virtual void nodeAdded(Node* node); + virtual void nodeKilled(Node* node); + +private: + + Q_INVOKABLE void addClient(const QUuid& uuid, const HifiSockAddr& address); + Q_INVOKABLE void removeClient(const QUuid& uuid); + Q_INVOKABLE void receivedData(const QByteArray& data, const HifiSockAddr& sender); + + class Point { + public: + glm::vec4 vertex; + quint8 color[4]; + quint8 normal[3]; + }; + + class PointVisitor : public MetavoxelVisitor { + public: + PointVisitor(QVector& points); + virtual bool visit(const MetavoxelInfo& info); + + private: + QVector& _points; + }; + + static ProgramObject _program; + static int _pointScaleLocation; + + QScriptEngine _scriptEngine; + MetavoxelData _data; + QVector _points; + PointVisitor _pointVisitor; + QOpenGLBuffer _buffer; + + QHash _clients; + QHash _clientsBySessionID; +}; + +/// A client session associated with a single server. +class MetavoxelClient : public QObject { + Q_OBJECT + +public: + + MetavoxelClient(const HifiSockAddr& address); + + const QUuid& getSessionID() const { return _sessionID; } + + void simulate(float deltaTime); + + void receivedData(const QByteArray& data, const HifiSockAddr& sender); + +private slots: + + void sendData(const QByteArray& data); + + void readPacket(Bitstream& in); + + void clearReceiveRecordsBefore(int index); + +private: + + void handleMessage(const QVariant& message, Bitstream& in); + + class ReceiveRecord { + public: + int packetNumber; + MetavoxelDataPointer data; + }; + + HifiSockAddr _address; + QUuid _sessionID; + + DatagramSequencer _sequencer; + + MetavoxelDataPointer _data; + + QList _receiveRecords; +}; + +#endif /* defined(__interface__MetavoxelSystem__) */ diff --git a/interface/src/renderer/MetavoxelSystem.cpp b/interface/src/renderer/MetavoxelSystem.cpp deleted file mode 100644 index 543a8b6301..0000000000 --- a/interface/src/renderer/MetavoxelSystem.cpp +++ /dev/null @@ -1,122 +0,0 @@ -// -// MetavoxelSystem.cpp -// interface -// -// Created by Andrzej Kapolka on 12/10/13. -// Copyright (c) 2013 High Fidelity, Inc. All rights reserved. -// - -#include -#include -#include - -#include - -#include "Application.h" -#include "MetavoxelSystem.h" - -ProgramObject MetavoxelSystem::_program; -int MetavoxelSystem::_pointScaleLocation; - -MetavoxelSystem::MetavoxelSystem() : - _pointVisitor(_points), - _buffer(QOpenGLBuffer::VertexBuffer) { -} - -void MetavoxelSystem::init() { - if (!_program.isLinked()) { - switchToResourcesParentIfRequired(); - _program.addShaderFromSourceFile(QGLShader::Vertex, "resources/shaders/metavoxel_point.vert"); - _program.link(); - - _pointScaleLocation = _program.uniformLocation("pointScale"); - } - - AttributeRegistry::getInstance()->configureScriptEngine(&_scriptEngine); - - QFile scriptFile("resources/scripts/sphere.js"); - scriptFile.open(QIODevice::ReadOnly); - QScriptValue guideFunction = _scriptEngine.evaluate(QTextStream(&scriptFile).readAll()); - _data.setAttributeValue(MetavoxelPath(), AttributeValue(AttributeRegistry::getInstance()->getGuideAttribute(), - encodeInline(PolymorphicDataPointer(new ScriptedMetavoxelGuide(guideFunction))))); - - _buffer.setUsagePattern(QOpenGLBuffer::DynamicDraw); - _buffer.create(); -} - -void MetavoxelSystem::simulate(float deltaTime) { - _points.clear(); - _data.guide(_pointVisitor); - - _buffer.bind(); - int bytes = _points.size() * sizeof(Point); - if (_buffer.size() < bytes) { - _buffer.allocate(_points.constData(), bytes); - } else { - _buffer.write(0, _points.constData(), bytes); - } - _buffer.release(); -} - -void MetavoxelSystem::render() { - int viewport[4]; - glGetIntegerv(GL_VIEWPORT, viewport); - const int VIEWPORT_WIDTH_INDEX = 2; - const int VIEWPORT_HEIGHT_INDEX = 3; - float viewportWidth = viewport[VIEWPORT_WIDTH_INDEX]; - float viewportHeight = viewport[VIEWPORT_HEIGHT_INDEX]; - float viewportDiagonal = sqrtf(viewportWidth*viewportWidth + viewportHeight*viewportHeight); - float worldDiagonal = glm::distance(Application::getInstance()->getViewFrustum()->getNearBottomLeft(), - Application::getInstance()->getViewFrustum()->getNearTopRight()); - - _program.bind(); - _program.setUniformValue(_pointScaleLocation, viewportDiagonal * - Application::getInstance()->getViewFrustum()->getNearClip() / worldDiagonal); - - _buffer.bind(); - - Point* pt = 0; - glVertexPointer(4, GL_FLOAT, sizeof(Point), &pt->vertex); - glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(Point), &pt->color); - glNormalPointer(GL_BYTE, sizeof(Point), &pt->normal); - - glEnableClientState(GL_VERTEX_ARRAY); - glEnableClientState(GL_COLOR_ARRAY); - glEnableClientState(GL_NORMAL_ARRAY); - - glEnable(GL_VERTEX_PROGRAM_POINT_SIZE_ARB); - - glDrawArrays(GL_POINTS, 0, _points.size()); - - glDisable(GL_VERTEX_PROGRAM_POINT_SIZE_ARB); - - glDisableClientState(GL_VERTEX_ARRAY); - glDisableClientState(GL_COLOR_ARRAY); - glDisableClientState(GL_NORMAL_ARRAY); - - _buffer.release(); - - _program.release(); -} - -MetavoxelSystem::PointVisitor::PointVisitor(QVector& points) : - MetavoxelVisitor(QVector() << - AttributeRegistry::getInstance()->getColorAttribute() << - AttributeRegistry::getInstance()->getNormalAttribute()), - _points(points) { -} - -bool MetavoxelSystem::PointVisitor::visit(const MetavoxelInfo& info) { - if (!info.isLeaf) { - return true; - } - QRgb color = info.attributeValues.at(0).getInlineValue(); - QRgb normal = info.attributeValues.at(1).getInlineValue(); - int alpha = qAlpha(color); - if (alpha > 0) { - Point point = { glm::vec4(info.minimum + glm::vec3(info.size, info.size, info.size) * 0.5f, info.size), - { qRed(color), qGreen(color), qBlue(color), alpha }, { qRed(normal), qGreen(normal), qBlue(normal) } }; - _points.append(point); - } - return false; -} diff --git a/interface/src/renderer/MetavoxelSystem.h b/interface/src/renderer/MetavoxelSystem.h deleted file mode 100644 index b8617d99a4..0000000000 --- a/interface/src/renderer/MetavoxelSystem.h +++ /dev/null @@ -1,61 +0,0 @@ -// -// MetavoxelSystem.h -// interface -// -// Created by Andrzej Kapolka on 12/10/13. -// Copyright (c) 2013 High Fidelity, Inc. All rights reserved. -// - -#ifndef __interface__MetavoxelSystem__ -#define __interface__MetavoxelSystem__ - -#include -#include -#include - -#include - -#include - -#include "ProgramObject.h" - -/// Renders a metavoxel tree. -class MetavoxelSystem { -public: - - MetavoxelSystem(); - - void init(); - - void simulate(float deltaTime); - void render(); - -private: - - class Point { - public: - glm::vec4 vertex; - quint8 color[4]; - quint8 normal[3]; - }; - - class PointVisitor : public MetavoxelVisitor { - public: - PointVisitor(QVector& points); - virtual bool visit(const MetavoxelInfo& info); - - private: - QVector& _points; - }; - - static ProgramObject _program; - static int _pointScaleLocation; - - QScriptEngine _scriptEngine; - MetavoxelData _data; - QVector _points; - PointVisitor _pointVisitor; - QOpenGLBuffer _buffer; -}; - -#endif /* defined(__interface__MetavoxelSystem__) */ diff --git a/libraries/metavoxels/CMakeLists.txt b/libraries/metavoxels/CMakeLists.txt index 0f9c1c695c..98b2baf7ac 100644 --- a/libraries/metavoxels/CMakeLists.txt +++ b/libraries/metavoxels/CMakeLists.txt @@ -13,6 +13,9 @@ find_package(Qt5Widgets REQUIRED) include(${MACRO_DIR}/SetupHifiLibrary.cmake) setup_hifi_library(${TARGET_NAME}) +include(${MACRO_DIR}/AutoMTC.cmake) +auto_mtc(${TARGET_NAME} ${ROOT_DIR}) + qt5_use_modules(${TARGET_NAME} Widgets Script) include(${MACRO_DIR}/IncludeGLM.cmake) diff --git a/libraries/metavoxels/src/AttributeRegistry.cpp b/libraries/metavoxels/src/AttributeRegistry.cpp index 5fb03035da..9a1f220034 100644 --- a/libraries/metavoxels/src/AttributeRegistry.cpp +++ b/libraries/metavoxels/src/AttributeRegistry.cpp @@ -11,7 +11,12 @@ #include "AttributeRegistry.h" #include "MetavoxelData.h" -AttributeRegistry AttributeRegistry::_instance; +REGISTER_META_OBJECT(QRgbAttribute) + +AttributeRegistry* AttributeRegistry::getInstance() { + static AttributeRegistry registry; + return ®istry; +} AttributeRegistry::AttributeRegistry() : _guideAttribute(registerAttribute(new PolymorphicAttribute("guide", PolymorphicDataPointer(new DefaultMetavoxelGuide())))), @@ -36,7 +41,7 @@ AttributePointer AttributeRegistry::registerAttribute(AttributePointer attribute } QScriptValue AttributeRegistry::getAttribute(QScriptContext* context, QScriptEngine* engine) { - return engine->newQObject(_instance.getAttribute(context->argument(0).toString()).data(), QScriptEngine::QtOwnership, + return engine->newQObject(getInstance()->getAttribute(context->argument(0).toString()).data(), QScriptEngine::QtOwnership, QScriptEngine::PreferExistingWrapperObject); } diff --git a/libraries/metavoxels/src/AttributeRegistry.h b/libraries/metavoxels/src/AttributeRegistry.h index a5f9c08f8b..4f2f1d79b2 100644 --- a/libraries/metavoxels/src/AttributeRegistry.h +++ b/libraries/metavoxels/src/AttributeRegistry.h @@ -32,7 +32,7 @@ class AttributeRegistry { public: /// Returns a pointer to the singleton registry instance. - static AttributeRegistry* getInstance() { return &_instance; } + static AttributeRegistry* getInstance(); AttributeRegistry(); @@ -65,8 +65,6 @@ private: static QScriptValue getAttribute(QScriptContext* context, QScriptEngine* engine); - static AttributeRegistry _instance; - QHash _attributes; AttributePointer _guideAttribute; AttributePointer _colorAttribute; @@ -140,8 +138,11 @@ public: virtual void* create(void* copy) const = 0; virtual void destroy(void* value) const = 0; - virtual bool read(Bitstream& in, void*& value) const = 0; - virtual bool write(Bitstream& out, void* value) const = 0; + virtual void read(Bitstream& in, void*& value, bool isLeaf) const = 0; + virtual void write(Bitstream& out, void* value, bool isLeaf) const = 0; + + virtual void readDelta(Bitstream& in, void*& value, void* reference, bool isLeaf) const { read(in, value, isLeaf); } + virtual void writeDelta(Bitstream& out, void* value, void* reference, bool isLeaf) const { write(out, value, isLeaf); } virtual bool equal(void* first, void* second) const = 0; @@ -163,18 +164,31 @@ public: virtual void* create(void* copy) const { void* value; new (&value) T(*(T*)©); return value; } virtual void destroy(void* value) const { ((T*)&value)->~T(); } - virtual bool read(Bitstream& in, void*& value) const { value = getDefaultValue(); in.read(&value, bits); return false; } - virtual bool write(Bitstream& out, void* value) const { out.write(&value, bits); return false; } + virtual void read(Bitstream& in, void*& value, bool isLeaf) const; + virtual void write(Bitstream& out, void* value, bool isLeaf) const; virtual bool equal(void* first, void* second) const { return decodeInline(first) == decodeInline(second); } virtual void* getDefaultValue() const { return encodeInline(_defaultValue); } -private: +protected: T _defaultValue; }; +template inline void InlineAttribute::read(Bitstream& in, void*& value, bool isLeaf) const { + if (isLeaf) { + value = getDefaultValue(); + in.read(&value, bits); + } +} + +template inline void InlineAttribute::write(Bitstream& out, void* value, bool isLeaf) const { + if (isLeaf) { + out.write(&value, bits); + } +} + /// Provides merging using the =, ==, += and /= operators. template class SimpleInlineAttribute : public InlineAttribute { public: @@ -198,9 +212,12 @@ template inline bool SimpleInlineAttribute::merge(vo /// Provides appropriate averaging for RGBA values. class QRgbAttribute : public InlineAttribute { + Q_OBJECT + Q_PROPERTY(int defaultValue MEMBER _defaultValue) + public: - QRgbAttribute(const QString& name, QRgb defaultValue = QRgb()); + Q_INVOKABLE QRgbAttribute(const QString& name = QString(), QRgb defaultValue = QRgb()); virtual bool merge(void*& parent, void* children[]) const; @@ -216,8 +233,8 @@ public: virtual void* create(void* copy) const { new T(*static_cast(copy)); } virtual void destroy(void* value) const { delete static_cast(value); } - virtual bool read(Bitstream& in, void*& value) const { in >> *static_cast(value); return true; } - virtual bool write(Bitstream& out, void* value) const { out << *static_cast(value); return true; } + virtual void read(Bitstream& in, void*& value, bool isLeaf) const; + virtual void write(Bitstream& out, void* value, bool isLeaf) const; virtual bool equal(void* first, void* second) const { return *static_cast(first) == *static_cast(second); } @@ -228,6 +245,18 @@ private: T _defaultValue; }; +template inline void PointerAttribute::read(Bitstream& in, void*& value, bool isLeaf) const { + if (isLeaf) { + in.read(value, sizeof(T) * 8); + } +} + +template inline void PointerAttribute::write(Bitstream& out, void* value, bool isLeaf) const { + if (isLeaf) { + out.write(value, sizeof(T) * 8); + } +} + /// Provides merging using the =, ==, += and /= operators. template class SimplePointerAttribute : public PointerAttribute { public: diff --git a/libraries/metavoxels/src/Bitstream.cpp b/libraries/metavoxels/src/Bitstream.cpp index ac91bdf767..e1ab79c974 100644 --- a/libraries/metavoxels/src/Bitstream.cpp +++ b/libraries/metavoxels/src/Bitstream.cpp @@ -6,12 +6,68 @@ // Copyright (c) 2013 High Fidelity, Inc. All rights reserved. // -#include +#include +#include +#include +#include + +#include "AttributeRegistry.h" #include "Bitstream.h" -Bitstream::Bitstream(QDataStream& underlying) - : _underlying(underlying), _byte(0), _position(0) { +REGISTER_SIMPLE_TYPE_STREAMER(QByteArray) +REGISTER_SIMPLE_TYPE_STREAMER(QString) +REGISTER_SIMPLE_TYPE_STREAMER(QVariantList) +REGISTER_SIMPLE_TYPE_STREAMER(bool) +REGISTER_SIMPLE_TYPE_STREAMER(int) + +IDStreamer::IDStreamer(Bitstream& stream) : + _stream(stream), + _bits(1) { +} + +void IDStreamer::setBitsFromValue(int value) { + _bits = 1; + while (value >= (1 << _bits) - 1) { + _bits++; + } +} + +IDStreamer& IDStreamer::operator<<(int value) { + _stream.write(&value, _bits); + if (value == (1 << _bits) - 1) { + _bits++; + } + return *this; +} + +IDStreamer& IDStreamer::operator>>(int& value) { + value = 0; + _stream.read(&value, _bits); + if (value == (1 << _bits) - 1) { + _bits++; + } + return *this; +} + +int Bitstream::registerMetaObject(const char* className, const QMetaObject* metaObject) { + getMetaObjects().insert(className, metaObject); + return 0; +} + +int Bitstream::registerTypeStreamer(int type, TypeStreamer* streamer) { + streamer->setType(type); + getTypeStreamers().insert(type, streamer); + return 0; +} + +Bitstream::Bitstream(QDataStream& underlying) : + _underlying(underlying), + _byte(0), + _position(0), + _metaObjectStreamer(*this), + _typeStreamerStreamer(*this), + _attributeStreamer(*this) { } const int BITS_IN_BYTE = 8; @@ -41,7 +97,8 @@ Bitstream& Bitstream::read(void* data, int bits, int offset) { _underlying >> _byte; } int bitsToRead = qMin(BITS_IN_BYTE - _position, qMin(BITS_IN_BYTE - offset, bits)); - *dest |= ((_byte >> _position) & ((1 << bitsToRead) - 1)) << offset; + int mask = ((1 << bitsToRead) - 1) << offset; + *dest = (*dest & ~mask) | (((_byte >> _position) << offset) & mask); _position = (_position + bitsToRead) & LAST_BIT_POSITION; if ((offset += bitsToRead) == BITS_IN_BYTE) { dest++; @@ -55,11 +112,40 @@ Bitstream& Bitstream::read(void* data, int bits, int offset) { void Bitstream::flush() { if (_position != 0) { _underlying << _byte; - _byte = 0; - _position = 0; + reset(); } } +void Bitstream::reset() { + _byte = 0; + _position = 0; +} + +Bitstream::WriteMappings Bitstream::getAndResetWriteMappings() { + WriteMappings mappings = { _metaObjectStreamer.getAndResetTransientOffsets(), + _typeStreamerStreamer.getAndResetTransientOffsets(), + _attributeStreamer.getAndResetTransientOffsets() }; + return mappings; +} + +void Bitstream::persistWriteMappings(const WriteMappings& mappings) { + _metaObjectStreamer.persistTransientOffsets(mappings.metaObjectOffsets); + _typeStreamerStreamer.persistTransientOffsets(mappings.typeStreamerOffsets); + _attributeStreamer.persistTransientOffsets(mappings.attributeOffsets); +} + +Bitstream::ReadMappings Bitstream::getAndResetReadMappings() { + ReadMappings mappings = { _metaObjectStreamer.getAndResetTransientValues(), + _typeStreamerStreamer.getAndResetTransientValues(), + _attributeStreamer.getAndResetTransientValues() }; + return mappings; +} + +void Bitstream::persistReadMappings(const ReadMappings& mappings) { + _metaObjectStreamer.persistTransientValues(mappings.metaObjectValues); + _typeStreamerStreamer.persistTransientValues(mappings.typeStreamerValues); + _attributeStreamer.persistTransientValues(mappings.attributeValues); +} Bitstream& Bitstream::operator<<(bool value) { if (value) { @@ -79,3 +165,171 @@ Bitstream& Bitstream::operator>>(bool& value) { _position = (_position + 1) & LAST_BIT_POSITION; return *this; } + +Bitstream& Bitstream::operator<<(int value) { + return write(&value, 32); +} + +Bitstream& Bitstream::operator>>(int& value) { + qint32 sizedValue; + read(&sizedValue, 32); + value = sizedValue; + return *this; +} + +Bitstream& Bitstream::operator<<(float value) { + return write(&value, 32); +} + +Bitstream& Bitstream::operator>>(float& value) { + return read(&value, 32); +} + +Bitstream& Bitstream::operator<<(const glm::vec3& value) { + return *this << value.x << value.y << value.z; +} + +Bitstream& Bitstream::operator>>(glm::vec3& value) { + return *this >> value.x >> value.y >> value.z; +} + +Bitstream& Bitstream::operator<<(const QByteArray& string) { + *this << string.size(); + return write(string.constData(), string.size() * BITS_IN_BYTE); +} + +Bitstream& Bitstream::operator>>(QByteArray& string) { + int size; + *this >> size; + string.resize(size); + return read(string.data(), size * BITS_IN_BYTE); +} + +Bitstream& Bitstream::operator<<(const QString& string) { + *this << string.size(); + return write(string.constData(), string.size() * sizeof(QChar) * BITS_IN_BYTE); +} + +Bitstream& Bitstream::operator>>(QString& string) { + int size; + *this >> size; + string.resize(size); + return read(string.data(), size * sizeof(QChar) * BITS_IN_BYTE); +} + +Bitstream& Bitstream::operator<<(const QVariant& value) { + const TypeStreamer* streamer = getTypeStreamers().value(value.userType()); + if (streamer) { + _typeStreamerStreamer << streamer; + streamer->write(*this, value); + } else { + qWarning() << "Non-streamable type: " << value.typeName() << "\n"; + } + return *this; +} + +Bitstream& Bitstream::operator>>(QVariant& value) { + const TypeStreamer* streamer; + _typeStreamerStreamer >> streamer; + if (streamer) { + value = streamer->read(*this); + } + return *this; +} + +Bitstream& Bitstream::operator<<(const QObject* object) { + if (!object) { + _metaObjectStreamer << NULL; + return *this; + } + const QMetaObject* metaObject = object->metaObject(); + _metaObjectStreamer << metaObject; + for (int i = 0; i < metaObject->propertyCount(); i++) { + QMetaProperty property = metaObject->property(i); + if (!property.isStored(object)) { + continue; + } + const TypeStreamer* streamer = getTypeStreamers().value(property.userType()); + if (streamer) { + streamer->write(*this, property.read(object)); + } + } + return *this; +} + +Bitstream& Bitstream::operator>>(QObject*& object) { + const QMetaObject* metaObject; + _metaObjectStreamer >> metaObject; + if (!metaObject) { + object = NULL; + return *this; + } + object = metaObject->newInstance(); + for (int i = 0; i < metaObject->propertyCount(); i++) { + QMetaProperty property = metaObject->property(i); + if (!property.isStored(object)) { + continue; + } + const TypeStreamer* streamer = getTypeStreamers().value(property.userType()); + if (streamer) { + property.write(object, streamer->read(*this)); + } + } + return *this; +} + +Bitstream& Bitstream::operator<<(const QMetaObject* metaObject) { + return *this << (metaObject ? QByteArray::fromRawData( + metaObject->className(), strlen(metaObject->className())) : QByteArray()); +} + +Bitstream& Bitstream::operator>>(const QMetaObject*& metaObject) { + QByteArray className; + *this >> className; + if (className.isEmpty()) { + metaObject = NULL; + return *this; + } + metaObject = getMetaObjects().value(className); + if (!metaObject) { + qWarning() << "Unknown class name: " << className << "\n"; + } + return *this; +} + +Bitstream& Bitstream::operator<<(const TypeStreamer* streamer) { + const char* typeName = QMetaType::typeName(streamer->getType()); + return *this << QByteArray::fromRawData(typeName, strlen(typeName)); +} + +Bitstream& Bitstream::operator>>(const TypeStreamer*& streamer) { + QByteArray typeName; + *this >> typeName; + streamer = getTypeStreamers().value(QMetaType::type(typeName.constData())); + if (!streamer) { + qWarning() << "Unknown type name: " << typeName << "\n"; + } + return *this; +} + +Bitstream& Bitstream::operator<<(const AttributePointer& attribute) { + return *this << (QObject*)attribute.data(); +} + +Bitstream& Bitstream::operator>>(AttributePointer& attribute) { + QObject* object; + *this >> object; + attribute = AttributeRegistry::getInstance()->registerAttribute(static_cast(object)); + return *this; +} + +QHash& Bitstream::getMetaObjects() { + static QHash metaObjects; + return metaObjects; +} + +QHash& Bitstream::getTypeStreamers() { + static QHash typeStreamers; + return typeStreamers; +} + diff --git a/libraries/metavoxels/src/Bitstream.h b/libraries/metavoxels/src/Bitstream.h index 12a1b88886..6709d4d03b 100644 --- a/libraries/metavoxels/src/Bitstream.h +++ b/libraries/metavoxels/src/Bitstream.h @@ -9,14 +9,174 @@ #ifndef __interface__Bitstream__ #define __interface__Bitstream__ -#include +#include +#include +#include +#include +#include +#include + +class QByteArray; class QDataStream; +struct QMetaObject; +class QObject; + +class Attribute; +class Bitstream; +class TypeStreamer; + +typedef QSharedPointer AttributePointer; + +/// Streams integer identifiers that conform to the following pattern: each ID encountered in the stream is either one that +/// has been sent (received) before, or is one more than the highest previously encountered ID (starting at zero). This allows +/// us to use the minimum number of bits to encode the IDs. +class IDStreamer { +public: + + IDStreamer(Bitstream& stream); + + void setBitsFromValue(int value); + + IDStreamer& operator<<(int value); + IDStreamer& operator>>(int& value); + +private: + + Bitstream& _stream; + int _bits; +}; + +/// Provides a means to stream repeated values efficiently. The value is first streamed along with a unique ID. When +/// subsequently streamed, only the ID is sent. +template class RepeatedValueStreamer { +public: + + RepeatedValueStreamer(Bitstream& stream) : _stream(stream), _idStreamer(stream), + _lastPersistentID(0), _lastTransientOffset(0) { } + + QHash getAndResetTransientOffsets(); + + void persistTransientOffsets(const QHash& transientOffsets); + + QHash getAndResetTransientValues(); + + void persistTransientValues(const QHash& transientValues); + + RepeatedValueStreamer& operator<<(T value); + RepeatedValueStreamer& operator>>(T& value); + +private: + + Bitstream& _stream; + IDStreamer _idStreamer; + int _lastPersistentID; + int _lastTransientOffset; + QHash _persistentIDs; + QHash _transientOffsets; + QHash _persistentValues; + QHash _transientValues; +}; + +template inline QHash RepeatedValueStreamer::getAndResetTransientOffsets() { + QHash transientOffsets; + _transientOffsets.swap(transientOffsets); + _lastTransientOffset = 0; + _idStreamer.setBitsFromValue(_lastPersistentID); + return transientOffsets; +} + +template inline void RepeatedValueStreamer::persistTransientOffsets(const QHash& transientOffsets) { + int oldLastPersistentID = _lastPersistentID; + for (typename QHash::const_iterator it = transientOffsets.constBegin(); it != transientOffsets.constEnd(); it++) { + int id = oldLastPersistentID + it.value(); + _lastPersistentID = qMax(_lastPersistentID, id); + _persistentIDs.insert(it.key(), id); + } + _idStreamer.setBitsFromValue(_lastPersistentID); +} + +template inline QHash RepeatedValueStreamer::getAndResetTransientValues() { + QHash transientValues; + _transientValues.swap(transientValues); + _idStreamer.setBitsFromValue(_lastPersistentID); + return transientValues; +} + +template inline void RepeatedValueStreamer::persistTransientValues(const QHash& transientValues) { + int oldLastPersistentID = _lastPersistentID; + for (typename QHash::const_iterator it = transientValues.constBegin(); it != transientValues.constEnd(); it++) { + int id = oldLastPersistentID + it.key(); + _lastPersistentID = qMax(_lastPersistentID, id); + _persistentValues.insert(id, it.value()); + } + _idStreamer.setBitsFromValue(_lastPersistentID); +} + +template inline RepeatedValueStreamer& RepeatedValueStreamer::operator<<(T value) { + int id = _persistentIDs.value(value); + if (id == 0) { + int& offset = _transientOffsets[value]; + if (offset == 0) { + _idStreamer << (_lastPersistentID + (offset = ++_lastTransientOffset)); + _stream << value; + + } else { + _idStreamer << (_lastPersistentID + offset); + } + } else { + _idStreamer << id; + } + return *this; +} + +template inline RepeatedValueStreamer& RepeatedValueStreamer::operator>>(T& value) { + int id; + _idStreamer >> id; + if (id <= _lastPersistentID) { + value = _persistentValues.value(id); + + } else { + int offset = id - _lastPersistentID; + typename QHash::iterator it = _transientValues.find(offset); + if (it == _transientValues.end()) { + _stream >> value; + _transientValues.insert(offset, value); + + } else { + value = *it; + } + } + return *this; +} /// A stream for bit-aligned data. class Bitstream { public: + class WriteMappings { + public: + QHash metaObjectOffsets; + QHash typeStreamerOffsets; + QHash attributeOffsets; + }; + + class ReadMappings { + public: + QHash metaObjectValues; + QHash typeStreamerValues; + QHash attributeValues; + }; + + /// Registers a metaobject under its name so that instances of it can be streamed. + /// \return zero; the function only returns a value so that it can be used in static initialization + static int registerMetaObject(const char* className, const QMetaObject* metaObject); + + /// Registers a streamer for the specified Qt-registered type. + /// \return zero; the function only returns a value so that it can be used in static initialization + static int registerTypeStreamer(int type, TypeStreamer* streamer); + + /// Creates a new bitstream. Note: the stream may be used for reading or writing, but not both. Bitstream(QDataStream& underlying); /// Writes a set of bits to the underlying stream. @@ -32,14 +192,145 @@ public: /// Flushes any unwritten bits to the underlying stream. void flush(); + /// Resets to the initial state. + void reset(); + + /// Returns a reference to the attribute streamer. + RepeatedValueStreamer& getAttributeStreamer() { return _attributeStreamer; } + + /// Returns the set of transient mappings gathered during writing and resets them. + WriteMappings getAndResetWriteMappings(); + + /// Persists a set of write mappings recorded earlier. + void persistWriteMappings(const WriteMappings& mappings); + + /// Returns the set of transient mappings gathered during reading and resets them. + ReadMappings getAndResetReadMappings(); + + /// Persists a set of read mappings recorded earlier. + void persistReadMappings(const ReadMappings& mappings); + Bitstream& operator<<(bool value); Bitstream& operator>>(bool& value); + Bitstream& operator<<(int value); + Bitstream& operator>>(int& value); + + Bitstream& operator<<(float value); + Bitstream& operator>>(float& value); + + Bitstream& operator<<(const glm::vec3& value); + Bitstream& operator>>(glm::vec3& value); + + Bitstream& operator<<(const QByteArray& string); + Bitstream& operator>>(QByteArray& string); + + Bitstream& operator<<(const QString& string); + Bitstream& operator>>(QString& string); + + Bitstream& operator<<(const QVariant& value); + Bitstream& operator>>(QVariant& value); + + template Bitstream& operator<<(const QList& list); + template Bitstream& operator>>(QList& list); + + Bitstream& operator<<(const QObject* object); + Bitstream& operator>>(QObject*& object); + + Bitstream& operator<<(const QMetaObject* metaObject); + Bitstream& operator>>(const QMetaObject*& metaObject); + + Bitstream& operator<<(const TypeStreamer* streamer); + Bitstream& operator>>(const TypeStreamer*& streamer); + + Bitstream& operator<<(const AttributePointer& attribute); + Bitstream& operator>>(AttributePointer& attribute); + private: QDataStream& _underlying; quint8 _byte; int _position; + + RepeatedValueStreamer _metaObjectStreamer; + RepeatedValueStreamer _typeStreamerStreamer; + RepeatedValueStreamer _attributeStreamer; + + static QHash& getMetaObjects(); + static QHash& getTypeStreamers(); }; +template inline Bitstream& Bitstream::operator<<(const QList& list) { + *this << list.size(); + foreach (const T& entry, list) { + *this << entry; + } + return *this; +} + +template inline Bitstream& Bitstream::operator>>(QList& list) { + int size; + *this >> size; + list.clear(); + list.reserve(size); + for (int i = 0; i < size; i++) { + T entry; + *this >> entry; + list.append(entry); + } + return *this; +} + +/// Macro for registering streamable meta-objects. +#define REGISTER_META_OBJECT(x) static int x##Registration = Bitstream::registerMetaObject(#x, &x::staticMetaObject); + +/// Interface for objects that can write values to and read values from bitstreams. +class TypeStreamer { +public: + + void setType(int type) { _type = type; } + int getType() const { return _type; } + + virtual void write(Bitstream& out, const QVariant& value) const = 0; + virtual QVariant read(Bitstream& in) const = 0; + +private: + + int _type; +}; + +/// A streamer that works with Bitstream's operators. +template class SimpleTypeStreamer : public TypeStreamer { +public: + + virtual void write(Bitstream& out, const QVariant& value) const { out << value.value(); } + virtual QVariant read(Bitstream& in) const { T value; in >> value; return QVariant::fromValue(value); } +}; + +/// Macro for registering simple type streamers. +#define REGISTER_SIMPLE_TYPE_STREAMER(x) static int x##Streamer = \ + Bitstream::registerTypeStreamer(QMetaType::type(#x), new SimpleTypeStreamer()); + +/// Declares the metatype and the streaming operators. The last lines +/// ensure that the generated file will be included in the link phase. +#define STRINGIFY(x) #x +#define DECLARE_STREAMABLE_METATYPE(X) Q_DECLARE_METATYPE(X) \ + Bitstream& operator<<(Bitstream& out, const X& obj); \ + Bitstream& operator>>(Bitstream& in, X& obj); \ + static const int* _TypePtr##X = &X::Type; \ + _Pragma(STRINGIFY(unused(_TypePtr##X))) + +/// Registers a streamable type and its streamer. +template int registerStreamableMetaType() { + int type = qRegisterMetaType(); + Bitstream::registerTypeStreamer(type, new SimpleTypeStreamer()); + return type; +} + +/// Flags a class as streamable (use as you would Q_OBJECT). +#define STREAMABLE public: static const int Type; private: + +/// Flags a field or base class as streaming. +#define STREAM + #endif /* defined(__interface__Bitstream__) */ diff --git a/libraries/metavoxels/src/DatagramSequencer.cpp b/libraries/metavoxels/src/DatagramSequencer.cpp new file mode 100644 index 0000000000..82ea27dfab --- /dev/null +++ b/libraries/metavoxels/src/DatagramSequencer.cpp @@ -0,0 +1,179 @@ +// +// DatagramSequencer.cpp +// metavoxels +// +// Created by Andrzej Kapolka on 12/20/13. +// Copyright (c) 2013 High Fidelity, Inc. All rights reserved. +// + +#include + +#include + +#include "DatagramSequencer.h" + +const int MAX_DATAGRAM_SIZE = 1500; + +DatagramSequencer::DatagramSequencer(const QByteArray& datagramHeader) : + _outgoingPacketStream(&_outgoingPacketData, QIODevice::WriteOnly), + _outputStream(_outgoingPacketStream), + _incomingDatagramStream(&_incomingDatagramBuffer), + _datagramHeaderSize(datagramHeader.size()), + _outgoingPacketNumber(0), + _outgoingDatagram(MAX_DATAGRAM_SIZE, 0), + _outgoingDatagramBuffer(&_outgoingDatagram), + _outgoingDatagramStream(&_outgoingDatagramBuffer), + _incomingPacketNumber(0), + _incomingPacketStream(&_incomingPacketData, QIODevice::ReadOnly), + _inputStream(_incomingPacketStream) { + + _outgoingPacketStream.setByteOrder(QDataStream::LittleEndian); + _incomingDatagramStream.setByteOrder(QDataStream::LittleEndian); + _incomingPacketStream.setByteOrder(QDataStream::LittleEndian); + _outgoingDatagramStream.setByteOrder(QDataStream::LittleEndian); + + memcpy(_outgoingDatagram.data(), datagramHeader.constData(), _datagramHeaderSize); +} + +/// Simple RAII-style object to keep a device open when in scope. +class QIODeviceOpener { +public: + + QIODeviceOpener(QIODevice* device, QIODevice::OpenMode mode) : _device(device) { _device->open(mode); } + ~QIODeviceOpener() { _device->close(); } + +private: + + QIODevice* _device; +}; + +Bitstream& DatagramSequencer::startPacket() { + // start with the list of acknowledgements + _outgoingPacketStream << (quint32)_receiveRecords.size(); + foreach (const ReceiveRecord& record, _receiveRecords) { + _outgoingPacketStream << (quint32)record.packetNumber; + } + return _outputStream; +} + +void DatagramSequencer::endPacket() { + _outputStream.flush(); + sendPacket(QByteArray::fromRawData(_outgoingPacketData.constData(), _outgoingPacketStream.device()->pos())); + _outgoingPacketStream.device()->seek(0); +} + +void DatagramSequencer::receivedDatagram(const QByteArray& datagram) { + _incomingDatagramBuffer.setData(datagram.constData() + _datagramHeaderSize, datagram.size() - _datagramHeaderSize); + QIODeviceOpener opener(&_incomingDatagramBuffer, QIODevice::ReadOnly); + + // read the sequence number + quint32 sequenceNumber; + _incomingDatagramStream >> sequenceNumber; + + // if it's less than the last, ignore + if (sequenceNumber < _incomingPacketNumber) { + return; + } + + // read the size and offset + quint32 packetSize, offset; + _incomingDatagramStream >> packetSize >> offset; + + // if it's greater, reset + if (sequenceNumber > _incomingPacketNumber) { + _incomingPacketNumber = sequenceNumber; + _incomingPacketData.resize(packetSize); + _offsetsReceived.clear(); + _offsetsReceived.insert(offset); + _remainingBytes = packetSize; + + } else { + // make sure it's not a duplicate + if (_offsetsReceived.contains(offset)) { + return; + } + _offsetsReceived.insert(offset); + } + + // copy in the data + memcpy(_incomingPacketData.data() + offset, _incomingDatagramBuffer.data().constData() + _incomingDatagramBuffer.pos(), + _incomingDatagramBuffer.bytesAvailable()); + + // see if we're done + if ((_remainingBytes -= _incomingDatagramBuffer.bytesAvailable()) > 0) { + return; + } + + // read the list of acknowledged packets + quint32 acknowledgementCount; + _incomingPacketStream >> acknowledgementCount; + for (int i = 0; i < acknowledgementCount; i++) { + quint32 packetNumber; + _incomingPacketStream >> packetNumber; + if (_sendRecords.isEmpty()) { + continue; + } + int index = packetNumber - _sendRecords.first().packetNumber; + if (index < 0 || index >= _sendRecords.size()) { + continue; + } + QList::iterator it = _sendRecords.begin() + index; + sendRecordAcknowledged(*it); + emit sendAcknowledged(index); + _sendRecords.erase(_sendRecords.begin(), it + 1); + } + + emit readyToRead(_inputStream); + _incomingPacketStream.device()->seek(0); + _inputStream.reset(); + + // record the receipt + ReceiveRecord record = { _incomingPacketNumber, _inputStream.getAndResetReadMappings() }; + _receiveRecords.append(record); +} + +void DatagramSequencer::sendRecordAcknowledged(const SendRecord& record) { + // stop acknowledging the recorded packets (TODO: replace with interpolation search?) + ReceiveRecord compare = { record.lastReceivedPacketNumber }; + QList::iterator it = qBinaryFind(_receiveRecords.begin(), _receiveRecords.end(), compare); + if (it != _receiveRecords.end()) { + _inputStream.persistReadMappings(it->mappings); + emit receiveAcknowledged(it - _receiveRecords.begin()); + _receiveRecords.erase(_receiveRecords.begin(), it + 1); + } + _outputStream.persistWriteMappings(record.mappings); +} + +void DatagramSequencer::sendPacket(const QByteArray& packet) { + QIODeviceOpener opener(&_outgoingDatagramBuffer, QIODevice::WriteOnly); + + // increment the packet number + _outgoingPacketNumber++; + + // record the send + SendRecord record = { _outgoingPacketNumber, _receiveRecords.isEmpty() ? 0 : _receiveRecords.last().packetNumber, + _outputStream.getAndResetWriteMappings() }; + _sendRecords.append(record); + + // write the sequence number and size, which are the same between all fragments + _outgoingDatagramBuffer.seek(_datagramHeaderSize); + _outgoingDatagramStream << (quint32)_outgoingPacketNumber; + _outgoingDatagramStream << (quint32)packet.size(); + int initialPosition = _outgoingDatagramBuffer.pos(); + + // break the packet into MTU-sized datagrams + int offset = 0; + do { + _outgoingDatagramBuffer.seek(initialPosition); + _outgoingDatagramStream << (quint32)offset; + + int payloadSize = qMin((int)(_outgoingDatagram.size() - _outgoingDatagramBuffer.pos()), packet.size() - offset); + memcpy(_outgoingDatagram.data() + _outgoingDatagramBuffer.pos(), packet.constData() + offset, payloadSize); + + emit readyToWrite(QByteArray::fromRawData(_outgoingDatagram.constData(), _outgoingDatagramBuffer.pos() + payloadSize)); + + offset += payloadSize; + + } while(offset < packet.size()); +} + diff --git a/libraries/metavoxels/src/DatagramSequencer.h b/libraries/metavoxels/src/DatagramSequencer.h new file mode 100644 index 0000000000..e5e4b8c4be --- /dev/null +++ b/libraries/metavoxels/src/DatagramSequencer.h @@ -0,0 +1,109 @@ +// +// DatagramSequencer.h +// metavoxels +// +// Created by Andrzej Kapolka on 12/20/13. +// Copyright (c) 2013 High Fidelity, Inc. All rights reserved. +// + +#ifndef __interface__DatagramSequencer__ +#define __interface__DatagramSequencer__ + +#include +#include +#include +#include +#include + +#include "Bitstream.h" + +/// Performs simple datagram sequencing, packet fragmentation and reassembly. +class DatagramSequencer : public QObject { + Q_OBJECT + +public: + + DatagramSequencer(const QByteArray& datagramHeader = QByteArray()); + + /// Returns the packet number of the last packet sent. + int getOutgoingPacketNumber() const { return _outgoingPacketNumber; } + + /// Returns the packet number of the last packet received (or the packet currently being assembled). + int getIncomingPacketNumber() const { return _incomingPacketNumber; } + + /// Starts a new packet for transmission. + /// \return a reference to the Bitstream to use for writing to the packet + Bitstream& startPacket(); + + /// Sends the packet currently being written. + void endPacket(); + + /// Processes a datagram received from the other party, emitting readyToRead when the entire packet + /// has been successfully assembled. + void receivedDatagram(const QByteArray& datagram); + +signals: + + /// Emitted when a datagram is ready to be transmitted. + void readyToWrite(const QByteArray& datagram); + + /// Emitted when a packet is available to read. + void readyToRead(Bitstream& input); + + /// Emitted when a sent packet has been acknowledged by the remote side. + /// \param index the index of the packet in our list of send records + void sendAcknowledged(int index); + + /// Emitted when our acknowledgement of a received packet has been acknowledged by the remote side. + /// \param index the index of the packet in our list of receive records + void receiveAcknowledged(int index); + +private: + + class SendRecord { + public: + int packetNumber; + int lastReceivedPacketNumber; + Bitstream::WriteMappings mappings; + }; + + class ReceiveRecord { + public: + int packetNumber; + Bitstream::ReadMappings mappings; + + bool operator<(const ReceiveRecord& other) const { return packetNumber < other.packetNumber; } + }; + + /// Notes that the described send was acknowledged by the other party. + void sendRecordAcknowledged(const SendRecord& record); + + /// Sends a packet to the other party, fragmenting it into multiple datagrams (and emitting + /// readyToWrite) as necessary. + void sendPacket(const QByteArray& packet); + + QList _sendRecords; + QList _receiveRecords; + + QByteArray _outgoingPacketData; + QDataStream _outgoingPacketStream; + Bitstream _outputStream; + + QBuffer _incomingDatagramBuffer; + QDataStream _incomingDatagramStream; + int _datagramHeaderSize; + + int _outgoingPacketNumber; + QByteArray _outgoingDatagram; + QBuffer _outgoingDatagramBuffer; + QDataStream _outgoingDatagramStream; + + int _incomingPacketNumber; + QByteArray _incomingPacketData; + QDataStream _incomingPacketStream; + Bitstream _inputStream; + QSet _offsetsReceived; + int _remainingBytes; +}; + +#endif /* defined(__interface__DatagramSequencer__) */ diff --git a/libraries/metavoxels/src/MetavoxelData.cpp b/libraries/metavoxels/src/MetavoxelData.cpp index 636982b63a..849d85960e 100644 --- a/libraries/metavoxels/src/MetavoxelData.cpp +++ b/libraries/metavoxels/src/MetavoxelData.cpp @@ -11,11 +11,22 @@ #include "MetavoxelData.h" +MetavoxelData::MetavoxelData() { +} + +MetavoxelData::MetavoxelData(const MetavoxelData& other) : _roots(other._roots) { + incrementRootReferenceCounts(); +} + MetavoxelData::~MetavoxelData() { - for (QHash::const_iterator it = _roots.constBegin(); it != _roots.constEnd(); it++) { - it.value()->destroy(it.key()); - delete it.value(); - } + decrementRootReferenceCounts(); +} + +MetavoxelData& MetavoxelData::operator=(const MetavoxelData& other) { + decrementRootReferenceCounts(); + _roots = other._roots; + incrementRootReferenceCounts(); + return *this; } void MetavoxelData::guide(MetavoxelVisitor& visitor) { @@ -43,8 +54,7 @@ void MetavoxelData::setAttributeValue(const MetavoxelPath& path, const Attribute node = new MetavoxelNode(attributeValue.getAttribute()); } if (node->setAttributeValue(path, 0, attributeValue) && attributeValue.isDefault()) { - node->destroy(attributeValue.getAttribute()); - delete node; + node->decrementReferenceCount(attributeValue.getAttribute()); _roots.remove(attributeValue.getAttribute()); } } @@ -64,7 +74,76 @@ AttributeValue MetavoxelData::getAttributeValue(const MetavoxelPath& path, const return node->getAttributeValue(attribute); } -MetavoxelNode::MetavoxelNode(const AttributeValue& attributeValue) { +void MetavoxelData::read(Bitstream& in) { + // save the old roots and clear + QHash oldRoots = _roots; + _roots.clear(); + + // read in the new roots, reusing old ones where appropriate + qint32 rootCount; + in >> rootCount; + for (int i = 0; i < rootCount; i++) { + AttributePointer attribute; + in.getAttributeStreamer() >> attribute; + MetavoxelNode* root = oldRoots.take(attribute); + if (!root) { + root = new MetavoxelNode(attribute); + } + _roots.insert(attribute, root); + root->read(attribute, in); + } + + // clear out the remaining old roots + for (QHash::const_iterator it = oldRoots.constBegin(); it != oldRoots.constEnd(); it++) { + it.value()->decrementReferenceCount(it.key()); + } +} + +void MetavoxelData::write(Bitstream& out) const { + out << (qint32)_roots.size(); + for (QHash::const_iterator it = _roots.constBegin(); it != _roots.constEnd(); it++) { + out.getAttributeStreamer() << it.key(); + it.value()->write(it.key(), out); + } +} + +void MetavoxelData::readDelta(const MetavoxelData& reference, Bitstream& in) { +} + +void MetavoxelData::writeDelta(const MetavoxelData& reference, Bitstream& out) const { +} + +void MetavoxelData::incrementRootReferenceCounts() { + for (QHash::const_iterator it = _roots.constBegin(); it != _roots.constEnd(); it++) { + it.value()->incrementReferenceCount(); + } +} + +void MetavoxelData::decrementRootReferenceCounts() { + for (QHash::const_iterator it = _roots.constBegin(); it != _roots.constEnd(); it++) { + it.value()->decrementReferenceCount(it.key()); + } +} + +void writeDelta(const MetavoxelDataPointer& data, const MetavoxelDataPointer& reference, Bitstream& out) { + if (data == reference) { + out << false; + return; + } + out << true; + data->writeDelta(*reference, out); +} + +void readDelta(MetavoxelDataPointer& data, const MetavoxelDataPointer& reference, Bitstream& in) { + bool changed; + in >> changed; + if (changed) { + data.detach(); + data->readDelta(*reference, in); + } +} + +MetavoxelNode::MetavoxelNode(const AttributeValue& attributeValue) : _referenceCount(1) { _attributeValue = attributeValue.copy(); for (int i = 0; i < CHILD_COUNT; i++) { _children[i] = NULL; @@ -118,12 +197,96 @@ bool MetavoxelNode::isLeaf() const { return true; } +void MetavoxelNode::read(const AttributePointer& attribute, Bitstream& in) { + bool leaf; + in >> leaf; + attribute->read(in, _attributeValue, leaf); + if (leaf) { + clearChildren(attribute); + + } else { + void* childValues[CHILD_COUNT]; + for (int i = 0; i < CHILD_COUNT; i++) { + if (!_children[i]) { + _children[i] = new MetavoxelNode(attribute); + } + _children[i]->read(attribute, in); + childValues[i] = _children[i]->_attributeValue; + } + attribute->merge(_attributeValue, childValues); + } +} + +void MetavoxelNode::write(const AttributePointer& attribute, Bitstream& out) const { + bool leaf = isLeaf(); + out << leaf; + attribute->write(out, _attributeValue, leaf); + if (!leaf) { + for (int i = 0; i < CHILD_COUNT; i++) { + _children[i]->write(attribute, out); + } + } +} + +void MetavoxelNode::readDelta(const AttributePointer& attribute, const MetavoxelNode& reference, Bitstream& in) { + bool different; + in >> different; + if (!different) { + return; + } + bool leaf; + in >> leaf; + attribute->readDelta(in, _attributeValue, reference._attributeValue, leaf); + if (leaf) { + clearChildren(attribute); + + } else { + if (reference.isLeaf()) { + for (int i = 0; i < CHILD_COUNT; i++) { + _children[i]->read(attribute, in); + } + } else { + for (int i = 0; i < CHILD_COUNT; i++) { + _children[i]->readDelta(attribute, *reference._children[i], in); + } + } + } +} + +void MetavoxelNode::writeDelta(const AttributePointer& attribute, const MetavoxelNode& reference, Bitstream& out) const { + if (this == &reference) { + out << false; + return; + } + out << true; + bool leaf = isLeaf(); + out << leaf; + attribute->writeDelta(out, _attributeValue, reference._attributeValue, leaf); + if (!leaf) { + if (reference.isLeaf()) { + for (int i = 0; i < CHILD_COUNT; i++) { + _children[i]->write(attribute, out); + } + } else { + for (int i = 0; i < CHILD_COUNT; i++) { + _children[i]->writeDelta(attribute, *reference._children[i], out); + } + } + } +} + +void MetavoxelNode::decrementReferenceCount(const AttributePointer& attribute) { + if (--_referenceCount == 0) { + destroy(attribute); + delete this; + } +} + void MetavoxelNode::destroy(const AttributePointer& attribute) { attribute->destroy(_attributeValue); for (int i = 0; i < CHILD_COUNT; i++) { if (_children[i]) { - _children[i]->destroy(attribute); - delete _children[i]; + _children[i]->decrementReferenceCount(attribute); } } } @@ -131,8 +294,7 @@ void MetavoxelNode::destroy(const AttributePointer& attribute) { void MetavoxelNode::clearChildren(const AttributePointer& attribute) { for (int i = 0; i < CHILD_COUNT; i++) { if (_children[i]) { - _children[i]->destroy(attribute); - delete _children[i]; + _children[i]->decrementReferenceCount(attribute); _children[i] = NULL; } } diff --git a/libraries/metavoxels/src/MetavoxelData.h b/libraries/metavoxels/src/MetavoxelData.h index fc7045cff4..d588c8a687 100644 --- a/libraries/metavoxels/src/MetavoxelData.h +++ b/libraries/metavoxels/src/MetavoxelData.h @@ -10,7 +10,9 @@ #define __interface__MetavoxelData__ #include +#include #include +#include #include #include #include @@ -27,11 +29,15 @@ class MetavoxelVisitation; class MetavoxelVisitor; /// The base metavoxel representation shared between server and client. -class MetavoxelData { +class MetavoxelData : public QSharedData { public: + MetavoxelData(); + MetavoxelData(const MetavoxelData& other); ~MetavoxelData(); + MetavoxelData& operator=(const MetavoxelData& other); + /// Applies the specified visitor to the contained voxels. void guide(MetavoxelVisitor& visitor); @@ -41,11 +47,26 @@ public: /// Retrieves the attribute value corresponding to the specified path. AttributeValue getAttributeValue(const MetavoxelPath& path, const AttributePointer& attribute) const; + void read(Bitstream& in); + void write(Bitstream& out) const; + + void readDelta(const MetavoxelData& reference, Bitstream& in); + void writeDelta(const MetavoxelData& reference, Bitstream& out) const; + private: + void incrementRootReferenceCounts(); + void decrementRootReferenceCounts(); + QHash _roots; }; +typedef QExplicitlySharedDataPointer MetavoxelDataPointer; + +void writeDelta(const MetavoxelDataPointer& data, const MetavoxelDataPointer& reference, Bitstream& out); + +void readDelta(MetavoxelDataPointer& data, const MetavoxelDataPointer& reference, Bitstream& in); + /// A single node within a metavoxel layer. class MetavoxelNode { public: @@ -69,6 +90,19 @@ public: bool isLeaf() const; + void read(const AttributePointer& attribute, Bitstream& in); + void write(const AttributePointer& attribute, Bitstream& out) const; + + void readDelta(const AttributePointer& attribute, const MetavoxelNode& reference, Bitstream& in); + void writeDelta(const AttributePointer& attribute, const MetavoxelNode& reference, Bitstream& out) const; + + /// Increments the node's reference count. + void incrementReferenceCount() { _referenceCount++; } + + /// Decrements the node's reference count. If the resulting reference count is zero, destroys the node + /// and calls delete this. + void decrementReferenceCount(const AttributePointer& attribute); + void destroy(const AttributePointer& attribute); private: @@ -76,6 +110,7 @@ private: void clearChildren(const AttributePointer& attribute); + int _referenceCount; void* _attributeValue; MetavoxelNode* _children[CHILD_COUNT]; }; diff --git a/libraries/metavoxels/src/MetavoxelMessages.h b/libraries/metavoxels/src/MetavoxelMessages.h new file mode 100644 index 0000000000..3951e16d22 --- /dev/null +++ b/libraries/metavoxels/src/MetavoxelMessages.h @@ -0,0 +1,32 @@ +// +// MetavoxelMessages.h +// metavoxels +// +// Created by Andrzej Kapolka on 12/31/13. +// Copyright (c) 2013 High Fidelity, Inc. All rights reserved. +// + +#ifndef __interface__MetavoxelMessages__ +#define __interface__MetavoxelMessages__ + +#include "Bitstream.h" + +/// A message containing the state of a client. +class ClientStateMessage { + STREAMABLE + +public: + + STREAM glm::vec3 position; +}; + +DECLARE_STREAMABLE_METATYPE(ClientStateMessage) + +/// A message preceding metavoxel delta information. The actual delta will follow it in the stream. +class MetavoxelDeltaMessage { + STREAMABLE +}; + +DECLARE_STREAMABLE_METATYPE(MetavoxelDeltaMessage) + +#endif /* defined(__interface__MetavoxelMessages__) */ diff --git a/libraries/metavoxels/src/MetavoxelUtil.cpp b/libraries/metavoxels/src/MetavoxelUtil.cpp new file mode 100644 index 0000000000..29b8c57168 --- /dev/null +++ b/libraries/metavoxels/src/MetavoxelUtil.cpp @@ -0,0 +1,29 @@ +// +// MetavoxelUtil.cpp +// metavoxels +// +// Created by Andrzej Kapolka on 12/30/13. +// Copyright (c) 2013 High Fidelity, Inc. All rights reserved. +// + +#include +#include + +#include +#include + +#include "MetavoxelUtil.h" + +QUuid readSessionID(const QByteArray& data, const HifiSockAddr& sender, int& headerPlusIDSize) { + // get the header size + int headerSize = numBytesForPacketHeader(reinterpret_cast(data.constData())); + + // read the session id + const int UUID_BYTES = 16; + headerPlusIDSize = headerSize + UUID_BYTES; + if (data.size() < headerPlusIDSize) { + qWarning() << "Metavoxel data too short [size=" << data.size() << ", sender=" << sender << "]\n"; + return QUuid(); + } + return QUuid::fromRfc4122(QByteArray::fromRawData(data.constData() + headerSize, UUID_BYTES)); +} diff --git a/libraries/metavoxels/src/MetavoxelUtil.h b/libraries/metavoxels/src/MetavoxelUtil.h new file mode 100644 index 0000000000..f7e0edbadf --- /dev/null +++ b/libraries/metavoxels/src/MetavoxelUtil.h @@ -0,0 +1,23 @@ +// +// MetavoxelUtil.h +// metavoxels +// +// Created by Andrzej Kapolka on 12/30/13. +// Copyright (c) 2013 High Fidelity, Inc. All rights reserved. +// + +#ifndef __interface__MetavoxelUtil__ +#define __interface__MetavoxelUtil__ + +#include + +class QByteArray; + +class HifiSockAddr; + +/// Reads and returns the session ID from a datagram. +/// \param[out] headerPlusIDSize the size of the header (including the session ID) within the data +/// \return the session ID, or a null ID if invalid (in which case a warning will be logged) +QUuid readSessionID(const QByteArray& data, const HifiSockAddr& sender, int& headerPlusIDSize); + +#endif /* defined(__interface__MetavoxelUtil__) */ diff --git a/libraries/shared/src/Assignment.cpp b/libraries/shared/src/Assignment.cpp index 4943098812..a0168e4bf0 100644 --- a/libraries/shared/src/Assignment.cpp +++ b/libraries/shared/src/Assignment.cpp @@ -29,6 +29,8 @@ Assignment::Type Assignment::typeForNodeType(NODE_TYPE nodeType) { return Assignment::VoxelServerType; case NODE_TYPE_PARTICLE_SERVER: return Assignment::ParticleServerType; + case NODE_TYPE_METAVOXEL_SERVER: + return Assignment::MetavoxelServerType; default: return Assignment::AllTypes; } @@ -180,6 +182,8 @@ const char* Assignment::getTypeName() const { return "voxel-server"; case Assignment::ParticleServerType: return "particle-server"; + case Assignment::MetavoxelServerType: + return "metavoxel-server"; default: return "unknown"; } diff --git a/libraries/shared/src/Assignment.h b/libraries/shared/src/Assignment.h index 1aac273e36..5a7a21c112 100644 --- a/libraries/shared/src/Assignment.h +++ b/libraries/shared/src/Assignment.h @@ -29,6 +29,7 @@ public: AgentType, VoxelServerType, ParticleServerType, + MetavoxelServerType, AllTypes }; diff --git a/libraries/shared/src/HifiSockAddr.cpp b/libraries/shared/src/HifiSockAddr.cpp index 0a6f5dce2d..55c950c580 100644 --- a/libraries/shared/src/HifiSockAddr.cpp +++ b/libraries/shared/src/HifiSockAddr.cpp @@ -11,6 +11,8 @@ #include #include +static int hifiSockAddrMetaTypeId = qMetaTypeId(); + HifiSockAddr::HifiSockAddr() : _address(), _port(0) diff --git a/libraries/shared/src/Node.cpp b/libraries/shared/src/Node.cpp index 70379801c6..739c1c03a9 100644 --- a/libraries/shared/src/Node.cpp +++ b/libraries/shared/src/Node.cpp @@ -52,6 +52,7 @@ Node::~Node() { const char* NODE_TYPE_NAME_DOMAIN = "Domain"; const char* NODE_TYPE_NAME_VOXEL_SERVER = "Voxel Server"; const char* NODE_TYPE_NAME_PARTICLE_SERVER = "Particle Server"; +const char* NODE_TYPE_NAME_METAVOXEL_SERVER = "Metavoxel Server"; const char* NODE_TYPE_NAME_AGENT = "Agent"; const char* NODE_TYPE_NAME_AUDIO_MIXER = "Audio Mixer"; const char* NODE_TYPE_NAME_AVATAR_MIXER = "Avatar Mixer"; @@ -68,6 +69,8 @@ const char* Node::getTypeName() const { return NODE_TYPE_NAME_VOXEL_SERVER; case NODE_TYPE_PARTICLE_SERVER: return NODE_TYPE_NAME_PARTICLE_SERVER; + case NODE_TYPE_METAVOXEL_SERVER: + return NODE_TYPE_NAME_METAVOXEL_SERVER; case NODE_TYPE_AGENT: return NODE_TYPE_NAME_AGENT; case NODE_TYPE_AUDIO_MIXER: diff --git a/libraries/shared/src/NodeList.cpp b/libraries/shared/src/NodeList.cpp index c763a52e05..0a50a659b2 100644 --- a/libraries/shared/src/NodeList.cpp +++ b/libraries/shared/src/NodeList.cpp @@ -703,7 +703,8 @@ Node* NodeList::addOrUpdateNode(const QUuid& uuid, char nodeType, node->lock(); if (node->getType() == NODE_TYPE_AUDIO_MIXER || - node->getType() == NODE_TYPE_VOXEL_SERVER) { + node->getType() == NODE_TYPE_VOXEL_SERVER || + node->getType() == NODE_TYPE_METAVOXEL_SERVER) { // until the Audio class also uses our nodeList, we need to update // the lastRecvTimeUsecs for the audio mixer so it doesn't get killed and re-added continously node->setLastHeardMicrostamp(usecTimestampNow()); diff --git a/libraries/shared/src/NodeTypes.h b/libraries/shared/src/NodeTypes.h index c9723f4477..37e0503bab 100644 --- a/libraries/shared/src/NodeTypes.h +++ b/libraries/shared/src/NodeTypes.h @@ -20,6 +20,7 @@ typedef char NODE_TYPE; const NODE_TYPE NODE_TYPE_DOMAIN = 'D'; const NODE_TYPE NODE_TYPE_VOXEL_SERVER = 'V'; const NODE_TYPE NODE_TYPE_PARTICLE_SERVER = 'P'; +const NODE_TYPE NODE_TYPE_METAVOXEL_SERVER = 'm'; const NODE_TYPE NODE_TYPE_ENVIRONMENT_SERVER = 'E'; const NODE_TYPE NODE_TYPE_AGENT = 'I'; const NODE_TYPE NODE_TYPE_AUDIO_MIXER = 'M'; diff --git a/libraries/shared/src/PacketHeaders.h b/libraries/shared/src/PacketHeaders.h index a59ae5e5e5..d1a2b501f6 100644 --- a/libraries/shared/src/PacketHeaders.h +++ b/libraries/shared/src/PacketHeaders.h @@ -51,6 +51,7 @@ const PACKET_TYPE PACKET_TYPE_PARTICLE_DATA = 'v'; const PACKET_TYPE PACKET_TYPE_PARTICLE_ADD_OR_EDIT = 'a'; const PACKET_TYPE PACKET_TYPE_PARTICLE_ERASE = 'x'; const PACKET_TYPE PACKET_TYPE_PARTICLE_ADD_RESPONSE = 'b'; +const PACKET_TYPE PACKET_TYPE_METAVOXEL_DATA = 't'; typedef char PACKET_VERSION; diff --git a/libraries/shared/src/ThreadedAssignment.cpp b/libraries/shared/src/ThreadedAssignment.cpp index 095999adca..b16c180c9d 100644 --- a/libraries/shared/src/ThreadedAssignment.cpp +++ b/libraries/shared/src/ThreadedAssignment.cpp @@ -6,6 +6,9 @@ // Copyright (c) 2013 HighFidelity, Inc. All rights reserved. // +#include + +#include "Logging.h" #include "ThreadedAssignment.h" ThreadedAssignment::ThreadedAssignment(const unsigned char* dataBuffer, int numBytes) : @@ -23,6 +26,26 @@ void ThreadedAssignment::setFinished(bool isFinished) { } } +void ThreadedAssignment::commonInit(const char* targetName, NODE_TYPE nodeType) { + // change the logging target name while the assignment is running + Logging::setTargetName(targetName); + + NodeList* nodeList = NodeList::getInstance(); + nodeList->setOwnerType(nodeType); + + QTimer* domainServerTimer = new QTimer(this); + connect(domainServerTimer, SIGNAL(timeout()), this, SLOT(checkInWithDomainServerOrExit())); + domainServerTimer->start(DOMAIN_SERVER_CHECK_IN_USECS / 1000); + + QTimer* pingNodesTimer = new QTimer(this); + connect(pingNodesTimer, SIGNAL(timeout()), nodeList, SLOT(pingInactiveNodes())); + pingNodesTimer->start(PING_INACTIVE_NODE_INTERVAL_USECS / 1000); + + QTimer* silentNodeTimer = new QTimer(this); + connect(silentNodeTimer, SIGNAL(timeout()), nodeList, SLOT(removeSilentNodes())); + silentNodeTimer->start(NODE_SILENCE_THRESHOLD_USECS / 1000); +} + void ThreadedAssignment::checkInWithDomainServerOrExit() { if (NodeList::getInstance()->getNumNoReplyDomainCheckIns() == MAX_SILENT_DOMAIN_SERVER_CHECK_INS) { setFinished(true); diff --git a/libraries/shared/src/ThreadedAssignment.h b/libraries/shared/src/ThreadedAssignment.h index 2d6e49b724..0dfd8a320f 100644 --- a/libraries/shared/src/ThreadedAssignment.h +++ b/libraries/shared/src/ThreadedAssignment.h @@ -23,6 +23,7 @@ public slots: virtual void processDatagram(const QByteArray& dataByteArray, const HifiSockAddr& senderSockAddr) = 0; protected: + void commonInit(const char* targetName, NODE_TYPE nodeType); bool _isFinished; private slots: void checkInWithDomainServerOrExit(); diff --git a/tools/mtc/CMakeLists.txt b/tools/mtc/CMakeLists.txt new file mode 100644 index 0000000000..95cb95d573 --- /dev/null +++ b/tools/mtc/CMakeLists.txt @@ -0,0 +1,11 @@ +cmake_minimum_required(VERSION 2.8) + +set(TARGET_NAME mtc) + +set(ROOT_DIR ../..) +set(MACRO_DIR ${ROOT_DIR}/cmake/macros) + +include(${MACRO_DIR}/SetupHifiProject.cmake) +setup_hifi_project(${TARGET_NAME} TRUE) + + diff --git a/tools/mtc/src/main.cpp b/tools/mtc/src/main.cpp new file mode 100644 index 0000000000..77f0a069b5 --- /dev/null +++ b/tools/mtc/src/main.cpp @@ -0,0 +1,180 @@ +// +// main.cpp +// mtc +// +// Created by Andrzej Kapolka on 12/31/13. +// Copyright (c) 2013 High Fidelity, Inc. All rights reserved. +// + +#include + +#include +#include +#include +#include +#include +#include +#include + +using namespace std; + +class Class { +public: + QString name; + QStringList bases; +}; + +class Streamable { +public: + Class clazz; + QStringList fields; +}; + +void processInput(QTextStream& in, QList* streamables) { + Class clazz; + Streamable currentStreamable; + + QRegExp exp( + "(/\\*.*\\*/)|" // multi-line comments + "(//.*\n)|" // single-line comments + "(\\s*#.*\n)|" // preprocessor definitions + "(\\s*STREAMABLE\\s+)|" // STREAMABLE tag for classes + "(\\s*STREAM\\s+.*;)|" // STREAM tag for fields + "(\\s*class\\s+[^;]+\\{)" // class definition + ); + exp.setMinimal(true); + + QRegExp classExp("class (\\w+) ?:?([^:]*)\\{"); + + // read in the entire input and look for matches with our expression + QString all = in.readAll(); + for (int off = 0; (off = exp.indexIn(all, off)) != -1; off += exp.matchedLength()) { + QString match = exp.cap().simplified(); + if (match.startsWith("/*") || match.startsWith("//") || match.startsWith('#')) { + continue; // comment, preprocessor definition + } + if (match.startsWith("STREAMABLE")) { + if (clazz.name.isEmpty()) { + cerr << "Found STREAMABLE marker before class definition." << endl; + continue; + } + if (!currentStreamable.clazz.name.isEmpty()) { + streamables->append(currentStreamable); + } + currentStreamable.clazz = clazz; + currentStreamable.fields.clear(); + + } else if (match.startsWith("STREAM")) { + match.chop(1); // get rid of the semicolon + match = match.trimmed(); // and any space before it + currentStreamable.fields.append(match.mid(match.lastIndexOf(' ') + 1)); + + } else { // match.startsWith("class") + classExp.exactMatch(match); + clazz.name = classExp.cap(1); + clazz.bases.clear(); + foreach (const QString& bstr, classExp.cap(2).split(',')) { + QString base = bstr.trimmed(); + if (!base.isEmpty() && base.startsWith("STREAM")) { + clazz.bases.append(base.mid(base.lastIndexOf(' ') + 1)); + } + } + } + } + if (!currentStreamable.clazz.name.isEmpty()) { + streamables->append(currentStreamable); + } +} + +void generateOutput (QTextStream& out, const QList& streamables) { + foreach (const Streamable& str, streamables) { + const QString& name = str.clazz.name; + + out << "Bitstream& operator<< (Bitstream& out, const " << name << "& obj) {\n"; + foreach (const QString& base, str.clazz.bases) { + out << " out << static_cast(obj);\n"; + } + foreach (const QString& field, str.fields) { + out << " out << obj." << field << ";\n"; + } + out << " return out;\n"; + out << "}\n"; + + out << "Bitstream& operator>> (Bitstream& in, " << name << "& obj) {\n"; + foreach (const QString& base, str.clazz.bases) { + out << " in >> static_cast<" << base << "&>(obj);\n"; + } + foreach (const QString& field, str.fields) { + out << " in >> obj." << field << ";\n"; + } + out << " return in;\n"; + out << "}\n"; + + out << "const int " << name << "::Type = registerStreamableMetaType<" << name << ">();\n\n"; + } +} + +int main (int argc, char** argv) { + // process the command line arguments + QStringList inputs; + QString output; + for (int ii = 1; ii < argc; ii++) { + QString arg(argv[ii]); + if (!arg.startsWith('-')) { + inputs.append(arg); + continue; + } + QStringRef name = arg.midRef(1); + if (name == "o") { + if (++ii == argc) { + cerr << "Missing file name argument for -o" << endl; + return 1; + } + output = argv[ii]; + + } else { + cerr << "Unknown option " << arg.toStdString() << endl; + return 1; + } + } + if (inputs.isEmpty()) { + cerr << "Usage: mtc [OPTION]... input files" << endl; + cerr << "Where options include:" << endl; + cerr << " -o filename: Send output to filename rather than standard output." << endl; + return 0; + } + + QList streamables; + foreach (const QString& input, inputs) { + QFile ifile(input); + if (!ifile.open(QIODevice::ReadOnly | QIODevice::Text)) { + cerr << ("Couldn't open " + input + ": " + ifile.errorString()).toStdString() << endl; + continue; + } + QTextStream istream(&ifile); + int oldSize = streamables.size(); + processInput(istream, &streamables); + if (streamables.size() == oldSize) { + // no streamables; remove from list + inputs.removeOne(input); + } + } + + QFile ofile(output); + if (output.isNull()) { + ofile.open(stdout, QIODevice::WriteOnly | QIODevice::Text); + + } else if (!ofile.open(QIODevice::WriteOnly | QIODevice::Text)) { + cerr << ("Couldn't open " + output + ": " + ofile.errorString()).toStdString() << endl; + return 1; + } + + QTextStream ostream(&ofile); + ostream << "// generated by mtc\n"; + foreach (const QString& input, inputs) { + ostream << "#include \"" << input << "\"\n"; + } + generateOutput(ostream, streamables); + + return 0; +}