History

Merge branch 'master' of https://github.com/highfidelity/hifi into virtualEntities

ZappoMan committed 12 years ago

Commit 32d5eb32 parents 1b1c386 a6f0c9b159 files · +4368 −3616Browse files

Unified · Split

159 files
  1. CMakeLists.txt +12 −0
  2. assignment-client/CMakeLists.txt +8 −38
  3. assignment-client/src/Agent.cpp +1 −1
  4. assignment-client/src/audio/AudioMixer.cpp +71 −29
  5. assignment-client/src/audio/AudioMixer.h +4 −1
  6. assignment-client/src/audio/AudioMixerClientData.cpp +44 −1
  7. assignment-client/src/audio/AudioMixerClientData.h +5 −0
  8. assignment-client/src/audio/AudioMixerDatagramProcessor.cpp +47 −0
  9. assignment-client/src/audio/AudioMixerDatagramProcessor.h +32 −0
  10. assignment-client/src/avatars/ScriptableAvatar.cpp +2 −0
  11. cmake/macros/AutoMTC.cmake +2 −2
  12. cmake/macros/IncludeGLM.cmake +2 −2
  13. cmake/macros/IncludeHifiLibraryHeaders.cmake +14 −0
  14. cmake/macros/LinkHifiLibraries.cmake +34 −0
  15. cmake/macros/LinkHifiLibrary.cmake +0 −27
  16. cmake/macros/LinkSharedDependencies.cmake +25 −0
  17. cmake/macros/SetupHifiLibrary.cmake +17 −10
  18. cmake/macros/SetupHifiProject.cmake +15 −8
  19. cmake/modules/FindATL.cmake +29 −0
  20. cmake/modules/FindFaceplus.cmake +1 −1
  21. cmake/modules/FindFaceshift.cmake +1 −1
  22. cmake/modules/FindLeapMotion.cmake +1 −1
  23. cmake/modules/FindLibOVR.cmake +27 −18
  24. cmake/modules/FindPrioVR.cmake +1 −1
  25. cmake/modules/FindQxmpp.cmake +4 −2
  26. cmake/modules/FindRtMidi.cmake +1 −1
  27. cmake/modules/FindSixense.cmake +1 −1
  28. cmake/modules/FindVisage.cmake +18 −9
  29. domain-server/CMakeLists.txt +5 −35
  30. domain-server/resources/web/settings/describe.json +6 −0
  31. examples/clipboardExample.js +8 −1
  32. interface/CMakeLists.txt +26 −80
  33. interface/external/libovr/readme.txt +0 −0
  34. interface/interface_en.ts +6 −6
  35. interface/resources/shaders/metavoxel_heightfield.vert +14 −4
  36. interface/resources/shaders/metavoxel_heightfield_cascaded_shadow_map.frag +48 −0
  37. interface/resources/shaders/metavoxel_heightfield_cursor.frag +32 −0
  38. interface/resources/shaders/metavoxel_heightfield_cursor.vert +28 −0
  39. interface/resources/shaders/metavoxel_heightfield_shadow_map.frag +37 −0
  40. interface/src/Application.cpp +27 −15
  41. interface/src/Application.h +4 −1
  42. interface/src/Audio.cpp +42 −2
  43. interface/src/Audio.h +14 −3
  44. interface/src/Menu.cpp +44 −2
  45. interface/src/Menu.h +5 −0
  46. interface/src/MetavoxelSystem.cpp +779 −67
  47. interface/src/MetavoxelSystem.h +61 −9
  48. interface/src/avatar/Head.cpp +0 −3
  49. interface/src/avatar/Head.h +0 −2
  50. interface/src/avatar/MyAvatar.cpp +15 −8
  51. interface/src/avatar/SkeletonModel.cpp +65 −148
  52. interface/src/avatar/SkeletonModel.h +8 −11
  53. interface/src/avatar/SkeletonRagdoll.cpp +148 −0
  54. interface/src/avatar/SkeletonRagdoll.h +42 −0
  55. interface/src/devices/CaraFaceTracker.cpp +1 −1
  56. interface/src/devices/DdeFaceTracker.cpp +42 −5
  57. interface/src/devices/DdeFaceTracker.h +4 −0
  58. interface/src/devices/OculusManager.cpp +77 −13
  59. interface/src/devices/OculusManager.h +1 −0
  60. interface/src/devices/SixenseManager.cpp +3 −1
  61. interface/src/devices/SixenseManager.h +1 −1
  62. interface/src/renderer/JointState.h +1 −0
  63. interface/src/renderer/Model.cpp +5 −3
  64. interface/src/renderer/Model.h +3 −0
  65. interface/src/scripting/ClipboardScriptingInterface.cpp +31 −0
  66. interface/src/scripting/ClipboardScriptingInterface.h +3 −0
  67. interface/src/ui/ApplicationOverlay.cpp +29 −0
  68. interface/src/ui/ApplicationOverlay.h +2 −1
  69. interface/src/ui/MetavoxelEditor.cpp +94 −19
  70. interface/src/ui/MetavoxelEditor.h +58 −1
  71. interface/src/ui/VoxelImportDialog.cpp +94 −37
  72. interface/src/ui/VoxelImportDialog.h +21 −13
  73. interface/src/ui/overlays/Base3DOverlay.h +2 −0
  74. interface/src/voxels/OctreePacketProcessor.cpp +1 −1
  75. interface/src/voxels/VoxelImporter.cpp +33 −82
  76. interface/src/voxels/VoxelImporter.h +12 −11
  77. libraries/animation/CMakeLists.txt +5 −25
  78. libraries/audio/CMakeLists.txt +6 −21
  79. libraries/audio/src/AudioFilter.cpp +26 −0
  80. libraries/audio/src/AudioFilter.h +298 −0
  81. libraries/audio/src/AudioRingBuffer.cpp +2 −2
  82. libraries/audio/src/AudioRingBuffer.h +2 −4
  83. libraries/audio/src/InboundAudioStream.cpp +2 −0
  84. libraries/avatars/CMakeLists.txt +7 −25
  85. libraries/avatars/src/AvatarData.cpp +1 −1
  86. libraries/avatars/src/HeadData.cpp +2 −2
  87. libraries/avatars/src/Referential.cpp +1 −1
  88. libraries/embedded-webserver/CMakeLists.txt +4 −11
  89. libraries/entities/CMakeLists.txt +6 −34
  90. libraries/fbx/CMakeLists.txt +8 −23
  91. libraries/fbx/src/FBXReader.cpp +1 −1
  92. libraries/metavoxels/CMakeLists.txt +7 −22
  93. libraries/metavoxels/src/AttributeRegistry.cpp +297 −65
  94. libraries/metavoxels/src/AttributeRegistry.h +22 −4
  95. libraries/metavoxels/src/MetavoxelMessages.cpp +174 −0
  96. libraries/metavoxels/src/MetavoxelMessages.h +34 −0
  97. libraries/metavoxels/src/MetavoxelUtil.cpp +15 −0
  98. libraries/metavoxels/src/MetavoxelUtil.h +8 −0
  99. libraries/networking/CMakeLists.txt +9 −16
  100. libraries/networking/src/DomainHandler.cpp +4 −1
  101. libraries/networking/src/DomainHandler.h +1 −0
  102. libraries/networking/src/LimitedNodeList.cpp +25 −12
  103. libraries/networking/src/LimitedNodeList.h +1 −1
  104. libraries/networking/src/PacketHeaders.cpp +2 −0
  105. libraries/networking/src/PacketSender.h +0 −4
  106. libraries/networking/src/ReceivedPacketProcessor.h +0 −4
  107. libraries/networking/src/SequenceNumberStats.cpp +15 −10
  108. libraries/networking/src/SequenceNumberStats.h +13 −0
  109. libraries/networking/src/ThreadedAssignment.cpp +20 −3
  110. libraries/networking/src/ThreadedAssignment.h +7 −3
  111. libraries/octree/CMakeLists.txt +11 −22
  112. libraries/octree/src/EditPacketBuffer.cpp +1 −1
  113. libraries/octree/src/EditPacketBuffer.h +2 −2
  114. libraries/octree/src/JurisdictionListener.cpp +1 −1
  115. libraries/octree/src/JurisdictionListener.h +0 −4
  116. libraries/octree/src/JurisdictionSender.cpp +1 −1
  117. libraries/octree/src/OctreeEditPacketSender.cpp +3 −3
  118. libraries/octree/src/OctreeEditPacketSender.h +5 −5
  119. libraries/octree/src/OctreePacketData.cpp +2 −0
  120. libraries/octree/src/OctreeQuery.cpp +3 −2
  121. libraries/particles/CMakeLists.txt +7 −28
  122. libraries/particles/src/Particle.cpp +3 −3
  123. libraries/particles/src/Particle.h +1 −1
  124. libraries/particles/src/ParticleEditPacketSender.cpp +2 −2
  125. libraries/particles/src/ParticleEditPacketSender.h +1 −1
  126. libraries/script-engine/CMakeLists.txt +6 −30
  127. libraries/script-engine/src/Quat.cpp +1 −1
  128. libraries/shared/CMakeLists.txt +4 −30
  129. libraries/shared/external/pthread.h +0 −1403
  130. libraries/shared/external/sched.h +0 −189
  131. libraries/shared/src/AngularConstraint.cpp +2 −1
  132. libraries/shared/src/AngularConstraint.h +0 −1
  133. libraries/shared/src/ContactPoint.cpp +108 −72
  134. libraries/shared/src/ContactPoint.h +4 −2
  135. libraries/shared/src/GLMHelpers.cpp +299 −0
  136. libraries/shared/src/GLMHelpers.h +89 −0
  137. libraries/shared/src/MovingMinMaxAvg.h +1 −0
  138. libraries/shared/src/PhysicsSimulation.cpp +20 −20
  139. libraries/shared/src/PhysicsSimulation.h +2 −2
  140. libraries/shared/src/Ragdoll.cpp +23 −20
  141. libraries/shared/src/Ragdoll.h +14 −14
  142. libraries/shared/src/SharedUtil.cpp +1 −290
  143. libraries/shared/src/SharedUtil.h +0 −69
  144. libraries/voxels/CMakeLists.txt +11 −24
  145. libraries/voxels/src/VoxelEditPacketSender.h +1 −1
  146. tests/audio/CMakeLists.txt +4 −28
  147. tests/jitter/CMakeLists.txt +3 −28
  148. tests/jitter/src/main.cpp +258 −50
  149. tests/metavoxels/CMakeLists.txt +5 −25
  150. tests/networking/CMakeLists.txt +3 −28
  151. tests/networking/src/SequenceNumberStatsTests.cpp +4 −3
  152. tests/octree/CMakeLists.txt +14 −39
  153. tests/physics/CMakeLists.txt +4 −26
  154. tests/shared/CMakeLists.txt +4 −26
  155. tools/CMakeLists.txt +0 −1
  156. tools/bitstream2json/CMakeLists.txt +4 −21
  157. tools/json2bitstream/CMakeLists.txt +4 −21
  158. tools/mtc/CMakeLists.txt +2 −5
  159. voxel-edit/CMakeLists.txt +4 −35
CMakeLists.txt+12−0
@@ -44,6 +44,18 @@ set(CMAKE_INCLUDE_CURRENT_DIR ON) · 43 unmodified lines
4444 # Instruct CMake to run moc automatically when needed.
4545 set(CMAKE_AUTOMOC ON)
4646
47+set(HIFI_LIBRARY_DIR "${CMAKE_CURRENT_SOURCE_DIR}/libraries")
48+
49+# setup for find modules
50+set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules/")
51+
52+set(MACRO_DIR "${CMAKE_CURRENT_SOURCE_DIR}/cmake/macros")
53+
54+file(GLOB HIFI_CUSTOM_MACROS "cmake/macros/*.cmake")
55+foreach(CUSTOM_MACRO ${HIFI_CUSTOM_MACROS})
56+ include(${CUSTOM_MACRO})
57+endforeach()
58+
4759 # targets on all platforms
4860 add_subdirectory(assignment-client)
4961 add_subdirectory(domain-server)
assignment-client/CMakeLists.txt+8−38
@@ -1,47 +1,17 @@
11 set(TARGET_NAME assignment-client)
22
3−set(ROOT_DIR ..)
4−set(MACRO_DIR "${ROOT_DIR}/cmake/macros")
3+setup_hifi_project(Core Gui Network Script Widgets)
54
6−# setup for find modules
7−set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/../cmake/modules/")
8−
9−find_package(Qt5 COMPONENTS Network Script Widgets)
10−
11−include("${MACRO_DIR}/SetupHifiProject.cmake")
12−setup_hifi_project(${TARGET_NAME} TRUE)
13−
14−# include glm
15−include("${MACRO_DIR}/IncludeGLM.cmake")
16−include_glm(${TARGET_NAME} "${ROOT_DIR}")
5+include_glm()
176
187 # link in the shared libraries
19−include(${MACRO_DIR}/LinkHifiLibrary.cmake)
20−link_hifi_library(shared ${TARGET_NAME} "${ROOT_DIR}")
21−link_hifi_library(audio ${TARGET_NAME} "${ROOT_DIR}")
22−link_hifi_library(avatars ${TARGET_NAME} "${ROOT_DIR}")
23−link_hifi_library(octree ${TARGET_NAME} "${ROOT_DIR}")
24−link_hifi_library(voxels ${TARGET_NAME} "${ROOT_DIR}")
25−link_hifi_library(fbx ${TARGET_NAME} "${ROOT_DIR}")
26−link_hifi_library(particles ${TARGET_NAME} "${ROOT_DIR}")
27−link_hifi_library(entities ${TARGET_NAME} "${ROOT_DIR}")
28−link_hifi_library(metavoxels ${TARGET_NAME} "${ROOT_DIR}")
29−link_hifi_library(networking ${TARGET_NAME} "${ROOT_DIR}")
30−link_hifi_library(animation ${TARGET_NAME} "${ROOT_DIR}")
31−link_hifi_library(script-engine ${TARGET_NAME} "${ROOT_DIR}")
32−link_hifi_library(embedded-webserver ${TARGET_NAME} "${ROOT_DIR}")
8+link_hifi_libraries(
9+ audio avatars octree voxels fbx particles entities metavoxels
10+ networking animation shared script-engine embedded-webserver
11+)
3312
3413 if (UNIX)
35− target_link_libraries(${TARGET_NAME} ${CMAKE_DL_LIBS})
14+ list(APPEND ${TARGET_NAME}_LIBRARIES_TO_LINK ${CMAKE_DL_LIBS})
3615 endif (UNIX)
3716
38−IF (WIN32)
39− target_link_libraries(${TARGET_NAME} Winmm Ws2_32)
40−ENDIF(WIN32)
41−
42−target_link_libraries(${TARGET_NAME} Qt5::Network Qt5::Widgets Qt5::Script)
43−
44−# add a definition for ssize_t so that windows doesn't bail
45−if (WIN32)
46− add_definitions(-Dssize_t=long)
47−endif ()
17+link_shared_dependencies()
assignment-client/src/Agent.cpp+1−1
@@ -115,7 +115,7 @@ void Agent::readPendingDatagrams() { · 114 unmodified lines
115115 sourceNode->setLastHeardMicrostamp(usecTimestampNow());
116116
117117 QByteArray mutablePacket = receivedPacket;
118− ssize_t messageLength = mutablePacket.size();
118+ int messageLength = mutablePacket.size();
119119
120120 if (datagramPacketType == PacketTypeOctreeStats) {
121121
assignment-client/src/audio/AudioMixer.cpp+71−29
@@ -37,6 +37,7 @@ · 36 unmodified lines
3737 #include <QtCore/QJsonDocument>
3838 #include <QtCore/QJsonObject>
3939 #include <QtCore/QJsonValue>
40+#include <QtCore/QThread>
4041 #include <QtCore/QTimer>
4142 #include <QtNetwork/QNetworkRequest>
4243 #include <QtNetwork/QNetworkReply>
@@ -52,6 +53,7 @@ · 9 unmodified lines
5253
5354 #include "AudioRingBuffer.h"
5455 #include "AudioMixerClientData.h"
56+#include "AudioMixerDatagramProcessor.h"
5557 #include "AvatarAudioStream.h"
5658 #include "InjectedAudioStream.h"
5759
@@ -71,6 +73,8 @@ bool AudioMixer::_useDynamicJitterBuffers = false; · 13 unmodified lines
7173 int AudioMixer::_staticDesiredJitterBufferFrames = 0;
7274 int AudioMixer::_maxFramesOverDesired = 0;
7375
76+bool AudioMixer::_printStreamStats = false;
77+
7478 AudioMixer::AudioMixer(const QByteArray& packet) :
7579 ThreadedAssignment(packet),
7680 _trailingSleepRatio(1.0f),
@@ -295,38 +299,34 @@ void AudioMixer::prepareMixForListeningNode(Node* node) { · 218 unmodified lines
295299 }
296300 }
297301
298−void AudioMixer::readPendingDatagrams() {
299− QByteArray receivedPacket;
300− HifiSockAddr senderSockAddr;
302+void AudioMixer::readPendingDatagram(const QByteArray& receivedPacket, const HifiSockAddr& senderSockAddr) {
301303 NodeList* nodeList = NodeList::getInstance();
302304
303− while (readAvailableDatagram(receivedPacket, senderSockAddr)) {
304− if (nodeList->packetVersionAndHashMatch(receivedPacket)) {
305− // pull any new audio data from nodes off of the network stack
306− PacketType mixerPacketType = packetTypeForPacket(receivedPacket);
307− if (mixerPacketType == PacketTypeMicrophoneAudioNoEcho
308− || mixerPacketType == PacketTypeMicrophoneAudioWithEcho
309− || mixerPacketType == PacketTypeInjectAudio
310− || mixerPacketType == PacketTypeSilentAudioFrame
311− || mixerPacketType == PacketTypeAudioStreamStats) {
312−
313− nodeList->findNodeAndUpdateWithDataFromPacket(receivedPacket);
314− } else if (mixerPacketType == PacketTypeMuteEnvironment) {
315− QByteArray packet = receivedPacket;
316− populatePacketHeader(packet, PacketTypeMuteEnvironment);
317−
318− foreach (const SharedNodePointer& node, nodeList->getNodeHash()) {
319− if (node->getType() == NodeType::Agent && node->getActiveSocket() && node->getLinkedData() && node != nodeList->sendingNodeForPacket(receivedPacket)) {
320− nodeList->writeDatagram(packet, packet.size(), node);
321− }
305+ if (nodeList->packetVersionAndHashMatch(receivedPacket)) {
306+ // pull any new audio data from nodes off of the network stack
307+ PacketType mixerPacketType = packetTypeForPacket(receivedPacket);
308+ if (mixerPacketType == PacketTypeMicrophoneAudioNoEcho
309+ || mixerPacketType == PacketTypeMicrophoneAudioWithEcho
310+ || mixerPacketType == PacketTypeInjectAudio
311+ || mixerPacketType == PacketTypeSilentAudioFrame
312+ || mixerPacketType == PacketTypeAudioStreamStats) {
313+
314+ nodeList->findNodeAndUpdateWithDataFromPacket(receivedPacket);
315+ } else if (mixerPacketType == PacketTypeMuteEnvironment) {
316+ QByteArray packet = receivedPacket;
317+ populatePacketHeader(packet, PacketTypeMuteEnvironment);
318+
319+ foreach (const SharedNodePointer& node, nodeList->getNodeHash()) {
320+ if (node->getType() == NodeType::Agent && node->getActiveSocket() && node->getLinkedData() && node != nodeList->sendingNodeForPacket(receivedPacket)) {
321+ nodeList->writeDatagram(packet, packet.size(), node);
322322 }
323−
324− } else {
325− // let processNodeData handle it.
326− nodeList->processNodeData(senderSockAddr, receivedPacket);
327323 }
324+
325+ } else {
326+ // let processNodeData handle it.
327+ nodeList->processNodeData(senderSockAddr, receivedPacket);
328328 }
329− }
329+ }
330330 }
331331
332332 void AudioMixer::sendStatsPacket() {
@@ -390,7 +390,35 @@ void AudioMixer::run() { · 57 unmodified lines
390390 ThreadedAssignment::commonInit(AUDIO_MIXER_LOGGING_TARGET_NAME, NodeType::AudioMixer);
391391
392392 NodeList* nodeList = NodeList::getInstance();
393−
393+
394+ // we do not want this event loop to be the handler for UDP datagrams, so disconnect
395+ disconnect(&nodeList->getNodeSocket(), 0, this, 0);
396+
397+ // setup a QThread with us as parent that will house the AudioMixerDatagramProcessor
398+ _datagramProcessingThread = new QThread(this);
399+
400+ // create an AudioMixerDatagramProcessor and move it to that thread
401+ AudioMixerDatagramProcessor* datagramProcessor = new AudioMixerDatagramProcessor(nodeList->getNodeSocket(), thread());
402+ datagramProcessor->moveToThread(_datagramProcessingThread);
403+
404+ // remove the NodeList as the parent of the node socket
405+ nodeList->getNodeSocket().setParent(NULL);
406+ nodeList->getNodeSocket().moveToThread(_datagramProcessingThread);
407+
408+ // let the datagram processor handle readyRead from node socket
409+ connect(&nodeList->getNodeSocket(), &QUdpSocket::readyRead,
410+ datagramProcessor, &AudioMixerDatagramProcessor::readPendingDatagrams);
411+
412+ // connect to the datagram processing thread signal that tells us we have to handle a packet
413+ connect(datagramProcessor, &AudioMixerDatagramProcessor::packetRequiresProcessing, this, &AudioMixer::readPendingDatagram);
414+
415+ // delete the datagram processor and the associated thread when the QThread quits
416+ connect(_datagramProcessingThread, &QThread::finished, datagramProcessor, &QObject::deleteLater);
417+ connect(datagramProcessor, &QObject::destroyed, _datagramProcessingThread, &QThread::deleteLater);
418+
419+ // start the datagram processing thread
420+ _datagramProcessingThread->start();
421+
394422 nodeList->addNodeTypeToInterestSet(NodeType::Agent);
395423
396424 nodeList->linkedDataCreateCallback = attachNewNodeDataToNode;
@@ -448,7 +476,11 @@ void AudioMixer::run() { · 51 unmodified lines
448476 }
449477 qDebug() << "Max frames over desired:" << _maxFramesOverDesired;
450478
451−
479+ const QString PRINT_STREAM_STATS_JSON_KEY = "H-print-stream-stats";
480+ _printStreamStats = audioGroupObject[PRINT_STREAM_STATS_JSON_KEY].toBool();
481+ if (_printStreamStats) {
482+ qDebug() << "Stream stats will be printed to stdout";
483+ }
452484
453485 const QString UNATTENUATED_ZONE_KEY = "D-unattenuated-zone";
454486
@@ -547,6 +579,7 @@ void AudioMixer::run() { · 92 unmodified lines
547579 sendAudioStreamStats = true;
548580 }
549581
582+ bool streamStatsPrinted = false;
550583 foreach (const SharedNodePointer& node, nodeList->getNodeHash()) {
551584 if (node->getLinkedData()) {
552585 AudioMixerClientData* nodeData = (AudioMixerClientData*)node->getLinkedData();
@@ -581,12 +614,21 @@ void AudioMixer::run() { · 28 unmodified lines
581614 // send an audio stream stats packet if it's time
582615 if (sendAudioStreamStats) {
583616 nodeData->sendAudioStreamStatsPackets(node);
617+
618+ if (_printStreamStats) {
619+ printf("\nStats for agent %s:\n", node->getUUID().toString().toLatin1().data());
620+ nodeData->printUpstreamDownstreamStats();
621+ streamStatsPrinted = true;
622+ }
584623 }
585624
586625 ++_sumListeners;
587626 }
588627 }
589628 }
629+ if (streamStatsPrinted) {
630+ printf("\n----------------------------------------------------------------\n");
631+ }
590632
591633 ++_numStatFrames;
592634
assignment-client/src/audio/AudioMixer.h+4−1
@@ -33,7 +33,8 @@ public slots: · 32 unmodified lines
3333 /// threaded run of assignment
3434 void run();
3535
36− void readPendingDatagrams();
36+ void readPendingDatagrams() { }; // this will not be called since our datagram processing thread will handle
37+ void readPendingDatagram(const QByteArray& receivedPacket, const HifiSockAddr& senderSockAddr);
3738
3839 void sendStatsPacket();
3940
@@ -66,6 +67,8 @@ private: · 26 unmodified lines
6667 static int _staticDesiredJitterBufferFrames;
6768 static int _maxFramesOverDesired;
6869
70+ static bool _printStreamStats;
71+
6972 quint64 _lastSendAudioStreamStatsTime;
7073 };
7174
assignment-client/src/audio/AudioMixerClientData.cpp+44−1
@@ -22,7 +22,8 @@ · 21 unmodified lines
2222
2323 AudioMixerClientData::AudioMixerClientData() :
2424 _audioStreams(),
25− _outgoingMixedAudioSequenceNumber(0)
25+ _outgoingMixedAudioSequenceNumber(0),
26+ _downstreamAudioStreamStats()
2627 {
2728 }
2829
@@ -263,3 +264,45 @@ QString AudioMixerClientData::getAudioStreamStatsString() const { · 234 unmodified lines
263264 }
264265 return result;
265266 }
267+
268+void AudioMixerClientData::printUpstreamDownstreamStats() const {
269+ // print the upstream (mic stream) stats if the mic stream exists
270+ if (_audioStreams.contains(QUuid())) {
271+ printf("Upstream:\n");
272+ printAudioStreamStats(_audioStreams.value(QUuid())->getAudioStreamStats());
273+ }
274+ // print the downstream stats if they contain valid info
275+ if (_downstreamAudioStreamStats._packetStreamStats._received > 0) {
276+ printf("Downstream:\n");
277+ printAudioStreamStats(_downstreamAudioStreamStats);
278+ }
279+}
280+
281+void AudioMixerClientData::printAudioStreamStats(const AudioStreamStats& streamStats) const {
282+ printf(" Packet loss | overall: %5.2f%% (%d lost), last_30s: %5.2f%% (%d lost)\n",
283+ streamStats._packetStreamStats.getLostRate() * 100.0f,
284+ streamStats._packetStreamStats._lost,
285+ streamStats._packetStreamWindowStats.getLostRate() * 100.0f,
286+ streamStats._packetStreamWindowStats._lost);
287+
288+ printf(" Ringbuffer frames | desired: %u, avg_available(10s): %u, available: %u\n",
289+ streamStats._desiredJitterBufferFrames,
290+ streamStats._framesAvailableAverage,
291+ streamStats._framesAvailable);
292+
293+ printf(" Ringbuffer stats | starves: %u, prev_starve_lasted: %u, frames_dropped: %u, overflows: %u\n",
294+ streamStats._starveCount,
295+ streamStats._consecutiveNotMixedCount,
296+ streamStats._framesDropped,
297+ streamStats._overflowCount);
298+
299+ printf(" Inter-packet timegaps (overall) | min: %9s, max: %9s, avg: %9s\n",
300+ formatUsecTime(streamStats._timeGapMin).toLatin1().data(),
301+ formatUsecTime(streamStats._timeGapMax).toLatin1().data(),
302+ formatUsecTime(streamStats._timeGapAverage).toLatin1().data());
303+
304+ printf(" Inter-packet timegaps (last 30s) | min: %9s, max: %9s, avg: %9s\n",
305+ formatUsecTime(streamStats._timeGapWindowMin).toLatin1().data(),
306+ formatUsecTime(streamStats._timeGapWindowMax).toLatin1().data(),
307+ formatUsecTime(streamStats._timeGapWindowAverage).toLatin1().data());
308+}
assignment-client/src/audio/AudioMixerClientData.h+5−0
@@ -38,6 +38,11 @@ public: · 37 unmodified lines
3838 void incrementOutgoingMixedAudioSequenceNumber() { _outgoingMixedAudioSequenceNumber++; }
3939 quint16 getOutgoingSequenceNumber() const { return _outgoingMixedAudioSequenceNumber; }
4040
41+ void printUpstreamDownstreamStats() const;
42+
43+private:
44+ void printAudioStreamStats(const AudioStreamStats& streamStats) const;
45+
4146 private:
4247 QHash<QUuid, PositionalAudioStream*> _audioStreams; // mic stream stored under key of null UUID
4348
assignment-client/src/audio/AudioMixerDatagramProcessor.cpp+47−0
@@ -0,0 +1,47 @@
1+//
2+// AudioMixerDatagramProcessor.cpp
3+// assignment-client/src
4+//
5+// Created by Stephen Birarda on 2014-08-14.
6+// Copyright 2014 High Fidelity, Inc.
7+//
8+// Distributed under the Apache License, Version 2.0.
9+// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
10+//
11+
12+#include <QDebug>
13+
14+#include <HifiSockAddr.h>
15+#include <NodeList.h>
16+
17+#include "AudioMixerDatagramProcessor.h"
18+
19+AudioMixerDatagramProcessor::AudioMixerDatagramProcessor(QUdpSocket& nodeSocket, QThread* previousNodeSocketThread) :
20+ _nodeSocket(nodeSocket),
21+ _previousNodeSocketThread(previousNodeSocketThread)
22+{
23+
24+}
25+
26+AudioMixerDatagramProcessor::~AudioMixerDatagramProcessor() {
27+ // return the node socket to its previous thread
28+ _nodeSocket.moveToThread(_previousNodeSocketThread);
29+}
30+
31+void AudioMixerDatagramProcessor::readPendingDatagrams() {
32+
33+ HifiSockAddr senderSockAddr;
34+ static QByteArray incomingPacket;
35+
36+ // read everything that is available
37+ while (_nodeSocket.hasPendingDatagrams()) {
38+ incomingPacket.resize(_nodeSocket.pendingDatagramSize());
39+
40+ // just get this packet off the stack
41+ _nodeSocket.readDatagram(incomingPacket.data(), incomingPacket.size(),
42+ senderSockAddr.getAddressPointer(), senderSockAddr.getPortPointer());
43+
44+ // emit the signal to tell AudioMixer it needs to process a packet
45+ emit packetRequiresProcessing(incomingPacket, senderSockAddr);
46+ }
47+}
assignment-client/src/audio/AudioMixerDatagramProcessor.h+32−0
@@ -0,0 +1,32 @@
1+//
2+// AudioMixerDatagramProcessor.h
3+// assignment-client/src
4+//
5+// Created by Stephen Birarda on 2014-08-14.
6+// Copyright 2014 High Fidelity, Inc.
7+//
8+// Distributed under the Apache License, Version 2.0.
9+// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
10+//
11+
12+#ifndef hifi_AudioMixerDatagramProcessor_h
13+#define hifi_AudioMixerDatagramProcessor_h
14+
15+#include <qobject.h>
16+#include <qudpsocket.h>
17+
18+class AudioMixerDatagramProcessor : public QObject {
19+ Q_OBJECT
20+public:
21+ AudioMixerDatagramProcessor(QUdpSocket& nodeSocket, QThread* previousNodeSocketThread);
22+ ~AudioMixerDatagramProcessor();
23+public slots:
24+ void readPendingDatagrams();
25+signals:
26+ void packetRequiresProcessing(const QByteArray& receivedPacket, const HifiSockAddr& senderSockAddr);
27+private:
28+ QUdpSocket& _nodeSocket;
29+ QThread* _previousNodeSocketThread;
30+};
31+
32+#endif // hifi_AudioMixerDatagramProcessor_h
assignment-client/src/avatars/ScriptableAvatar.cpp+2−0
@@ -11,6 +11,8 @@ · 10 unmodified lines
1111
1212 #include <QThread>
1313
14+#include <GLMHelpers.h>
15+
1416 #include "ScriptableAvatar.h"
1517
1618 ScriptableAvatar::ScriptableAvatar(ScriptEngine* scriptEngine) : _scriptEngine(scriptEngine), _animation(NULL) {
cmake/macros/AutoMTC.cmake+2−2
@@ -8,8 +8,8 @@ · 7 unmodified lines
88 # See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
99 #
1010
11−macro(AUTO_MTC TARGET ROOT_DIR)
12− set(AUTOMTC_SRC ${TARGET}_automtc.cpp)
11+macro(AUTO_MTC)
12+ set(AUTOMTC_SRC ${TARGET_NAME}_automtc.cpp)
1313
1414 file(GLOB INCLUDE_FILES src/*.h)
1515
cmake/macros/IncludeGLM.cmake+2−2
@@ -7,7 +7,7 @@ · 6 unmodified lines
77 # See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
88 #
99
10−macro(INCLUDE_GLM TARGET ROOT_DIR)
10+macro(INCLUDE_GLM)
1111
1212 find_package(GLM REQUIRED)
1313 include_directories("${GLM_INCLUDE_DIRS}")
@@ -16,4 +16,4 @@ macro(INCLUDE_GLM TARGET ROOT_DIR) · 2 unmodified lines
1616 set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -isystem ${GLM_INCLUDE_DIRS}")
1717 endif ()
1818
19−endmacro(INCLUDE_GLM _target _root_dir)
19+endmacro(INCLUDE_GLM)
cmake/macros/IncludeHifiLibraryHeaders.cmake+14−0
@@ -0,0 +1,14 @@
1+#
2+# IncludeHifiLibraryHeaders.cmake
3+# cmake/macros
4+#
5+# Copyright 2014 High Fidelity, Inc.
6+# Created by Stephen Birarda on August 8, 2014
7+#
8+# Distributed under the Apache License, Version 2.0.
9+# See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
10+#
11+
12+macro(include_hifi_library_headers LIBRARY)
13+ include_directories("${HIFI_LIBRARY_DIR}/${LIBRARY}/src")
14+endmacro(include_hifi_library_headers _library _root_dir)
cmake/macros/LinkHifiLibraries.cmake+34−0
@@ -0,0 +1,34 @@
1+#
2+# LinkHifiLibrary.cmake
3+#
4+# Copyright 2013 High Fidelity, Inc.
5+#
6+# Distributed under the Apache License, Version 2.0.
7+# See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
8+#
9+
10+macro(LINK_HIFI_LIBRARIES)
11+
12+ file(RELATIVE_PATH RELATIVE_LIBRARY_DIR_PATH ${CMAKE_CURRENT_SOURCE_DIR} "${HIFI_LIBRARY_DIR}")
13+
14+ set(LIBRARIES_TO_LINK ${ARGN})
15+
16+ foreach(HIFI_LIBRARY ${LIBRARIES_TO_LINK})
17+ if (NOT TARGET ${HIFI_LIBRARY})
18+ add_subdirectory("${RELATIVE_LIBRARY_DIR_PATH}/${HIFI_LIBRARY}" "${RELATIVE_LIBRARY_DIR_PATH}/${HIFI_LIBRARY}")
19+ endif ()
20+
21+ include_directories("${HIFI_LIBRARY_DIR}/${HIFI_LIBRARY}/src")
22+
23+ add_dependencies(${TARGET_NAME} ${HIFI_LIBRARY})
24+
25+ # link the actual library - it is static so don't bubble it up
26+ target_link_libraries(${TARGET_NAME} ${HIFI_LIBRARY})
27+
28+ # ask the library what its dynamic dependencies are and link them
29+ get_target_property(LINKED_TARGET_DEPENDENCY_LIBRARIES ${HIFI_LIBRARY} DEPENDENCY_LIBRARIES)
30+ list(APPEND ${TARGET_NAME}_LIBRARIES_TO_LINK ${LINKED_TARGET_DEPENDENCY_LIBRARIES})
31+
32+ endforeach()
33+
34+endmacro(LINK_HIFI_LIBRARIES)
cmake/macros/LinkHifiLibrary.cmake+0−27
@@ -1,27 +0,0 @@
1−#
2−# LinkHifiLibrary.cmake
3−#
4−# Copyright 2013 High Fidelity, Inc.
5−#
6−# Distributed under the Apache License, Version 2.0.
7−# See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
8−#
9−
10−macro(LINK_HIFI_LIBRARY LIBRARY TARGET ROOT_DIR)
11−
12− if (NOT TARGET ${LIBRARY})
13− add_subdirectory("${ROOT_DIR}/libraries/${LIBRARY}" "${ROOT_DIR}/libraries/${LIBRARY}")
14− endif ()
15−
16− include_directories("${ROOT_DIR}/libraries/${LIBRARY}/src")
17−
18− add_dependencies(${TARGET} ${LIBRARY})
19− target_link_libraries(${TARGET} ${LIBRARY})
20−
21− if (APPLE)
22− # currently the "shared" library requires CoreServices
23− # link in required OS X framework
24− set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -framework CoreServices")
25− endif ()
26−
27−endmacro(LINK_HIFI_LIBRARY _library _target _root_dir)
cmake/macros/LinkSharedDependencies.cmake+25−0
@@ -0,0 +1,25 @@
1+#
2+# LinkSharedDependencies.cmake
3+# cmake/macros
4+#
5+# Copyright 2014 High Fidelity, Inc.
6+# Created by Stephen Birarda on August 8, 2014
7+#
8+# Distributed under the Apache License, Version 2.0.
9+# See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
10+#
11+
12+macro(LINK_SHARED_DEPENDENCIES)
13+ if (${TARGET_NAME}_LIBRARIES_TO_LINK)
14+ list(REMOVE_DUPLICATES ${TARGET_NAME}_LIBRARIES_TO_LINK)
15+
16+ # link these libraries to our target
17+ target_link_libraries(${TARGET_NAME} ${${TARGET_NAME}_LIBRARIES_TO_LINK})
18+ endif ()
19+
20+ # we've already linked our Qt modules, but we need to bubble them up to parents
21+ list(APPEND ${TARGET_NAME}_LIBRARIES_TO_LINK "${${TARGET}_QT_MODULES_TO_LINK}")
22+
23+ # set the property on this target so it can be retreived by targets linking to us
24+ set_target_properties(${TARGET_NAME} PROPERTIES DEPENDENCY_LIBRARIES "${${TARGET}_LIBRARIES_TO_LINK}")
25+endmacro(LINK_SHARED_DEPENDENCIES)
cmake/macros/SetupHifiLibrary.cmake+17−10
@@ -7,20 +7,27 @@ · 6 unmodified lines
77 # See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
88 #
99
10−macro(SETUP_HIFI_LIBRARY TARGET)
10+macro(SETUP_HIFI_LIBRARY)
1111
12− project(${TARGET})
12+ project(${TARGET_NAME})
1313
1414 # grab the implemenation and header files
1515 file(GLOB LIB_SRCS src/*.h src/*.cpp)
16− set(LIB_SRCS ${LIB_SRCS} ${WRAPPED_SRCS})
16+ set(LIB_SRCS ${LIB_SRCS})
1717
1818 # create a library and set the property so it can be referenced later
19− add_library(${TARGET} ${LIB_SRCS} ${ARGN})
19+ add_library(${TARGET_NAME} ${LIB_SRCS} ${AUTOMTC_SRC})
2020
21− find_package(Qt5Core REQUIRED)
22− qt5_use_modules(${TARGET} Core)
23−
24− target_link_libraries(${TARGET} ${QT_LIBRARIES})
25−
26−endmacro(SETUP_HIFI_LIBRARY _target)
21+ set(QT_MODULES_TO_LINK ${ARGN})
22+ list(APPEND QT_MODULES_TO_LINK Core)
23+
24+ find_package(Qt5 COMPONENTS ${QT_MODULES_TO_LINK} REQUIRED)
25+
26+ foreach(QT_MODULE ${QT_MODULES_TO_LINK})
27+ get_target_property(QT_LIBRARY_LOCATION Qt5::${QT_MODULE} LOCATION)
28+
29+ # add the actual path to the Qt module to our LIBRARIES_TO_LINK variable
30+ target_link_libraries(${TARGET_NAME} Qt5::${QT_MODULE})
31+ list(APPEND ${TARGET_NAME}_QT_MODULES_TO_LINK ${QT_LIBRARY_LOCATION})
32+ endforeach()
33+endmacro(SETUP_HIFI_LIBRARY)
cmake/macros/SetupHifiProject.cmake+15−8
@@ -7,8 +7,8 @@ · 6 unmodified lines
77 # See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
88 #
99
10−macro(SETUP_HIFI_PROJECT TARGET INCLUDE_QT)
11− project(${TARGET})
10+macro(SETUP_HIFI_PROJECT)
11+ project(${TARGET_NAME})
1212
1313 # grab the implemenation and header files
1414 file(GLOB TARGET_SRCS src/*)
@@ -23,12 +23,19 @@ macro(SETUP_HIFI_PROJECT TARGET INCLUDE_QT) · 8 unmodified lines
2323 endforeach()
2424
2525 # add the executable, include additional optional sources
26− add_executable(${TARGET} ${TARGET_SRCS} ${ARGN})
26+ add_executable(${TARGET_NAME} ${TARGET_SRCS} "${AUTOMTC_SRC}")
2727
28− if (${INCLUDE_QT})
29− find_package(Qt5Core REQUIRED)
30− qt5_use_modules(${TARGET} Core)
31− endif ()
28+ set(QT_MODULES_TO_LINK ${ARGN})
29+ list(APPEND QT_MODULES_TO_LINK Core)
30+
31+ find_package(Qt5 COMPONENTS ${QT_MODULES_TO_LINK} REQUIRED)
32+
33+ foreach(QT_MODULE ${QT_MODULES_TO_LINK})
34+ target_link_libraries(${TARGET_NAME} Qt5::${QT_MODULE})
35+
36+ # add the actual path to the Qt module to our LIBRARIES_TO_LINK variable
37+ get_target_property(QT_LIBRARY_LOCATION Qt5::${QT_MODULE} LOCATION)
38+ list(APPEND ${TARGET_NAME}_QT_MODULES_TO_LINK ${QT_LIBRARY_LOCATION})
39+ endforeach()
3240
33− target_link_libraries(${TARGET} ${QT_LIBRARIES})
3441 endmacro()
cmake/modules/FindATL.cmake+29−0
@@ -0,0 +1,29 @@
1+#
2+# FindATL.cmake
3+#
4+# Try to find the ATL library needed to use the LibOVR
5+#
6+# Once done this will define
7+#
8+# ATL_FOUND - system found ATL
9+# ATL_LIBRARIES - Link this to use ATL
10+#
11+# Created on 8/13/2013 by Stephen Birarda
12+# Copyright 2014 High Fidelity, Inc.
13+#
14+# Distributed under the Apache License, Version 2.0.
15+# See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
16+#
17+
18+if (WIN32)
19+ find_library(ATL_LIBRARY_RELEASE atls PATH_SUFFIXES "7600.16385.1/lib/ATL/i386" HINTS "C:\\WinDDK")
20+ find_library(ATL_LIBRARY_DEBUG atlsd PATH_SUFFIXES "7600.16385.1/lib/ATL/i386" HINTS "C:\\WinDDK")
21+
22+ include(SelectLibraryConfigurations)
23+ select_library_configurations(ATL)
24+endif ()
25+
26+set(ATL_LIBRARIES "${ATL_LIBRARY}")
27+
28+include(FindPackageHandleStandardArgs)
29+find_package_handle_standard_args(ATL DEFAULT_MSG ATL_LIBRARIES)
cmake/modules/FindFaceplus.cmake+1−1
@@ -19,6 +19,6 @@ if (WIN32) · 18 unmodified lines
1919 endif (WIN32)
2020
2121 include(FindPackageHandleStandardArgs)
22−find_package_handle_standard_args(FACEPLUS DEFAULT_MSG FACEPLUS_INCLUDE_DIRS FACEPLUS_LIBRARIES)
22+find_package_handle_standard_args(Faceplus DEFAULT_MSG FACEPLUS_INCLUDE_DIRS FACEPLUS_LIBRARIES)
2323
2424 mark_as_advanced(FACEPLUS_INCLUDE_DIRS FACEPLUS_LIBRARIES)
cmake/modules/FindFaceshift.cmake+1−1
@@ -40,6 +40,6 @@ select_library_configurations(FACESHIFT) · 39 unmodified lines
4040 set(FACESHIFT_LIBRARIES ${FACESHIFT_LIBRARY})
4141
4242 include(FindPackageHandleStandardArgs)
43−find_package_handle_standard_args(FACESHIFT DEFAULT_MSG FACESHIFT_INCLUDE_DIRS FACESHIFT_LIBRARIES)
43+find_package_handle_standard_args(Faceshift DEFAULT_MSG FACESHIFT_INCLUDE_DIRS FACESHIFT_LIBRARIES)
4444
4545 mark_as_advanced(FACESHIFT_INCLUDE_DIRS FACESHIFT_LIBRARIES FACESHIFT_SEARCH_DIRS)
cmake/modules/FindLeapMotion.cmake+1−1
@@ -30,6 +30,6 @@ select_library_configurations(LEAPMOTION) · 29 unmodified lines
3030 set(LEAPMOTION_LIBRARIES "${LEAPMOTION_LIBRARY}")
3131
3232 include(FindPackageHandleStandardArgs)
33−find_package_handle_standard_args(LEAPMOTION DEFAULT_MSG LEAPMOTION_INCLUDE_DIRS LEAPMOTION_LIBRARIES)
33+find_package_handle_standard_args(LeapMotion DEFAULT_MSG LEAPMOTION_INCLUDE_DIRS LEAPMOTION_LIBRARIES)
3434
3535 mark_as_advanced(LEAPMOTION_INCLUDE_DIRS LEAPMOTION_LIBRARIES LEAPMOTION_SEARCH_DIRS)
cmake/modules/FindLibOVR.cmake+27−18
@@ -19,16 +19,18 @@ · 18 unmodified lines
1919 #
2020
2121 include("${MACRO_DIR}/HifiLibrarySearchHints.cmake")
22−hifi_library_search_hints("oculus")
22+hifi_library_search_hints("libovr")
2323
24−find_path(LIBOVR_INCLUDE_DIRS OVR.h PATH_SUFFIXES Include HINTS ${OCULUS_SEARCH_DIRS})
25−find_path(LIBOVR_SRC_DIR Util_Render_Stereo.h PATH_SUFFIXES Src/Util HINTS ${OCULUS_SEARCH_DIRS})
24+find_path(LIBOVR_INCLUDE_DIRS OVR.h PATH_SUFFIXES Include HINTS ${LIBOVR_SEARCH_DIRS})
25+find_path(LIBOVR_SRC_DIR Util_Render_Stereo.h PATH_SUFFIXES Src/Util HINTS ${LIBOVR_SEARCH_DIRS})
2626
2727 include(SelectLibraryConfigurations)
2828
2929 if (APPLE)
30− find_library(LIBOVR_LIBRARY_DEBUG NAMES ovr PATH_SUFFIXES Lib/MacOS/Debug HINTS ${OCULUS_SEARCH_DIRS})
31− find_library(LIBOVR_LIBRARY_RELEASE NAMES ovr PATH_SUFFIXES Lib/MacOS/Release HINTS ${OCULUS_SEARCH_DIRS})
30+ find_library(LIBOVR_LIBRARY_DEBUG NAMES ovr PATH_SUFFIXES Lib/Mac/Debug HINTS ${LIBOVR_SEARCH_DIRS})
31+ find_library(LIBOVR_LIBRARY_RELEASE NAMES ovr PATH_SUFFIXES Lib/Mac/Release HINTS ${LIBOVR_SEARCH_DIRS})
32+ find_library(ApplicationServices ApplicationServices)
33+ find_library(IOKit IOKit)
3234 elseif (UNIX)
3335 find_library(UDEV_LIBRARY_RELEASE udev /usr/lib/x86_64-linux-gnu/)
3436 find_library(XINERAMA_LIBRARY_RELEASE Xinerama /usr/lib/x86_64-linux-gnu/)
@@ -39,29 +41,36 @@ elseif (UNIX) · 4 unmodified lines
3941 set(LINUX_ARCH_DIR "x86_64")
4042 endif()
4143
42− find_library(LIBOVR_LIBRARY_DEBUG NAMES ovr PATH_SUFFIXES Lib/Linux/Debug/${LINUX_ARCH_DIR} HINTS ${OCULUS_SEARCH_DIRS})
43− find_library(LIBOVR_LIBRARY_RELEASE NAMES ovr PATH_SUFFIXES Lib/Linux/Release/${LINUX_ARCH_DIR} HINTS ${OCULUS_SEARCH_DIRS})
44+ find_library(LIBOVR_LIBRARY_DEBUG NAMES ovr PATH_SUFFIXES Lib/Linux/Debug/${LINUX_ARCH_DIR} HINTS ${LIBOVR_SEARCH_DIRS})
45+ find_library(LIBOVR_LIBRARY_RELEASE NAMES ovr PATH_SUFFIXES Lib/Linux/Release/${LINUX_ARCH_DIR} HINTS ${LIBOVR_SEARCH_DIRS})
4446
4547 select_library_configurations(UDEV)
4648 select_library_configurations(XINERAMA)
4749
4850 elseif (WIN32)
49− find_library(LIBOVR_LIBRARY_DEBUG NAMES libovrd PATH_SUFFIXES Lib/Win32/VS2010 HINTS ${OCULUS_SEARCH_DIRS})
50− find_library(LIBOVR_LIBRARY_RELEASE NAMES libovr PATH_SUFFIXES Lib/Win32/VS2010 HINTS ${OCULUS_SEARCH_DIRS})
51+ find_library(LIBOVR_LIBRARY_DEBUG NAMES libovrd PATH_SUFFIXES Lib/Win32/VS2010 HINTS ${LIBOVR_SEARCH_DIRS})
52+ find_library(LIBOVR_LIBRARY_RELEASE NAMES libovr PATH_SUFFIXES Lib/Win32/VS2010 HINTS ${LIBOVR_SEARCH_DIRS})
53+ find_package(ATL)
5154 endif ()
5255
5356 select_library_configurations(LIBOVR)
54−set(LIBOVR_LIBRARIES "${LIBOVR_LIBRARY}")
57+set(LIBOVR_LIBRARIES ${LIBOVR_LIBRARY})
5558
56−if (UNIX AND NOT APPLE)
57− set(LIBOVR_LIBRARIES "${LIBOVR_LIBRARIES}" "${UDEV_LIBRARY}" "${XINERAMA_LIBRARY}")
59+list(APPEND LIBOVR_ARGS_LIST LIBOVR_INCLUDE_DIRS LIBOVR_SRC_DIR LIBOVR_LIBRARY)
60+
61+if (APPLE)
62+ list(APPEND LIBOVR_LIBRARIES ${IOKit} ${ApplicationServices})
63+ list(APPEND LIBOVR_ARGS_LIST IOKit ApplicationServices)
64+elseif (UNIX)
65+ list(APPEND LIBOVR_LIBRARIES "${UDEV_LIBRARY}" "${XINERAMA_LIBRARY}")
66+ list(APPEND LIBOVR_ARGS_LIST UDEV_LIBRARY XINERAMA_LIBRARY)
67+elseif (WIN32)
68+ list(APPEND LIBOVR_LIBRARIES ${ATL_LIBRARIES})
69+ list(APPEND LIBOVR_ARGS_LIST ATL_LIBRARIES)
5870 endif ()
5971
6072 include(FindPackageHandleStandardArgs)
61−if (UNIX AND NOT APPLE)
62− find_package_handle_standard_args(LIBOVR DEFAULT_MSG LIBOVR_INCLUDE_DIRS LIBOVR_SRC_DIR LIBOVR_LIBRARIES UDEV_LIBRARY XINERAMA_LIBRARY)
63−else ()
64− find_package_handle_standard_args(LIBOVR DEFAULT_MSG LIBOVR_INCLUDE_DIRS LIBOVR_SRC_DIR LIBOVR_LIBRARIES)
65−endif ()
6673
67−mark_as_advanced(LIBOVR_INCLUDE_DIRS LIBOVR_LIBRARIES OCULUS_SEARCH_DIRS)
74+find_package_handle_standard_args(LibOVR DEFAULT_MSG ${LIBOVR_ARGS_LIST})
75+
76+mark_as_advanced(LIBOVR_INCLUDE_DIRS LIBOVR_LIBRARIES LIBOVR_SEARCH_DIRS)
cmake/modules/FindPrioVR.cmake+1−1
@@ -19,6 +19,6 @@ if (WIN32) · 18 unmodified lines
1919 endif (WIN32)
2020
2121 include(FindPackageHandleStandardArgs)
22−find_package_handle_standard_args(PRIOVR DEFAULT_MSG PRIOVR_INCLUDE_DIRS PRIOVR_LIBRARIES)
22+find_package_handle_standard_args(PrioVR DEFAULT_MSG PRIOVR_INCLUDE_DIRS PRIOVR_LIBRARIES)
2323
2424 mark_as_advanced(PRIOVR_INCLUDE_DIRS PRIOVR_LIBRARIES)
cmake/modules/FindQxmpp.cmake+4−2
@@ -26,12 +26,14 @@ find_path(QXMPP_INCLUDE_DIRS QXmppClient.h PATH_SUFFIXES include/qxmpp HINTS ${Q · 25 unmodified lines
2626 find_library(QXMPP_LIBRARY_RELEASE NAMES qxmpp PATH_SUFFIXES lib HINTS ${QXMPP_SEARCH_DIRS})
2727 find_library(QXMPP_LIBRARY_DEBUG NAMES qxmpp_d PATH_SUFFIXES lib HINTS ${QXMPP_SEARCH_DIRS})
2828
29+find_package(Qt5 COMPONENTS Xml REQUIRED)
30+
2931 include(SelectLibraryConfigurations)
3032 select_library_configurations(QXMPP)
3133
32−set(QXMPP_LIBRARIES "${QXMPP_LIBRARY}")
34+set(QXMPP_LIBRARIES "${QXMPP_LIBRARY}" Qt5::Xml)
3335
3436 include(FindPackageHandleStandardArgs)
35−find_package_handle_standard_args(QXMPP DEFAULT_MSG QXMPP_INCLUDE_DIRS QXMPP_LIBRARIES)
37+find_package_handle_standard_args(QXmpp DEFAULT_MSG QXMPP_INCLUDE_DIRS QXMPP_LIBRARIES QXMPP_LIBRARY)
3638
3739 mark_as_advanced(QXMPP_INCLUDE_DIRS QXMPP_LIBRARIES QXMPP_SEARCH_DIRS)
cmake/modules/FindRtMidi.cmake+1−1
@@ -25,6 +25,6 @@ find_path(RTMIDI_INCLUDE_DIRS RtMidi.h PATH_SUFFIXES include HINTS ${RTMIDI_SEAR · 24 unmodified lines
2525 find_library(RTMIDI_LIBRARIES NAMES rtmidi PATH_SUFFIXES lib HINTS ${RTMIDI_SEARCH_DIRS})
2626
2727 include(FindPackageHandleStandardArgs)
28−find_package_handle_standard_args(RTMIDI DEFAULT_MSG RTMIDI_INCLUDE_DIRS RTMIDI_LIBRARIES)
28+find_package_handle_standard_args(RtMidi DEFAULT_MSG RTMIDI_INCLUDE_DIRS RTMIDI_LIBRARIES)
2929
3030 mark_as_advanced(RTMIDI_INCLUDE_DIRS RTMIDI_LIBRARIES RTMIDI_SEARCH_DIRS)
cmake/modules/FindSixense.cmake+1−1
@@ -40,6 +40,6 @@ select_library_configurations(SIXENSE) · 39 unmodified lines
4040 set(SIXENSE_LIBRARIES "${SIXENSE_LIBRARY}")
4141
4242 include(FindPackageHandleStandardArgs)
43−find_package_handle_standard_args(SIXENSE DEFAULT_MSG SIXENSE_INCLUDE_DIRS SIXENSE_LIBRARIES)
43+find_package_handle_standard_args(Sixense DEFAULT_MSG SIXENSE_INCLUDE_DIRS SIXENSE_LIBRARIES)
4444
4545 mark_as_advanced(SIXENSE_LIBRARIES SIXENSE_INCLUDE_DIRS SIXENSE_SEARCH_DIRS)
cmake/modules/FindVisage.cmake+18−9
@@ -32,6 +32,9 @@ if (APPLE) · 31 unmodified lines
3232 find_library(VISAGE_VISION_LIBRARY NAME vsvision PATH_SUFFIXES lib HINTS ${VISAGE_SEARCH_DIRS})
3333 find_library(VISAGE_OPENCV_LIBRARY NAME OpenCV PATH_SUFFIXES dependencies/OpenCV_MacOSX/lib HINTS ${VISAGE_SEARCH_DIRS})
3434
35+ find_library(AppKit AppKit)
36+ find_library(QuartzCore QuartzCore)
37+ find_library(QTKit QTKit)
3538 elseif (WIN32)
3639 find_path(VISAGE_XML_INCLUDE_DIR libxml/xmlreader.h PATH_SUFFIXES dependencies/libxml2/include HINTS ${VISAGE_SEARCH_DIRS})
3740 find_path(VISAGE_OPENCV_INCLUDE_DIR opencv/cv.h PATH_SUFFIXES dependencies/OpenCV/include HINTS ${VISAGE_SEARCH_DIRS})
@@ -43,16 +46,22 @@ elseif (WIN32) · 5 unmodified lines
4346 endif ()
4447
4548 include(FindPackageHandleStandardArgs)
46−find_package_handle_standard_args(VISAGE DEFAULT_MSG
47− VISAGE_BASE_INCLUDE_DIR VISAGE_XML_INCLUDE_DIR VISAGE_OPENCV_INCLUDE_DIR VISAGE_OPENCV2_INCLUDE_DIR
48− VISAGE_CORE_LIBRARY VISAGE_VISION_LIBRARY VISAGE_OPENCV_LIBRARY
49−)
49+
50+list(APPEND VISAGE_ARGS_LIST VISAGE_BASE_INCLUDE_DIR VISAGE_XML_INCLUDE_DIR
51+ VISAGE_OPENCV_INCLUDE_DIR VISAGE_OPENCV2_INCLUDE_DIR
52+ VISAGE_CORE_LIBRARY VISAGE_VISION_LIBRARY VISAGE_OPENCV_LIBRARY)
53+
54+if (APPLE)
55+ list(APPEND VISAGE_ARGS_LIST QuartzCore AppKit QTKit)
56+endif ()
57+
58+find_package_handle_standard_args(Visage DEFAULT_MSG ${VISAGE_ARGS_LIST})
5059
5160 set(VISAGE_INCLUDE_DIRS "${VISAGE_XML_INCLUDE_DIR}" "${VISAGE_OPENCV_INCLUDE_DIR}" "${VISAGE_OPENCV2_INCLUDE_DIR}" "${VISAGE_BASE_INCLUDE_DIR}")
5261 set(VISAGE_LIBRARIES "${VISAGE_CORE_LIBRARY}" "${VISAGE_VISION_LIBRARY}" "${VISAGE_OPENCV_LIBRARY}")
5362
54−mark_as_advanced(
55− VISAGE_INCLUDE_DIRS VISAGE_LIBRARIES
56− VISAGE_BASE_INCLUDE_DIR VISAGE_XML_INCLUDE_DIR VISAGE_OPENCV_INCLUDE_DIR VISAGE_OPENCV2_INCLUDE_DIR
57− VISAGE_CORE_LIBRARY VISAGE_VISION_LIBRARY VISAGE_OPENCV_LIBRARY VISAGE_SEARCH_DIRS
58−)
63+if (APPLE)
64+ list(APPEND VISAGE_LIBRARIES ${QuartzCore} ${AppKit} ${QTKit})
65+endif ()
66+
67+mark_as_advanced(VISAGE_INCLUDE_DIRS VISAGE_LIBRARIES)
domain-server/CMakeLists.txt+5−35
@@ -1,24 +1,7 @@
1−if (WIN32)
2− cmake_policy (SET CMP0020 NEW)
3−endif (WIN32)
4−
51 set(TARGET_NAME domain-server)
62
7−set(ROOT_DIR ..)
8−set(MACRO_DIR "${ROOT_DIR}/cmake/macros")
9−
10−# setup for find modules
11−set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/../cmake/modules/")
12−
13−# set up the external glm library
14−include(${MACRO_DIR}/IncludeGLM.cmake)
15−include_glm(${TARGET_NAME} "${ROOT_DIR}")
16−
17−find_package(Qt5Network REQUIRED)
18−
19−include(${MACRO_DIR}/SetupHifiProject.cmake)
20−
21−setup_hifi_project(${TARGET_NAME} TRUE)
3+# setup the project and link required Qt modules
4+setup_hifi_project(Network)
225
236 # remove and then copy the files for the webserver
247 add_custom_command(TARGET ${TARGET_NAME} POST_BUILD
@@ -29,20 +12,7 @@ add_custom_command(TARGET ${TARGET_NAME} POST_BUILD · 4 unmodified lines
2912 "${PROJECT_SOURCE_DIR}/resources/web"
3013 $<TARGET_FILE_DIR:${TARGET_NAME}>/resources/web)
3114
32−# link the shared hifi library
33−include(${MACRO_DIR}/LinkHifiLibrary.cmake)
34−link_hifi_library(networking ${TARGET_NAME} "${ROOT_DIR}")
35−link_hifi_library(shared ${TARGET_NAME} "${ROOT_DIR}")
36−link_hifi_library(embedded-webserver ${TARGET_NAME} "${ROOT_DIR}")
37−
38−IF (WIN32)
39− target_link_libraries(${TARGET_NAME} Winmm Ws2_32)
40−ENDIF(WIN32)
41−
42−# link QtNetwork
43−target_link_libraries(${TARGET_NAME} Qt5::Network)
15+# link the shared hifi libraries
16+link_hifi_libraries(embedded-webserver networking shared)
4417
45−# add a definition for ssize_t so that windows doesn't bail
46−if (WIN32)
47− add_definitions(-Dssize_t=long)
48−endif ()
18+link_shared_dependencies()
domain-server/resources/web/settings/describe.json+6−0
@@ -21,6 +21,12 @@ · 20 unmodified lines
2121 "placeholder": "10",
2222 "default": "10"
2323 },
24+ "H-print-stream-stats": {
25+ "type": "checkbox",
26+ "label": "Print Stream Stats:",
27+ "help": "If enabled, audio upstream and downstream stats of each agent will be printed each second to stdout",
28+ "default": false
29+ },
2430 "D-unattenuated-zone": {
2531 "label": "Unattenuated Zone",
2632 "help": "Boxes for source and listener (corner x, corner y, corner z, size x, size y, size z, corner x, corner y, corner z, size x, size y, size z)",
examples/clipboardExample.js+8−1
@@ -24,6 +24,7 @@ function setupMenus() { · 23 unmodified lines
2424 Menu.removeMenuItem("Edit", "Paste");
2525 Menu.removeMenuItem("Edit", "Delete");
2626 Menu.removeMenuItem("Edit", "Nudge");
27+ Menu.removeMenuItem("Edit", "Replace from File");
2728 Menu.removeMenuItem("File", "Export Voxels");
2829 Menu.removeMenuItem("File", "Import Voxels");
2930
@@ -32,6 +33,7 @@ function setupMenus() { · 2 unmodified lines
3233 Menu.addMenuItem({ menuName: "Edit", menuItemName: "Copy", shortcutKey: "CTRL+C", afterItem: "Cut" });
3334 Menu.addMenuItem({ menuName: "Edit", menuItemName: "Paste", shortcutKey: "CTRL+V", afterItem: "Copy" });
3435 Menu.addMenuItem({ menuName: "Edit", menuItemName: "Nudge", shortcutKey: "CTRL+N", afterItem: "Paste" });
36+ Menu.addMenuItem({ menuName: "Edit", menuItemName: "Replace from File", shortcutKey: "CTRL+R", afterItem: "Nudge" });
3537 Menu.addMenuItem({ menuName: "Edit", menuItemName: "Delete", shortcutKeyEvent: { text: "backspace" }, afterItem: "Nudge" });
3638 Menu.addMenuItem({ menuName: "File", menuItemName: "Export Voxels", shortcutKey: "CTRL+E", afterItem: "Voxels" });
3739 Menu.addMenuItem({ menuName: "File", menuItemName: "Import Voxels", shortcutKey: "CTRL+I", afterItem: "Export Voxels" });
@@ -60,7 +62,6 @@ function menuItemEvent(menuItem) { · 22 unmodified lines
6062 print("deleting...");
6163 Clipboard.deleteVoxel(selectedVoxel.x, selectedVoxel.y, selectedVoxel.z, selectedVoxel.s);
6264 }
63−
6465 if (menuItem == "Export Voxels") {
6566 print("export");
6667 Clipboard.exportVoxel(selectedVoxel.x, selectedVoxel.y, selectedVoxel.z, selectedVoxel.s);
@@ -73,6 +74,12 @@ function menuItemEvent(menuItem) { · 6 unmodified lines
7374 print("nudge");
7475 Clipboard.nudgeVoxel(selectedVoxel.x, selectedVoxel.y, selectedVoxel.z, selectedVoxel.s, { x: -1, y: 0, z: 0 });
7576 }
77+ if (menuItem == "Replace from File") {
78+ var filename = Window.browse("Select file to load replacement", "", "Voxel Files (*.png *.svo *.schematic)");
79+ if (filename) {
80+ Clipboard.importVoxel(filename, selectedVoxel);
81+ }
82+ }
7683 }
7784
7885 var selectCube = Overlays.addOverlay("cube", {
interface/CMakeLists.txt+26−80
@@ -1,18 +1,13 @@
1−set(ROOT_DIR ..)
2−set(MACRO_DIR "${ROOT_DIR}/cmake/macros")
3−
41 set(TARGET_NAME interface)
52 project(${TARGET_NAME})
63
7−# setup for find modules
8−set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/../cmake/modules/")
9−
104 # set a default root dir for each of our optional externals if it was not passed
11−set(OPTIONAL_EXTERNALS "faceplus" "faceshift" "oculus" "priovr" "sixense" "visage" "leapmotion" "rtmidi" "qxmpp")
5+set(OPTIONAL_EXTERNALS "Faceplus" "Faceshift" "LibOVR" "PrioVR" "Sixense" "Visage" "LeapMotion" "RtMidi" "Qxmpp")
126 foreach(EXTERNAL ${OPTIONAL_EXTERNALS})
13− string(TOUPPER ${EXTERNAL} UPPER_EXTERNAL)
14− if (NOT ${UPPER_EXTERNAL}_ROOT_DIR)
15− set(${UPPER_EXTERNAL}_ROOT_DIR "${CMAKE_CURRENT_SOURCE_DIR}/external/${EXTERNAL}")
7+ string(TOUPPER ${EXTERNAL} ${EXTERNAL}_UPPERCASE)
8+ if (NOT ${${EXTERNAL}_UPPERCASE}_ROOT_DIR)
9+ string(TOLOWER ${EXTERNAL} ${EXTERNAL}_LOWERCASE)
10+ set(${${EXTERNAL}_UPPERCASE}_ROOT_DIR "${CMAKE_CURRENT_SOURCE_DIR}/external/${${EXTERNAL}_LOWERCASE}")
1611 endif ()
1712 endforeach()
1813
@@ -38,8 +33,7 @@ elseif (WIN32) · 19 unmodified lines
3833 endif ()
3934
4035 # set up the external glm library
41−include("${MACRO_DIR}/IncludeGLM.cmake")
42−include_glm(${TARGET_NAME} "${ROOT_DIR}")
36+include_glm()
4337
4438 # create the InterfaceConfig.h file based on GL_HEADERS above
4539 configure_file(InterfaceConfig.h.in "${PROJECT_BINARY_DIR}/includes/InterfaceConfig.h")
@@ -52,7 +46,7 @@ foreach(SUBDIR avatar devices renderer ui starfield location scripting voxels pa · 6 unmodified lines
5246 set(INTERFACE_SRCS ${INTERFACE_SRCS} "${SUBDIR_SRCS}")
5347 endforeach(SUBDIR)
5448
55−find_package(Qt5 COMPONENTS Core Gui Multimedia Network OpenGL Script Svg WebKit WebKitWidgets Xml UiTools)
49+find_package(Qt5 COMPONENTS Gui Multimedia Network OpenGL Script Svg WebKitWidgets)
5650
5751 # grab the ui files in resources/ui
5852 file (GLOB_RECURSE QT_UI_FILES ui/*.ui)
@@ -97,60 +91,36 @@ endif() · 38 unmodified lines
9791 # create the executable, make it a bundle on OS X
9892 add_executable(${TARGET_NAME} MACOSX_BUNDLE ${INTERFACE_SRCS} ${QM})
9993
100−# link in the hifi shared library
101−include(${MACRO_DIR}/LinkHifiLibrary.cmake)
102−
10394 # link required hifi libraries
104−link_hifi_library(shared ${TARGET_NAME} "${ROOT_DIR}")
105−link_hifi_library(octree ${TARGET_NAME} "${ROOT_DIR}")
106−link_hifi_library(voxels ${TARGET_NAME} "${ROOT_DIR}")
107−link_hifi_library(fbx ${TARGET_NAME} "${ROOT_DIR}")
108−link_hifi_library(metavoxels ${TARGET_NAME} "${ROOT_DIR}")
109−link_hifi_library(networking ${TARGET_NAME} "${ROOT_DIR}")
110−link_hifi_library(particles ${TARGET_NAME} "${ROOT_DIR}")
111−link_hifi_library(entities ${TARGET_NAME} "${ROOT_DIR}")
112−link_hifi_library(avatars ${TARGET_NAME} "${ROOT_DIR}")
113−link_hifi_library(audio ${TARGET_NAME} "${ROOT_DIR}")
114−link_hifi_library(animation ${TARGET_NAME} "${ROOT_DIR}")
115−link_hifi_library(script-engine ${TARGET_NAME} "${ROOT_DIR}")
95+link_hifi_libraries(shared octree voxels fbx metavoxels networking particles entities avatars audio animation script-engine)
11696
11797 # find any optional and required libraries
118−find_package(Faceplus)
119−find_package(Faceshift)
120−find_package(LibOVR)
121−find_package(PrioVR)
122−find_package(SDL)
123−find_package(Sixense)
124−find_package(Visage)
125−find_package(LeapMotion)
126−find_package(ZLIB)
127−find_package(Qxmpp)
128−find_package(RtMidi)
98+find_package(ZLIB REQUIRED)
12999 find_package(OpenSSL REQUIRED)
130100
131101 # perform standard include and linking for found externals
132102 foreach(EXTERNAL ${OPTIONAL_EXTERNALS})
133− string(TOUPPER ${EXTERNAL} UPPER_EXTERNAL)
134103
135− if (${UPPER_EXTERNAL} MATCHES "OCULUS")
136− # the oculus directory is named OCULUS and not LIBOVR so hack to fix that here
137− set(UPPER_EXTERNAL "LIBOVR")
104+ if (${${EXTERNAL}_UPPERCASE}_REQUIRED)
105+ find_package(${EXTERNAL} REQUIRED)
106+ else ()
107+ find_package(${EXTERNAL})
138108 endif ()
139109
140− if (${UPPER_EXTERNAL}_FOUND AND NOT DISABLE_${UPPER_EXTERNAL})
141− add_definitions(-DHAVE_${UPPER_EXTERNAL})
110+ if (${${EXTERNAL}_UPPERCASE}_FOUND AND NOT DISABLE_${${EXTERNAL}_UPPERCASE})
111+ add_definitions(-DHAVE_${${EXTERNAL}_UPPERCASE})
142112
143113 # include the library directories (ignoring warnings)
144− include_directories(SYSTEM ${${UPPER_EXTERNAL}_INCLUDE_DIRS})
114+ include_directories(SYSTEM ${${${EXTERNAL}_UPPERCASE}_INCLUDE_DIRS})
145115
146116 # perform the system include hack for OS X to ignore warnings
147117 if (APPLE)
148− foreach(EXTERNAL_INCLUDE_DIR ${${UPPER_EXTERNAL}_INCLUDE_DIRS})
118+ foreach(EXTERNAL_INCLUDE_DIR ${${${EXTERNAL}_UPPERCASE}_INCLUDE_DIRS})
149119 SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -isystem ${EXTERNAL_INCLUDE_DIR}")
150120 endforeach()
151121 endif ()
152122
153− target_link_libraries(${TARGET_NAME} ${${UPPER_EXTERNAL}_LIBRARIES})
123+ target_link_libraries(${TARGET_NAME} ${${${EXTERNAL}_UPPERCASE}_LIBRARIES})
154124
155125 endif ()
156126 endforeach()
@@ -179,17 +149,11 @@ endif () · 22 unmodified lines
179149
180150 # include headers for interface and InterfaceConfig.
181151 include_directories("${PROJECT_SOURCE_DIR}/src" "${PROJECT_BINARY_DIR}/includes")
182−
183−# include external library headers
184−# use system flag so warnings are supressed
185−include_directories(SYSTEM "${OPENSSL_INCLUDE_DIR}")
152+include_directories("${OPENSSL_INCLUDE_DIR}")
186153
187154 target_link_libraries(
188− ${TARGET_NAME}
189− "${ZLIB_LIBRARIES}"
190− ${OPENSSL_LIBRARIES}
191− Qt5::Core Qt5::Gui Qt5::Multimedia Qt5::Network Qt5::OpenGL
192− Qt5::Script Qt5::Svg Qt5::WebKit Qt5::WebKitWidgets Qt5::Xml Qt5::UiTools
155+ ${TARGET_NAME} ${ZLIB_LIBRARIES} ${OPENSSL_LIBRARIES}
156+ Qt5::Gui Qt5::Network Qt5::Multimedia Qt5::OpenGL Qt5::Script Qt5::Svg Qt5::WebKitWidgets
193157 )
194158
195159 # assume we are using a Qt build without bearer management
@@ -197,30 +161,12 @@ add_definitions(-DQT_NO_BEARERMANAGEMENT) · 1 unmodified line
197161
198162 if (APPLE)
199163 # link in required OS X frameworks and include the right GL headers
200− find_library(AppKit AppKit)
201164 find_library(CoreAudio CoreAudio)
202− find_library(CoreServices CoreServices)
203− find_library(Carbon Carbon)
204− find_library(Foundation Foundation)
165+ find_library(CoreFoundation CoreFoundation)
205166 find_library(GLUT GLUT)
206167 find_library(OpenGL OpenGL)
207− find_library(IOKit IOKit)
208− find_library(QTKit QTKit)
209− find_library(QuartzCore QuartzCore)
210168
211− target_link_libraries(
212− ${TARGET_NAME}
213− ${AppKit}
214− ${CoreAudio}
215− ${CoreServices}
216− ${Carbon}
217− ${Foundation}
218− ${GLUT}
219− ${OpenGL}
220− ${IOKit}
221− ${QTKit}
222− ${QuartzCore}
223− )
169+ target_link_libraries(${TARGET_NAME} ${CoreAudio} ${CoreFoundation} ${GLUT} ${OpenGL})
224170
225171 # install command for OS X bundle
226172 INSTALL(TARGETS ${TARGET_NAME}
@@ -254,9 +200,9 @@ else (APPLE) · 27 unmodified lines
254200 # we're using static GLEW, so define GLEW_STATIC
255201 add_definitions(-DGLEW_STATIC)
256202
257− # add a definition for ssize_t so that windows doesn't bail
258− add_definitions(-Dssize_t=long)
259−
260203 target_link_libraries(${TARGET_NAME} "${GLEW_LIBRARIES}" wsock32.lib opengl32.lib)
261204 endif()
262205 endif (APPLE)
206+
207+# link any dependencies bubbled up from our linked dependencies
208+link_shared_dependencies()
interface/external/libovr/readme.txtrenamed from interface/external/oculus/readme.txt+0−0

File renamed without changes.

interface/interface_en.ts+6−6
@@ -245,28 +245,28 @@ · 244 unmodified lines
245245 <context>
246246 <name>QObject</name>
247247 <message>
248− <location filename="src/ui/ImportDialog.cpp" line="22"/>
249− <location filename="src/ui/ImportDialog.cpp" line="23"/>
248+ <location filename="src/ui/VoxelImportDialog.cpp" line="22"/>
249+ <location filename="src/ui/VoxelImportDialog.cpp" line="23"/>
250250 <source>Import Voxels</source>
251251 <translation type="unfinished"></translation>
252252 </message>
253253 <message>
254− <location filename="src/ui/ImportDialog.cpp" line="24"/>
254+ <location filename="src/ui/VoxelImportDialog.cpp" line="24"/>
255255 <source>Loading ...</source>
256256 <translation type="unfinished"></translation>
257257 </message>
258258 <message>
259− <location filename="src/ui/ImportDialog.cpp" line="25"/>
259+ <location filename="src/ui/VoxelImportDialog.cpp" line="25"/>
260260 <source>Place voxels</source>
261261 <translation type="unfinished"></translation>
262262 </message>
263263 <message>
264− <location filename="src/ui/ImportDialog.cpp" line="26"/>
264+ <location filename="src/ui/VoxelImportDialog.cpp" line="26"/>
265265 <source>&lt;b&gt;Import&lt;/b&gt; %1 as voxels</source>
266266 <translation type="unfinished"></translation>
267267 </message>
268268 <message>
269− <location filename="src/ui/ImportDialog.cpp" line="27"/>
269+ <location filename="src/ui/VoxelImportDialog.cpp" line="27"/>
270270 <source>Cancel</source>
271271 <translation type="unfinished"></translation>
272272 </message>
interface/resources/shaders/metavoxel_heightfield.vert+14−4
@@ -17,6 +17,12 @@ uniform sampler2D heightMap; · 16 unmodified lines
1717 // the distance between height points in texture space
1818 uniform float heightScale;
1919
20+// the scale between height and color textures
21+uniform float colorScale;
22+
23+// the interpolated position
24+varying vec4 position;
25+
2026 // the interpolated normal
2127 varying vec4 normal;
2228
@@ -29,13 +35,17 @@ void main(void) { · 6 unmodified lines
2935 texture2D(heightMap, heightCoord + vec2(0.0, heightScale)).r;
3036 normal = normalize(gl_ModelViewMatrix * vec4(deltaX, heightScale, deltaZ, 0.0));
3137
32− // pass along the texture coordinates
33− gl_TexCoord[0] = gl_MultiTexCoord0;
34−
3538 // add the height to the position
3639 float height = texture2D(heightMap, heightCoord).r;
37− gl_Position = gl_ModelViewProjectionMatrix * (gl_Vertex + vec4(0.0, height, 0.0, 0.0));
40+ position = gl_ModelViewMatrix * (gl_Vertex + vec4(0.0, height, 0.0, 0.0));
41+ gl_Position = gl_ProjectionMatrix * position;
3842
3943 // the zero height should be invisible
4044 gl_FrontColor = vec4(1.0, 1.0, 1.0, step(height, 0.0));
45+
46+ // pass along the scaled/offset texture coordinates
47+ gl_TexCoord[0] = (gl_MultiTexCoord0 - vec4(heightScale, heightScale, 0.0, 0.0)) * colorScale;
48+
49+ // and the shadow texture coordinates
50+ gl_TexCoord[1] = vec4(dot(gl_EyePlaneS[0], position), dot(gl_EyePlaneT[0], position), dot(gl_EyePlaneR[0], position), 1.0);
4151 }
interface/resources/shaders/metavoxel_heightfield_cascaded_shadow_map.frag+48−0
@@ -0,0 +1,48 @@
1+#version 120
2+
3+//
4+// metavoxel_heightfield.frag
5+// fragment shader
6+//
7+// Created by Andrzej Kapolka on 7/28/14.
8+// Copyright 2014 High Fidelity, Inc.
9+//
10+// Distributed under the Apache License, Version 2.0.
11+// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
12+//
13+
14+// the diffuse texture
15+uniform sampler2D diffuseMap;
16+
17+// the shadow texture
18+uniform sampler2DShadow shadowMap;
19+
20+// the distances to the cascade sections
21+uniform vec3 shadowDistances;
22+
23+// the inverse of the size of the shadow map
24+const float shadowScale = 1.0 / 2048.0;
25+
26+// the interpolated position
27+varying vec4 position;
28+
29+// the interpolated normal
30+varying vec4 normal;
31+
32+void main(void) {
33+ // compute the index of the cascade to use and the corresponding texture coordinates
34+ int shadowIndex = int(dot(step(vec3(position.z), shadowDistances), vec3(1.0, 1.0, 1.0)));
35+ vec3 shadowTexCoord = vec3(dot(gl_EyePlaneS[shadowIndex], position), dot(gl_EyePlaneT[shadowIndex], position),
36+ dot(gl_EyePlaneR[shadowIndex], position));
37+
38+ // compute the base color based on OpenGL lighting model
39+ float diffuse = dot(normalize(normal), gl_LightSource[0].position);
40+ float facingLight = step(0.0, diffuse) * 0.25 *
41+ (shadow2D(shadowMap, shadowTexCoord + vec3(-shadowScale, -shadowScale, 0.0)).r +
42+ shadow2D(shadowMap, shadowTexCoord + vec3(-shadowScale, shadowScale, 0.0)).r +
43+ shadow2D(shadowMap, shadowTexCoord + vec3(shadowScale, -shadowScale, 0.0)).r +
44+ shadow2D(shadowMap, shadowTexCoord + vec3(shadowScale, shadowScale, 0.0)).r);
45+ vec4 base = gl_Color * (gl_FrontLightModelProduct.sceneColor + gl_FrontLightProduct[0].ambient +
46+ gl_FrontLightProduct[0].diffuse * (diffuse * facingLight));
47+ gl_FragColor = base * texture2D(diffuseMap, gl_TexCoord[0].st);
48+}
interface/resources/shaders/metavoxel_heightfield_cursor.frag+32−0
@@ -0,0 +1,32 @@
1+#version 120
2+
3+//
4+// metavoxel_heightfield_cursor.frag
5+// fragment shader
6+//
7+// Created by Andrzej Kapolka on 8/7/14.
8+// Copyright 2014 High Fidelity, Inc.
9+//
10+// Distributed under the Apache License, Version 2.0.
11+// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
12+//
13+
14+// the inner radius of the outline, squared
15+const float SQUARED_OUTLINE_INNER_RADIUS = 0.81;
16+
17+// the outer radius of the outline, squared
18+const float SQUARED_OUTLINE_OUTER_RADIUS = 1.0;
19+
20+// the inner radius of the inset, squared
21+const float SQUARED_INSET_INNER_RADIUS = 0.855625;
22+
23+// the outer radius of the inset, squared
24+const float SQUARED_INSET_OUTER_RADIUS = 0.950625;
25+
26+void main(void) {
27+ // use the distance to compute the ring color, then multiply it by the varying color
28+ float squaredDistance = dot(gl_TexCoord[0].st, gl_TexCoord[0].st);
29+ float alpha = step(SQUARED_OUTLINE_INNER_RADIUS, squaredDistance) * step(squaredDistance, SQUARED_OUTLINE_OUTER_RADIUS);
30+ float white = step(SQUARED_INSET_INNER_RADIUS, squaredDistance) * step(squaredDistance, SQUARED_INSET_OUTER_RADIUS);
31+ gl_FragColor = gl_Color * vec4(white, white, white, alpha);
32+}
interface/resources/shaders/metavoxel_heightfield_cursor.vert+28−0
@@ -0,0 +1,28 @@
1+#version 120
2+
3+//
4+// metavoxel_heighfield_cursor.vert
5+// vertex shader
6+//
7+// Created by Andrzej Kapolka on 8/7/14.
8+// Copyright 2014 High Fidelity, Inc.
9+//
10+// Distributed under the Apache License, Version 2.0.
11+// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
12+//
13+
14+// the height texture
15+uniform sampler2D heightMap;
16+
17+void main(void) {
18+ // compute the view space coordinates
19+ float height = texture2D(heightMap, gl_MultiTexCoord0.st).r;
20+ vec4 viewPosition = gl_ModelViewMatrix * (gl_Vertex + vec4(0.0, height, 0.0, 0.0));
21+ gl_Position = gl_ProjectionMatrix * viewPosition;
22+
23+ // generate the texture coordinates from the view position
24+ gl_TexCoord[0] = vec4(dot(viewPosition, gl_EyePlaneS[4]), dot(viewPosition, gl_EyePlaneT[4]), 0.0, 1.0);
25+
26+ // the zero height should be invisible
27+ gl_FrontColor = vec4(1.0, 1.0, 1.0, 1.0 - step(height, 0.0));
28+}
interface/resources/shaders/metavoxel_heightfield_shadow_map.frag+37−0
@@ -0,0 +1,37 @@
1+#version 120
2+
3+//
4+// metavoxel_heightfield.frag
5+// fragment shader
6+//
7+// Created by Andrzej Kapolka on 7/28/14.
8+// Copyright 2014 High Fidelity, Inc.
9+//
10+// Distributed under the Apache License, Version 2.0.
11+// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
12+//
13+
14+// the diffuse texture
15+uniform sampler2D diffuseMap;
16+
17+// the shadow texture
18+uniform sampler2DShadow shadowMap;
19+
20+// the inverse of the size of the shadow map
21+const float shadowScale = 1.0 / 2048.0;
22+
23+// the interpolated normal
24+varying vec4 normal;
25+
26+void main(void) {
27+ // compute the base color based on OpenGL lighting model
28+ float diffuse = dot(normalize(normal), gl_LightSource[0].position);
29+ float facingLight = step(0.0, diffuse) * 0.25 *
30+ (shadow2D(shadowMap, gl_TexCoord[1].stp + vec3(-shadowScale, -shadowScale, 0.0)).r +
31+ shadow2D(shadowMap, gl_TexCoord[1].stp + vec3(-shadowScale, shadowScale, 0.0)).r +
32+ shadow2D(shadowMap, gl_TexCoord[1].stp + vec3(shadowScale, -shadowScale, 0.0)).r +
33+ shadow2D(shadowMap, gl_TexCoord[1].stp + vec3(shadowScale, shadowScale, 0.0)).r);
34+ vec4 base = gl_Color * (gl_FrontLightModelProduct.sceneColor + gl_FrontLightProduct[0].ambient +
35+ gl_FrontLightProduct[0].diffuse * (diffuse * facingLight));
36+ gl_FragColor = base * texture2D(diffuseMap, gl_TexCoord[0].st);
37+}
interface/src/Application.cpp+27−15
@@ -137,7 +137,8 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) : · 136 unmodified lines
137137 _frameCount(0),
138138 _fps(60.0f),
139139 _justStarted(true),
140− _voxelImporter(NULL),
140+ _voxelImportDialog(NULL),
141+ _voxelImporter(),
141142 _importSucceded(false),
142143 _sharedVoxelSystem(TREE_SCALE, DEFAULT_MAX_VOXELS_PER_SYSTEM, &_clipboard),
143144 _entityClipboardRenderer(),
@@ -242,6 +243,8 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) : · 98 unmodified lines
242243
243244 connect(&domainHandler, SIGNAL(hostnameChanged(const QString&)), SLOT(domainChanged(const QString&)));
244245 connect(&domainHandler, SIGNAL(connectedToDomain(const QString&)), SLOT(connectedToDomain(const QString&)));
246+ connect(&domainHandler, SIGNAL(connectedToDomain(const QString&)), SLOT(updateWindowTitle()));
247+ connect(&domainHandler, SIGNAL(disconnectedFromDomain()), SLOT(updateWindowTitle()));
245248 connect(&domainHandler, &DomainHandler::settingsReceived, this, &Application::domainSettingsReceived);
246249
247250 // hookup VoxelEditSender to PaymentManager so we can pay for octree edits
@@ -430,7 +433,7 @@ Application::~Application() { · 182 unmodified lines
430433 delete idleTimer;
431434
432435 _sharedVoxelSystem.changeTree(new VoxelTree);
433− delete _voxelImporter;
436+ delete _voxelImportDialog;
434437
435438 // let the avatar mixer know we're out
436439 MyAvatar::sendKillAvatar();
@@ -465,8 +468,8 @@ void Application::saveSettings() { · 28 unmodified lines
465468 Menu::getInstance()->saveSettings();
466469 _rearMirrorTools->saveSettings(_settings);
467470
468− if (_voxelImporter) {
469− _voxelImporter->saveSettings(_settings);
471+ if (_voxelImportDialog) {
472+ _voxelImportDialog->saveSettings(_settings);
470473 }
471474 _settings->sync();
472475 _numChangedSettings = 0;
@@ -599,7 +602,7 @@ void Application::paintGL() { · 126 unmodified lines
599602
600603 if (_myCamera.getMode() == CAMERA_MODE_FIRST_PERSON) {
601604 _myCamera.setTightness(0.0f); // In first person, camera follows (untweaked) head exactly without delay
602− _myCamera.setTargetPosition(_myAvatar->getHead()->getFilteredEyePosition());
605+ _myCamera.setTargetPosition(_myAvatar->getHead()->getEyePosition());
603606 _myCamera.setTargetRotation(_myAvatar->getHead()->getCameraOrientation());
604607
605608 } else if (_myCamera.getMode() == CAMERA_MODE_THIRD_PERSON) {
@@ -621,7 +624,7 @@ void Application::paintGL() { · 15 unmodified lines
621624 _myCamera.setTargetPosition(_myAvatar->getHead()->getEyePosition() + glm::vec3(0, _raiseMirror * _myAvatar->getScale(), 0));
622625 } else {
623626 _myCamera.setTightness(0.0f);
624− glm::vec3 eyePosition = _myAvatar->getHead()->getFilteredEyePosition();
627+ glm::vec3 eyePosition = _myAvatar->getHead()->getEyePosition();
625628 float headHeight = eyePosition.y - _myAvatar->getPosition().y;
626629 _myCamera.setDistance(MIRROR_FULLSCREEN_DISTANCE * _scaleMirror);
627630 _myCamera.setTargetPosition(_myAvatar->getPosition() + glm::vec3(0, headHeight + (_raiseMirror * _myAvatar->getScale()), 0));
@@ -814,9 +817,8 @@ bool Application::event(QEvent* event) { · 186 unmodified lines
814817 QFileOpenEvent* fileEvent = static_cast<QFileOpenEvent*>(event);
815818 bool isHifiSchemeURL = !fileEvent->url().isEmpty() && fileEvent->url().toLocalFile().startsWith(CUSTOM_URL_SCHEME);
816819 if (isHifiSchemeURL) {
817− Menu::getInstance()->goTo(fileEvent->url().toString());
820+ Menu::getInstance()->goToURL(fileEvent->url().toLocalFile());
818821 }
819−
820822 return false;
821823 }
822824 return QApplication::event(event);
@@ -1440,6 +1442,15 @@ void Application::setEnable3DTVMode(bool enable3DTVMode) { · 617 unmodified lines
14401442 }
14411443
14421444 void Application::setEnableVRMode(bool enableVRMode) {
1445+ if (enableVRMode) {
1446+ if (!OculusManager::isConnected()) {
1447+ // attempt to reconnect the Oculus manager - it's possible this was a workaround
1448+ // for the sixense crash
1449+ OculusManager::disconnect();
1450+ OculusManager::connect();
1451+ }
1452+ }
1453+
14431454 resizeGL(_glWidget->width(), _glWidget->height());
14441455 }
14451456
@@ -1599,17 +1610,17 @@ void Application::exportVoxels(const VoxelDetail& sourceVoxel) { · 153 unmodified lines
15991610 void Application::importVoxels() {
16001611 _importSucceded = false;
16011612
1602− if (!_voxelImporter) {
1603− _voxelImporter = new VoxelImporter(_window);
1604− _voxelImporter->loadSettings(_settings);
1613+ if (!_voxelImportDialog) {
1614+ _voxelImportDialog = new VoxelImportDialog(_window);
1615+ _voxelImportDialog->loadSettings(_settings);
16051616 }
16061617
1607− if (!_voxelImporter->exec()) {
1618+ if (!_voxelImportDialog->exec()) {
16081619 qDebug() << "Import succeeded." << endl;
16091620 _importSucceded = true;
16101621 } else {
16111622 qDebug() << "Import failed." << endl;
1612− if (_sharedVoxelSystem.getTree() == _voxelImporter->getVoxelTree()) {
1623+ if (_sharedVoxelSystem.getTree() == _voxelImporter.getVoxelTree()) {
16131624 _sharedVoxelSystem.killLocalVoxels();
16141625 _sharedVoxelSystem.changeTree(&_clipboard);
16151626 }
@@ -1724,7 +1735,7 @@ void Application::init() { · 108 unmodified lines
17241735 // Cleanup of the original shared tree
17251736 _sharedVoxelSystem.init();
17261737
1727− _voxelImporter = new VoxelImporter(_window);
1738+ _voxelImportDialog = new VoxelImportDialog(_window);
17281739
17291740 _environment.init();
17301741
@@ -3322,9 +3333,10 @@ void Application::updateWindowTitle(){ · 1591 unmodified lines
33223333 QString buildVersion = " (build " + applicationVersion() + ")";
33233334 NodeList* nodeList = NodeList::getInstance();
33243335
3336+ QString connectionStatus = nodeList->getDomainHandler().isConnected() ? "" : " (NOT CONNECTED) ";
33253337 QString username = AccountManager::getInstance().getAccountInfo().getUsername();
33263338 QString title = QString() + (!username.isEmpty() ? username + " @ " : QString())
3327− + nodeList->getDomainHandler().getHostname() + buildVersion;
3339+ + nodeList->getDomainHandler().getHostname() + connectionStatus + buildVersion;
33283340
33293341 AccountManager& accountManager = AccountManager::getInstance();
33303342 if (accountManager.getAccountInfo().hasBalance()) {
interface/src/Application.h+4−1
@@ -87,6 +87,7 @@ · 86 unmodified lines
8787 #include "ui/overlays/Overlays.h"
8888 #include "ui/ApplicationOverlay.h"
8989 #include "ui/RunningScriptsWidget.h"
90+#include "ui/VoxelImportDialog.h"
9091 #include "voxels/VoxelFade.h"
9192 #include "voxels/VoxelHideShowThread.h"
9293 #include "voxels/VoxelImporter.h"
@@ -193,6 +194,7 @@ public: · 100 unmodified lines
193194 Camera* getCamera() { return &_myCamera; }
194195 ViewFrustum* getViewFrustum() { return &_viewFrustum; }
195196 ViewFrustum* getShadowViewFrustum() { return &_shadowViewFrustum; }
197+ VoxelImporter* getVoxelImporter() { return &_voxelImporter; }
196198 VoxelSystem* getVoxels() { return &_voxels; }
197199 VoxelTree* getVoxelTree() { return _voxels.getTree(); }
198200 const OctreePacketProcessor& getOctreePacketProcessor() const { return _octreeProcessor; }
@@ -463,7 +465,8 @@ private: · 264 unmodified lines
463465
464466 VoxelSystem _voxels;
465467 VoxelTree _clipboard; // if I copy/paste
466− VoxelImporter* _voxelImporter;
468+ VoxelImportDialog* _voxelImportDialog;
469+ VoxelImporter _voxelImporter;
467470 bool _importSucceded;
468471 VoxelSystem _sharedVoxelSystem;
469472 ViewFrustum _sharedVoxelSystemViewFrustum;
interface/src/Audio.cpp+42−2
@@ -56,7 +56,6 @@ static const int MUTE_ICON_SIZE = 24; · 55 unmodified lines
5656
5757 static const int RECEIVED_AUDIO_STREAM_CAPACITY_FRAMES = 100;
5858
59−
6059 Audio::Audio(QObject* parent) :
6160 AbstractAudioInterface(parent),
6261 _audioInput(NULL),
@@ -83,6 +82,7 @@ Audio::Audio(QObject* parent) : · 20 unmodified lines
8382 _noiseGateSampleCounter(0),
8483 _noiseGateOpen(false),
8584 _noiseGateEnabled(true),
85+ _peqEnabled(false),
8686 _toneInjectionEnabled(false),
8787 _noiseGateFramesToClose(0),
8888 _totalInputAudioSamples(0),
@@ -132,6 +132,7 @@ void Audio::init(QGLWidget *parent) { · 43 unmodified lines
132132 void Audio::reset() {
133133 _receivedAudioStream.reset();
134134 resetStats();
135+ _peq.reset();
135136 }
136137
137138 void Audio::resetStats() {
@@ -418,9 +419,15 @@ void Audio::start() { · 280 unmodified lines
418419 if (!outputFormatSupported) {
419420 qDebug() << "Unable to set up audio output because of a problem with output format.";
420421 }
422+
423+ _peq.initialize( _inputFormat.sampleRate(), _audioInput->bufferSize() );
424+
421425 }
422426
423427 void Audio::stop() {
428+
429+ _peq.finalize();
430+
424431 // "switch" to invalid devices in order to shut down the state
425432 switchInputToAudioDevice(QAudioDeviceInfo());
426433 switchOutputToAudioDevice(QAudioDeviceInfo());
@@ -462,7 +469,15 @@ void Audio::handleAudioInput() { · 35 unmodified lines
462469 int inputSamplesRequired = (int)((float)NETWORK_BUFFER_LENGTH_SAMPLES_PER_CHANNEL * inputToNetworkInputRatio);
463470
464471 QByteArray inputByteArray = _inputDevice->readAll();
465−
472+
473+ if (_peqEnabled && !_muted) {
474+ // we wish to pre-filter our captured input, prior to loopback
475+
476+ int16_t* ioBuffer = (int16_t*)inputByteArray.data();
477+
478+ _peq.render( ioBuffer, ioBuffer, inputByteArray.size() / sizeof(int16_t) );
479+ }
480+
466481 if (Menu::getInstance()->isOptionChecked(MenuOption::EchoLocalAudio) && !_muted && _audioOutput) {
467482 // if this person wants local loopback add that to the locally injected audio
468483
@@ -1172,6 +1187,31 @@ void Audio::renderToolBox(int x, int y, bool boxed) { · 703 unmodified lines
11721187 glDisable(GL_TEXTURE_2D);
11731188 }
11741189
1190+void Audio::toggleAudioFilter() {
1191+ _peqEnabled = !_peqEnabled;
1192+}
1193+
1194+void Audio::selectAudioFilterFlat() {
1195+ if (Menu::getInstance()->isOptionChecked(MenuOption::AudioFilterFlat)) {
1196+ _peq.loadProfile(0);
1197+ }
1198+}
1199+void Audio::selectAudioFilterTrebleCut() {
1200+ if (Menu::getInstance()->isOptionChecked(MenuOption::AudioFilterTrebleCut)) {
1201+ _peq.loadProfile(1);
1202+ }
1203+}
1204+void Audio::selectAudioFilterBassCut() {
1205+ if (Menu::getInstance()->isOptionChecked(MenuOption::AudioFilterBassCut)) {
1206+ _peq.loadProfile(2);
1207+ }
1208+}
1209+void Audio::selectAudioFilterSmiley() {
1210+ if (Menu::getInstance()->isOptionChecked(MenuOption::AudioFilterSmiley)) {
1211+ _peq.loadProfile(3);
1212+ }
1213+}
1214+
11751215 void Audio::toggleScope() {
11761216 _scopeEnabled = !_scopeEnabled;
11771217 if (_scopeEnabled) {
interface/src/Audio.h+14−3
@@ -19,6 +19,7 @@ · 18 unmodified lines
1919 #include "AudioStreamStats.h"
2020 #include "RingBufferHistory.h"
2121 #include "MovingMinMaxAvg.h"
22+#include "AudioFilter.h"
2223
2324 #include <QAudio>
2425 #include <QAudioInput>
@@ -125,7 +126,12 @@ public slots: · 100 unmodified lines
125126 void selectAudioScopeFiveFrames();
126127 void selectAudioScopeTwentyFrames();
127128 void selectAudioScopeFiftyFrames();
128−
129+ void toggleAudioFilter();
130+ void selectAudioFilterFlat();
131+ void selectAudioFilterTrebleCut();
132+ void selectAudioFilterBassCut();
133+ void selectAudioFilterSmiley();
134+
129135 virtual void handleAudioByteArray(const QByteArray& audioByteArray);
130136
131137 void sendDownstreamAudioStatsPacket();
@@ -252,12 +258,12 @@ private: · 120 unmodified lines
252258 // Audio scope methods for rendering
253259 void renderBackground(const float* color, int x, int y, int width, int height);
254260 void renderGrid(const float* color, int x, int y, int width, int height, int rows, int cols);
255− void renderLineStrip(const float* color, int x, int y, int n, int offset, const QByteArray* byteArray);
261+ void renderLineStrip(const float* color, int x, int y, int n, int offset, const QByteArray* byteArray);
256262
257263 // audio stats methods for rendering
258264 void renderAudioStreamStats(const AudioStreamStats& streamStats, int horizontalOffset, int& verticalOffset,
259265 float scale, float rotation, int font, const float* color, bool isDownstreamStats = false);
260−
266+
261267 // Audio scope data
262268 static const unsigned int NETWORK_SAMPLES_PER_FRAME = NETWORK_BUFFER_LENGTH_SAMPLES_PER_CHANNEL;
263269 static const unsigned int DEFAULT_FRAMES_PER_SCOPE = 5;
@@ -270,6 +276,11 @@ private: · 6 unmodified lines
270276 int _scopeOutputOffset;
271277 int _framesPerScope;
272278 int _samplesPerScope;
279+
280+ // Multi-band parametric EQ
281+ bool _peqEnabled;
282+ AudioFilterPEQ3 _peq;
283+
273284 QMutex _guard;
274285 QByteArray* _scopeInput;
275286 QByteArray* _scopeOutputLeft;
interface/src/Menu.cpp+44−2
@@ -486,6 +486,48 @@ Menu::Menu() : · 485 unmodified lines
486486 true,
487487 appInstance->getAudio(),
488488 SLOT(toggleAudioNoiseReduction()));
489+
490+ addCheckableActionToQMenuAndActionHash(audioDebugMenu, MenuOption::AudioFilter,
491+ 0,
492+ false,
493+ appInstance->getAudio(),
494+ SLOT(toggleAudioFilter()));
495+
496+ QMenu* audioFilterMenu = audioDebugMenu->addMenu("Audio Filter Options");
497+ addDisabledActionAndSeparator(audioFilterMenu, "Filter Response");
498+ {
499+ QAction *flat = addCheckableActionToQMenuAndActionHash(audioFilterMenu, MenuOption::AudioFilterFlat,
500+ 0,
501+ true,
502+ appInstance->getAudio(),
503+ SLOT(selectAudioFilterFlat()));
504+
505+ QAction *trebleCut = addCheckableActionToQMenuAndActionHash(audioFilterMenu, MenuOption::AudioFilterTrebleCut,
506+ 0,
507+ false,
508+ appInstance->getAudio(),
509+ SLOT(selectAudioFilterTrebleCut()));
510+
511+ QAction *bassCut = addCheckableActionToQMenuAndActionHash(audioFilterMenu, MenuOption::AudioFilterBassCut,
512+ 0,
513+ false,
514+ appInstance->getAudio(),
515+ SLOT(selectAudioFilterBassCut()));
516+
517+ QAction *smiley = addCheckableActionToQMenuAndActionHash(audioFilterMenu, MenuOption::AudioFilterSmiley,
518+ 0,
519+ false,
520+ appInstance->getAudio(),
521+ SLOT(selectAudioFilterSmiley()));
522+
523+
524+ QActionGroup* audioFilterGroup = new QActionGroup(audioFilterMenu);
525+ audioFilterGroup->addAction(flat);
526+ audioFilterGroup->addAction(trebleCut);
527+ audioFilterGroup->addAction(bassCut);
528+ audioFilterGroup->addAction(smiley);
529+ }
530+
489531 addCheckableActionToQMenuAndActionHash(audioDebugMenu, MenuOption::EchoServerAudio);
490532 addCheckableActionToQMenuAndActionHash(audioDebugMenu, MenuOption::EchoLocalAudio);
491533 addCheckableActionToQMenuAndActionHash(audioDebugMenu, MenuOption::StereoAudio, 0, false,
@@ -1111,8 +1153,8 @@ void Menu::goTo() { · 619 unmodified lines
11111153 }
11121154
11131155 bool Menu::goToURL(QString location) {
1114− if (location.startsWith(CUSTOM_URL_SCHEME + "//")) {
1115− QStringList urlParts = location.remove(0, CUSTOM_URL_SCHEME.length() + 2).split('/', QString::SkipEmptyParts);
1156+ if (location.startsWith(CUSTOM_URL_SCHEME + "/")) {
1157+ QStringList urlParts = location.remove(0, CUSTOM_URL_SCHEME.length()).split('/', QString::SkipEmptyParts);
11161158
11171159 if (urlParts.count() > 1) {
11181160 // if url has 2 or more parts, the first one is domain name
interface/src/Menu.h+5−0
@@ -311,6 +311,11 @@ namespace MenuOption { · 310 unmodified lines
311311 const QString Animations = "Animations...";
312312 const QString Atmosphere = "Atmosphere";
313313 const QString Attachments = "Attachments...";
314+ const QString AudioFilter = "Audio Filter Bank";
315+ const QString AudioFilterFlat = "Flat Response";
316+ const QString AudioFilterTrebleCut= "Treble Cut";
317+ const QString AudioFilterBassCut = "Bass Cut";
318+ const QString AudioFilterSmiley = "Smiley Curve";
314319 const QString AudioNoiseReduction = "Audio Noise Reduction";
315320 const QString AudioScope = "Audio Scope";
316321 const QString AudioScopeFiftyFrames = "Fifty";
interface/src/MetavoxelSystem.cpp+779−67
@@ -37,6 +37,9 @@ void MetavoxelSystem::init() { · 36 unmodified lines
3737 _pointBufferAttribute = AttributeRegistry::getInstance()->registerAttribute(new BufferDataAttribute("pointBuffer"));
3838 _heightfieldBufferAttribute = AttributeRegistry::getInstance()->registerAttribute(
3939 new BufferDataAttribute("heightfieldBuffer"));
40+
41+ _heightfieldBufferAttribute->setLODThresholdMultiplier(
42+ AttributeRegistry::getInstance()->getHeightfieldAttribute()->getLODThresholdMultiplier());
4043 }
4144
4245 MetavoxelLOD MetavoxelSystem::getLOD() {
@@ -118,6 +121,334 @@ void MetavoxelSystem::render() { · 75 unmodified lines
118121 guideToAugmented(renderVisitor);
119122 }
120123
124+class RayHeightfieldIntersectionVisitor : public RayIntersectionVisitor {
125+public:
126+
127+ float intersectionDistance;
128+
129+ RayHeightfieldIntersectionVisitor(const glm::vec3& origin, const glm::vec3& direction, const MetavoxelLOD& lod);
130+
131+ virtual int visit(MetavoxelInfo& info, float distance);
132+};
133+
134+RayHeightfieldIntersectionVisitor::RayHeightfieldIntersectionVisitor(const glm::vec3& origin,
135+ const glm::vec3& direction, const MetavoxelLOD& lod) :
136+ RayIntersectionVisitor(origin, direction, QVector<AttributePointer>() <<
137+ Application::getInstance()->getMetavoxels()->getHeightfieldBufferAttribute(), QVector<AttributePointer>(), lod),
138+ intersectionDistance(FLT_MAX) {
139+}
140+
141+static const float EIGHT_BIT_MAXIMUM_RECIPROCAL = 1.0f / 255.0f;
142+
143+int RayHeightfieldIntersectionVisitor::visit(MetavoxelInfo& info, float distance) {
144+ if (!info.isLeaf) {
145+ return _order;
146+ }
147+ const HeightfieldBuffer* buffer = static_cast<HeightfieldBuffer*>(
148+ info.inputValues.at(0).getInlineValue<BufferDataPointer>().data());
149+ if (!buffer) {
150+ return STOP_RECURSION;
151+ }
152+ const QByteArray& contents = buffer->getHeight();
153+ const uchar* src = (const uchar*)contents.constData();
154+ int size = glm::sqrt((float)contents.size());
155+ int unextendedSize = size - HeightfieldBuffer::HEIGHT_EXTENSION;
156+ int highest = HeightfieldBuffer::HEIGHT_BORDER + unextendedSize;
157+ float heightScale = unextendedSize * EIGHT_BIT_MAXIMUM_RECIPROCAL;
158+
159+ // find the initial location in heightfield coordinates
160+ glm::vec3 entry = (_origin + distance * _direction - info.minimum) * (float)unextendedSize / info.size;
161+ entry.x += HeightfieldBuffer::HEIGHT_BORDER;
162+ entry.z += HeightfieldBuffer::HEIGHT_BORDER;
163+ glm::vec3 floors = glm::floor(entry);
164+ glm::vec3 ceils = glm::ceil(entry);
165+ if (floors.x == ceils.x) {
166+ if (_direction.x > 0.0f) {
167+ ceils.x += 1.0f;
168+ } else {
169+ floors.x -= 1.0f;
170+ }
171+ }
172+ if (floors.z == ceils.z) {
173+ if (_direction.z > 0.0f) {
174+ ceils.z += 1.0f;
175+ } else {
176+ floors.z -= 1.0f;
177+ }
178+ }
179+
180+ bool withinBounds = true;
181+ float accumulatedDistance = 0.0f;
182+ while (withinBounds) {
183+ // find the heights at the corners of the current cell
184+ int floorX = qMin(qMax((int)floors.x, HeightfieldBuffer::HEIGHT_BORDER), highest);
185+ int floorZ = qMin(qMax((int)floors.z, HeightfieldBuffer::HEIGHT_BORDER), highest);
186+ int ceilX = qMin(qMax((int)ceils.x, HeightfieldBuffer::HEIGHT_BORDER), highest);
187+ int ceilZ = qMin(qMax((int)ceils.z, HeightfieldBuffer::HEIGHT_BORDER), highest);
188+ float upperLeft = src[floorZ * size + floorX] * heightScale;
189+ float upperRight = src[floorZ * size + ceilX] * heightScale;
190+ float lowerLeft = src[ceilZ * size + floorX] * heightScale;
191+ float lowerRight = src[ceilZ * size + ceilX] * heightScale;
192+
193+ // find the distance to the next x coordinate
194+ float xDistance = FLT_MAX;
195+ if (_direction.x > 0.0f) {
196+ xDistance = (ceils.x - entry.x) / _direction.x;
197+ } else if (_direction.x < 0.0f) {
198+ xDistance = (floors.x - entry.x) / _direction.x;
199+ }
200+
201+ // and the distance to the next z coordinate
202+ float zDistance = FLT_MAX;
203+ if (_direction.z > 0.0f) {
204+ zDistance = (ceils.z - entry.z) / _direction.z;
205+ } else if (_direction.z < 0.0f) {
206+ zDistance = (floors.z - entry.z) / _direction.z;
207+ }
208+
209+ // the exit distance is the lower of those two
210+ float exitDistance = qMin(xDistance, zDistance);
211+ glm::vec3 exit, nextFloors = floors, nextCeils = ceils;
212+ if (exitDistance == FLT_MAX) {
213+ if (_direction.y > 0.0f) {
214+ return SHORT_CIRCUIT; // line points upwards; no collisions possible
215+ }
216+ withinBounds = false; // line points downwards; check this cell only
217+
218+ } else {
219+ // find the exit point and the next cell, and determine whether it's still within the bounds
220+ exit = entry + exitDistance * _direction;
221+ withinBounds = (exit.y >= HeightfieldBuffer::HEIGHT_BORDER && exit.y <= highest);
222+ if (exitDistance == xDistance) {
223+ if (_direction.x > 0.0f) {
224+ nextFloors.x += 1.0f;
225+ withinBounds &= (nextCeils.x += 1.0f) <= highest;
226+ } else {
227+ withinBounds &= (nextFloors.x -= 1.0f) >= HeightfieldBuffer::HEIGHT_BORDER;
228+ nextCeils.x -= 1.0f;
229+ }
230+ }
231+ if (exitDistance == zDistance) {
232+ if (_direction.z > 0.0f) {
233+ nextFloors.z += 1.0f;
234+ withinBounds &= (nextCeils.z += 1.0f) <= highest;
235+ } else {
236+ withinBounds &= (nextFloors.z -= 1.0f) >= HeightfieldBuffer::HEIGHT_BORDER;
237+ nextCeils.z -= 1.0f;
238+ }
239+ }
240+ // check the vertical range of the ray against the ranges of the cell heights
241+ if (qMin(entry.y, exit.y) > qMax(qMax(upperLeft, upperRight), qMax(lowerLeft, lowerRight)) ||
242+ qMax(entry.y, exit.y) < qMin(qMin(upperLeft, upperRight), qMin(lowerLeft, lowerRight))) {
243+ entry = exit;
244+ floors = nextFloors;
245+ ceils = nextCeils;
246+ accumulatedDistance += exitDistance;
247+ continue;
248+ }
249+ }
250+ // having passed the bounds check, we must check against the planes
251+ glm::vec3 relativeEntry = entry - glm::vec3(floors.x, upperLeft, floors.z);
252+
253+ // first check the triangle including the Z+ segment
254+ glm::vec3 lowerNormal(lowerLeft - lowerRight, 1.0f, upperLeft - lowerLeft);
255+ float lowerProduct = glm::dot(lowerNormal, _direction);
256+ if (lowerProduct < 0.0f) {
257+ float planeDistance = -glm::dot(lowerNormal, relativeEntry) / lowerProduct;
258+ glm::vec3 intersection = relativeEntry + planeDistance * _direction;
259+ if (intersection.x >= 0.0f && intersection.x <= 1.0f && intersection.z >= 0.0f && intersection.z <= 1.0f &&
260+ intersection.z >= intersection.x) {
261+ intersectionDistance = qMin(intersectionDistance, distance +
262+ (accumulatedDistance + planeDistance) * (info.size / unextendedSize));
263+ return SHORT_CIRCUIT;
264+ }
265+ }
266+
267+ // then the one with the X+ segment
268+ glm::vec3 upperNormal(upperLeft - upperRight, 1.0f, upperRight - lowerRight);
269+ float upperProduct = glm::dot(upperNormal, _direction);
270+ if (upperProduct < 0.0f) {
271+ float planeDistance = -glm::dot(upperNormal, relativeEntry) / upperProduct;
272+ glm::vec3 intersection = relativeEntry + planeDistance * _direction;
273+ if (intersection.x >= 0.0f && intersection.x <= 1.0f && intersection.z >= 0.0f && intersection.z <= 1.0f &&
274+ intersection.x >= intersection.z) {
275+ intersectionDistance = qMin(intersectionDistance, distance +
276+ (accumulatedDistance + planeDistance) * (info.size / unextendedSize));
277+ return SHORT_CIRCUIT;
278+ }
279+ }
280+
281+ // no joy; continue on our way
282+ entry = exit;
283+ floors = nextFloors;
284+ ceils = nextCeils;
285+ accumulatedDistance += exitDistance;
286+ }
287+
288+ return STOP_RECURSION;
289+}
290+
291+bool MetavoxelSystem::findFirstRayHeightfieldIntersection(const glm::vec3& origin,
292+ const glm::vec3& direction, float& distance) {
293+ RayHeightfieldIntersectionVisitor visitor(origin, direction, getLOD());
294+ guideToAugmented(visitor);
295+ if (visitor.intersectionDistance == FLT_MAX) {
296+ return false;
297+ }
298+ distance = visitor.intersectionDistance;
299+ return true;
300+}
301+
302+class HeightfieldHeightVisitor : public MetavoxelVisitor {
303+public:
304+
305+ float height;
306+
307+ HeightfieldHeightVisitor(const MetavoxelLOD& lod, const glm::vec3& location);
308+
309+ virtual int visit(MetavoxelInfo& info);
310+
311+private:
312+
313+ glm::vec3 _location;
314+};
315+
316+HeightfieldHeightVisitor::HeightfieldHeightVisitor(const MetavoxelLOD& lod, const glm::vec3& location) :
317+ MetavoxelVisitor(QVector<AttributePointer>() <<
318+ Application::getInstance()->getMetavoxels()->getHeightfieldBufferAttribute(), QVector<AttributePointer>(), lod),
319+ height(-FLT_MAX),
320+ _location(location) {
321+}
322+
323+static const int REVERSE_ORDER = MetavoxelVisitor::encodeOrder(7, 6, 5, 4, 3, 2, 1, 0);
324+
325+int HeightfieldHeightVisitor::visit(MetavoxelInfo& info) {
326+ glm::vec3 relative = _location - info.minimum;
327+ if (relative.x < 0.0f || relative.z < 0.0f || relative.x > info.size || relative.z > info.size ||
328+ height >= info.minimum.y + info.size) {
329+ return STOP_RECURSION;
330+ }
331+ if (!info.isLeaf) {
332+ return REVERSE_ORDER;
333+ }
334+ const HeightfieldBuffer* buffer = static_cast<HeightfieldBuffer*>(
335+ info.inputValues.at(0).getInlineValue<BufferDataPointer>().data());
336+ if (!buffer) {
337+ return STOP_RECURSION;
338+ }
339+ const QByteArray& contents = buffer->getHeight();
340+ const uchar* src = (const uchar*)contents.constData();
341+ int size = glm::sqrt((float)contents.size());
342+ int unextendedSize = size - HeightfieldBuffer::HEIGHT_EXTENSION;
343+ int highest = HeightfieldBuffer::HEIGHT_BORDER + unextendedSize;
344+ relative *= unextendedSize / info.size;
345+ relative.x += HeightfieldBuffer::HEIGHT_BORDER;
346+ relative.z += HeightfieldBuffer::HEIGHT_BORDER;
347+
348+ // find the bounds of the cell containing the point and the shared vertex heights
349+ glm::vec3 floors = glm::floor(relative);
350+ glm::vec3 ceils = glm::ceil(relative);
351+ glm::vec3 fracts = glm::fract(relative);
352+ int floorX = qMin(qMax((int)floors.x, HeightfieldBuffer::HEIGHT_BORDER), highest);
353+ int floorZ = qMin(qMax((int)floors.z, HeightfieldBuffer::HEIGHT_BORDER), highest);
354+ int ceilX = qMin(qMax((int)ceils.x, HeightfieldBuffer::HEIGHT_BORDER), highest);
355+ int ceilZ = qMin(qMax((int)ceils.z, HeightfieldBuffer::HEIGHT_BORDER), highest);
356+ float upperLeft = src[floorZ * size + floorX];
357+ float lowerRight = src[ceilZ * size + ceilX];
358+ float interpolatedHeight = glm::mix(upperLeft, lowerRight, fracts.z);
359+
360+ // the final vertex (and thus which triangle we check) depends on which half we're on
361+ if (fracts.x >= fracts.z) {
362+ float upperRight = src[floorZ * size + ceilX];
363+ interpolatedHeight = glm::mix(interpolatedHeight, glm::mix(upperRight, lowerRight, fracts.z),
364+ (fracts.x - fracts.z) / (1.0f - fracts.z));
365+
366+ } else {
367+ float lowerLeft = src[ceilZ * size + floorX];
368+ interpolatedHeight = glm::mix(glm::mix(upperLeft, lowerLeft, fracts.z), interpolatedHeight, fracts.x / fracts.z);
369+ }
370+ if (interpolatedHeight == 0.0f) {
371+ return STOP_RECURSION; // ignore zero values
372+ }
373+
374+ // convert the interpolated height into world space
375+ height = qMax(height, info.minimum.y + interpolatedHeight * info.size * EIGHT_BIT_MAXIMUM_RECIPROCAL);
376+ return SHORT_CIRCUIT;
377+}
378+
379+float MetavoxelSystem::getHeightfieldHeight(const glm::vec3& location) {
380+ HeightfieldHeightVisitor visitor(getLOD(), location);
381+ guideToAugmented(visitor);
382+ return visitor.height;
383+}
384+
385+class HeightfieldCursorRenderVisitor : public MetavoxelVisitor {
386+public:
387+
388+ HeightfieldCursorRenderVisitor(const Box& bounds);
389+
390+ virtual int visit(MetavoxelInfo& info);
391+
392+private:
393+
394+ Box _bounds;
395+};
396+
397+HeightfieldCursorRenderVisitor::HeightfieldCursorRenderVisitor(const Box& bounds) :
398+ MetavoxelVisitor(QVector<AttributePointer>() <<
399+ Application::getInstance()->getMetavoxels()->getHeightfieldBufferAttribute()),
400+ _bounds(bounds) {
401+}
402+
403+int HeightfieldCursorRenderVisitor::visit(MetavoxelInfo& info) {
404+ if (!info.getBounds().intersects(_bounds)) {
405+ return STOP_RECURSION;
406+ }
407+ if (!info.isLeaf) {
408+ return DEFAULT_ORDER;
409+ }
410+ BufferDataPointer buffer = info.inputValues.at(0).getInlineValue<BufferDataPointer>();
411+ if (buffer) {
412+ buffer->render(true);
413+ }
414+ return STOP_RECURSION;
415+}
416+
417+void MetavoxelSystem::renderHeightfieldCursor(const glm::vec3& position, float radius) {
418+ glDepthFunc(GL_LEQUAL);
419+ glEnable(GL_CULL_FACE);
420+ glEnable(GL_POLYGON_OFFSET_FILL);
421+ glPolygonOffset(-1.0f, -1.0f);
422+
423+ glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
424+
425+ glEnableClientState(GL_VERTEX_ARRAY);
426+ glEnableClientState(GL_TEXTURE_COORD_ARRAY);
427+
428+ DefaultMetavoxelRendererImplementation::getHeightfieldCursorProgram().bind();
429+
430+ glActiveTexture(GL_TEXTURE4);
431+ float scale = 1.0f / radius;
432+ glm::vec4 sCoefficients(scale, 0.0f, 0.0f, -scale * position.x);
433+ glm::vec4 tCoefficients(0.0f, 0.0f, scale, -scale * position.z);
434+ glTexGenfv(GL_S, GL_EYE_PLANE, (const GLfloat*)&sCoefficients);
435+ glTexGenfv(GL_T, GL_EYE_PLANE, (const GLfloat*)&tCoefficients);
436+ glActiveTexture(GL_TEXTURE0);
437+
438+ glm::vec3 extents(radius, radius, radius);
439+ HeightfieldCursorRenderVisitor visitor(Box(position - extents, position + extents));
440+ guideToAugmented(visitor);
441+
442+ DefaultMetavoxelRendererImplementation::getHeightfieldCursorProgram().release();
443+
444+ glDisableClientState(GL_TEXTURE_COORD_ARRAY);
445+ glDisableClientState(GL_VERTEX_ARRAY);
446+
447+ glDisable(GL_POLYGON_OFFSET_FILL);
448+ glDisable(GL_CULL_FACE);
449+ glDepthFunc(GL_LESS);
450+}
451+
121452 void MetavoxelSystem::deleteTextures(int heightID, int colorID) {
122453 glDeleteTextures(1, (GLuint*)&heightID);
123454 glDeleteTextures(1, (GLuint*)&colorID);
@@ -239,7 +570,7 @@ PointBuffer::PointBuffer(const BufferPointVector& points) : · 115 unmodified lines
239570 _points(points) {
240571 }
241572
242−void PointBuffer::render() {
573+void PointBuffer::render(bool cursor) {
243574 // initialize buffer, etc. on first render
244575 if (!_buffer.isCreated()) {
245576 _buffer.setUsagePattern(QOpenGLBuffer::StaticDraw);
@@ -265,16 +596,32 @@ void PointBuffer::render() { · 19 unmodified lines
265596 _buffer.release();
266597 }
267598
599+const int HeightfieldBuffer::HEIGHT_BORDER = 1;
600+const int HeightfieldBuffer::SHARED_EDGE = 1;
601+const int HeightfieldBuffer::HEIGHT_EXTENSION = 2 * HeightfieldBuffer::HEIGHT_BORDER + HeightfieldBuffer::SHARED_EDGE;
602+
268603 HeightfieldBuffer::HeightfieldBuffer(const glm::vec3& translation, float scale,
269− const QByteArray& height, const QByteArray& color, bool clearAfterLoading) :
604+ const QByteArray& height, const QByteArray& color) :
270605 _translation(translation),
271606 _scale(scale),
607+ _heightBounds(translation, translation + glm::vec3(scale, scale, scale)),
608+ _colorBounds(_heightBounds),
272609 _height(height),
273610 _color(color),
274− _clearAfterLoading(clearAfterLoading),
275611 _heightTextureID(0),
276612 _colorTextureID(0),
277− _heightSize(glm::sqrt(height.size())) {
613+ _heightSize(glm::sqrt(height.size())),
614+ _heightIncrement(scale / (_heightSize - HEIGHT_EXTENSION)),
615+ _colorSize(glm::sqrt(color.size() / HeightfieldData::COLOR_BYTES)),
616+ _colorIncrement(scale / (_colorSize - SHARED_EDGE)) {
617+
618+ _heightBounds.minimum.x -= _heightIncrement * HEIGHT_BORDER;
619+ _heightBounds.minimum.z -= _heightIncrement * HEIGHT_BORDER;
620+ _heightBounds.maximum.x += _heightIncrement * (SHARED_EDGE + HEIGHT_BORDER);
621+ _heightBounds.maximum.z += _heightIncrement * (SHARED_EDGE + HEIGHT_BORDER);
622+
623+ _colorBounds.maximum.x += _colorIncrement * SHARED_EDGE;
624+ _colorBounds.maximum.z += _colorIncrement * SHARED_EDGE;
278625 }
279626
280627 HeightfieldBuffer::~HeightfieldBuffer() {
@@ -288,25 +635,49 @@ HeightfieldBuffer::~HeightfieldBuffer() { · 7 unmodified lines
288635 }
289636 }
290637
638+QByteArray HeightfieldBuffer::getUnextendedHeight() const {
639+ int srcSize = glm::sqrt(_height.size());
640+ int destSize = srcSize - 3;
641+ QByteArray unextended(destSize * destSize, 0);
642+ const char* src = _height.constData() + srcSize + 1;
643+ char* dest = unextended.data();
644+ for (int z = 0; z < destSize; z++, src += srcSize, dest += destSize) {
645+ memcpy(dest, src, destSize);
646+ }
647+ return unextended;
648+}
649+
650+QByteArray HeightfieldBuffer::getUnextendedColor() const {
651+ int srcSize = glm::sqrt(_color.size() / HeightfieldData::COLOR_BYTES);
652+ int destSize = srcSize - 1;
653+ QByteArray unextended(destSize * destSize * HeightfieldData::COLOR_BYTES, 0);
654+ const char* src = _color.constData();
655+ int srcStride = srcSize * HeightfieldData::COLOR_BYTES;
656+ char* dest = unextended.data();
657+ int destStride = destSize * HeightfieldData::COLOR_BYTES;
658+ for (int z = 0; z < destSize; z++, src += srcStride, dest += destStride) {
659+ memcpy(dest, src, destStride);
660+ }
661+ return unextended;
662+}
663+
291664 class HeightfieldPoint {
292665 public:
293666 glm::vec2 textureCoord;
294667 glm::vec3 vertex;
295668 };
296669
297−void HeightfieldBuffer::render() {
670+void HeightfieldBuffer::render(bool cursor) {
298671 // initialize textures, etc. on first render
299672 if (_heightTextureID == 0) {
300673 glGenTextures(1, &_heightTextureID);
301674 glBindTexture(GL_TEXTURE_2D, _heightTextureID);
675+ glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
302676 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
303677 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
304678 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
305679 glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, _heightSize, _heightSize, 0,
306680 GL_LUMINANCE, GL_UNSIGNED_BYTE, _height.constData());
307− if (_clearAfterLoading) {
308− _height.clear();
309− }
310681
311682 glGenTextures(1, &_colorTextureID);
312683 glBindTexture(GL_TEXTURE_2D, _colorTextureID);
@@ -318,32 +689,32 @@ void HeightfieldBuffer::render() { · 5 unmodified lines
318689 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 1, 1, 0, GL_RGB, GL_UNSIGNED_BYTE, WHITE_COLOR);
319690
320691 } else {
321− int colorSize = glm::sqrt(_color.size() / 3);
692+ int colorSize = glm::sqrt(_color.size() / HeightfieldData::COLOR_BYTES);
322693 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, colorSize, colorSize, 0, GL_RGB, GL_UNSIGNED_BYTE, _color.constData());
323− if (_clearAfterLoading) {
324− _color.clear();
325− }
326694 }
327695 }
328696 // create the buffer objects lazily
329− int sizeWithSkirt = _heightSize + 2;
330− int vertexCount = sizeWithSkirt * sizeWithSkirt;
331− int rows = sizeWithSkirt - 1;
332− int indexCount = rows * rows * 4;
697+ int innerSize = _heightSize - 2 * HeightfieldBuffer::HEIGHT_BORDER;
698+ int vertexCount = _heightSize * _heightSize;
699+ int rows = _heightSize - 1;
700+ int indexCount = rows * rows * 3 * 2;
333701 BufferPair& bufferPair = _bufferPairs[_heightSize];
334702 if (!bufferPair.first.isCreated()) {
335703 QVector<HeightfieldPoint> vertices(vertexCount);
336704 HeightfieldPoint* point = vertices.data();
337705
338− float step = 1.0f / (_heightSize - 1);
339− float z = -step;
340− for (int i = 0; i < sizeWithSkirt; i++, z += step) {
341− float x = -step;
706+ float vertexStep = 1.0f / (innerSize - 1);
707+ float z = -vertexStep;
708+ float textureStep = 1.0f / _heightSize;
709+ float t = textureStep / 2.0f;
710+ for (int i = 0; i < _heightSize; i++, z += vertexStep, t += textureStep) {
711+ float x = -vertexStep;
712+ float s = textureStep / 2.0f;
342713 const float SKIRT_LENGTH = 0.25f;
343− float baseY = (i == 0 || i == sizeWithSkirt - 1) ? -SKIRT_LENGTH : 0.0f;
344− for (int j = 0; j < sizeWithSkirt; j++, point++, x += step) {
345− point->vertex = glm::vec3(x, (j == 0 || j == sizeWithSkirt - 1) ? -SKIRT_LENGTH : baseY, z);
346− point->textureCoord = glm::vec2(x, z);
714+ float baseY = (i == 0 || i == _heightSize - 1) ? -SKIRT_LENGTH : 0.0f;
715+ for (int j = 0; j < _heightSize; j++, point++, x += vertexStep, s += textureStep) {
716+ point->vertex = glm::vec3(x, (j == 0 || j == _heightSize - 1) ? -SKIRT_LENGTH : baseY, z);
717+ point->textureCoord = glm::vec2(s, t);
347718 }
348719 }
349720
@@ -355,13 +726,16 @@ void HeightfieldBuffer::render() { · 5 unmodified lines
355726 QVector<int> indices(indexCount);
356727 int* index = indices.data();
357728 for (int i = 0; i < rows; i++) {
358− int lineIndex = i * sizeWithSkirt;
359− int nextLineIndex = (i + 1) * sizeWithSkirt;
729+ int lineIndex = i * _heightSize;
730+ int nextLineIndex = (i + 1) * _heightSize;
360731 for (int j = 0; j < rows; j++) {
361732 *index++ = lineIndex + j;
362733 *index++ = nextLineIndex + j;
734+ *index++ = nextLineIndex + j + 1;
735+
363736 *index++ = nextLineIndex + j + 1;
364737 *index++ = lineIndex + j + 1;
738+ *index++ = lineIndex + j;
365739 }
366740 }
367741
@@ -385,17 +759,33 @@ void HeightfieldBuffer::render() { · 17 unmodified lines
385759
386760 glBindTexture(GL_TEXTURE_2D, _heightTextureID);
387761
388− glActiveTexture(GL_TEXTURE1);
389− glBindTexture(GL_TEXTURE_2D, _colorTextureID);
762+ if (!cursor) {
763+ int heightScaleLocation = DefaultMetavoxelRendererImplementation::getHeightScaleLocation();
764+ int colorScaleLocation = DefaultMetavoxelRendererImplementation::getColorScaleLocation();
765+ ProgramObject* program = &DefaultMetavoxelRendererImplementation::getHeightfieldProgram();
766+ if (Menu::getInstance()->isOptionChecked(MenuOption::SimpleShadows)) {
767+ heightScaleLocation = DefaultMetavoxelRendererImplementation::getShadowMapHeightScaleLocation();
768+ colorScaleLocation = DefaultMetavoxelRendererImplementation::getShadowMapColorScaleLocation();
769+ program = &DefaultMetavoxelRendererImplementation::getShadowMapHeightfieldProgram();
770+
771+ } else if (Menu::getInstance()->isOptionChecked(MenuOption::CascadedShadows)) {
772+ heightScaleLocation = DefaultMetavoxelRendererImplementation::getCascadedShadowMapHeightScaleLocation();
773+ colorScaleLocation = DefaultMetavoxelRendererImplementation::getCascadedShadowMapColorScaleLocation();
774+ program = &DefaultMetavoxelRendererImplementation::getCascadedShadowMapHeightfieldProgram();
775+ }
776+ program->setUniformValue(heightScaleLocation, 1.0f / _heightSize);
777+ program->setUniformValue(colorScaleLocation, (float)_heightSize / innerSize);
778+ glActiveTexture(GL_TEXTURE1);
779+ glBindTexture(GL_TEXTURE_2D, _colorTextureID);
780+ }
390781
391− DefaultMetavoxelRendererImplementation::getHeightfieldProgram().setUniformValue(
392− DefaultMetavoxelRendererImplementation::getHeightScaleLocation(), 1.0f / _heightSize);
393−
394− glDrawRangeElements(GL_QUADS, 0, vertexCount - 1, indexCount, GL_UNSIGNED_INT, 0);
782+ glDrawRangeElements(GL_TRIANGLES, 0, vertexCount - 1, indexCount, GL_UNSIGNED_INT, 0);
395783
396− glBindTexture(GL_TEXTURE_2D, 0);
784+ if (!cursor) {
785+ glBindTexture(GL_TEXTURE_2D, 0);
786+ glActiveTexture(GL_TEXTURE0);
787+ }
397788
398− glActiveTexture(GL_TEXTURE0);
399789 glBindTexture(GL_TEXTURE_2D, 0);
400790
401791 glPopMatrix();
@@ -444,17 +834,19 @@ BufferDataAttribute::BufferDataAttribute(const QString& name) : · 42 unmodified lines
444834 }
445835
446836 bool BufferDataAttribute::merge(void*& parent, void* children[], bool postRead) const {
447− BufferDataPointer firstChild = decodeInline<BufferDataPointer>(children[0]);
448− for (int i = 1; i < MERGE_COUNT; i++) {
449− if (firstChild != decodeInline<BufferDataPointer>(children[i])) {
450− *(BufferDataPointer*)&parent = _defaultValue;
837+ *(BufferDataPointer*)&parent = _defaultValue;
838+ for (int i = 0; i < MERGE_COUNT; i++) {
839+ if (decodeInline<BufferDataPointer>(children[i])) {
451840 return false;
452841 }
453842 }
454− *(BufferDataPointer*)&parent = firstChild;
455843 return true;
456844 }
457845
846+AttributeValue BufferDataAttribute::inherit(const AttributeValue& parentValue) const {
847+ return AttributeValue(parentValue.getAttribute());
848+}
849+
458850 void DefaultMetavoxelRendererImplementation::init() {
459851 if (!_pointProgram.isLinked()) {
460852 _pointProgram.addShaderFromSourceFile(QGLShader::Vertex, Application::resourcesPath() + "shaders/metavoxel_point.vert");
@@ -474,7 +866,47 @@ void DefaultMetavoxelRendererImplementation::init() { · 13 unmodified lines
474866 _heightfieldProgram.setUniformValue("heightMap", 0);
475867 _heightfieldProgram.setUniformValue("diffuseMap", 1);
476868 _heightScaleLocation = _heightfieldProgram.uniformLocation("heightScale");
869+ _colorScaleLocation = _heightfieldProgram.uniformLocation("colorScale");
477870 _heightfieldProgram.release();
871+
872+ _shadowMapHeightfieldProgram.addShaderFromSourceFile(QGLShader::Vertex, Application::resourcesPath() +
873+ "shaders/metavoxel_heightfield.vert");
874+ _shadowMapHeightfieldProgram.addShaderFromSourceFile(QGLShader::Fragment, Application::resourcesPath() +
875+ "shaders/metavoxel_heightfield_shadow_map.frag");
876+ _shadowMapHeightfieldProgram.link();
877+
878+ _shadowMapHeightfieldProgram.bind();
879+ _shadowMapHeightfieldProgram.setUniformValue("heightMap", 0);
880+ _shadowMapHeightfieldProgram.setUniformValue("diffuseMap", 1);
881+ _shadowMapHeightfieldProgram.setUniformValue("shadowMap", 2);
882+ _shadowMapHeightScaleLocation = _shadowMapHeightfieldProgram.uniformLocation("heightScale");
883+ _shadowMapColorScaleLocation = _shadowMapHeightfieldProgram.uniformLocation("colorScale");
884+ _shadowMapHeightfieldProgram.release();
885+
886+ _cascadedShadowMapHeightfieldProgram.addShaderFromSourceFile(QGLShader::Vertex, Application::resourcesPath() +
887+ "shaders/metavoxel_heightfield.vert");
888+ _cascadedShadowMapHeightfieldProgram.addShaderFromSourceFile(QGLShader::Fragment, Application::resourcesPath() +
889+ "shaders/metavoxel_heightfield_cascaded_shadow_map.frag");
890+ _cascadedShadowMapHeightfieldProgram.link();
891+
892+ _cascadedShadowMapHeightfieldProgram.bind();
893+ _cascadedShadowMapHeightfieldProgram.setUniformValue("heightMap", 0);
894+ _cascadedShadowMapHeightfieldProgram.setUniformValue("diffuseMap", 1);
895+ _cascadedShadowMapHeightfieldProgram.setUniformValue("shadowMap", 2);
896+ _cascadedShadowMapHeightScaleLocation = _cascadedShadowMapHeightfieldProgram.uniformLocation("heightScale");
897+ _cascadedShadowMapColorScaleLocation = _cascadedShadowMapHeightfieldProgram.uniformLocation("colorScale");
898+ _shadowDistancesLocation = _cascadedShadowMapHeightfieldProgram.uniformLocation("shadowDistances");
899+ _cascadedShadowMapHeightfieldProgram.release();
900+
901+ _heightfieldCursorProgram.addShaderFromSourceFile(QGLShader::Vertex, Application::resourcesPath() +
902+ "shaders/metavoxel_heightfield_cursor.vert");
903+ _heightfieldCursorProgram.addShaderFromSourceFile(QGLShader::Fragment, Application::resourcesPath() +
904+ "shaders/metavoxel_heightfield_cursor.frag");
905+ _heightfieldCursorProgram.link();
906+
907+ _heightfieldCursorProgram.bind();
908+ _heightfieldCursorProgram.setUniformValue("heightMap", 0);
909+ _heightfieldCursorProgram.release();
478910 }
479911 }
480912
@@ -549,33 +981,279 @@ bool PointAugmentVisitor::postVisit(MetavoxelInfo& info) { · 68 unmodified lines
549981 return true;
550982 }
551983
552−class HeightfieldAugmentVisitor : public MetavoxelVisitor {
984+class HeightfieldFetchVisitor : public MetavoxelVisitor {
553985 public:
986+
987+ HeightfieldFetchVisitor(const MetavoxelLOD& lod, const QVector<Box>& intersections);
988+
989+ void init(HeightfieldBuffer* buffer) { _buffer = buffer; }
990+
991+ virtual int visit(MetavoxelInfo& info);
554992
555− HeightfieldAugmentVisitor(const MetavoxelLOD& lod);
993+private:
994+
995+ const QVector<Box>& _intersections;
996+ HeightfieldBuffer* _buffer;
997+};
998+
999+HeightfieldFetchVisitor::HeightfieldFetchVisitor(const MetavoxelLOD& lod, const QVector<Box>& intersections) :
1000+ MetavoxelVisitor(QVector<AttributePointer>() << AttributeRegistry::getInstance()->getHeightfieldAttribute() <<
1001+ AttributeRegistry::getInstance()->getHeightfieldColorAttribute(), QVector<AttributePointer>(), lod),
1002+ _intersections(intersections) {
1003+}
1004+
1005+int HeightfieldFetchVisitor::visit(MetavoxelInfo& info) {
1006+ Box bounds = info.getBounds();
1007+ const Box& heightBounds = _buffer->getHeightBounds();
1008+ if (!bounds.intersects(heightBounds)) {
1009+ return STOP_RECURSION;
1010+ }
1011+ if (!info.isLeaf && info.size > _buffer->getScale()) {
1012+ return DEFAULT_ORDER;
1013+ }
1014+ HeightfieldDataPointer height = info.inputValues.at(0).getInlineValue<HeightfieldDataPointer>();
1015+ if (!height) {
1016+ return STOP_RECURSION;
1017+ }
1018+ foreach (const Box& intersection, _intersections) {
1019+ Box overlap = intersection.getIntersection(bounds);
1020+ if (overlap.isEmpty()) {
1021+ continue;
1022+ }
1023+ float heightIncrement = _buffer->getHeightIncrement();
1024+ int destX = (overlap.minimum.x - heightBounds.minimum.x) / heightIncrement;
1025+ int destY = (overlap.minimum.z - heightBounds.minimum.z) / heightIncrement;
1026+ int destWidth = glm::ceil((overlap.maximum.x - overlap.minimum.x) / heightIncrement);
1027+ int destHeight = glm::ceil((overlap.maximum.z - overlap.minimum.z) / heightIncrement);
1028+ int heightSize = _buffer->getHeightSize();
1029+ char* dest = _buffer->getHeight().data() + destY * heightSize + destX;
1030+
1031+ const QByteArray& srcHeight = height->getContents();
1032+ int srcSize = glm::sqrt(srcHeight.size());
1033+ float srcIncrement = info.size / srcSize;
1034+
1035+ if (info.size == _buffer->getScale() && srcSize == (heightSize - HeightfieldBuffer::HEIGHT_EXTENSION)) {
1036+ // easy case: same resolution
1037+ int srcX = (overlap.minimum.x - info.minimum.x) / srcIncrement;
1038+ int srcY = (overlap.minimum.z - info.minimum.z) / srcIncrement;
1039+
1040+ const char* src = srcHeight.constData() + srcY * srcSize + srcX;
1041+ for (int y = 0; y < destHeight; y++, src += srcSize, dest += heightSize) {
1042+ memcpy(dest, src, destWidth);
1043+ }
1044+ } else {
1045+ // more difficult: different resolutions
1046+ float srcX = (overlap.minimum.x - info.minimum.x) / srcIncrement;
1047+ float srcY = (overlap.minimum.z - info.minimum.z) / srcIncrement;
1048+ float srcAdvance = heightIncrement / srcIncrement;
1049+ int shift = 0;
1050+ float size = _buffer->getScale();
1051+ while (size < info.size) {
1052+ shift++;
1053+ size *= 2.0f;
1054+ }
1055+ const int EIGHT_BIT_MAXIMUM = 255;
1056+ int subtract = (_buffer->getTranslation().y - info.minimum.y) * EIGHT_BIT_MAXIMUM / _buffer->getScale();
1057+ for (int y = 0; y < destHeight; y++, dest += heightSize, srcY += srcAdvance) {
1058+ const uchar* src = (const uchar*)srcHeight.constData() + (int)srcY * srcSize;
1059+ float lineSrcX = srcX;
1060+ for (char* lineDest = dest, *end = dest + destWidth; lineDest != end; lineDest++, lineSrcX += srcAdvance) {
1061+ *lineDest = qMin(qMax(0, (src[(int)lineSrcX] << shift) - subtract), EIGHT_BIT_MAXIMUM);
1062+ }
1063+ }
1064+ }
1065+
1066+ int colorSize = _buffer->getColorSize();
1067+ if (colorSize == 0) {
1068+ return STOP_RECURSION;
1069+ }
1070+ HeightfieldDataPointer color = info.inputValues.at(1).getInlineValue<HeightfieldDataPointer>();
1071+ if (!color) {
1072+ return STOP_RECURSION;
1073+ }
1074+ const Box& colorBounds = _buffer->getColorBounds();
1075+ overlap = colorBounds.getIntersection(overlap);
1076+ float colorIncrement = _buffer->getColorIncrement();
1077+ destX = (overlap.minimum.x - colorBounds.minimum.x) / colorIncrement;
1078+ destY = (overlap.minimum.z - colorBounds.minimum.z) / colorIncrement;
1079+ destWidth = glm::ceil((overlap.maximum.x - overlap.minimum.x) / colorIncrement);
1080+ destHeight = glm::ceil((overlap.maximum.z - overlap.minimum.z) / colorIncrement);
1081+ dest = _buffer->getColor().data() + (destY * colorSize + destX) * HeightfieldData::COLOR_BYTES;
1082+ int destStride = colorSize * HeightfieldData::COLOR_BYTES;
1083+ int destBytes = destWidth * HeightfieldData::COLOR_BYTES;
1084+
1085+ const QByteArray& srcColor = color->getContents();
1086+ srcSize = glm::sqrt(srcColor.size() / HeightfieldData::COLOR_BYTES);
1087+ int srcStride = srcSize * HeightfieldData::COLOR_BYTES;
1088+ srcIncrement = info.size / srcSize;
1089+
1090+ if (srcIncrement == colorIncrement) {
1091+ // easy case: same resolution
1092+ int srcX = (overlap.minimum.x - info.minimum.x) / srcIncrement;
1093+ int srcY = (overlap.minimum.z - info.minimum.z) / srcIncrement;
1094+
1095+ const char* src = srcColor.constData() + (srcY * srcSize + srcX) * HeightfieldData::COLOR_BYTES;
1096+ for (int y = 0; y < destHeight; y++, src += srcStride, dest += destStride) {
1097+ memcpy(dest, src, destBytes);
1098+ }
1099+ } else {
1100+ // more difficult: different resolutions
1101+ float srcX = (overlap.minimum.x - info.minimum.x) / srcIncrement;
1102+ float srcY = (overlap.minimum.z - info.minimum.z) / srcIncrement;
1103+ float srcAdvance = colorIncrement / srcIncrement;
1104+ for (int y = 0; y < destHeight; y++, dest += destStride, srcY += srcAdvance) {
1105+ const char* src = srcColor.constData() + (int)srcY * srcStride;
1106+ float lineSrcX = srcX;
1107+ for (char* lineDest = dest, *end = dest + destBytes; lineDest != end; lineDest += HeightfieldData::COLOR_BYTES,
1108+ lineSrcX += srcAdvance) {
1109+ const char* lineSrc = src + (int)lineSrcX * HeightfieldData::COLOR_BYTES;
1110+ lineDest[0] = lineSrc[0];
1111+ lineDest[1] = lineSrc[1];
1112+ lineDest[2] = lineSrc[2];
1113+ }
1114+ }
1115+ }
1116+ }
1117+ return STOP_RECURSION;
1118+}
1119+
1120+class HeightfieldRegionVisitor : public MetavoxelVisitor {
1121+public:
1122+
1123+ QVector<Box> regions;
1124+ Box regionBounds;
1125+
1126+ HeightfieldRegionVisitor(const MetavoxelLOD& lod);
5561127
5571128 virtual int visit(MetavoxelInfo& info);
1129+
1130+private:
1131+
1132+ void addRegion(const Box& unextended, const Box& extended);
1133+
1134+ QVector<Box> _intersections;
1135+ HeightfieldFetchVisitor _fetchVisitor;
5581136 };
5591137
560−HeightfieldAugmentVisitor::HeightfieldAugmentVisitor(const MetavoxelLOD& lod) :
1138+HeightfieldRegionVisitor::HeightfieldRegionVisitor(const MetavoxelLOD& lod) :
5611139 MetavoxelVisitor(QVector<AttributePointer>() << AttributeRegistry::getInstance()->getHeightfieldAttribute() <<
562− AttributeRegistry::getInstance()->getHeightfieldColorAttribute(), QVector<AttributePointer>() <<
563− Application::getInstance()->getMetavoxels()->getHeightfieldBufferAttribute(), lod) {
564−}
565−
566−int HeightfieldAugmentVisitor::visit(MetavoxelInfo& info) {
567− if (info.isLeaf) {
568− HeightfieldBuffer* buffer = NULL;
569− HeightfieldDataPointer height = info.inputValues.at(0).getInlineValue<HeightfieldDataPointer>();
570− if (height) {
571− HeightfieldDataPointer color = info.inputValues.at(1).getInlineValue<HeightfieldDataPointer>();
572− buffer = new HeightfieldBuffer(info.minimum, info.size, height->getContents(),
573− color ? color->getContents() : QByteArray());
1140+ AttributeRegistry::getInstance()->getHeightfieldColorAttribute() <<
1141+ Application::getInstance()->getMetavoxels()->getHeightfieldBufferAttribute(), QVector<AttributePointer>() <<
1142+ Application::getInstance()->getMetavoxels()->getHeightfieldBufferAttribute(), lod),
1143+ regionBounds(glm::vec3(FLT_MAX, FLT_MAX, FLT_MAX), glm::vec3(-FLT_MAX, -FLT_MAX, -FLT_MAX)),
1144+ _fetchVisitor(lod, _intersections) {
1145+}
1146+
1147+int HeightfieldRegionVisitor::visit(MetavoxelInfo& info) {
1148+ if (!info.isLeaf) {
1149+ return DEFAULT_ORDER;
1150+ }
1151+ HeightfieldBuffer* buffer = NULL;
1152+ HeightfieldDataPointer height = info.inputValues.at(0).getInlineValue<HeightfieldDataPointer>();
1153+ if (height) {
1154+ const QByteArray& heightContents = height->getContents();
1155+ int size = glm::sqrt(heightContents.size());
1156+ int extendedSize = size + HeightfieldBuffer::HEIGHT_EXTENSION;
1157+ int heightContentsSize = extendedSize * extendedSize;
1158+
1159+ HeightfieldDataPointer color = info.inputValues.at(1).getInlineValue<HeightfieldDataPointer>();
1160+ int colorContentsSize = 0;
1161+ if (color) {
1162+ const QByteArray& colorContents = color->getContents();
1163+ int colorSize = glm::sqrt(colorContents.size() / HeightfieldData::COLOR_BYTES);
1164+ int extendedColorSize = colorSize + HeightfieldBuffer::SHARED_EDGE;
1165+ colorContentsSize = extendedColorSize * extendedColorSize * HeightfieldData::COLOR_BYTES;
5741166 }
575− info.outputValues[0] = AttributeValue(_outputs.at(0), encodeInline(BufferDataPointer(buffer)));
1167+
1168+ const HeightfieldBuffer* existingBuffer = static_cast<const HeightfieldBuffer*>(
1169+ info.inputValues.at(2).getInlineValue<BufferDataPointer>().data());
1170+ Box bounds = info.getBounds();
1171+ if (existingBuffer && existingBuffer->getHeight().size() == heightContentsSize &&
1172+ existingBuffer->getColor().size() == colorContentsSize) {
1173+ // we already have a buffer of the correct resolution
1174+ addRegion(bounds, existingBuffer->getHeightBounds());
1175+ return STOP_RECURSION;
1176+ }
1177+ // we must create a new buffer and update its borders
1178+ buffer = new HeightfieldBuffer(info.minimum, info.size, QByteArray(heightContentsSize, 0),
1179+ QByteArray(colorContentsSize, 0));
1180+ const Box& heightBounds = buffer->getHeightBounds();
1181+ addRegion(bounds, heightBounds);
1182+
1183+ _intersections.clear();
1184+ _intersections.append(Box(heightBounds.minimum,
1185+ glm::vec3(bounds.maximum.x, heightBounds.maximum.y, bounds.minimum.z)));
1186+ _intersections.append(Box(glm::vec3(bounds.maximum.x, heightBounds.minimum.y, heightBounds.minimum.z),
1187+ glm::vec3(heightBounds.maximum.x, heightBounds.maximum.y, bounds.maximum.z)));
1188+ _intersections.append(Box(glm::vec3(bounds.minimum.x, heightBounds.minimum.y, bounds.maximum.z),
1189+ heightBounds.maximum));
1190+ _intersections.append(Box(glm::vec3(heightBounds.minimum.x, heightBounds.minimum.y, bounds.minimum.z),
1191+ glm::vec3(bounds.minimum.x, heightBounds.maximum.y, heightBounds.maximum.z)));
1192+
1193+ _fetchVisitor.init(buffer);
1194+ _data->guide(_fetchVisitor);
1195+ }
1196+ info.outputValues[0] = AttributeValue(_outputs.at(0), encodeInline(BufferDataPointer(buffer)));
1197+ return STOP_RECURSION;
1198+}
1199+
1200+void HeightfieldRegionVisitor::addRegion(const Box& unextended, const Box& extended) {
1201+ regions.append(unextended);
1202+ regionBounds.add(extended);
1203+}
1204+
1205+class HeightfieldUpdateVisitor : public MetavoxelVisitor {
1206+public:
1207+
1208+ HeightfieldUpdateVisitor(const MetavoxelLOD& lod, const QVector<Box>& regions, const Box& regionBounds);
1209+
1210+ virtual int visit(MetavoxelInfo& info);
1211+
1212+private:
1213+
1214+ const QVector<Box>& _regions;
1215+ const Box& _regionBounds;
1216+ QVector<Box> _intersections;
1217+ HeightfieldFetchVisitor _fetchVisitor;
1218+};
1219+
1220+HeightfieldUpdateVisitor::HeightfieldUpdateVisitor(const MetavoxelLOD& lod, const QVector<Box>& regions,
1221+ const Box& regionBounds) :
1222+ MetavoxelVisitor(QVector<AttributePointer>() <<
1223+ Application::getInstance()->getMetavoxels()->getHeightfieldBufferAttribute(), QVector<AttributePointer>() <<
1224+ Application::getInstance()->getMetavoxels()->getHeightfieldBufferAttribute(), lod),
1225+ _regions(regions),
1226+ _regionBounds(regionBounds),
1227+ _fetchVisitor(lod, _intersections) {
1228+}
1229+
1230+int HeightfieldUpdateVisitor::visit(MetavoxelInfo& info) {
1231+ if (!info.getBounds().intersects(_regionBounds)) {
1232+ return STOP_RECURSION;
1233+ }
1234+ if (!info.isLeaf) {
1235+ return DEFAULT_ORDER;
1236+ }
1237+ const HeightfieldBuffer* buffer = static_cast<const HeightfieldBuffer*>(
1238+ info.inputValues.at(0).getInlineValue<BufferDataPointer>().data());
1239+ if (!buffer) {
1240+ return STOP_RECURSION;
1241+ }
1242+ _intersections.clear();
1243+ foreach (const Box& region, _regions) {
1244+ if (region.intersects(buffer->getHeightBounds())) {
1245+ _intersections.append(region.getIntersection(buffer->getHeightBounds()));
1246+ }
1247+ }
1248+ if (_intersections.isEmpty()) {
5761249 return STOP_RECURSION;
5771250 }
578− return DEFAULT_ORDER;
1251+ HeightfieldBuffer* newBuffer = new HeightfieldBuffer(info.minimum, info.size,
1252+ buffer->getHeight(), buffer->getColor());
1253+ _fetchVisitor.init(newBuffer);
1254+ _data->guide(_fetchVisitor);
1255+ info.outputValues[0] = AttributeValue(_outputs.at(0), encodeInline(BufferDataPointer(newBuffer)));
1256+ return STOP_RECURSION;
5791257 }
5801258
5811259 void DefaultMetavoxelRendererImplementation::augment(MetavoxelData& data, const MetavoxelData& previous,
@@ -602,8 +1280,12 @@ void DefaultMetavoxelRendererImplementation::augment(MetavoxelData& data, const · 20 unmodified lines
6021280 PointAugmentVisitor pointAugmentVisitor(lod);
6031281 data.guideToDifferent(expandedPrevious, pointAugmentVisitor);
6041282
605− HeightfieldAugmentVisitor heightfieldAugmentVisitor(lod);
606− data.guideToDifferent(expandedPrevious, heightfieldAugmentVisitor);
1283+ HeightfieldRegionVisitor heightfieldRegionVisitor(lod);
1284+ data.guideToDifferent(expandedPrevious, heightfieldRegionVisitor);
1285+
1286+ HeightfieldUpdateVisitor heightfieldUpdateVisitor(lod, heightfieldRegionVisitor.regions,
1287+ heightfieldRegionVisitor.regionBounds);
1288+ data.guide(heightfieldUpdateVisitor);
6071289 }
6081290
6091291 class SpannerSimulateVisitor : public SpannerVisitor {
@@ -675,7 +1357,7 @@ bool SpannerRenderVisitor::visit(Spanner* spanner, const glm::vec3& clipMinimum, · 65 unmodified lines
6751357 class BufferRenderVisitor : public MetavoxelVisitor {
6761358 public:
6771359
678− BufferRenderVisitor(const AttributePointer& attribute, const MetavoxelLOD& lod);
1360+ BufferRenderVisitor(const AttributePointer& attribute);
6791361
6801362 virtual int visit(MetavoxelInfo& info);
6811363
@@ -685,8 +1367,8 @@ private: · 3 unmodified lines
6851367 int _containmentDepth;
6861368 };
6871369
688−BufferRenderVisitor::BufferRenderVisitor(const AttributePointer& attribute, const MetavoxelLOD& lod) :
689− MetavoxelVisitor(QVector<AttributePointer>() << attribute, QVector<AttributePointer>(), lod),
1370+BufferRenderVisitor::BufferRenderVisitor(const AttributePointer& attribute) :
1371+ MetavoxelVisitor(QVector<AttributePointer>() << attribute),
6901372 _order(encodeOrder(Application::getInstance()->getViewFrustum()->getDirection())),
6911373 _containmentDepth(INT_MAX) {
6921374 }
@@ -700,11 +1382,14 @@ int BufferRenderVisitor::visit(MetavoxelInfo& info) { · 7 unmodified lines
7001382 }
7011383 _containmentDepth = (intersection == Frustum::CONTAINS_INTERSECTION) ? _depth : INT_MAX;
7021384 }
1385+ if (!info.isLeaf) {
1386+ return _order;
1387+ }
7031388 BufferDataPointer buffer = info.inputValues.at(0).getInlineValue<BufferDataPointer>();
7041389 if (buffer) {
7051390 buffer->render();
7061391 }
707− return info.isLeaf ? STOP_RECURSION : _order;
1392+ return STOP_RECURSION;
7081393 }
7091394
7101395 void DefaultMetavoxelRendererImplementation::render(MetavoxelData& data, MetavoxelInfo& info, const MetavoxelLOD& lod) {
@@ -733,7 +1418,7 @@ void DefaultMetavoxelRendererImplementation::render(MetavoxelData& data, Metavox · 22 unmodified lines
7331418
7341419 glDisable(GL_BLEND);
7351420
736− BufferRenderVisitor pointRenderVisitor(Application::getInstance()->getMetavoxels()->getPointBufferAttribute(), lod);
1421+ BufferRenderVisitor pointRenderVisitor(Application::getInstance()->getMetavoxels()->getPointBufferAttribute());
7371422 data.guide(pointRenderVisitor);
7381423
7391424 glDisable(GL_VERTEX_PROGRAM_POINT_SIZE_ARB);
@@ -749,16 +1434,34 @@ void DefaultMetavoxelRendererImplementation::render(MetavoxelData& data, Metavox · 9 unmodified lines
7491434
7501435 glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
7511436
752− _heightfieldProgram.bind();
1437+ ProgramObject* program = &_heightfieldProgram;
1438+ if (Menu::getInstance()->getShadowsEnabled()) {
1439+ if (Menu::getInstance()->isOptionChecked(MenuOption::CascadedShadows)) {
1440+ program = &_cascadedShadowMapHeightfieldProgram;
1441+ program->bind();
1442+ program->setUniform(_shadowDistancesLocation, Application::getInstance()->getShadowDistances());
1443+
1444+ } else {
1445+ program = &_shadowMapHeightfieldProgram;
1446+ }
1447+ glActiveTexture(GL_TEXTURE2);
1448+ glBindTexture(GL_TEXTURE_2D, Application::getInstance()->getTextureCache()->getShadowDepthTextureID());
1449+ glActiveTexture(GL_TEXTURE0);
1450+ }
1451+
1452+ program->bind();
7531453
7541454 glEnableClientState(GL_TEXTURE_COORD_ARRAY);
7551455
756− BufferRenderVisitor heightfieldRenderVisitor(Application::getInstance()->getMetavoxels()->getHeightfieldBufferAttribute(),
757− lod);
1456+ BufferRenderVisitor heightfieldRenderVisitor(Application::getInstance()->getMetavoxels()->getHeightfieldBufferAttribute());
7581457 data.guide(heightfieldRenderVisitor);
7591458
760− _heightfieldProgram.release();
1459+ program->release();
7611460
1461+ glActiveTexture(GL_TEXTURE2);
1462+ glBindTexture(GL_TEXTURE_2D, 0);
1463+ glActiveTexture(GL_TEXTURE0);
1464+
7621465 glDisableClientState(GL_TEXTURE_COORD_ARRAY);
7631466 glDisableClientState(GL_VERTEX_ARRAY);
7641467
@@ -771,6 +1474,15 @@ ProgramObject DefaultMetavoxelRendererImplementation::_pointProgram; · 6 unmodified lines
7711474 int DefaultMetavoxelRendererImplementation::_pointScaleLocation;
7721475 ProgramObject DefaultMetavoxelRendererImplementation::_heightfieldProgram;
7731476 int DefaultMetavoxelRendererImplementation::_heightScaleLocation;
1477+int DefaultMetavoxelRendererImplementation::_colorScaleLocation;
1478+ProgramObject DefaultMetavoxelRendererImplementation::_shadowMapHeightfieldProgram;
1479+int DefaultMetavoxelRendererImplementation::_shadowMapHeightScaleLocation;
1480+int DefaultMetavoxelRendererImplementation::_shadowMapColorScaleLocation;
1481+ProgramObject DefaultMetavoxelRendererImplementation::_cascadedShadowMapHeightfieldProgram;
1482+int DefaultMetavoxelRendererImplementation::_cascadedShadowMapHeightScaleLocation;
1483+int DefaultMetavoxelRendererImplementation::_cascadedShadowMapColorScaleLocation;
1484+int DefaultMetavoxelRendererImplementation::_shadowDistancesLocation;
1485+ProgramObject DefaultMetavoxelRendererImplementation::_heightfieldCursorProgram;
7741486
7751487 static void enableClipPlane(GLenum plane, float x, float y, float z, float w) {
7761488 GLdouble coefficients[] = { x, y, z, w };
interface/src/MetavoxelSystem.h+61−9
@@ -43,6 +43,12 @@ public: · 42 unmodified lines
4343 void simulate(float deltaTime);
4444 void render();
4545
46+ void renderHeightfieldCursor(const glm::vec3& position, float radius);
47+
48+ bool findFirstRayHeightfieldIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance);
49+
50+ Q_INVOKABLE float getHeightfieldHeight(const glm::vec3& location);
51+
4652 Q_INVOKABLE void deleteTextures(int heightID, int colorID);
4753
4854 protected:
@@ -105,7 +111,7 @@ public: · 56 unmodified lines
105111
106112 virtual ~BufferData();
107113
108− virtual void render() = 0;
114+ virtual void render(bool cursor = false) = 0;
109115 };
110116
111117 typedef QExplicitlySharedDataPointer<BufferData> BufferDataPointer;
@@ -116,7 +122,7 @@ public: · 4 unmodified lines
116122
117123 PointBuffer(const BufferPointVector& points);
118124
119− virtual void render();
125+ virtual void render(bool cursor = false);
120126
121127 private:
122128
@@ -129,29 +135,50 @@ private: · 6 unmodified lines
129135 class HeightfieldBuffer : public BufferData {
130136 public:
131137
132− /// Creates a new heightfield buffer.
133− /// \param clearAfterLoading if true, clear the data arrays after we load them into textures in order to reclaim the space
134− HeightfieldBuffer(const glm::vec3& translation, float scale, const QByteArray& height, const QByteArray& color,
135− bool clearAfterLoading = true);
138+ static const int HEIGHT_BORDER;
139+ static const int SHARED_EDGE;
140+ static const int HEIGHT_EXTENSION;
141+
142+ HeightfieldBuffer(const glm::vec3& translation, float scale, const QByteArray& height, const QByteArray& color);
136143 ~HeightfieldBuffer();
137144
138145 const glm::vec3& getTranslation() const { return _translation; }
146+ float getScale() const { return _scale; }
147+
148+ const Box& getHeightBounds() const { return _heightBounds; }
149+ const Box& getColorBounds() const { return _colorBounds; }
139150
151+ QByteArray& getHeight() { return _height; }
140152 const QByteArray& getHeight() const { return _height; }
153+
154+ QByteArray& getColor() { return _color; }
141155 const QByteArray& getColor() const { return _color; }
142156
143− virtual void render();
157+ QByteArray getUnextendedHeight() const;
158+ QByteArray getUnextendedColor() const;
159+
160+ int getHeightSize() const { return _heightSize; }
161+ float getHeightIncrement() const { return _heightIncrement; }
162+
163+ int getColorSize() const { return _colorSize; }
164+ float getColorIncrement() const { return _colorIncrement; }
165+
166+ virtual void render(bool cursor = false);
144167
145168 private:
146169
147170 glm::vec3 _translation;
148171 float _scale;
172+ Box _heightBounds;
173+ Box _colorBounds;
149174 QByteArray _height;
150175 QByteArray _color;
151− bool _clearAfterLoading;
152176 GLuint _heightTextureID;
153177 GLuint _colorTextureID;
154178 int _heightSize;
179+ float _heightIncrement;
180+ int _colorSize;
181+ float _colorIncrement;
155182
156183 typedef QPair<QOpenGLBuffer, QOpenGLBuffer> BufferPair;
157184 static QHash<int, BufferPair> _bufferPairs;
@@ -180,6 +207,8 @@ public: · 22 unmodified lines
180207 Q_INVOKABLE BufferDataAttribute(const QString& name = QString());
181208
182209 virtual bool merge(void*& parent, void* children[], bool postRead = false) const;
210+
211+ virtual AttributeValue inherit(const AttributeValue& parentValue) const;
183212 };
184213
185214 /// Renders metavoxels as points.
@@ -192,7 +221,18 @@ public: · 6 unmodified lines
192221
193222 static ProgramObject& getHeightfieldProgram() { return _heightfieldProgram; }
194223 static int getHeightScaleLocation() { return _heightScaleLocation; }
195−
224+ static int getColorScaleLocation() { return _colorScaleLocation; }
225+
226+ static ProgramObject& getShadowMapHeightfieldProgram() { return _shadowMapHeightfieldProgram; }
227+ static int getShadowMapHeightScaleLocation() { return _shadowMapHeightScaleLocation; }
228+ static int getShadowMapColorScaleLocation() { return _shadowMapColorScaleLocation; }
229+
230+ static ProgramObject& getCascadedShadowMapHeightfieldProgram() { return _cascadedShadowMapHeightfieldProgram; }
231+ static int getCascadedShadowMapHeightScaleLocation() { return _cascadedShadowMapHeightScaleLocation; }
232+ static int getCascadedShadowMapColorScaleLocation() { return _cascadedShadowMapColorScaleLocation; }
233+
234+ static ProgramObject& getHeightfieldCursorProgram() { return _heightfieldCursorProgram; }
235+
196236 Q_INVOKABLE DefaultMetavoxelRendererImplementation();
197237
198238 virtual void augment(MetavoxelData& data, const MetavoxelData& previous, MetavoxelInfo& info, const MetavoxelLOD& lod);
@@ -206,6 +246,18 @@ private: · 7 unmodified lines
206246
207247 static ProgramObject _heightfieldProgram;
208248 static int _heightScaleLocation;
249+ static int _colorScaleLocation;
250+
251+ static ProgramObject _shadowMapHeightfieldProgram;
252+ static int _shadowMapHeightScaleLocation;
253+ static int _shadowMapColorScaleLocation;
254+
255+ static ProgramObject _cascadedShadowMapHeightfieldProgram;
256+ static int _cascadedShadowMapHeightScaleLocation;
257+ static int _cascadedShadowMapColorScaleLocation;
258+ static int _shadowDistancesLocation;
259+
260+ static ProgramObject _heightfieldCursorProgram;
209261 };
210262
211263 /// Base class for spanner renderers; provides clipping.
interface/src/avatar/Head.cpp+0−3
@@ -163,9 +163,6 @@ void Head::simulate(float deltaTime, bool isMine, bool billboard) { · 162 unmodified lines
163163 }
164164 _eyePosition = calculateAverageEyePosition();
165165
166− float velocityFilter = glm::clamp(1.0f - glm::length(_filteredEyePosition - _eyePosition), 0.0f, 1.0f);
167− _filteredEyePosition = velocityFilter * _filteredEyePosition + (1.0f - velocityFilter) * _eyePosition;
168−
169166 }
170167
171168 void Head::relaxLean(float deltaTime) {
interface/src/avatar/Head.h+0−2
@@ -88,7 +88,6 @@ public: · 87 unmodified lines
8888
8989 const bool getReturnToCenter() const { return _returnHeadToCenter; } // Do you want head to try to return to center (depends on interface detected)
9090 float getAverageLoudness() const { return _averageLoudness; }
91− glm::vec3 getFilteredEyePosition() const { return _filteredEyePosition; }
9291 /// \return the point about which scaling occurs.
9392 glm::vec3 getScalePivot() const;
9493
@@ -121,7 +120,6 @@ private: · 26 unmodified lines
121120 glm::vec3 _leftEyePosition;
122121 glm::vec3 _rightEyePosition;
123122 glm::vec3 _eyePosition;
124− glm::vec3 _filteredEyePosition; // velocity filtered world space eye position
125123
126124 float _scale;
127125 float _lastLoudness;
interface/src/avatar/MyAvatar.cpp+15−8
@@ -83,10 +83,11 @@ MyAvatar::MyAvatar() : · 82 unmodified lines
8383 for (int i = 0; i < MAX_DRIVE_KEYS; i++) {
8484 _driveKeys[i] = 0.0f;
8585 }
86− _skeletonModel.setEnableShapes(true);
87− // The skeleton is both a PhysicsEntity and Ragdoll, so we add it to the simulation once for each type.
8886 _physicsSimulation.setEntity(&_skeletonModel);
89− _physicsSimulation.setRagdoll(&_skeletonModel);
87+
88+ _skeletonModel.setEnableShapes(true);
89+ Ragdoll* ragdoll = _skeletonModel.buildRagdoll();
90+ _physicsSimulation.setRagdoll(ragdoll);
9091 }
9192
9293 MyAvatar::~MyAvatar() {
@@ -206,9 +207,9 @@ void MyAvatar::simulate(float deltaTime) { · 113 unmodified lines
206207 {
207208 PerformanceTimer perfTimer("ragdoll");
208209 if (Menu::getInstance()->isOptionChecked(MenuOption::CollideAsRagdoll)) {
209− const float minError = 0.01f;
210− const float maxIterations = 10;
211− const quint64 maxUsec = 2000;
210+ const float minError = 0.00001f;
211+ const float maxIterations = 3;
212+ const quint64 maxUsec = 4000;
212213 _physicsSimulation.setTranslation(_position);
213214 _physicsSimulation.stepForward(deltaTime, minError, maxIterations, maxUsec);
214215 } else {
@@ -255,6 +256,12 @@ void MyAvatar::updateFromTrackers(float deltaTime) { · 40 unmodified lines
255256 estimatedRotation.x *= -1.0f;
256257 estimatedRotation.z *= -1.0f;
257258
259+ } else if (OculusManager::isConnected()) {
260+ estimatedPosition = OculusManager::getRelativePosition();
261+ estimatedPosition.x *= -1.0f;
262+
263+ const float OCULUS_LEAN_SCALE = 0.05f;
264+ estimatedPosition /= OCULUS_LEAN_SCALE;
258265 } else {
259266 FaceTracker* tracker = Application::getInstance()->getActiveFaceTracker();
260267 if (tracker) {
@@ -1598,10 +1605,10 @@ void MyAvatar::updateCollisionWithAvatars(float deltaTime) { · 1337 unmodified lines
15981605 if (simulation != &(_physicsSimulation)) {
15991606 skeleton->setEnableShapes(true);
16001607 _physicsSimulation.addEntity(skeleton);
1601− _physicsSimulation.addRagdoll(skeleton);
1608+ _physicsSimulation.addRagdoll(skeleton->getRagdoll());
16021609 }
16031610 } else if (simulation == &(_physicsSimulation)) {
1604− _physicsSimulation.removeRagdoll(skeleton);
1611+ _physicsSimulation.removeRagdoll(skeleton->getRagdoll());
16051612 _physicsSimulation.removeEntity(skeleton);
16061613 skeleton->setEnableShapes(false);
16071614 }
interface/src/avatar/SkeletonModel.cpp+65−148
@@ -23,13 +23,19 @@ · 22 unmodified lines
2323 #include "Menu.h"
2424 #include "MuscleConstraint.h"
2525 #include "SkeletonModel.h"
26+#include "SkeletonRagdoll.h"
2627
2728 SkeletonModel::SkeletonModel(Avatar* owningAvatar, QObject* parent) :
2829 Model(parent),
29− Ragdoll(),
3030 _owningAvatar(owningAvatar),
3131 _boundingShape(),
32− _boundingShapeLocalOffset(0.0f) {
32+ _boundingShapeLocalOffset(0.0f),
33+ _ragdoll(NULL) {
34+}
35+
36+SkeletonModel::~SkeletonModel() {
37+ delete _ragdoll;
38+ _ragdoll = NULL;
3339 }
3440
3541 void SkeletonModel::setJointStates(QVector<JointState> states) {
@@ -155,9 +161,6 @@ void SkeletonModel::getBodyShapes(QVector<const Shape*>& shapes) const { · 119 unmodified lines
155161 void SkeletonModel::renderIKConstraints() {
156162 renderJointConstraints(getRightHandJointIndex());
157163 renderJointConstraints(getLeftHandJointIndex());
158− //if (isActive() && _owningAvatar->isMyAvatar()) {
159− // renderRagdoll();
160− //}
161164 }
162165
163166 class IndexValue {
@@ -432,6 +435,10 @@ bool SkeletonModel::getHeadPosition(glm::vec3& headPosition) const { · 268 unmodified lines
432435 }
433436
434437 bool SkeletonModel::getNeckPosition(glm::vec3& neckPosition) const {
438+ if (_owningAvatar->isMyAvatar() &&
439+ Menu::getInstance()->isOptionChecked(MenuOption::CollideAsRagdoll)) {
440+ return isActive() && getVisibleJointPositionInWorldFrame(_geometry->getFBXGeometry().neckJointIndex, neckPosition);
441+ }
435442 return isActive() && getJointPositionInWorldFrame(_geometry->getFBXGeometry().neckJointIndex, neckPosition);
436443 }
437444
@@ -445,11 +452,16 @@ bool SkeletonModel::getNeckParentRotationFromDefaultOrientation(glm::quat& neckP · 7 unmodified lines
445452 }
446453 int parentIndex = geometry.joints.at(geometry.neckJointIndex).parentIndex;
447454 glm::quat worldFrameRotation;
448− if (getJointRotationInWorldFrame(parentIndex, worldFrameRotation)) {
455+ bool success = false;
456+ if (Menu::getInstance()->isOptionChecked(MenuOption::CollideAsRagdoll)) {
457+ success = getVisibleJointRotationInWorldFrame(parentIndex, worldFrameRotation);
458+ } else {
459+ success = getJointRotationInWorldFrame(parentIndex, worldFrameRotation);
460+ }
461+ if (success) {
449462 neckParentRotation = worldFrameRotation * _jointStates[parentIndex].getFBXJoint().inverseDefaultRotation;
450− return true;
451463 }
452− return false;
464+ return success;
453465 }
454466
455467 bool SkeletonModel::getEyePositions(glm::vec3& firstEyePosition, glm::vec3& secondEyePosition) const {
@@ -480,21 +492,25 @@ bool SkeletonModel::getEyePositions(glm::vec3& firstEyePosition, glm::vec3& seco · 24 unmodified lines
480492 }
481493
482494 void SkeletonModel::renderRagdoll() {
495+ if (!_ragdoll) {
496+ return;
497+ }
498+ const QVector<VerletPoint>& points = _ragdoll->getPoints();
483499 const int BALL_SUBDIVISIONS = 6;
484500 glDisable(GL_DEPTH_TEST);
485501 glDisable(GL_LIGHTING);
486502 glPushMatrix();
487503
488504 Application::getInstance()->loadTranslatedViewMatrix(_translation);
489− int numPoints = _ragdollPoints.size();
505+ int numPoints = points.size();
490506 float alpha = 0.3f;
491507 float radius1 = 0.008f;
492508 float radius2 = 0.01f;
493− glm::vec3 simulationTranslation = getTranslationInSimulationFrame();
509+ glm::vec3 simulationTranslation = _ragdoll->getTranslationInSimulationFrame();
494510 for (int i = 0; i < numPoints; ++i) {
495511 glPushMatrix();
496512 // NOTE: ragdollPoints are in simulation-frame but we want them to be model-relative
497− glm::vec3 position = _ragdollPoints[i]._position - simulationTranslation;
513+ glm::vec3 position = points[i]._position - simulationTranslation;
498514 glTranslatef(position.x, position.y, position.z);
499515 // draw each point as a yellow hexagon with black border
500516 glColor4f(0.0f, 0.0f, 0.0f, alpha);
@@ -508,109 +524,18 @@ void SkeletonModel::renderRagdoll() { · 7 unmodified lines
508524 glEnable(GL_LIGHTING);
509525 }
510526
511−// virtual
512−void SkeletonModel::initRagdollPoints() {
513− clearRagdollConstraintsAndPoints();
514− _muscleConstraints.clear();
515−
516− initRagdollTransform();
517− // one point for each joint
518− int numStates = _jointStates.size();
519− _ragdollPoints.fill(VerletPoint(), numStates);
520− for (int i = 0; i < numStates; ++i) {
521− const JointState& state = _jointStates.at(i);
522− // _ragdollPoints start in model-frame
523− _ragdollPoints[i].initPosition(state.getPosition());
524− }
525−}
526−
527−void SkeletonModel::buildRagdollConstraints() {
528− // NOTE: the length of DistanceConstraints is computed and locked in at this time
529− // so make sure the ragdoll positions are in a normal configuration before here.
530− const int numPoints = _ragdollPoints.size();
531− assert(numPoints == _jointStates.size());
532−
533− float minBone = FLT_MAX;
534− float maxBone = -FLT_MAX;
535− QMultiMap<int, int> families;
536− for (int i = 0; i < numPoints; ++i) {
537− const JointState& state = _jointStates.at(i);
538− int parentIndex = state.getParentIndex();
539− if (parentIndex == -1) {
540− FixedConstraint* anchor = new FixedConstraint(&_translationInSimulationFrame, &(_ragdollPoints[i]));
541− _fixedConstraints.push_back(anchor);
542− } else {
543− DistanceConstraint* bone = new DistanceConstraint(&(_ragdollPoints[i]), &(_ragdollPoints[parentIndex]));
544− bone->setDistance(state.getDistanceToParent());
545− _boneConstraints.push_back(bone);
546− families.insert(parentIndex, i);
547− }
548− float boneLength = glm::length(state.getPositionInParentFrame());
549− if (boneLength > maxBone) {
550− maxBone = boneLength;
551− } else if (boneLength < minBone) {
552− minBone = boneLength;
553− }
554− }
555− // Joints that have multiple children effectively have rigid constraints between the children
556− // in the parent frame, so we add DistanceConstraints between children in the same family.
557− QMultiMap<int, int>::iterator itr = families.begin();
558− while (itr != families.end()) {
559− QList<int> children = families.values(itr.key());
560− int numChildren = children.size();
561− if (numChildren > 1) {
562− for (int i = 1; i < numChildren; ++i) {
563− DistanceConstraint* bone = new DistanceConstraint(&(_ragdollPoints[children[i-1]]), &(_ragdollPoints[children[i]]));
564− _boneConstraints.push_back(bone);
565− }
566− if (numChildren > 2) {
567− DistanceConstraint* bone = new DistanceConstraint(&(_ragdollPoints[children[numChildren-1]]), &(_ragdollPoints[children[0]]));
568− _boneConstraints.push_back(bone);
569− }
570− }
571− ++itr;
572− }
573−
574− float MAX_STRENGTH = 0.6f;
575− float MIN_STRENGTH = 0.05f;
576− // each joint gets a MuscleConstraint to its parent
577− for (int i = 1; i < numPoints; ++i) {
578− const JointState& state = _jointStates.at(i);
579− int p = state.getParentIndex();
580− if (p == -1) {
581− continue;
582− }
583− MuscleConstraint* constraint = new MuscleConstraint(&(_ragdollPoints[p]), &(_ragdollPoints[i]));
584− _muscleConstraints.push_back(constraint);
585−
586− // Short joints are more susceptible to wiggle so we modulate the strength based on the joint's length:
587− // long = weak and short = strong.
588− constraint->setIndices(p, i);
589− float boneLength = glm::length(state.getPositionInParentFrame());
590−
591− float strength = MIN_STRENGTH + (MAX_STRENGTH - MIN_STRENGTH) * (maxBone - boneLength) / (maxBone - minBone);
592− if (!families.contains(i)) {
593− // Although muscles only pull on the children not parents, nevertheless those joints that have
594− // parents AND children are more stable than joints at the end such as fingers. For such joints we
595− // bestow maximum strength which helps reduce wiggle.
596− strength = MAX_MUSCLE_STRENGTH;
597− }
598− constraint->setStrength(strength);
599− }
600−
601−}
602−
603527 void SkeletonModel::updateVisibleJointStates() {
604− if (_showTrueJointTransforms) {
528+ if (_showTrueJointTransforms || !_ragdoll) {
605529 // no need to update visible transforms
606530 return;
607531 }
532+ const QVector<VerletPoint>& ragdollPoints = _ragdoll->getPoints();
608533 QVector<glm::vec3> points;
609534 points.reserve(_jointStates.size());
610535 glm::quat invRotation = glm::inverse(_rotation);
611536 for (int i = 0; i < _jointStates.size(); i++) {
612537 JointState& state = _jointStates[i];
613− points.push_back(_ragdollPoints[i]._position);
538+ points.push_back(ragdollPoints[i]._position);
614539
615540 // get the parent state (this is the state that we want to rotate)
616541 int parentIndex = state.getParentIndex();
@@ -644,15 +569,14 @@ void SkeletonModel::updateVisibleJointStates() { · 27 unmodified lines
644569 }
645570 }
646571
647−// virtual
648−void SkeletonModel::stepRagdollForward(float deltaTime) {
649− setRagdollTransform(_translation, _rotation);
650− Ragdoll::stepRagdollForward(deltaTime);
651− updateMuscles();
652− int numConstraints = _muscleConstraints.size();
653− for (int i = 0; i < numConstraints; ++i) {
654− _muscleConstraints[i]->enforce();
572+SkeletonRagdoll* SkeletonModel::buildRagdoll() {
573+ if (!_ragdoll) {
574+ _ragdoll = new SkeletonRagdoll(this);
575+ if (_enableShapes) {
576+ buildShapes();
577+ }
655578 }
579+ return _ragdoll;
656580 }
657581
658582 float DENSITY_OF_WATER = 1000.0f; // kg/m^3
@@ -670,8 +594,13 @@ void SkeletonModel::buildShapes() { · 11 unmodified lines
670594 return;
671595 }
672596
673− initRagdollPoints();
674− float massScale = getMassScale();
597+ if (!_ragdoll) {
598+ _ragdoll = new SkeletonRagdoll(this);
599+ }
600+ _ragdoll->initPoints();
601+ QVector<VerletPoint>& points = _ragdoll->getPoints();
602+
603+ float massScale = _ragdoll->getMassScale();
675604
676605 float uniformScale = extractUniformScale(_scale);
677606 const int numStates = _jointStates.size();
@@ -691,14 +620,14 @@ void SkeletonModel::buildShapes() { · 13 unmodified lines
691620 Shape* shape = NULL;
692621 int parentIndex = joint.parentIndex;
693622 if (type == Shape::SPHERE_SHAPE) {
694− shape = new VerletSphereShape(radius, &(_ragdollPoints[i]));
623+ shape = new VerletSphereShape(radius, &(points[i]));
695624 shape->setEntity(this);
696− _ragdollPoints[i].setMass(massScale * glm::max(MIN_JOINT_MASS, DENSITY_OF_WATER * shape->getVolume()));
625+ points[i].setMass(massScale * glm::max(MIN_JOINT_MASS, DENSITY_OF_WATER * shape->getVolume()));
697626 } else if (type == Shape::CAPSULE_SHAPE) {
698627 assert(parentIndex != -1);
699− shape = new VerletCapsuleShape(radius, &(_ragdollPoints[parentIndex]), &(_ragdollPoints[i]));
628+ shape = new VerletCapsuleShape(radius, &(points[parentIndex]), &(points[i]));
700629 shape->setEntity(this);
701− _ragdollPoints[i].setMass(massScale * glm::max(MIN_JOINT_MASS, DENSITY_OF_WATER * shape->getVolume()));
630+ points[i].setMass(massScale * glm::max(MIN_JOINT_MASS, DENSITY_OF_WATER * shape->getVolume()));
702631 }
703632 if (parentIndex != -1) {
704633 // always disable collisions between joint and its parent
@@ -708,7 +637,7 @@ void SkeletonModel::buildShapes() { · 3 unmodified lines
708637 } else {
709638 // give the base joint a very large mass since it doesn't actually move
710639 // in the local-frame simulation (it defines the origin)
711− _ragdollPoints[i].setMass(VERY_BIG_MASS);
640+ points[i].setMass(VERY_BIG_MASS);
712641 }
713642 _shapes.push_back(shape);
714643 }
@@ -720,17 +649,11 @@ void SkeletonModel::buildShapes() { · 5 unmodified lines
720649 // joints that are currently colliding.
721650 disableCurrentSelfCollisions();
722651
723− buildRagdollConstraints();
652+ _ragdoll->buildConstraints();
724653
725654 // ... then move shapes back to current joint positions
726− if (_ragdollPoints.size() == numStates) {
727− int numStates = _jointStates.size();
728− for (int i = 0; i < numStates; ++i) {
729− // ragdollPoints start in model-frame
730− _ragdollPoints[i].initPosition(_jointStates.at(i).getPosition());
731− }
732− }
733− enforceRagdollConstraints();
655+ _ragdoll->slamPointPositions();
656+ _ragdoll->enforceConstraints();
734657 }
735658
736659 void SkeletonModel::moveShapesTowardJoints(float deltaTime) {
@@ -738,8 +661,9 @@ void SkeletonModel::moveShapesTowardJoints(float deltaTime) { · 1 unmodified line
738661 // unravel a skelton that has become tangled in its constraints. So let's keep this
739662 // around for a while just in case.
740663 const int numStates = _jointStates.size();
741− assert(_jointStates.size() == _ragdollPoints.size());
742− if (_ragdollPoints.size() != numStates) {
664+ QVector<VerletPoint>& ragdollPoints = _ragdoll->getPoints();
665+ assert(_jointStates.size() == ragdollPoints.size());
666+ if (ragdollPoints.size() != numStates) {
743667 return;
744668 }
745669
@@ -748,32 +672,22 @@ void SkeletonModel::moveShapesTowardJoints(float deltaTime) { · 2 unmodified lines
748672 float fraction = glm::clamp(deltaTime / RAGDOLL_FOLLOWS_JOINTS_TIMESCALE, 0.0f, 1.0f);
749673
750674 float oneMinusFraction = 1.0f - fraction;
751− glm::vec3 simulationTranslation = getTranslationInSimulationFrame();
675+ glm::vec3 simulationTranslation = _ragdoll->getTranslationInSimulationFrame();
752676 for (int i = 0; i < numStates; ++i) {
753677 // ragdollPoints are in simulation-frame but jointStates are in model-frame
754− _ragdollPoints[i].initPosition(oneMinusFraction * _ragdollPoints[i]._position +
678+ ragdollPoints[i].initPosition(oneMinusFraction * ragdollPoints[i]._position +
755679 fraction * (simulationTranslation + _rotation * (_jointStates.at(i).getPosition())));
756680 }
757681 }
758682
759−void SkeletonModel::updateMuscles() {
760− int numConstraints = _muscleConstraints.size();
761− for (int i = 0; i < numConstraints; ++i) {
762− MuscleConstraint* constraint = _muscleConstraints[i];
763− int j = constraint->getParentIndex();
764− int k = constraint->getChildIndex();
765− assert(j != -1 && k != -1);
766− // ragdollPoints are in simulation-frame but jointStates are in model-frame
767− constraint->setChildOffset(_rotation * (_jointStates.at(k).getPosition() - _jointStates.at(j).getPosition()));
768− }
769−}
770−
771683 void SkeletonModel::computeBoundingShape(const FBXGeometry& geometry) {
772684 // compute default joint transforms
773685 int numStates = _jointStates.size();
774686 QVector<glm::mat4> transforms;
775687 transforms.fill(glm::mat4(), numStates);
776688
689+ QVector<VerletPoint>& ragdollPoints = _ragdoll->getPoints();
690+
777691 // compute the default transforms and slam the ragdoll positions accordingly
778692 // (which puts the shapes where we want them)
779693 for (int i = 0; i < numStates; i++) {
@@ -782,7 +696,7 @@ void SkeletonModel::computeBoundingShape(const FBXGeometry& geometry) { · 2 unmodified lines
782696 int parentIndex = joint.parentIndex;
783697 if (parentIndex == -1) {
784698 transforms[i] = _jointStates[i].getTransform();
785− _ragdollPoints[i].initPosition(extractTranslation(transforms[i]));
699+ ragdollPoints[i].initPosition(extractTranslation(transforms[i]));
786700 continue;
787701 }
788702
@@ -790,7 +704,7 @@ void SkeletonModel::computeBoundingShape(const FBXGeometry& geometry) { · 1 unmodified line
790704 transforms[i] = transforms[parentIndex] * glm::translate(joint.translation)
791705 * joint.preTransform * glm::mat4_cast(modifiedRotation) * joint.postTransform;
792706 // setting the ragdollPoints here slams the VerletShapes into their default positions
793− _ragdollPoints[i].initPosition(extractTranslation(transforms[i]));
707+ ragdollPoints[i].initPosition(extractTranslation(transforms[i]));
794708 }
795709
796710 // compute bounding box that encloses all shapes
@@ -909,9 +823,12 @@ const int BALL_SUBDIVISIONS = 10; · 112 unmodified lines
909823
910824 // virtual
911825 void SkeletonModel::renderJointCollisionShapes(float alpha) {
826+ if (!_ragdoll) {
827+ return;
828+ }
912829 glPushMatrix();
913830 Application::getInstance()->loadTranslatedViewMatrix(_translation);
914− glm::vec3 simulationTranslation = getTranslationInSimulationFrame();
831+ glm::vec3 simulationTranslation = _ragdoll->getTranslationInSimulationFrame();
915832 for (int i = 0; i < _shapes.size(); i++) {
916833 Shape* shape = _shapes[i];
917834 if (!shape) {
interface/src/avatar/SkeletonModel.h+8−11
@@ -15,18 +15,20 @@ · 14 unmodified lines
1515 #include "renderer/Model.h"
1616
1717 #include <CapsuleShape.h>
18−#include <Ragdoll.h>
18+#include "SkeletonRagdoll.h"
1919
2020 class Avatar;
2121 class MuscleConstraint;
22+class SkeletonRagdoll;
2223
2324 /// A skeleton loaded from a model.
24−class SkeletonModel : public Model, public Ragdoll {
25+class SkeletonModel : public Model {
2526 Q_OBJECT
2627
2728 public:
2829
2930 SkeletonModel(Avatar* owningAvatar, QObject* parent = NULL);
31+ ~SkeletonModel();
3032
3133 void setJointStates(QVector<JointState> states);
3234
@@ -96,12 +98,11 @@ public: · 63 unmodified lines
9698 bool getEyePositions(glm::vec3& firstEyePosition, glm::vec3& secondEyePosition) const;
9799
98100 virtual void updateVisibleJointStates();
99−
100− // virtual overrride from Ragdoll
101− virtual void stepRagdollForward(float deltaTime);
102101
102+ SkeletonRagdoll* buildRagdoll();
103+ SkeletonRagdoll* getRagdoll() { return _ragdoll; }
104+
103105 void moveShapesTowardJoints(float fraction);
104− void updateMuscles();
105106
106107 void computeBoundingShape(const FBXGeometry& geometry);
107108 void renderBoundingCollisionShapes(float alpha);
@@ -115,10 +116,6 @@ public: · 7 unmodified lines
115116
116117 protected:
117118
118− // virtual overrrides from Ragdoll
119− void initRagdollPoints();
120− void buildRagdollConstraints();
121−
122119 void buildShapes();
123120
124121 /// \param jointIndex index of joint in model
@@ -147,7 +144,7 @@ private: · 22 unmodified lines
147144
148145 CapsuleShape _boundingShape;
149146 glm::vec3 _boundingShapeLocalOffset;
150− QVector<MuscleConstraint*> _muscleConstraints;
147+ SkeletonRagdoll* _ragdoll;
151148 };
152149
153150 #endif // hifi_SkeletonModel_h
interface/src/avatar/SkeletonRagdoll.cpp+148−0
@@ -0,0 +1,148 @@
1+//
2+// SkeletonRagdoll.cpp
3+// interface/src/avatar
4+//
5+// Created by Andrew Meadows 2014.08.14
6+// Copyright 2014 High Fidelity, Inc.
7+//
8+// Distributed under the Apache License, Version 2.0.
9+// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
10+//
11+
12+#include <DistanceConstraint.h>
13+#include <FixedConstraint.h>
14+
15+#include "SkeletonRagdoll.h"
16+#include "MuscleConstraint.h"
17+#include "../renderer/Model.h"
18+
19+SkeletonRagdoll::SkeletonRagdoll(Model* model) : Ragdoll(), _model(model) {
20+ assert(_model);
21+}
22+
23+SkeletonRagdoll::~SkeletonRagdoll() {
24+}
25+
26+// virtual
27+void SkeletonRagdoll::stepForward(float deltaTime) {
28+ setTransform(_model->getTranslation(), _model->getRotation());
29+ Ragdoll::stepForward(deltaTime);
30+ updateMuscles();
31+ int numConstraints = _muscleConstraints.size();
32+ for (int i = 0; i < numConstraints; ++i) {
33+ _muscleConstraints[i]->enforce();
34+ }
35+}
36+
37+void SkeletonRagdoll::slamPointPositions() {
38+ QVector<JointState>& jointStates = _model->getJointStates();
39+ int numStates = jointStates.size();
40+ for (int i = 0; i < numStates; ++i) {
41+ _points[i].initPosition(jointStates.at(i).getPosition());
42+ }
43+}
44+
45+// virtual
46+void SkeletonRagdoll::initPoints() {
47+ clearConstraintsAndPoints();
48+ _muscleConstraints.clear();
49+
50+ initTransform();
51+ // one point for each joint
52+ QVector<JointState>& jointStates = _model->getJointStates();
53+ int numStates = jointStates.size();
54+ _points.fill(VerletPoint(), numStates);
55+ slamPointPositions();
56+}
57+
58+// virtual
59+void SkeletonRagdoll::buildConstraints() {
60+ QVector<JointState>& jointStates = _model->getJointStates();
61+
62+ // NOTE: the length of DistanceConstraints is computed and locked in at this time
63+ // so make sure the ragdoll positions are in a normal configuration before here.
64+ const int numPoints = _points.size();
65+ assert(numPoints == jointStates.size());
66+
67+ float minBone = FLT_MAX;
68+ float maxBone = -FLT_MAX;
69+ QMultiMap<int, int> families;
70+ for (int i = 0; i < numPoints; ++i) {
71+ const JointState& state = jointStates.at(i);
72+ int parentIndex = state.getParentIndex();
73+ if (parentIndex == -1) {
74+ FixedConstraint* anchor = new FixedConstraint(&_translationInSimulationFrame, &(_points[i]));
75+ _fixedConstraints.push_back(anchor);
76+ } else {
77+ DistanceConstraint* bone = new DistanceConstraint(&(_points[i]), &(_points[parentIndex]));
78+ bone->setDistance(state.getDistanceToParent());
79+ _boneConstraints.push_back(bone);
80+ families.insert(parentIndex, i);
81+ }
82+ float boneLength = glm::length(state.getPositionInParentFrame());
83+ if (boneLength > maxBone) {
84+ maxBone = boneLength;
85+ } else if (boneLength < minBone) {
86+ minBone = boneLength;
87+ }
88+ }
89+ // Joints that have multiple children effectively have rigid constraints between the children
90+ // in the parent frame, so we add DistanceConstraints between children in the same family.
91+ QMultiMap<int, int>::iterator itr = families.begin();
92+ while (itr != families.end()) {
93+ QList<int> children = families.values(itr.key());
94+ int numChildren = children.size();
95+ if (numChildren > 1) {
96+ for (int i = 1; i < numChildren; ++i) {
97+ DistanceConstraint* bone = new DistanceConstraint(&(_points[children[i-1]]), &(_points[children[i]]));
98+ _boneConstraints.push_back(bone);
99+ }
100+ if (numChildren > 2) {
101+ DistanceConstraint* bone = new DistanceConstraint(&(_points[children[numChildren-1]]), &(_points[children[0]]));
102+ _boneConstraints.push_back(bone);
103+ }
104+ }
105+ ++itr;
106+ }
107+
108+ float MAX_STRENGTH = 0.6f;
109+ float MIN_STRENGTH = 0.05f;
110+ // each joint gets a MuscleConstraint to its parent
111+ for (int i = 1; i < numPoints; ++i) {
112+ const JointState& state = jointStates.at(i);
113+ int p = state.getParentIndex();
114+ if (p == -1) {
115+ continue;
116+ }
117+ MuscleConstraint* constraint = new MuscleConstraint(&(_points[p]), &(_points[i]));
118+ _muscleConstraints.push_back(constraint);
119+
120+ // Short joints are more susceptible to wiggle so we modulate the strength based on the joint's length:
121+ // long = weak and short = strong.
122+ constraint->setIndices(p, i);
123+ float boneLength = glm::length(state.getPositionInParentFrame());
124+
125+ float strength = MIN_STRENGTH + (MAX_STRENGTH - MIN_STRENGTH) * (maxBone - boneLength) / (maxBone - minBone);
126+ if (!families.contains(i)) {
127+ // Although muscles only pull on the children not parents, nevertheless those joints that have
128+ // parents AND children are more stable than joints at the end such as fingers. For such joints we
129+ // bestow maximum strength which helps reduce wiggle.
130+ strength = MAX_MUSCLE_STRENGTH;
131+ }
132+ constraint->setStrength(strength);
133+ }
134+}
135+
136+void SkeletonRagdoll::updateMuscles() {
137+ QVector<JointState>& jointStates = _model->getJointStates();
138+ int numConstraints = _muscleConstraints.size();
139+ glm::quat rotation = _model->getRotation();
140+ for (int i = 0; i < numConstraints; ++i) {
141+ MuscleConstraint* constraint = _muscleConstraints[i];
142+ int j = constraint->getParentIndex();
143+ int k = constraint->getChildIndex();
144+ assert(j != -1 && k != -1);
145+ // ragdollPoints are in simulation-frame but jointStates are in model-frame
146+ constraint->setChildOffset(rotation * (jointStates.at(k).getPosition() - jointStates.at(j).getPosition()));
147+ }
148+}
interface/src/avatar/SkeletonRagdoll.h+42−0
@@ -0,0 +1,42 @@
1+//
2+// SkeletonkRagdoll.h
3+// interface/src/avatar
4+//
5+// Created by Andrew Meadows 2014.08.14
6+// Copyright 2014 High Fidelity, Inc.
7+//
8+// Distributed under the Apache License, Version 2.0.
9+// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
10+//
11+
12+#ifndef hifi_SkeletonRagdoll_h
13+#define hifi_SkeletonRagdoll_h
14+
15+#include <QVector>
16+
17+#include <Ragdoll.h>
18+
19+#include "../renderer/JointState.h"
20+
21+class MuscleConstraint;
22+class Model;
23+
24+class SkeletonRagdoll : public Ragdoll {
25+public:
26+
27+ SkeletonRagdoll(Model* model);
28+ virtual ~SkeletonRagdoll();
29+
30+ void slamPointPositions();
31+ virtual void stepForward(float deltaTime);
32+
33+ virtual void initPoints();
34+ virtual void buildConstraints();
35+
36+ void updateMuscles();
37+private:
38+ Model* _model;
39+ QVector<MuscleConstraint*> _muscleConstraints;
40+};
41+
42+#endif // hifi_SkeletonRagdoll_h
interface/src/devices/CaraFaceTracker.cpp+1−1
@@ -10,7 +10,7 @@ · 9 unmodified lines
1010 //
1111
1212 #include "CaraFaceTracker.h"
13−#include <SharedUtil.h>
13+#include <GLMHelpers.h>
1414
1515 //qt
1616 #include <QJsonDocument>
interface/src/devices/DdeFaceTracker.cpp+42−5
@@ -178,6 +178,31 @@ float rescaleCoef(float ddeCoef) { · 177 unmodified lines
178178 return (ddeCoef - DDE_MIN_RANGE) / (DDE_MAX_RANGE - DDE_MIN_RANGE);
179179 }
180180
181+const int MIN = 0;
182+const int AVG = 1;
183+const int MAX = 2;
184+const float LONG_TERM_AVERAGE = 0.999f;
185+
186+
187+void resetCoefficient(float * coefficient, float currentValue) {
188+ coefficient[MIN] = coefficient[MAX] = coefficient[AVG] = currentValue;
189+}
190+
191+float updateAndGetCoefficient(float * coefficient, float currentValue, bool scaleToRange = false) {
192+ coefficient[MIN] = (currentValue < coefficient[MIN]) ? currentValue : coefficient[MIN];
193+ coefficient[MAX] = (currentValue > coefficient[MAX]) ? currentValue : coefficient[MAX];
194+ coefficient[AVG] = LONG_TERM_AVERAGE * coefficient[AVG] + (1.f - LONG_TERM_AVERAGE) * currentValue;
195+ if (coefficient[MAX] > coefficient[MIN]) {
196+ if (scaleToRange) {
197+ return glm::clamp((currentValue - coefficient[AVG]) / (coefficient[MAX] - coefficient[MIN]), 0.f, 1.f);
198+ } else {
199+ return glm::clamp(currentValue - coefficient[AVG], 0.f, 1.f);
200+ }
201+ } else {
202+ return 0.f;
203+ }
204+}
205+
181206 void DdeFaceTracker::decodePacket(const QByteArray& buffer) {
182207 if(buffer.size() > MIN_PACKET_SIZE) {
183208 Packet packet;
@@ -189,14 +214,17 @@ void DdeFaceTracker::decodePacket(const QByteArray& buffer) { · 5 unmodified lines
189214 memcpy(&translation, packet.translation, sizeof(packet.translation));
190215 glm::quat rotation;
191216 memcpy(&rotation, &packet.rotation, sizeof(packet.rotation));
192− if (_reset) {
217+ if (_reset || (_lastReceiveTimestamp == 0)) {
193218 memcpy(&_referenceTranslation, &translation, sizeof(glm::vec3));
194219 memcpy(&_referenceRotation, &rotation, sizeof(glm::quat));
220+
221+ resetCoefficient(_rightEye, packet.expressions[0]);
222+ resetCoefficient(_leftEye, packet.expressions[1]);
195223 _reset = false;
196224 }
197225
198226 // Compute relative translation
199− float LEAN_DAMPING_FACTOR = 40;
227+ float LEAN_DAMPING_FACTOR = 200.0f;
200228 translation -= _referenceTranslation;
201229 translation /= LEAN_DAMPING_FACTOR;
202230 translation.x *= -1;
@@ -208,10 +236,19 @@ void DdeFaceTracker::decodePacket(const QByteArray& buffer) { · 5 unmodified lines
208236 _headTranslation = translation;
209237 _headRotation = rotation;
210238
239+ if (_lastReceiveTimestamp == 0) {
240+ // On first packet, reset coefficients
241+ }
242+
211243 // Set blendshapes
212− float BLINK_MAGNIFIER = 2.0f;
213− _blendshapeCoefficients[_leftBlinkIndex] = rescaleCoef(packet.expressions[1]) * BLINK_MAGNIFIER;
214− _blendshapeCoefficients[_rightBlinkIndex] = rescaleCoef(packet.expressions[0]) * BLINK_MAGNIFIER;
244+ float EYE_MAGNIFIER = 4.0f;
245+
246+ float rightEye = (updateAndGetCoefficient(_rightEye, packet.expressions[0])) * EYE_MAGNIFIER;
247+ _blendshapeCoefficients[_rightBlinkIndex] = rightEye;
248+ float leftEye = (updateAndGetCoefficient(_leftEye, packet.expressions[1])) * EYE_MAGNIFIER;
249+ _blendshapeCoefficients[_leftBlinkIndex] = leftEye;
250+
251+ // Right eye = packet.expressions[0];
215252
216253 float leftBrow = 1.0f - rescaleCoef(packet.expressions[14]);
217254 if (leftBrow < 0.5f) {
interface/src/devices/DdeFaceTracker.h+4−0
@@ -84,6 +84,10 @@ private: · 83 unmodified lines
8484 int _mouthSmileRightIndex;
8585
8686 int _jawOpenIndex;
87+
88+ float _leftEye[3];
89+ float _rightEye[3];
90+
8791 };
8892
8993 #endif // hifi_DdeFaceTracker_h
interface/src/devices/OculusManager.cpp+77−13
@@ -44,7 +44,7 @@ ovrFovPort OculusManager::_eyeFov[ovrEye_Count]; · 43 unmodified lines
4444 ovrEyeRenderDesc OculusManager::_eyeRenderDesc[ovrEye_Count];
4545 ovrSizei OculusManager::_renderTargetSize;
4646 ovrVector2f OculusManager::_UVScaleOffset[ovrEye_Count][2];
47−GLuint OculusManager::_vertices[ovrEye_Count] = { 0, 0 };
47+GLuint OculusManager::_vertices[ovrEye_Count] = { 0, 0 };
4848 GLuint OculusManager::_indices[ovrEye_Count] = { 0, 0 };
4949 GLsizei OculusManager::_meshSize[ovrEye_Count] = { 0, 0 };
5050 ovrFrameTiming OculusManager::_hmdFrameTiming;
@@ -66,12 +66,14 @@ void OculusManager::connect() { · 15 unmodified lines
6666 UserActivityLogger::getInstance().connectedDevice("hmd", "oculus");
6767 }
6868 _isConnected = true;
69−
69+#if defined(__APPLE__) || defined(_WIN32)
70+ _eyeFov[0] = _ovrHmd->DefaultEyeFov[0];
71+ _eyeFov[1] = _ovrHmd->DefaultEyeFov[1];
72+#else
7073 ovrHmd_GetDesc(_ovrHmd, &_ovrHmdDesc);
71−
7274 _eyeFov[0] = _ovrHmdDesc.DefaultEyeFov[0];
7375 _eyeFov[1] = _ovrHmdDesc.DefaultEyeFov[1];
74−
76+#endif
7577 //Get texture size
7678 ovrSizei recommendedTex0Size = ovrHmd_GetFovTextureSize(_ovrHmd, ovrEye_Left,
7779 _eyeFov[0], 1.0f);
@@ -86,11 +88,21 @@ void OculusManager::connect() { · 8 unmodified lines
8688 _eyeRenderDesc[0] = ovrHmd_GetRenderDesc(_ovrHmd, ovrEye_Left, _eyeFov[0]);
8789 _eyeRenderDesc[1] = ovrHmd_GetRenderDesc(_ovrHmd, ovrEye_Right, _eyeFov[1]);
8890
91+#if defined(__APPLE__) || defined(_WIN32)
92+ ovrHmd_SetEnabledCaps(_ovrHmd, ovrHmdCap_LowPersistence);
93+#else
8994 ovrHmd_SetEnabledCaps(_ovrHmd, ovrHmdCap_LowPersistence | ovrHmdCap_LatencyTest);
95+#endif
9096
97+#if defined(__APPLE__) || defined(_WIN32)
98+ ovrHmd_ConfigureTracking(_ovrHmd, ovrTrackingCap_Orientation | ovrTrackingCap_Position |
99+ ovrTrackingCap_MagYawCorrection,
100+ ovrTrackingCap_Orientation);
101+#else
91102 ovrHmd_StartSensor(_ovrHmd, ovrSensorCap_Orientation | ovrSensorCap_YawCorrection |
92103 ovrSensorCap_Position,
93104 ovrSensorCap_Orientation);
105+#endif
94106
95107 if (!_camera) {
96108 _camera = new Camera;
@@ -123,6 +135,10 @@ void OculusManager::connect() { · 26 unmodified lines
123135
124136 } else {
125137 _isConnected = false;
138+
139+ // we're definitely not in "VR mode" so tell the menu that
140+ Menu::getInstance()->getActionForOption(MenuOption::EnableVRMode)->setChecked(false);
141+
126142 ovrHmd_Destroy(_ovrHmd);
127143 ovr_Shutdown();
128144 }
@@ -183,6 +199,16 @@ void OculusManager::generateDistortionMesh() { · 54 unmodified lines
183199 DistortionVertex* v = pVBVerts;
184200 ovrDistortionVertex* ov = meshData.pVertexData;
185201 for (unsigned int vertNum = 0; vertNum < meshData.VertexCount; vertNum++) {
202+#if defined(__APPLE__) || defined(_WIN32)
203+ v->pos.x = ov->ScreenPosNDC.x;
204+ v->pos.y = ov->ScreenPosNDC.y;
205+ v->texR.x = ov->TanEyeAnglesR.x;
206+ v->texR.y = ov->TanEyeAnglesR.y;
207+ v->texG.x = ov->TanEyeAnglesG.x;
208+ v->texG.y = ov->TanEyeAnglesG.y;
209+ v->texB.x = ov->TanEyeAnglesB.x;
210+ v->texB.y = ov->TanEyeAnglesB.y;
211+#else
186212 v->pos.x = ov->Pos.x;
187213 v->pos.y = ov->Pos.y;
188214 v->texR.x = ov->TexR.x;
@@ -191,6 +217,7 @@ void OculusManager::generateDistortionMesh() { · 2 unmodified lines
191217 v->texG.y = ov->TexG.y;
192218 v->texB.x = ov->TexB.x;
193219 v->texB.y = ov->TexB.y;
220+#endif
194221 v->color.r = v->color.g = v->color.b = (GLubyte)(ov->VignetteFactor * 255.99f);
195222 v->color.a = (GLubyte)(ov->TimeWarpFactor * 255.99f);
196223 v++;
@@ -291,12 +318,23 @@ void OculusManager::display(const glm::quat &bodyOrientation, const glm::vec3 &p · 94 unmodified lines
291318 glPushMatrix();
292319
293320 glm::quat orientation;
294−
321+ glm::vec3 trackerPosition;
322+
323+#if defined(__APPLE__) || defined(_WIN32)
324+ ovrTrackingState ts = ovrHmd_GetTrackingState(_ovrHmd, ovr_GetTimeInSeconds());
325+ ovrVector3f ovrHeadPosition = ts.HeadPose.ThePose.Position;
326+
327+ trackerPosition = glm::vec3(ovrHeadPosition.x, ovrHeadPosition.y, ovrHeadPosition.z);
328+ trackerPosition = bodyOrientation * trackerPosition;
329+#endif
330+
295331 //Render each eye into an fbo
296332 for (int eyeIndex = 0; eyeIndex < ovrEye_Count; eyeIndex++) {
297−
333+#if defined(__APPLE__) || defined(_WIN32)
334+ ovrEyeType eye = _ovrHmd->EyeRenderOrder[eyeIndex];
335+#else
298336 ovrEyeType eye = _ovrHmdDesc.EyeRenderOrder[eyeIndex];
299−
337+#endif
300338 //Set the camera rotation for this eye
301339 eyeRenderPose[eye] = ovrHmd_GetEyePose(_ovrHmd, eye);
302340 orientation.x = eyeRenderPose[eye].Orientation.x;
@@ -305,7 +343,8 @@ void OculusManager::display(const glm::quat &bodyOrientation, const glm::vec3 &p · 2 unmodified lines
305343 orientation.w = eyeRenderPose[eye].Orientation.w;
306344
307345 _camera->setTargetRotation(bodyOrientation * orientation);
308− _camera->setTargetPosition(position);
346+ _camera->setTargetPosition(position + trackerPosition);
347+
309348 _camera->update(1.0f / Application::getInstance()->getFps());
310349
311350 Matrix4f proj = ovrMatrix4f_Projection(_eyeRenderDesc[eye].Fov, whichCamera.getNearClip(), whichCamera.getFarClip(), true);
@@ -425,19 +464,32 @@ void OculusManager::renderDistortionMesh(ovrPosef eyeRenderPose[ovrEye_Count]) { · 113 unmodified lines
425464 //Tries to reconnect to the sensors
426465 void OculusManager::reset() {
427466 #ifdef HAVE_LIBOVR
428− disconnect();
429− connect();
467+ if (_isConnected) {
468+ ovrHmd_RecenterPose(_ovrHmd);
469+ }
430470 #endif
431471 }
432472
433473 //Gets the current predicted angles from the oculus sensors
434474 void OculusManager::getEulerAngles(float& yaw, float& pitch, float& roll) {
435475 #ifdef HAVE_LIBOVR
476+#if defined(__APPLE__) || defined(_WIN32)
477+ ovrTrackingState ts = ovrHmd_GetTrackingState(_ovrHmd, ovr_GetTimeInSeconds());
478+#else
436479 ovrSensorState ss = ovrHmd_GetSensorState(_ovrHmd, _hmdFrameTiming.ScanoutMidpointSeconds);
437−
480+#endif
481+#if defined(__APPLE__) || defined(_WIN32)
482+ if (ts.StatusFlags & (ovrStatus_OrientationTracked | ovrStatus_PositionTracked)) {
483+#else
438484 if (ss.StatusFlags & (ovrStatus_OrientationTracked | ovrStatus_PositionTracked)) {
439− ovrPosef pose = ss.Predicted.Pose;
440− Quatf orientation = Quatf(pose.Orientation);
485+#endif
486+
487+#if defined(__APPLE__) || defined(_WIN32)
488+ ovrPosef headPose = ts.HeadPose.ThePose;
489+#else
490+ ovrPosef headPose = ss.Predicted.Pose;
491+#endif
492+ Quatf orientation = Quatf(headPose.Orientation);
441493 orientation.GetEulerAngles<Axis_Y, Axis_X, Axis_Z, Rotate_CCW, Handed_R>(&yaw, &pitch, &roll);
442494 } else {
443495 yaw = 0.0f;
@@ -450,6 +502,18 @@ void OculusManager::getEulerAngles(float& yaw, float& pitch, float& roll) { · 6 unmodified lines
450502 roll = 0.0f;
451503 #endif
452504 }
505+
506+glm::vec3 OculusManager::getRelativePosition() {
507+#if (defined(__APPLE__) || defined(_WIN32)) && HAVE_LIBOVR
508+ ovrTrackingState trackingState = ovrHmd_GetTrackingState(_ovrHmd, ovr_GetTimeInSeconds());
509+ ovrVector3f headPosition = trackingState.HeadPose.ThePose.Position;
510+
511+ return glm::vec3(headPosition.x, headPosition.y, headPosition.z);
512+#else
513+ // no positional tracking in Linux yet
514+ return glm::vec3(0.0f, 0.0f, 0.0f);
515+#endif
516+}
453517
454518 //Used to set the size of the glow framebuffers
455519 QSize OculusManager::getRenderTargetSize() {
interface/src/devices/OculusManager.h+1−0
@@ -40,6 +40,7 @@ public: · 39 unmodified lines
4040 /// param \pitch[out] pitch in radians
4141 /// param \roll[out] roll in radians
4242 static void getEulerAngles(float& yaw, float& pitch, float& roll);
43+ static glm::vec3 getRelativePosition();
4344 static QSize getRenderTargetSize();
4445
4546 private:
interface/src/devices/SixenseManager.cpp+3−1
@@ -19,6 +19,7 @@ · 18 unmodified lines
1919 #include "UserActivityLogger.h"
2020
2121 #ifdef HAVE_SIXENSE
22+
2223 const int CALIBRATION_STATE_IDLE = 0;
2324 const int CALIBRATION_STATE_X = 1;
2425 const int CALIBRATION_STATE_Y = 2;
@@ -41,8 +42,9 @@ SixenseManager::SixenseManager() { · 16 unmodified lines
4142 // By default we assume the _neckBase (in orb frame) is as high above the orb
4243 // as the "torso" is below it.
4344 _neckBase = glm::vec3(NECK_X, -NECK_Y, NECK_Z);
44−
45+
4546 sixenseInit();
47+
4648 #endif
4749 _hydrasConnected = false;
4850 _triggerPressed[0] = false;
interface/src/devices/SixenseManager.h+1−1
@@ -71,7 +71,7 @@ private: · 70 unmodified lines
7171 glm::vec3 _reachUp;
7272 glm::vec3 _reachForward;
7373 float _lastDistance;
74−
74+
7575 #endif
7676 bool _hydrasConnected;
7777 quint64 _lastMovement;
interface/src/renderer/JointState.h+1−0
@@ -16,6 +16,7 @@ · 15 unmodified lines
1616 #include <glm/gtx/quaternion.hpp>
1717 #include <glm/gtx/transform.hpp>
1818
19+#include <GLMHelpers.h>
1920 #include <FBXReader.h>
2021
2122 class AngularConstraint;
interface/src/renderer/Model.cpp+5−3
@@ -1449,9 +1449,11 @@ void Model::renderMeshes(float alpha, RenderMode mode, bool translucent, bool re · 1448 unmodified lines
14491449 if (cascadedShadows) {
14501450 activeProgram->setUniform(activeLocations->shadowDistances, Application::getInstance()->getShadowDistances());
14511451 }
1452− activeProgram->setUniformValueArray(activeLocations->localLightDirections,
1453− (const GLfloat*)_localLightDirections, MAX_LOCAL_LIGHTS, 4);
1454−
1452+ if (mode != SHADOW_RENDER_MODE) {
1453+ activeProgram->setUniformValueArray(activeLocations->localLightDirections,
1454+ (const GLfloat*)_localLightDirections, MAX_LOCAL_LIGHTS, 4);
1455+ }
1456+
14551457 if (mesh.blendshapes.isEmpty()) {
14561458 if (!(mesh.tangents.isEmpty() || mode == SHADOW_RENDER_MODE)) {
14571459 activeProgram->setAttributeBuffer(activeLocations->tangent, GL_FLOAT, vertexCount * 2 * sizeof(glm::vec3), 3);
interface/src/renderer/Model.h+3−0
@@ -164,6 +164,9 @@ public: · 163 unmodified lines
164164 const QVector<LocalLight>& getLocalLights() const { return _localLights; }
165165
166166 void setShowTrueJointTransforms(bool show) { _showTrueJointTransforms = show; }
167+
168+ QVector<JointState>& getJointStates() { return _jointStates; }
169+ const QVector<JointState>& getJointStates() const { return _jointStates; }
167170
168171 protected:
169172 QSharedPointer<NetworkGeometry> _geometry;
interface/src/scripting/ClipboardScriptingInterface.cpp+31−0
@@ -87,6 +87,37 @@ bool ClipboardScriptingInterface::importVoxels() { · 86 unmodified lines
8787 return Application::getInstance()->getImportSucceded();
8888 }
8989
90+bool ClipboardScriptingInterface::importVoxels(const QString& filename) {
91+ qDebug() << "Importing ... ";
92+
93+ VoxelImporter* importer = Application::getInstance()->getVoxelImporter();
94+
95+ if (!importer->validImportFile(filename)) {
96+ return false;
97+ }
98+
99+ QEventLoop loop;
100+ connect(importer, SIGNAL(importDone()), &loop, SLOT(quit()));
101+ importer->import(filename);
102+ loop.exec();
103+
104+ return true;
105+}
106+
107+bool ClipboardScriptingInterface::importVoxels(const QString& filename, float x, float y, float z, float s) {
108+ bool success = importVoxels(filename);
109+
110+ if (success) {
111+ pasteVoxel(x, y, z, s);
112+ }
113+
114+ return success;
115+}
116+
117+bool ClipboardScriptingInterface::importVoxels(const QString& filename, const VoxelDetail& destinationVoxel) {
118+ return importVoxels(filename, destinationVoxel.x, destinationVoxel.y, destinationVoxel.z, destinationVoxel.s);
119+}
120+
90121 void ClipboardScriptingInterface::nudgeVoxel(const VoxelDetail& sourceVoxel, const glm::vec3& nudgeVec) {
91122 nudgeVoxel(sourceVoxel.x, sourceVoxel.y, sourceVoxel.z, sourceVoxel.s, nudgeVec);
92123 }
interface/src/scripting/ClipboardScriptingInterface.h+3−0
@@ -39,6 +39,9 @@ public slots: · 38 unmodified lines
3939 void exportVoxel(float x, float y, float z, float s);
4040
4141 bool importVoxels();
42+ bool importVoxels(const QString& filename);
43+ bool importVoxels(const QString& filename, float x, float y, float z, float s);
44+ bool importVoxels(const QString& filename, const VoxelDetail& destinationVoxel);
4245
4346 void nudgeVoxel(const VoxelDetail& sourceVoxel, const glm::vec3& nudgeVec);
4447 void nudgeVoxel(float x, float y, float z, float s, const glm::vec3& nudgeVec);
interface/src/ui/ApplicationOverlay.cpp+29−0
@@ -57,6 +57,9 @@ ApplicationOverlay::~ApplicationOverlay() { · 56 unmodified lines
5757 const float WHITE_TEXT[] = { 0.93f, 0.93f, 0.93f };
5858 const float RETICLE_COLOR[] = { 0.0f, 198.0f / 255.0f, 244.0f / 255.0f };
5959
60+const float CONNECTION_STATUS_BORDER_COLOR[] = { 1.0f, 0.0f, 0.0f };
61+const float CONNECTION_STATUS_BORDER_LINE_WIDTH = 4.0f;
62+
6063 // Renders the overlays either to a texture or to the screen
6164 void ApplicationOverlay::renderOverlay(bool renderToTexture) {
6265
@@ -115,6 +118,8 @@ void ApplicationOverlay::renderOverlay(bool renderToTexture) { · 52 unmodified lines
115118
116119 renderPointers();
117120
121+ renderDomainConnectionStatusBorder();
122+
118123 glPopMatrix();
119124
120125
@@ -1234,6 +1239,30 @@ void ApplicationOverlay::renderTexturedHemisphere() { · 1113 unmodified lines
12341239
12351240 }
12361241
1242+void ApplicationOverlay::renderDomainConnectionStatusBorder() {
1243+ NodeList* nodeList = NodeList::getInstance();
1244+
1245+ if (nodeList && !nodeList->getDomainHandler().isConnected()) {
1246+ QGLWidget* glWidget = Application::getInstance()->getGLWidget();
1247+ int right = glWidget->width();
1248+ int bottom = glWidget->height();
1249+
1250+ glColor3f(CONNECTION_STATUS_BORDER_COLOR[0],
1251+ CONNECTION_STATUS_BORDER_COLOR[1],
1252+ CONNECTION_STATUS_BORDER_COLOR[2]);
1253+ glLineWidth(CONNECTION_STATUS_BORDER_LINE_WIDTH);
1254+
1255+ glBegin(GL_LINE_LOOP);
1256+
1257+ glVertex2i(0, 0);
1258+ glVertex2i(0, bottom);
1259+ glVertex2i(right, bottom);
1260+ glVertex2i(right, 0);
1261+
1262+ glEnd();
1263+ }
1264+}
1265+
12371266 QOpenGLFramebufferObject* ApplicationOverlay::getFramebufferObject() {
12381267 QSize size = Application::getInstance()->getGLWidget()->size();
12391268 if (!_framebufferObject || _framebufferObject->size() != size) {
interface/src/ui/ApplicationOverlay.h+2−1
@@ -56,6 +56,7 @@ private: · 55 unmodified lines
5656 void renderAudioMeter();
5757 void renderStatsAndLogs();
5858 void renderTexturedHemisphere();
59+ void renderDomainConnectionStatusBorder();
5960
6061 QOpenGLFramebufferObject* _framebufferObject;
6162 float _trailingAudioLoudness;
@@ -76,4 +77,4 @@ private: · 14 unmodified lines
7677 GLuint _crosshairTexture;
7778 };
7879
79−#endif // hifi_ApplicationOverlay_h
80+#endif // hifi_ApplicationOverlay_h
interface/src/ui/MetavoxelEditor.cpp+94−19
@@ -118,6 +118,8 @@ MetavoxelEditor::MetavoxelEditor() : · 117 unmodified lines
118118 addTool(new SetSpannerTool(this));
119119 addTool(new ImportHeightfieldTool(this));
120120 addTool(new EraseHeightfieldTool(this));
121+ addTool(new HeightfieldHeightBrushTool(this));
122+ addTool(new HeightfieldColorBrushTool(this));
121123
122124 updateAttributes();
123125
@@ -954,11 +956,11 @@ void ImportHeightfieldTool::apply() { · 830 unmodified lines
954956 HeightfieldBuffer* buffer = static_cast<HeightfieldBuffer*>(bufferData.data());
955957 MetavoxelData data;
956958 data.setSize(scale);
957− HeightfieldDataPointer heightPointer(new HeightfieldData(buffer->getHeight()));
959+ HeightfieldDataPointer heightPointer(new HeightfieldData(buffer->getUnextendedHeight()));
958960 data.setRoot(AttributeRegistry::getInstance()->getHeightfieldAttribute(), new MetavoxelNode(AttributeValue(
959961 AttributeRegistry::getInstance()->getHeightfieldAttribute(), encodeInline(heightPointer))));
960962 if (!buffer->getColor().isEmpty()) {
961− HeightfieldDataPointer colorPointer(new HeightfieldData(buffer->getColor()));
963+ HeightfieldDataPointer colorPointer(new HeightfieldData(buffer->getUnextendedColor()));
962964 data.setRoot(AttributeRegistry::getInstance()->getHeightfieldColorAttribute(), new MetavoxelNode(AttributeValue(
963965 AttributeRegistry::getInstance()->getHeightfieldColorAttribute(), encodeInline(colorPointer))));
964966 }
@@ -1001,34 +1003,38 @@ void ImportHeightfieldTool::updatePreview() { · 36 unmodified lines
10011003 if (_heightImage.width() > 0 && _heightImage.height() > 0) {
10021004 float z = 0.0f;
10031005 int blockSize = pow(2.0, _blockSize->value());
1004− int blockAdvancement = blockSize - 1;
1005− for (int i = 0; i < _heightImage.height(); i += blockAdvancement, z++) {
1006+ int heightSize = blockSize + HeightfieldBuffer::HEIGHT_EXTENSION;
1007+ int colorSize = blockSize + HeightfieldBuffer::SHARED_EDGE;
1008+ for (int i = 0; i < _heightImage.height(); i += blockSize, z++) {
10061009 float x = 0.0f;
1007− for (int j = 0; j < _heightImage.width(); j += blockAdvancement, x++) {
1008− QByteArray height(blockSize * blockSize, 0);
1009− int rows = qMin(blockSize, _heightImage.height() - i);
1010− int columns = qMin(blockSize, _heightImage.width() - j);
1011− const int BYTES_PER_COLOR = 3;
1010+ for (int j = 0; j < _heightImage.width(); j += blockSize, x++) {
1011+ QByteArray height(heightSize * heightSize, 0);
1012+ int extendedI = qMax(i - HeightfieldBuffer::HEIGHT_BORDER, 0);
1013+ int extendedJ = qMax(j - HeightfieldBuffer::HEIGHT_BORDER, 0);
1014+ int offsetY = extendedI - i + HeightfieldBuffer::HEIGHT_BORDER;
1015+ int offsetX = extendedJ - j + HeightfieldBuffer::HEIGHT_BORDER;
1016+ int rows = qMin(heightSize - offsetY, _heightImage.height() - extendedI);
1017+ int columns = qMin(heightSize - offsetX, _heightImage.width() - extendedJ);
10121018 for (int y = 0; y < rows; y++) {
1013− uchar* src = _heightImage.scanLine(i + y) + j * BYTES_PER_COLOR;
1014− char* dest = height.data() + y * blockSize;
1019+ uchar* src = _heightImage.scanLine(extendedI + y) + extendedJ * HeightfieldData::COLOR_BYTES;
1020+ char* dest = height.data() + (y + offsetY) * heightSize + offsetX;
10151021 for (int x = 0; x < columns; x++) {
10161022 *dest++ = *src;
1017− src += BYTES_PER_COLOR;
1023+ src += HeightfieldData::COLOR_BYTES;
10181024 }
10191025 }
1020−
10211026 QByteArray color;
10221027 if (!_colorImage.isNull()) {
1023− color = QByteArray(blockSize * blockSize * BYTES_PER_COLOR, 0);
1024− rows = qMax(0, qMin(blockSize, _colorImage.height() - i));
1025− columns = qMax(0, qMin(blockSize, _colorImage.width() - j));
1028+ color = QByteArray(colorSize * colorSize * HeightfieldData::COLOR_BYTES, 0);
1029+ rows = qMax(0, qMin(colorSize, _colorImage.height() - i));
1030+ columns = qMax(0, qMin(colorSize, _colorImage.width() - j));
10261031 for (int y = 0; y < rows; y++) {
1027− memcpy(color.data() + y * blockSize * BYTES_PER_COLOR,
1028− _colorImage.scanLine(i + y) + j * BYTES_PER_COLOR, columns * BYTES_PER_COLOR);
1032+ memcpy(color.data() + y * colorSize * HeightfieldData::COLOR_BYTES,
1033+ _colorImage.scanLine(i + y) + j * HeightfieldData::COLOR_BYTES,
1034+ columns * HeightfieldData::COLOR_BYTES);
10291035 }
10301036 }
1031− buffers.append(BufferDataPointer(new HeightfieldBuffer(glm::vec3(x, 0.0f, z), 1.0f, height, color, false)));
1037+ buffers.append(BufferDataPointer(new HeightfieldBuffer(glm::vec3(x, 0.0f, z), 1.0f, height, color)));
10321038 }
10331039 }
10341040 }
@@ -1080,3 +1086,72 @@ void EraseHeightfieldTool::apply() { · 45 unmodified lines
10801086 message.edit = QVariant::fromValue(edit);
10811087 Application::getInstance()->getMetavoxels()->applyEdit(message, true);
10821088 }
1089+
1090+HeightfieldBrushTool::HeightfieldBrushTool(MetavoxelEditor* editor, const QString& name) :
1091+ MetavoxelTool(editor, name, false) {
1092+
1093+ QWidget* widget = new QWidget();
1094+ widget->setLayout(_form = new QFormLayout());
1095+ layout()->addWidget(widget);
1096+
1097+ _form->addRow("Radius:", _radius = new QDoubleSpinBox());
1098+ _radius->setSingleStep(0.01);
1099+ _radius->setMaximum(FLT_MAX);
1100+ _radius->setValue(1.0);
1101+}
1102+
1103+void HeightfieldBrushTool::render() {
1104+ if (Application::getInstance()->isMouseHidden()) {
1105+ return;
1106+ }
1107+
1108+ // find the intersection with the heightfield
1109+ glm::vec3 origin = Application::getInstance()->getMouseRayOrigin();
1110+ glm::vec3 direction = Application::getInstance()->getMouseRayDirection();
1111+
1112+ float distance;
1113+ if (!Application::getInstance()->getMetavoxels()->findFirstRayHeightfieldIntersection(origin, direction, distance)) {
1114+ return;
1115+ }
1116+ Application::getInstance()->getMetavoxels()->renderHeightfieldCursor(
1117+ _position = origin + distance * direction, _radius->value());
1118+}
1119+
1120+bool HeightfieldBrushTool::eventFilter(QObject* watched, QEvent* event) {
1121+ if (event->type() == QEvent::Wheel) {
1122+ float angle = static_cast<QWheelEvent*>(event)->angleDelta().y();
1123+ const float ANGLE_SCALE = 1.0f / 1000.0f;
1124+ _radius->setValue(_radius->value() * glm::pow(2.0f, angle * ANGLE_SCALE));
1125+ return true;
1126+
1127+ } else if (event->type() == QEvent::MouseButtonPress) {
1128+ MetavoxelEditMessage message = { createEdit(static_cast<QMouseEvent*>(event)->button() == Qt::RightButton) };
1129+ Application::getInstance()->getMetavoxels()->applyEdit(message, true);
1130+ return true;
1131+ }
1132+ return false;
1133+}
1134+
1135+HeightfieldHeightBrushTool::HeightfieldHeightBrushTool(MetavoxelEditor* editor) :
1136+ HeightfieldBrushTool(editor, "Height Brush") {
1137+
1138+ _form->addRow("Height:", _height = new QDoubleSpinBox());
1139+ _height->setMinimum(-FLT_MAX);
1140+ _height->setMaximum(FLT_MAX);
1141+ _height->setValue(1.0);
1142+}
1143+
1144+QVariant HeightfieldHeightBrushTool::createEdit(bool alternate) {
1145+ return QVariant::fromValue(PaintHeightfieldHeightEdit(_position, _radius->value(),
1146+ alternate ? -_height->value() : _height->value()));
1147+}
1148+
1149+HeightfieldColorBrushTool::HeightfieldColorBrushTool(MetavoxelEditor* editor) :
1150+ HeightfieldBrushTool(editor, "Color Brush") {
1151+
1152+ _form->addRow("Color:", _color = new QColorEditor(this));
1153+}
1154+
1155+QVariant HeightfieldColorBrushTool::createEdit(bool alternate) {
1156+ return QVariant::fromValue(PaintHeightfieldColorEdit(_position, _radius->value(), _color->getColor()));
1157+}
interface/src/ui/MetavoxelEditor.h+58−1
@@ -18,6 +18,7 @@ · 17 unmodified lines
1818 #include "MetavoxelSystem.h"
1919 #include "renderer/ProgramObject.h"
2020
21+class QColorEditor;
2122 class QComboBox;
2223 class QDoubleSpinBox;
2324 class QGroupBox;
@@ -282,7 +283,7 @@ private: · 258 unmodified lines
282283 HeightfieldPreview _preview;
283284 };
284285
285−// Allows clearing heighfield blocks.
286+/// Allows clearing heighfield blocks.
286287 class EraseHeightfieldTool : public HeightfieldTool {
287288 Q_OBJECT
288289
@@ -302,4 +303,60 @@ private: · 13 unmodified lines
302303 QSpinBox* _length;
303304 };
304305
306+/// Base class for tools that allow painting on heightfields.
307+class HeightfieldBrushTool : public MetavoxelTool {
308+ Q_OBJECT
309+
310+public:
311+
312+ HeightfieldBrushTool(MetavoxelEditor* editor, const QString& name);
313+
314+ virtual void render();
315+
316+ virtual bool eventFilter(QObject* watched, QEvent* event);
317+
318+protected:
319+
320+ virtual QVariant createEdit(bool alternate) = 0;
321+
322+ QFormLayout* _form;
323+ QDoubleSpinBox* _radius;
324+
325+ glm::vec3 _position;
326+};
327+
328+/// Allows raising or lowering parts of the heightfield.
329+class HeightfieldHeightBrushTool : public HeightfieldBrushTool {
330+ Q_OBJECT
331+
332+public:
333+
334+ HeightfieldHeightBrushTool(MetavoxelEditor* editor);
335+
336+protected:
337+
338+ virtual QVariant createEdit(bool alternate);
339+
340+private:
341+
342+ QDoubleSpinBox* _height;
343+};
344+
345+/// Allows coloring parts of the heightfield.
346+class HeightfieldColorBrushTool : public HeightfieldBrushTool {
347+ Q_OBJECT
348+
349+public:
350+
351+ HeightfieldColorBrushTool(MetavoxelEditor* editor);
352+
353+protected:
354+
355+ virtual QVariant createEdit(bool alternate);
356+
357+private:
358+
359+ QColorEditor* _color;
360+};
361+
305362 #endif // hifi_MetavoxelEditor_h
interface/src/ui/VoxelImportDialog.cpprenamed from interface/src/ui/ImportDialog.cpp+94−37
@@ -1,5 +1,5 @@
11 //
2−// ImportDialog.cpp
2+// VoxelImportDialog.cpp
33 // interface/src/ui
44 //
55 // Created by Clement Brisset on 8/12/13.
@@ -20,7 +20,11 @@ · 14 unmodified lines
2020
2121 #include "Application.h"
2222
23−#include "ImportDialog.h"
23+#include "VoxelImportDialog.h"
24+#include "voxels/VoxelImporter.h"
25+
26+const QString SETTINGS_GROUP_NAME = "VoxelImport";
27+const QString IMPORT_DIALOG_SETTINGS_KEY = "VoxelImportDialogSettings";
2428
2529 const QString WINDOW_NAME = QObject::tr("Import Voxels");
2630 const QString IMPORT_BUTTON_NAME = QObject::tr("Import Voxels");
@@ -97,12 +101,14 @@ QString HiFiIconProvider::type(const QFileInfo &info) const { · 70 unmodified lines
97101 return QFileIconProvider::type(info);
98102 }
99103
100−ImportDialog::ImportDialog(QWidget* parent) :
104+VoxelImportDialog::VoxelImportDialog(QWidget* parent) :
101105 QFileDialog(parent, WINDOW_NAME, DOWNLOAD_LOCATION, NULL),
102− _progressBar(this),
103− _importButton(IMPORT_BUTTON_NAME, this),
104106 _cancelButton(CANCEL_BUTTON_NAME, this),
105− _mode(importMode) {
107+ _importButton(IMPORT_BUTTON_NAME, this),
108+ _importer(Application::getInstance()->getVoxelImporter()),
109+ _mode(importMode),
110+ _progressBar(this),
111+ _didImport(false) {
106112
107113 setOption(QFileDialog::DontUseNativeDialog, true);
108114 setFileMode(QFileDialog::ExistingFile);
@@ -113,41 +119,54 @@ ImportDialog::ImportDialog(QWidget* parent) : · 4 unmodified lines
113119
114120 _progressBar.setRange(0, 100);
115121
116− connect(&_importButton, SIGNAL(pressed()), SLOT(accept()));
117− connect(&_cancelButton, SIGNAL(pressed()), SIGNAL(canceled()));
122+ connect(&_importButton, SIGNAL(pressed()), this, SLOT(accept()));
123+ connect(&_cancelButton, SIGNAL(pressed()), this, SLOT(cancel()));
118124 connect(this, SIGNAL(currentChanged(QString)), SLOT(saveCurrentFile(QString)));
119125 }
120126
121−void ImportDialog::reset() {
127+void VoxelImportDialog::cancel() {
128+ switch (getMode()) {
129+ case importMode:
130+ _importer->cancel();
131+ close();
132+ break;
133+ default:
134+ _importer->reset();
135+ setMode(importMode);
136+ break;
137+ }
138+ emit canceled();
139+}
140+
141+void VoxelImportDialog::saveSettings(QSettings* settings) {
142+ settings->beginGroup(SETTINGS_GROUP_NAME);
143+ settings->setValue(IMPORT_DIALOG_SETTINGS_KEY, saveState());
144+ settings->endGroup();
145+}
146+
147+void VoxelImportDialog::loadSettings(QSettings* settings) {
148+ settings->beginGroup(SETTINGS_GROUP_NAME);
149+ restoreState(settings->value(IMPORT_DIALOG_SETTINGS_KEY).toByteArray());
150+ settings->endGroup();
151+}
152+
153+bool VoxelImportDialog::prompt() {
154+ reset();
155+ exec();
156+ return _didImport;
157+}
158+
159+void VoxelImportDialog::reset() {
122160 setMode(importMode);
123− _progressBar.setValue(0);
161+ _didImport = false;
124162 }
125163
126−void ImportDialog::setMode(dialogMode mode) {
164+void VoxelImportDialog::setMode(dialogMode mode) {
165+ dialogMode previousMode = _mode;
127166 _mode = mode;
128167
129168 switch (_mode) {
130− case loadingMode:
131− _importButton.setEnabled(false);
132− _importButton.setText(LOADING_BUTTON_NAME);
133− findChild<QWidget*>("sidebar")->setEnabled(false);
134− findChild<QWidget*>("treeView")->setEnabled(false);
135− findChild<QWidget*>("backButton")->setEnabled(false);
136− findChild<QWidget*>("forwardButton")->setEnabled(false);
137− findChild<QWidget*>("toParentButton")->setEnabled(false);
138− break;
139− case placeMode:
140− _progressBar.setValue(100);
141− _importButton.setEnabled(true);
142− _importButton.setText(PLACE_BUTTON_NAME);
143− findChild<QWidget*>("sidebar")->setEnabled(false);
144− findChild<QWidget*>("treeView")->setEnabled(false);
145− findChild<QWidget*>("backButton")->setEnabled(false);
146− findChild<QWidget*>("forwardButton")->setEnabled(false);
147− findChild<QWidget*>("toParentButton")->setEnabled(false);
148− break;
149169 case importMode:
150− default:
151170 _progressBar.setValue(0);
152171 _importButton.setEnabled(true);
153172 _importButton.setText(IMPORT_BUTTON_NAME);
@@ -157,22 +176,60 @@ void ImportDialog::setMode(dialogMode mode) { · 3 unmodified lines
157176 findChild<QWidget*>("forwardButton")->setEnabled(true);
158177 findChild<QWidget*>("toParentButton")->setEnabled(true);
159178 break;
179+ case loadingMode:
180+ // Connect to VoxelImporter signals
181+ connect(_importer, SIGNAL(importProgress(int)), this, SLOT(updateProgressBar(int)));
182+ connect(_importer, SIGNAL(importDone()), this, SLOT(afterImport()));
183+
184+ _importButton.setEnabled(false);
185+ _importButton.setText(LOADING_BUTTON_NAME);
186+ findChild<QWidget*>("sidebar")->setEnabled(false);
187+ findChild<QWidget*>("treeView")->setEnabled(false);
188+ findChild<QWidget*>("backButton")->setEnabled(false);
189+ findChild<QWidget*>("forwardButton")->setEnabled(false);
190+ findChild<QWidget*>("toParentButton")->setEnabled(false);
191+ break;
192+ case finishedMode:
193+ if (previousMode == loadingMode) {
194+ // Disconnect from VoxelImporter signals
195+ disconnect(_importer, SIGNAL(importProgress(int)), this, SLOT(setProgressBarValue(int)));
196+ disconnect(_importer, SIGNAL(importDone()), this, SLOT(afterImport()));
197+ }
198+ setMode(importMode);
199+ break;
160200 }
161201 }
162202
163−void ImportDialog::setProgressBarValue(int value) {
203+void VoxelImportDialog::setProgressBarValue(int value) {
164204 _progressBar.setValue(value);
165205 }
166206
167−void ImportDialog::accept() {
168− emit accepted();
207+void VoxelImportDialog::accept() {
208+ if (getMode() == importMode) {
209+ QString filename = getCurrentFile();
210+
211+ // If file is invalid we ignore the call
212+ if (!_importer->validImportFile(filename)) {
213+ return;
214+ }
215+ // Let's prepare the dialog window for import
216+ setMode(loadingMode);
217+
218+ _importer->import(filename);
219+ }
220+}
221+
222+void VoxelImportDialog::afterImport() {
223+ setMode(finishedMode);
224+ _didImport = true;
225+ close();
169226 }
170227
171−void ImportDialog::saveCurrentFile(QString filename) {
228+void VoxelImportDialog::saveCurrentFile(QString filename) {
172229 _currentFile = QFileInfo(filename).isFile() ? filename : "";
173230 }
174231
175−void ImportDialog::setLayout() {
232+void VoxelImportDialog::setLayout() {
176233 QGridLayout* gridLayout = (QGridLayout*) layout();
177234 gridLayout->addWidget(&_progressBar, 2, 0, 2, 1);
178235 gridLayout->addWidget(&_cancelButton, 2, 1, 2, 1);
@@ -258,7 +315,7 @@ void ImportDialog::setLayout() { · 79 unmodified lines
258315
259316 }
260317
261−void ImportDialog::setImportTypes() {
318+void VoxelImportDialog::setImportTypes() {
262319 QFile config(Application::resourcesPath() + "config/config.json");
263320 config.open(QFile::ReadOnly | QFile::Text);
264321 QJsonDocument document = QJsonDocument::fromJson(config.readAll());
interface/src/ui/VoxelImportDialog.hrenamed from interface/src/ui/ImportDialog.h+21−13
@@ -1,5 +1,5 @@
11 //
2−// ImportDialog.h
2+// VoxelImportDialog.h
33 // interface/src/ui
44 //
55 // Created by Clement Brisset on 8/12/13.
@@ -9,8 +9,8 @@ · 3 unmodified lines
99 // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
1010 //
1111
12−#ifndef hifi_ImportDialog_h
13−#define hifi_ImportDialog_h
12+#ifndef hifi_VoxelImportDialog_h
13+#define hifi_VoxelImportDialog_h
1414
1515 #include "InterfaceConfig.h"
1616
@@ -23,6 +23,8 @@ · 6 unmodified lines
2323
2424 #include <SharedUtil.h>
2525
26+#include "voxels/VoxelImporter.h"
27+
2628 class HiFiIconProvider : public QFileIconProvider {
2729 public:
2830 HiFiIconProvider(const QHash<QString, QString> map) { iconsMap = map; };
@@ -35,39 +37,45 @@ public: · 6 unmodified lines
3537 enum dialogMode {
3638 importMode,
3739 loadingMode,
38− placeMode
40+ finishedMode
3941 };
4042
41−class ImportDialog : public QFileDialog {
43+class VoxelImportDialog : public QFileDialog {
4244 Q_OBJECT
4345
4446 public:
45− ImportDialog(QWidget* parent = NULL);
46− void reset();
47+ VoxelImportDialog(QWidget* parent = NULL);
4748
4849 QString getCurrentFile() const { return _currentFile; }
4950 dialogMode getMode() const { return _mode; }
51+
5052 void setMode(dialogMode mode);
53+ void reset();
54+ bool prompt();
55+ void loadSettings(QSettings* settings);
56+ void saveSettings(QSettings* settings);
5157
5258 signals:
5359 void canceled();
54−
55−public slots:
56− void setProgressBarValue(int value);
5760
5861 private slots:
62+ void setProgressBarValue(int value);
5963 void accept();
64+ void cancel();
6065 void saveCurrentFile(QString filename);
66+ void afterImport();
6167
6268 private:
69+ QPushButton _cancelButton;
6370 QString _currentFile;
64− QProgressBar _progressBar;
6571 QPushButton _importButton;
66− QPushButton _cancelButton;
72+ VoxelImporter* _importer;
6773 dialogMode _mode;
74+ QProgressBar _progressBar;
75+ bool _didImport;
6876
6977 void setLayout();
7078 void setImportTypes();
7179 };
7280
73−#endif // hifi_ImportDialog_h
81+#endif // hifi_VoxelImportDialog_h
interface/src/ui/overlays/Base3DOverlay.h+2−0
@@ -11,6 +11,8 @@ · 10 unmodified lines
1111 #ifndef hifi_Base3DOverlay_h
1212 #define hifi_Base3DOverlay_h
1313
14+#include <glm/glm.hpp>
15+
1416 #include "Overlay.h"
1517
1618 class Base3DOverlay : public Overlay {
interface/src/voxels/OctreePacketProcessor.cpp+1−1
@@ -26,7 +26,7 @@ void OctreePacketProcessor::processPacket(const SharedNodePointer& sendingNode, · 25 unmodified lines
2626 if (packetsToProcessCount() > WAY_BEHIND && Application::getInstance()->getLogger()->extraDebugging()) {
2727 qDebug("OctreePacketProcessor::processPacket() packets to process=%d", packetsToProcessCount());
2828 }
29− ssize_t messageLength = mutablePacket.size();
29+ int messageLength = mutablePacket.size();
3030
3131 Application* app = Application::getInstance();
3232 bool wasStatsPacket = false;
interface/src/voxels/VoxelImporter.cpp+33−82
@@ -20,8 +20,7 @@ · 19 unmodified lines
2020
2121 #include "voxels/VoxelImporter.h"
2222
23−const QString SETTINGS_GROUP_NAME = "VoxelImport";
24−const QString IMPORT_DIALOG_SETTINGS_KEY = "ImportDialogSettings";
23+const QStringList SUPPORTED_EXTENSIONS = QStringList() << "png" << "svo" << "schematic";
2524
2625 class ImportTask : public QObject, public QRunnable {
2726 public:
@@ -32,104 +31,46 @@ private: · 4 unmodified lines
3231 QString _filename;
3332 };
3433
35−VoxelImporter::VoxelImporter(QWidget* parent) :
36− QObject(parent),
34+VoxelImporter::VoxelImporter() :
3735 _voxelTree(true),
38− _importDialog(parent),
39− _task(NULL),
40− _didImport(false)
36+ _task(NULL)
4137 {
4238 LocalVoxelsList::getInstance()->addPersistantTree(IMPORT_TREE_NAME, &_voxelTree);
4339
44− connect(&_voxelTree, SIGNAL(importProgress(int)), &_importDialog, SLOT(setProgressBarValue(int)));
45− connect(&_importDialog, SIGNAL(canceled()), this, SLOT(cancel()));
46− connect(&_importDialog, SIGNAL(accepted()), this, SLOT(import()));
47−}
48−
49−void VoxelImporter::saveSettings(QSettings* settings) {
50− settings->beginGroup(SETTINGS_GROUP_NAME);
51− settings->setValue(IMPORT_DIALOG_SETTINGS_KEY, _importDialog.saveState());
52− settings->endGroup();
53−}
54−
55−void VoxelImporter::loadSettings(QSettings* settings) {
56− settings->beginGroup(SETTINGS_GROUP_NAME);
57− _importDialog.restoreState(settings->value(IMPORT_DIALOG_SETTINGS_KEY).toByteArray());
58− settings->endGroup();
40+ connect(&_voxelTree, SIGNAL(importProgress(int)), this, SIGNAL(importProgress(int)));
5941 }
6042
6143 VoxelImporter::~VoxelImporter() {
6244 cleanupTask();
6345 }
6446
47+void VoxelImporter::cancel() {
48+ if (_task) {
49+ disconnect(_task, 0, 0, 0);
50+ }
51+ reset();
52+}
53+
6554 void VoxelImporter::reset() {
6655 _voxelTree.eraseAllOctreeElements();
67− _importDialog.reset();
68−
6956 cleanupTask();
7057 }
7158
72−int VoxelImporter::exec() {
73− reset();
74− _importDialog.exec();
75−
76− if (!_didImport) {
77− // if the import is rejected, we make sure to cleanup before leaving
59+void VoxelImporter::import(const QString& filename) {
60+ // If present, abort existing import
61+ if (_task) {
7862 cleanupTask();
79− return 1;
80− } else {
81− _didImport = false;
82− return 0;
8363 }
84−}
8564
86−void VoxelImporter::import() {
87− switch (_importDialog.getMode()) {
88− case loadingMode:
89− _importDialog.setMode(placeMode);
90− return;
91− case placeMode:
92− // Means the user chose to import
93− _didImport = true;
94− _importDialog.close();
95− return;
96− case importMode:
97− default:
98− QString filename = _importDialog.getCurrentFile();
99− // if it's not a file, we ignore the call
100− if (!QFileInfo(filename).isFile()) {
101− return;
102− }
103−
104− // Let's prepare the dialog window for import
105− _importDialog.setMode(loadingMode);
106−
107− // If not already done, we switch to the local tree
108− if (Application::getInstance()->getSharedVoxelSystem()->getTree() != &_voxelTree) {
109− Application::getInstance()->getSharedVoxelSystem()->changeTree(&_voxelTree);
110− }
111−
112− // Creation and launch of the import task on the thread pool
113− _task = new ImportTask(filename);
114− connect(_task, SIGNAL(destroyed()), SLOT(import()));
115− QThreadPool::globalInstance()->start(_task);
116− break;
65+ // If not already done, we switch to the local tree
66+ if (Application::getInstance()->getSharedVoxelSystem()->getTree() != &_voxelTree) {
67+ Application::getInstance()->getSharedVoxelSystem()->changeTree(&_voxelTree);
11768 }
118−}
11969
120−void VoxelImporter::cancel() {
121− switch (_importDialog.getMode()) {
122− case loadingMode:
123− disconnect(_task, 0, 0, 0);
124− cleanupTask();
125− case placeMode:
126− _importDialog.setMode(importMode);
127− break;
128− case importMode:
129− default:
130− _importDialog.close();
131− break;
132− }
70+ // Creation and launch of the import task on the thread pool
71+ _task = new ImportTask(filename);
72+ connect(_task, SIGNAL(destroyed()), SLOT(finishImport()));
73+ QThreadPool::globalInstance()->start(_task);
13374 }
13475
13576 void VoxelImporter::cleanupTask() {
@@ -140,6 +81,16 @@ void VoxelImporter::cleanupTask() { · 4 unmodified lines
14081 }
14182 }
14283
84+void VoxelImporter::finishImport() {
85+ cleanupTask();
86+ emit importDone();
87+}
88+
89+bool VoxelImporter::validImportFile(const QString& filename) {
90+ QFileInfo fileInfo = QFileInfo(filename);
91+ return fileInfo.isFile() && SUPPORTED_EXTENSIONS.indexOf(fileInfo.suffix().toLower()) != -1;
92+}
93+
14394 ImportTask::ImportTask(const QString &filename)
14495 : _filename(filename)
14596 {
@@ -151,7 +102,7 @@ void ImportTask::run() { · 5 unmodified lines
151102 // We start by cleaning up the shared voxel system just in case
152103 voxelSystem->killLocalVoxels();
153104
154− // Then we call the righ method for the job
105+ // Then we call the right method for the job
155106 if (_filename.endsWith(".png", Qt::CaseInsensitive)) {
156107 voxelSystem->getTree()->readFromSquareARGB32Pixels(_filename.toLocal8Bit().data());
157108 } else if (_filename.endsWith(".svo", Qt::CaseInsensitive)) {
@@ -163,6 +114,6 @@ void ImportTask::run() { · 5 unmodified lines
163114 qDebug() << "[ERROR] Invalid file extension." << endl;
164115 }
165116
166− // Here we reaverage the tree so that he is ready for preview
117+ // Here we reaverage the tree so that it is ready for preview
167118 voxelSystem->getTree()->reaverageOctreeElements();
168119 }
interface/src/voxels/VoxelImporter.h+12−11
@@ -14,8 +14,8 @@ · 13 unmodified lines
1414
1515 #include <QThread>
1616 #include <QRunnable>
17+#include <QStringList>
1718
18−#include "ui/ImportDialog.h"
1919 #include "voxels/VoxelSystem.h"
2020
2121 class ImportTask;
@@ -23,28 +23,29 @@ class ImportTask; · 1 unmodified line
2323 class VoxelImporter : public QObject {
2424 Q_OBJECT
2525 public:
26− VoxelImporter(QWidget* parent = NULL);
26+ VoxelImporter();
2727 ~VoxelImporter();
2828
2929 void reset();
30− void loadSettings(QSettings* settings);
31− void saveSettings(QSettings* settings);
32−
30+ void cancel();
3331 VoxelTree* getVoxelTree() { return &_voxelTree; }
32+ bool validImportFile(const QString& filename);
3433
3534 public slots:
36− int exec();
37− void import();
38− void cancel();
35+ void import(const QString& filename);
36+
37+signals:
38+ void importDone();
39+ void importProgress(int);
3940
4041 private:
4142 VoxelTree _voxelTree;
42− ImportDialog _importDialog;
43−
4443 ImportTask* _task;
45− bool _didImport;
4644
4745 void cleanupTask();
46+
47+private slots:
48+ void finishImport();
4849 };
4950
5051 #endif // hifi_VoxelImporter_h
libraries/animation/CMakeLists.txt+5−25
@@ -1,29 +1,9 @@
1−set(ROOT_DIR ../..)
2−set(MACRO_DIR "${ROOT_DIR}/cmake/macros")
3−
4−# setup for find modules
5−set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/../../cmake/modules/")
6−
71 set(TARGET_NAME animation)
82
9−find_package(Qt5Widgets REQUIRED)
10−
11−include(${MACRO_DIR}/SetupHifiLibrary.cmake)
12−setup_hifi_library(${TARGET_NAME})
13−
14−include(${MACRO_DIR}/IncludeGLM.cmake)
15−include_glm(${TARGET_NAME} "${ROOT_DIR}")
16−
17−include(${MACRO_DIR}/LinkHifiLibrary.cmake)
18−link_hifi_library(shared ${TARGET_NAME} "${ROOT_DIR}")
19−link_hifi_library(fbx ${TARGET_NAME} "${ROOT_DIR}")
20−
21−# link ZLIB
22−find_package(ZLIB)
3+# use setup_hifi_library macro to setup our project and link appropriate Qt modules
4+setup_hifi_library(Network Script)
235
24−target_link_libraries(${TARGET_NAME} "${ZLIB_LIBRARIES}" Qt5::Widgets)
6+link_hifi_libraries(shared fbx)
257
26−# add a definition for ssize_t so that windows doesn't bail
27−if (WIN32)
28− add_definitions(-Dssize_t=long)
29−endif ()
8+# call macro to link our dependencies and bubble them up via a property on our target
9+link_shared_dependencies()
libraries/audio/CMakeLists.txt+6−21
@@ -1,26 +1,11 @@
1−set(ROOT_DIR ../..)
2−set(MACRO_DIR "${ROOT_DIR}/cmake/macros")
3−
4−# setup for find modules
5−set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/../../cmake/modules/")
6−
71 set(TARGET_NAME audio)
82
9−find_package(Qt5 COMPONENTS Script)
10−include_directories(SYSTEM "${Qt5Script_INCLUDE_DIRS}")
11−
12−# set up the external glm library
13−include(${MACRO_DIR}/SetupHifiLibrary.cmake)
14−setup_hifi_library(${TARGET_NAME})
3+# use setup_hifi_library macro to setup our project and link appropriate Qt modules
4+setup_hifi_library(Network)
155
16−include(${MACRO_DIR}/IncludeGLM.cmake)
17−include_glm(${TARGET_NAME} "${ROOT_DIR}")
6+include_glm()
187
19−include(${MACRO_DIR}/LinkHifiLibrary.cmake)
20−link_hifi_library(shared ${TARGET_NAME} "${ROOT_DIR}")
21−link_hifi_library(networking ${TARGET_NAME} "${ROOT_DIR}")
8+link_hifi_libraries(networking shared)
229
23−# add a definition for ssize_t so that windows doesn't bail
24−if (WIN32)
25− add_definitions(-Dssize_t=long)
26−endif ()
10+# call macro to link our dependencies and bubble them up via a property on our target
11+link_shared_dependencies()
libraries/audio/src/AudioFilter.cpp+26−0
@@ -0,0 +1,26 @@
1+//
2+// AudioFilter.cpp
3+// hifi
4+//
5+// Created by Craig Hansen-Sturm on 8/10/14.
6+// Copyright 2014 High Fidelity, Inc.
7+//
8+// Distributed under the Apache License, Version 2.0.
9+// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
10+//
11+
12+#include <math.h>
13+#include <vector>
14+#include <SharedUtil.h>
15+#include "AudioRingBuffer.h"
16+#include "AudioFilter.h"
17+
18+template<>
19+AudioFilterPEQ3::FilterParameter AudioFilterPEQ3::_profiles[ AudioFilterPEQ3::_profileCount ][ AudioFilterPEQ3::_filterCount ] = {
20+
21+ // Freq Gain Q Freq Gain Q Freq Gain Q
22+ { { 300.0f, 1.0f, 1.0f }, { 1000.0f, 1.0f, 1.0f }, { 4000.0f, 1.0f, 1.0f } }, // flat response (default)
23+ { { 300.0f, 1.0f, 1.0f }, { 1000.0f, 1.0f, 1.0f }, { 4000.0f, 0.1f, 1.0f } }, // treble cut
24+ { { 300.0f, 0.1f, 1.0f }, { 1000.0f, 1.0f, 1.0f }, { 4000.0f, 1.0f, 1.0f } }, // bass cut
25+ { { 300.0f, 1.5f, 0.71f }, { 1000.0f, 0.5f, 1.0f }, { 4000.0f, 1.50f, 0.71f } } // smiley curve
26+};
libraries/audio/src/AudioFilter.h+298−0
@@ -0,0 +1,298 @@
1+//
2+// AudioFilter.h
3+// hifi
4+//
5+// Created by Craig Hansen-Sturm on 8/9/14.
6+// Copyright 2014 High Fidelity, Inc.
7+//
8+// Distributed under the Apache License, Version 2.0.
9+// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
10+//
11+
12+#ifndef hifi_AudioFilter_h
13+#define hifi_AudioFilter_h
14+
15+////////////////////////////////////////////////////////////////////////////////////////////
16+// Implements a standard biquad filter in "Direct Form 1"
17+// Reference http://www.musicdsp.org/files/Audio-EQ-Cookbook.txt
18+//
19+class AudioBiquad {
20+
21+ //
22+ // private data
23+ //
24+ float _a0; // gain
25+ float _a1; // feedforward 1
26+ float _a2; // feedforward 2
27+ float _b1; // feedback 1
28+ float _b2; // feedback 2
29+
30+ float _xm1;
31+ float _xm2;
32+ float _ym1;
33+ float _ym2;
34+
35+public:
36+
37+ //
38+ // ctor/dtor
39+ //
40+ AudioBiquad()
41+ : _xm1(0.)
42+ , _xm2(0.)
43+ , _ym1(0.)
44+ , _ym2(0.) {
45+ setParameters(0.,0.,0.,0.,0.);
46+ }
47+
48+ ~AudioBiquad() {
49+ }
50+
51+ //
52+ // public interface
53+ //
54+ void setParameters( const float a0, const float a1, const float a2, const float b1, const float b2 ) {
55+ _a0 = a0; _a1 = a1; _a2 = a2; _b1 = b1; _b2 = b2;
56+ }
57+
58+ void getParameters( float& a0, float& a1, float& a2, float& b1, float& b2 ) {
59+ a0 = _a0; a1 = _a1; a2 = _a2; b1 = _b1; b2 = _b2;
60+ }
61+
62+ void render( const float* in, float* out, const int frames) {
63+
64+ float x;
65+ float y;
66+
67+ for (int i = 0; i < frames; ++i) {
68+
69+ x = *in++;
70+
71+ // biquad
72+ y = (_a0 * x)
73+ + (_a1 * _xm1)
74+ + (_a2 * _xm2)
75+ - (_b1 * _ym1)
76+ - (_b2 * _ym2);
77+
78+ // update delay line
79+ _xm2 = _xm1;
80+ _xm1 = x;
81+ _ym2 = _ym1;
82+ _ym1 = y;
83+
84+ *out++ = y;
85+ }
86+ }
87+
88+ void reset() {
89+ _xm1 = _xm2 = _ym1 = _ym2 = 0.;
90+ }
91+};
92+
93+////////////////////////////////////////////////////////////////////////////////////////////
94+// Implements a single-band parametric EQ using a biquad "peaking EQ" configuration
95+//
96+// gain > 1.0 boosts the center frequency
97+// gain < 1.0 cuts the center frequency
98+//
99+class AudioParametricEQ {
100+
101+ //
102+ // private data
103+ //
104+ AudioBiquad _kernel;
105+ float _sampleRate;
106+ float _frequency;
107+ float _gain;
108+ float _slope;
109+
110+ // helpers
111+ void updateKernel() {
112+
113+ /*
114+ a0 = 1 + alpha*A
115+ a1 = -2*cos(w0)
116+ a2 = 1 - alpha*A
117+ b1 = -2*cos(w0)
118+ b2 = 1 - alpha/A
119+ */
120+
121+ const float a = _gain;
122+ const float omega = TWO_PI * _frequency / _sampleRate;
123+ const float alpha = 0.5f * sinf(omega) / _slope;
124+ const float gamma = 1.0f / ( 1.0f + (alpha/a) );
125+
126+ const float a0 = 1.0f + (alpha*a);
127+ const float a1 = -2.0f * cosf(omega);
128+ const float a2 = 1.0f - (alpha*a);
129+ const float b1 = a1;
130+ const float b2 = 1.0f - (alpha/a);
131+
132+ _kernel.setParameters( a0*gamma,a1*gamma,a2*gamma,b1*gamma,b2*gamma );
133+ }
134+
135+public:
136+ //
137+ // ctor/dtor
138+ //
139+ AudioParametricEQ() {
140+
141+ setParameters(0.,0.,0.,0.);
142+ updateKernel();
143+ }
144+
145+ ~AudioParametricEQ() {
146+ }
147+
148+ //
149+ // public interface
150+ //
151+ void setParameters( const float sampleRate, const float frequency, const float gain, const float slope ) {
152+
153+ _sampleRate = std::max(sampleRate,1.0f);
154+ _frequency = std::max(frequency,2.0f);
155+ _gain = std::max(gain,0.0f);
156+ _slope = std::max(slope,0.00001f);
157+
158+ updateKernel();
159+ }
160+
161+ void getParameters( float& sampleRate, float& frequency, float& gain, float& slope ) {
162+ sampleRate = _sampleRate; frequency = _frequency; gain = _gain; slope = _slope;
163+ }
164+
165+ void render(const float* in, float* out, const int frames ) {
166+ _kernel.render(in,out,frames);
167+ }
168+
169+ void reset() {
170+ _kernel.reset();
171+ }
172+};
173+
174+////////////////////////////////////////////////////////////////////////////////////////////
175+// Helper/convenience class that implements a bank of EQ objects
176+//
177+template< typename T, const int N>
178+class AudioFilterBank {
179+
180+ //
181+ // types
182+ //
183+ struct FilterParameter {
184+ float _p1;
185+ float _p2;
186+ float _p3;
187+ };
188+
189+ //
190+ // private static data
191+ //
192+ static const int _filterCount = N;
193+ static const int _profileCount = 4;
194+
195+ static FilterParameter _profiles[_profileCount][_filterCount];
196+
197+ //
198+ // private data
199+ //
200+ T _filters[ _filterCount ];
201+ float* _buffer;
202+ float _sampleRate;
203+ uint16_t _frameCount;
204+
205+public:
206+
207+ //
208+ // ctor/dtor
209+ //
210+ AudioFilterBank()
211+ : _buffer(NULL)
212+ , _sampleRate(0.)
213+ , _frameCount(0) {
214+ }
215+
216+ ~AudioFilterBank() {
217+ finalize();
218+ }
219+
220+ //
221+ // public interface
222+ //
223+ void initialize( const float sampleRate, const int frameCount ) {
224+ finalize();
225+
226+ _buffer = (float*)malloc( frameCount * sizeof(float) );
227+ if(!_buffer) {
228+ return;
229+ }
230+
231+ _sampleRate = sampleRate;
232+ _frameCount = frameCount;
233+
234+ reset();
235+ loadProfile(0); // load default profile "flat response" into the bank (see AudioFilter.cpp)
236+ }
237+
238+ void finalize() {
239+ if (_buffer ) {
240+ free (_buffer);
241+ _buffer = NULL;
242+ }
243+ }
244+
245+ void loadProfile( int profileIndex ) {
246+ if (profileIndex >= 0 && profileIndex < _profileCount) {
247+
248+ for (int i = 0; i < _filterCount; ++i) {
249+ FilterParameter p = _profiles[profileIndex][i];
250+
251+ _filters[i].setParameters(_sampleRate,p._p1,p._p2,p._p3);
252+ }
253+ }
254+ }
255+
256+ void render( const float* in, float* out, const int frameCount ) {
257+ for (int i = 0; i < _filterCount; ++i) {
258+ _filters[i].render( in, out, frameCount );
259+ }
260+ }
261+
262+ void render( const int16_t* in, int16_t* out, const int frameCount ) {
263+ if (!_buffer || ( frameCount > _frameCount ))
264+ return;
265+
266+ const int scale = (2 << ((8*sizeof(int16_t))-1));
267+
268+ // convert int16_t to float32 (normalized to -1. ... 1.)
269+ for (int i = 0; i < frameCount; ++i) {
270+ _buffer[i] = ((float)(*in++)) / scale;
271+ }
272+ // for this filter, we share input/output buffers at each stage, but our design does not mandate this
273+ render( _buffer, _buffer, frameCount );
274+
275+ // convert float32 to int16_t
276+ for (int i = 0; i < frameCount; ++i) {
277+ *out++ = (int16_t)(_buffer[i] * scale);
278+ }
279+ }
280+
281+ void reset() {
282+ for (int i = 0; i < _filterCount; ++i ) {
283+ _filters[i].reset();
284+ }
285+ }
286+
287+};
288+
289+////////////////////////////////////////////////////////////////////////////////////////////
290+// Specializations of AudioFilterBank
291+//
292+typedef AudioFilterBank< AudioParametricEQ, 1> AudioFilterPEQ1; // bank with one band of PEQ
293+typedef AudioFilterBank< AudioParametricEQ, 2> AudioFilterPEQ2; // bank with two bands of PEQ
294+typedef AudioFilterBank< AudioParametricEQ, 3> AudioFilterPEQ3; // bank with three bands of PEQ
295+// etc....
296+
297+
298+#endif // hifi_AudioFilter_h
libraries/audio/src/AudioRingBuffer.cpp+2−2
@@ -15,9 +15,9 @@ · 14 unmodified lines
1515
1616 #include <QtCore/QDebug>
1717
18−#include "PacketHeaders.h"
19−#include "AudioRingBuffer.h"
18+#include <PacketHeaders.h>
2019
20+#include "AudioRingBuffer.h"
2121
2222 AudioRingBuffer::AudioRingBuffer(int numFrameSamples, bool randomAccessMode, int numFramesCapacity) :
2323 _frameCapacity(numFramesCapacity),
libraries/audio/src/AudioRingBuffer.h+2−4
@@ -15,12 +15,10 @@ · 14 unmodified lines
1515 #include <limits>
1616 #include <stdint.h>
1717
18−#include <glm/glm.hpp>
19−
2018 #include <QtCore/QIODevice>
2119
22−#include "NodeData.h"
23−#include "SharedUtil.h"
20+#include <SharedUtil.h>
21+#include <NodeData.h>
2422
2523 const int SAMPLE_RATE = 24000;
2624
libraries/audio/src/InboundAudioStream.cpp+2−0
@@ -9,6 +9,8 @@ · 8 unmodified lines
99 // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
1010 //
1111
12+#include <glm/glm.hpp>
13+
1214 #include "InboundAudioStream.h"
1315 #include "PacketHeaders.h"
1416
libraries/avatars/CMakeLists.txt+7−25
@@ -1,30 +1,12 @@
1−set(ROOT_DIR ../..)
2−set(MACRO_DIR "${ROOT_DIR}/cmake/macros")
3−
4−# setup for find modules
5−set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/../../cmake/modules/")
6−
71 set(TARGET_NAME avatars)
82
9−find_package(Qt5Script REQUIRED)
10−
11−include(${MACRO_DIR}/SetupHifiLibrary.cmake)
12−setup_hifi_library(${TARGET_NAME})
13−
14−include(${MACRO_DIR}/IncludeGLM.cmake)
15−include_glm(${TARGET_NAME} "${ROOT_DIR}")
16−
17−include(${MACRO_DIR}/LinkHifiLibrary.cmake)
18−link_hifi_library(shared ${TARGET_NAME} "${ROOT_DIR}")
3+# use setup_hifi_library macro to setup our project and link appropriate Qt modules
4+setup_hifi_library(Network Script)
195
20−# link in the hifi voxels library
21−link_hifi_library(octree ${TARGET_NAME} "${ROOT_DIR}")
22−link_hifi_library(voxels ${TARGET_NAME} "${ROOT_DIR}")
23−link_hifi_library(networking ${TARGET_NAME} "${ROOT_DIR}")
6+include_glm()
247
25−target_link_libraries(${TARGET_NAME} Qt5::Script)
8+link_hifi_libraries(shared octree voxels networking)
9+include_hifi_library_headers(fbx)
2610
27−# add a definition for ssize_t so that windows doesn't bail
28−if (WIN32)
29− add_definitions(-Dssize_t=long)
30−endif ()
11+# call macro to link our dependencies and bubble them up via a property on our target
12+link_shared_dependencies()
libraries/avatars/src/AvatarData.cpp+1−1
@@ -22,7 +22,7 @@ · 21 unmodified lines
2222 #include <NetworkAccessManager.h>
2323 #include <NodeList.h>
2424 #include <PacketHeaders.h>
25−#include <SharedUtil.h>
25+#include <GLMHelpers.h>
2626 #include <StreamUtils.h>
2727 #include <UUID.h>
2828 #include <VoxelConstants.h>
libraries/avatars/src/HeadData.cpp+2−2
@@ -11,13 +11,13 @@ · 10 unmodified lines
1111
1212 #include <glm/gtx/quaternion.hpp>
1313
14+#include <FBXReader.h>
15+#include <GLMHelpers.h>
1416 #include <OctreeConstants.h>
1517
1618 #include "AvatarData.h"
1719 #include "HeadData.h"
1820
19−#include "../fbx/src/FBXReader.h"
20−
2121 HeadData::HeadData(AvatarData* owningAvatar) :
2222 _baseYaw(0.0f),
2323 _basePitch(0.0f),
libraries/avatars/src/Referential.cpp+1−1
@@ -9,7 +9,7 @@ · 8 unmodified lines
99 // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
1010 //
1111
12−#include <SharedUtil.h>
12+#include <GLMHelpers.h>
1313
1414 #include "AvatarData.h"
1515 #include "Referential.h"
libraries/embedded-webserver/CMakeLists.txt+4−11
@@ -1,14 +1,7 @@
1−set(ROOT_DIR ../..)
2−set(MACRO_DIR "${ROOT_DIR}/cmake/macros")
3−
4−# setup for find modules
5−set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/../../cmake/modules/")
6−
71 set(TARGET_NAME embedded-webserver)
82
9−find_package(Qt5Network REQUIRED)
10−
11−include(${MACRO_DIR}/SetupHifiLibrary.cmake)
12−setup_hifi_library(${TARGET_NAME})
3+# use setup_hifi_library macro to setup our project and link appropriate Qt modules
4+setup_hifi_library(Network)
135
14−target_link_libraries(${TARGET_NAME} Qt5::Network)
6+# call macro to link our dependencies and bubble them up via a property on our target
7+link_shared_dependencies()
libraries/entities/CMakeLists.txt+6−34
@@ -1,39 +1,11 @@
1−set(ROOT_DIR ../..)
2−set(MACRO_DIR "${ROOT_DIR}/cmake/macros")
3−
4−# setup for find modules
5−set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/../../cmake/modules/")
6−
71 set(TARGET_NAME entities)
82
9−find_package(Qt5Widgets REQUIRED)
10−
11−include(${MACRO_DIR}/AutoMTC.cmake)
12−auto_mtc(${TARGET_NAME} "${ROOT_DIR}")
13−
14−include(${MACRO_DIR}/SetupHifiLibrary.cmake)
15−setup_hifi_library(${TARGET_NAME} "${AUTOMTC_SRC}")
16−
17−include(${MACRO_DIR}/IncludeGLM.cmake)
18−include_glm(${TARGET_NAME} "${ROOT_DIR}")
19−
20−include(${MACRO_DIR}/LinkHifiLibrary.cmake)
21−link_hifi_library(shared ${TARGET_NAME} "${ROOT_DIR}")
22−link_hifi_library(octree ${TARGET_NAME} "${ROOT_DIR}")
23−link_hifi_library(fbx ${TARGET_NAME} "${ROOT_DIR}")
24−link_hifi_library(networking ${TARGET_NAME} "${ROOT_DIR}")
25−link_hifi_library(animation ${TARGET_NAME} "${ROOT_DIR}")
26−
27−# for streamable
28−link_hifi_library(metavoxels ${TARGET_NAME} "${ROOT_DIR}")
3+# use setup_hifi_library macro to setup our project and link appropriate Qt modules
4+setup_hifi_library(Network Script)
295
30−# link ZLIB
31−find_package(ZLIB)
6+include_glm()
327
33−include_directories(SYSTEM "${ZLIB_INCLUDE_DIRS}")
34−target_link_libraries(${TARGET_NAME} "${ZLIB_LIBRARIES}" Qt5::Widgets)
8+link_hifi_libraries(shared octree fbx networking animation)
359
36−# add a definition for ssize_t so that windows doesn't bail
37−if (WIN32)
38− add_definitions(-Dssize_t=long)
39−endif ()
10+# call macro to link our dependencies and bubble them up via a property on our target
11+link_shared_dependencies()
libraries/fbx/CMakeLists.txt+8−23
@@ -1,30 +1,15 @@
1−set(ROOT_DIR ../..)
2−set(MACRO_DIR "${ROOT_DIR}/cmake/macros")
3−
4−# setup for find modules
5−set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/../../cmake/modules/")
6−
71 set(TARGET_NAME fbx)
82
9−include(${MACRO_DIR}/SetupHifiLibrary.cmake)
10−setup_hifi_library(${TARGET_NAME})
11−
12−include(${MACRO_DIR}/IncludeGLM.cmake)
13−include_glm(${TARGET_NAME} "${ROOT_DIR}")
3+# use setup_hifi_library macro to setup our project and link appropriate Qt modules
4+setup_hifi_library()
145
15−include(${MACRO_DIR}/LinkHifiLibrary.cmake)
16−link_hifi_library(shared ${TARGET_NAME} "${ROOT_DIR}")
17−link_hifi_library(networking ${TARGET_NAME} "${ROOT_DIR}")
18−link_hifi_library(octree ${TARGET_NAME} "${ROOT_DIR}")
19−link_hifi_library(voxels ${TARGET_NAME} "${ROOT_DIR}")
6+include_glm()
207
21−# link ZLIB
22−find_package(ZLIB)
8+link_hifi_libraries(shared networking octree voxels)
239
10+find_package(ZLIB REQUIRED)
2411 include_directories(SYSTEM "${ZLIB_INCLUDE_DIRS}")
25−target_link_libraries(${TARGET_NAME} "${ZLIB_LIBRARIES}" Qt5::Widgets)
12+list(APPEND ${TARGET_NAME}_LIBRARIES_TO_LINK "${ZLIB_LIBRARIES}")
2613
27−# add a definition for ssize_t so that windows doesn't bail
28−if (WIN32)
29− add_definitions(-Dssize_t=long)
30−endif ()
14+# call macro to link our dependencies and bubble them up via a property on our target
15+link_shared_dependencies()
libraries/fbx/src/FBXReader.cpp+1−1
@@ -23,9 +23,9 @@ · 22 unmodified lines
2323 #include <glm/gtx/transform.hpp>
2424
2525 #include <GeometryUtil.h>
26+#include <GLMHelpers.h>
2627 #include <OctalCode.h>
2728 #include <Shape.h>
28−#include <SharedUtil.h>
2929
3030 #include <VoxelTree.h>
3131
libraries/metavoxels/CMakeLists.txt+7−22
@@ -1,29 +1,14 @@
1−set(ROOT_DIR ../..)
2−set(MACRO_DIR "${ROOT_DIR}/cmake/macros")
3−
4−# setup for find modules
5−set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/../../cmake/modules/")
6−
71 set(TARGET_NAME metavoxels)
82
9−find_package(Qt5 COMPONENTS Network Script Widgets)
3+auto_mtc()
104
11−include(${MACRO_DIR}/AutoMTC.cmake)
12−auto_mtc(${TARGET_NAME} "${ROOT_DIR}")
13−
14−include(${MACRO_DIR}/SetupHifiLibrary.cmake)
15−setup_hifi_library(${TARGET_NAME} "${AUTOMTC_SRC}")
5+# use setup_hifi_library macro to setup our project and link appropriate Qt modules
6+setup_hifi_library(Network Script Widgets)
167
178 # link in the networking library
18−include(${MACRO_DIR}/LinkHifiLibrary.cmake)
19−link_hifi_library(networking ${TARGET_NAME} "${ROOT_DIR}")
20−
21−include(${MACRO_DIR}/IncludeGLM.cmake)
22−include_glm(${TARGET_NAME} "${ROOT_DIR}")
9+link_hifi_libraries(shared networking)
2310
24−target_link_libraries(${TARGET_NAME} Qt5::Network Qt5::Widgets Qt5::Script)
11+include_glm()
2512
26−# add a definition for ssize_t so that windows doesn't bail
27−if (WIN32)
28− add_definitions(-Dssize_t=long)
29−endif ()
13+# call macro to link our dependencies and bubble them up via a property on our target
14+link_shared_dependencies()
libraries/metavoxels/src/AttributeRegistry.cpp+297−65
@@ -489,22 +489,92 @@ HeightfieldData::HeightfieldData(const QByteArray& contents) : · 488 unmodified lines
489489 _contents(contents) {
490490 }
491491
492−const int BYTES_PER_PIXEL = 3;
492+HeightfieldData::HeightfieldData(Bitstream& in, int bytes, bool color) {
493+ read(in, bytes, color);
494+}
493495
494−HeightfieldData::HeightfieldData(Bitstream& in, int bytes, bool color) :
495− _encoded(in.readAligned(bytes)) {
496−
497− QImage image = QImage::fromData(_encoded).convertToFormat(QImage::Format_RGB888);
498− if (color) {
499− _contents.resize(image.width() * image.height() * BYTES_PER_PIXEL);
500− memcpy(_contents.data(), image.constBits(), _contents.size());
496+enum HeightfieldImage { NULL_HEIGHTFIELD_IMAGE, NORMAL_HEIGHTFIELD_IMAGE, DEFLATED_HEIGHTFIELD_IMAGE };
497+
498+static QByteArray encodeHeightfieldImage(const QImage& image) {
499+ if (image.isNull()) {
500+ return QByteArray(1, NULL_HEIGHTFIELD_IMAGE);
501+ }
502+ QBuffer buffer;
503+ buffer.open(QIODevice::WriteOnly);
504+ const int JPEG_ENCODE_THRESHOLD = 16;
505+ if (image.width() >= JPEG_ENCODE_THRESHOLD && image.height() >= JPEG_ENCODE_THRESHOLD) {
506+ qint32 offsetX = image.offset().x(), offsetY = image.offset().y();
507+ buffer.write((char*)&offsetX, sizeof(qint32));
508+ buffer.write((char*)&offsetY, sizeof(qint32));
509+ image.save(&buffer, "JPG");
510+ return QByteArray(1, DEFLATED_HEIGHTFIELD_IMAGE) + qCompress(buffer.data());
501511
502512 } else {
503− _contents.resize(image.width() * image.height());
504− char* dest = _contents.data();
505− for (const uchar* src = image.constBits(), *end = src + _contents.size() * BYTES_PER_PIXEL;
506− src != end; src += BYTES_PER_PIXEL) {
507− *dest++ = *src;
513+ buffer.putChar(NORMAL_HEIGHTFIELD_IMAGE);
514+ image.save(&buffer, "PNG");
515+ return buffer.data();
516+ }
517+}
518+
519+const QImage decodeHeightfieldImage(const QByteArray& data) {
520+ switch (data.at(0)) {
521+ case NULL_HEIGHTFIELD_IMAGE:
522+ default:
523+ return QImage();
524+
525+ case NORMAL_HEIGHTFIELD_IMAGE:
526+ return QImage::fromData(QByteArray::fromRawData(data.constData() + 1, data.size() - 1));
527+
528+ case DEFLATED_HEIGHTFIELD_IMAGE: {
529+ QByteArray inflated = qUncompress((const uchar*)data.constData() + 1, data.size() - 1);
530+ const int OFFSET_SIZE = sizeof(qint32) * 2;
531+ QImage image = QImage::fromData((const uchar*)inflated.constData() + OFFSET_SIZE, inflated.size() - OFFSET_SIZE);
532+ const qint32* offsets = (const qint32*)inflated.constData();
533+ image.setOffset(QPoint(offsets[0], offsets[1]));
534+ return image;
535+ }
536+ }
537+}
538+
539+HeightfieldData::HeightfieldData(Bitstream& in, int bytes, const HeightfieldDataPointer& reference, bool color) {
540+ if (!reference) {
541+ read(in, bytes, color);
542+ return;
543+ }
544+ QMutexLocker locker(&reference->_encodedDeltaMutex);
545+ reference->_encodedDelta = in.readAligned(bytes);
546+ reference->_deltaData = this;
547+ _contents = reference->_contents;
548+ QImage image = decodeHeightfieldImage(reference->_encodedDelta);
549+ if (image.isNull()) {
550+ return;
551+ }
552+ QPoint offset = image.offset();
553+ image = image.convertToFormat(QImage::Format_RGB888);
554+ if (offset.x() == 0) {
555+ set(image, color);
556+ return;
557+ }
558+ int minX = offset.x() - 1;
559+ int minY = offset.y() - 1;
560+ if (color) {
561+ int size = glm::sqrt(_contents.size() / (float)COLOR_BYTES);
562+ char* dest = _contents.data() + (minY * size + minX) * COLOR_BYTES;
563+ int destStride = size * COLOR_BYTES;
564+ int srcStride = image.width() * COLOR_BYTES;
565+ for (int y = 0; y < image.height(); y++) {
566+ memcpy(dest, image.constScanLine(y), srcStride);
567+ dest += destStride;
568+ }
569+ } else {
570+ int size = glm::sqrt((float)_contents.size());
571+ char* lineDest = _contents.data() + minY * size + minX;
572+ for (int y = 0; y < image.height(); y++) {
573+ const uchar* src = image.constScanLine(y);
574+ for (char* dest = lineDest, *end = dest + image.width(); dest != end; dest++, src += COLOR_BYTES) {
575+ *dest = *src;
576+ }
577+ lineDest += size;
508578 }
509579 }
510580 }
@@ -514,7 +584,7 @@ void HeightfieldData::write(Bitstream& out, bool color) { · 3 unmodified lines
514584 if (_encoded.isEmpty()) {
515585 QImage image;
516586 if (color) {
517− int size = glm::sqrt(_contents.size() / (float)BYTES_PER_PIXEL);
587+ int size = glm::sqrt(_contents.size() / (float)COLOR_BYTES);
518588 image = QImage((uchar*)_contents.data(), size, size, QImage::Format_RGB888);
519589 } else {
520590 int size = glm::sqrt((float)_contents.size());
@@ -526,38 +596,170 @@ void HeightfieldData::write(Bitstream& out, bool color) { · 5 unmodified lines
526596 *dest++ = *src;
527597 }
528598 }
529− QBuffer buffer(&_encoded);
530− buffer.open(QIODevice::WriteOnly);
531− image.save(&buffer, "JPG");
599+ _encoded = encodeHeightfieldImage(image);
532600 }
533601 out << _encoded.size();
534602 out.writeAligned(_encoded);
535603 }
536604
605+void HeightfieldData::writeDelta(Bitstream& out, const HeightfieldDataPointer& reference, bool color) {
606+ if (!reference || reference->getContents().size() != _contents.size()) {
607+ write(out, color);
608+ return;
609+ }
610+ QMutexLocker locker(&reference->_encodedDeltaMutex);
611+ if (reference->_encodedDelta.isEmpty() || reference->_deltaData != this) {
612+ QImage image;
613+ int minX, minY;
614+ if (color) {
615+ int size = glm::sqrt(_contents.size() / (float)COLOR_BYTES);
616+ minX = size;
617+ minY = size;
618+ int maxX = -1, maxY = -1;
619+ const char* src = _contents.constData();
620+ const char* ref = reference->_contents.constData();
621+ for (int y = 0; y < size; y++) {
622+ bool difference = false;
623+ for (int x = 0; x < size; x++, src += COLOR_BYTES, ref += COLOR_BYTES) {
624+ if (src[0] != ref[0] || src[1] != ref[1] || src[2] != ref[2]) {
625+ minX = qMin(minX, x);
626+ maxX = qMax(maxX, x);
627+ difference = true;
628+ }
629+ }
630+ if (difference) {
631+ minY = qMin(minY, y);
632+ maxY = qMax(maxY, y);
633+ }
634+ }
635+ if (maxX >= minX) {
636+ int width = maxX - minX + 1;
637+ int height = maxY - minY + 1;
638+ image = QImage(width, height, QImage::Format_RGB888);
639+ src = _contents.constData() + (minY * size + minX) * COLOR_BYTES;
640+ int srcStride = size * COLOR_BYTES;
641+ int destStride = width * COLOR_BYTES;
642+ for (int y = 0; y < height; y++) {
643+ memcpy(image.scanLine(y), src, destStride);
644+ src += srcStride;
645+ }
646+ }
647+ } else {
648+ int size = glm::sqrt((float)_contents.size());
649+ minX = size;
650+ minY = size;
651+ int maxX = -1, maxY = -1;
652+ const char* src = _contents.constData();
653+ const char* ref = reference->_contents.constData();
654+ for (int y = 0; y < size; y++) {
655+ bool difference = false;
656+ for (int x = 0; x < size; x++) {
657+ if (*src++ != *ref++) {
658+ minX = qMin(minX, x);
659+ maxX = qMax(maxX, x);
660+ difference = true;
661+ }
662+ }
663+ if (difference) {
664+ minY = qMin(minY, y);
665+ maxY = qMax(maxY, y);
666+ }
667+ }
668+ if (maxX >= minX) {
669+ int width = qMax(maxX - minX + 1, 0);
670+ int height = qMax(maxY - minY + 1, 0);
671+ image = QImage(width, height, QImage::Format_RGB888);
672+ const uchar* lineSrc = (const uchar*)_contents.constData() + minY * size + minX;
673+ for (int y = 0; y < height; y++) {
674+ uchar* dest = image.scanLine(y);
675+ for (const uchar* src = lineSrc, *end = src + width; src != end; src++) {
676+ *dest++ = *src;
677+ *dest++ = *src;
678+ *dest++ = *src;
679+ }
680+ lineSrc += size;
681+ }
682+ }
683+ }
684+ image.setOffset(QPoint(minX + 1, minY + 1));
685+ reference->_encodedDelta = encodeHeightfieldImage(image);
686+ reference->_deltaData = this;
687+ }
688+ out << reference->_encodedDelta.size();
689+ out.writeAligned(reference->_encodedDelta);
690+}
691+
692+void HeightfieldData::read(Bitstream& in, int bytes, bool color) {
693+ set(decodeHeightfieldImage(_encoded = in.readAligned(bytes)).convertToFormat(QImage::Format_RGB888), color);
694+}
695+
696+void HeightfieldData::set(const QImage& image, bool color) {
697+ if (color) {
698+ _contents.resize(image.width() * image.height() * COLOR_BYTES);
699+ memcpy(_contents.data(), image.constBits(), _contents.size());
700+
701+ } else {
702+ _contents.resize(image.width() * image.height());
703+ char* dest = _contents.data();
704+ for (const uchar* src = image.constBits(), *end = src + _contents.size() * COLOR_BYTES;
705+ src != end; src += COLOR_BYTES) {
706+ *dest++ = *src;
707+ }
708+ }
709+}
710+
537711 HeightfieldAttribute::HeightfieldAttribute(const QString& name) :
538712 InlineAttribute<HeightfieldDataPointer>(name) {
539713 }
540714
541715 void HeightfieldAttribute::read(Bitstream& in, void*& value, bool isLeaf) const {
542− if (isLeaf) {
543− int size;
544− in >> size;
545− if (size == 0) {
546− *(HeightfieldDataPointer*)&value = HeightfieldDataPointer();
547− } else {
548− *(HeightfieldDataPointer*)&value = HeightfieldDataPointer(new HeightfieldData(in, size, false));
549− }
716+ if (!isLeaf) {
717+ return;
718+ }
719+ int size;
720+ in >> size;
721+ if (size == 0) {
722+ *(HeightfieldDataPointer*)&value = HeightfieldDataPointer();
723+ } else {
724+ *(HeightfieldDataPointer*)&value = HeightfieldDataPointer(new HeightfieldData(in, size, false));
550725 }
551726 }
552727
553728 void HeightfieldAttribute::write(Bitstream& out, void* value, bool isLeaf) const {
554− if (isLeaf) {
555− HeightfieldDataPointer data = decodeInline<HeightfieldDataPointer>(value);
556− if (data) {
557− data->write(out, false);
558− } else {
559− out << 0;
560− }
729+ if (!isLeaf) {
730+ return;
731+ }
732+ HeightfieldDataPointer data = decodeInline<HeightfieldDataPointer>(value);
733+ if (data) {
734+ data->write(out, false);
735+ } else {
736+ out << 0;
737+ }
738+}
739+
740+void HeightfieldAttribute::readDelta(Bitstream& in, void*& value, void* reference, bool isLeaf) const {
741+ if (!isLeaf) {
742+ return;
743+ }
744+ int size;
745+ in >> size;
746+ if (size == 0) {
747+ *(HeightfieldDataPointer*)&value = HeightfieldDataPointer();
748+ } else {
749+ *(HeightfieldDataPointer*)&value = HeightfieldDataPointer(new HeightfieldData(
750+ in, size, decodeInline<HeightfieldDataPointer>(reference), false));
751+ }
752+}
753+
754+void HeightfieldAttribute::writeDelta(Bitstream& out, void* value, void* reference, bool isLeaf) const {
755+ if (!isLeaf) {
756+ return;
757+ }
758+ HeightfieldDataPointer data = decodeInline<HeightfieldDataPointer>(value);
759+ if (data) {
760+ data->writeDelta(out, decodeInline<HeightfieldDataPointer>(reference), false);
761+ } else {
762+ out << 0;
561763 }
562764 }
563765
@@ -635,25 +837,53 @@ HeightfieldColorAttribute::HeightfieldColorAttribute(const QString& name) : · 71 unmodified lines
635837 }
636838
637839 void HeightfieldColorAttribute::read(Bitstream& in, void*& value, bool isLeaf) const {
638− if (isLeaf) {
639− int size;
640− in >> size;
641− if (size == 0) {
642− *(HeightfieldDataPointer*)&value = HeightfieldDataPointer();
643− } else {
644− *(HeightfieldDataPointer*)&value = HeightfieldDataPointer(new HeightfieldData(in, size, true));
645− }
840+ if (!isLeaf) {
841+ return;
842+ }
843+ int size;
844+ in >> size;
845+ if (size == 0) {
846+ *(HeightfieldDataPointer*)&value = HeightfieldDataPointer();
847+ } else {
848+ *(HeightfieldDataPointer*)&value = HeightfieldDataPointer(new HeightfieldData(in, size, true));
646849 }
647850 }
648851
649852 void HeightfieldColorAttribute::write(Bitstream& out, void* value, bool isLeaf) const {
650− if (isLeaf) {
651− HeightfieldDataPointer data = decodeInline<HeightfieldDataPointer>(value);
652− if (data) {
653− data->write(out, true);
654− } else {
655− out << 0;
656− }
853+ if (!isLeaf) {
854+ return;
855+ }
856+ HeightfieldDataPointer data = decodeInline<HeightfieldDataPointer>(value);
857+ if (data) {
858+ data->write(out, true);
859+ } else {
860+ out << 0;
861+ }
862+}
863+
864+void HeightfieldColorAttribute::readDelta(Bitstream& in, void*& value, void* reference, bool isLeaf) const {
865+ if (!isLeaf) {
866+ return;
867+ }
868+ int size;
869+ in >> size;
870+ if (size == 0) {
871+ *(HeightfieldDataPointer*)&value = HeightfieldDataPointer();
872+ } else {
873+ *(HeightfieldDataPointer*)&value = HeightfieldDataPointer(new HeightfieldData(
874+ in, size, decodeInline<HeightfieldDataPointer>(reference), true));
875+ }
876+}
877+
878+void HeightfieldColorAttribute::writeDelta(Bitstream& out, void* value, void* reference, bool isLeaf) const {
879+ if (!isLeaf) {
880+ return;
881+ }
882+ HeightfieldDataPointer data = decodeInline<HeightfieldDataPointer>(value);
883+ if (data) {
884+ data->writeDelta(out, decodeInline<HeightfieldDataPointer>(reference), true);
885+ } else {
886+ out << 0;
657887 }
658888 }
659889
@@ -669,8 +899,8 @@ bool HeightfieldColorAttribute::merge(void*& parent, void* children[], bool post · 9 unmodified lines
669899 *(HeightfieldDataPointer*)&parent = HeightfieldDataPointer();
670900 return true;
671901 }
672− int size = glm::sqrt(maxSize / (float)BYTES_PER_PIXEL);
673− QByteArray contents(size * size * BYTES_PER_PIXEL, 0);
902+ int size = glm::sqrt(maxSize / (float)HeightfieldData::COLOR_BYTES);
903+ QByteArray contents(size * size * HeightfieldData::COLOR_BYTES, 0);
674904 int halfSize = size / 2;
675905 for (int i = 0; i < MERGE_COUNT; i++) {
676906 HeightfieldDataPointer child = decodeInline<HeightfieldDataPointer>(children[i]);
@@ -678,7 +908,7 @@ bool HeightfieldColorAttribute::merge(void*& parent, void* children[], bool post · 1 unmodified line
678908 continue;
679909 }
680910 const QByteArray& childContents = child->getContents();
681− int childSize = glm::sqrt(childContents.size() / (float)BYTES_PER_PIXEL);
911+ int childSize = glm::sqrt(childContents.size() / (float)HeightfieldData::COLOR_BYTES);
682912 const int INDEX_MASK = 1;
683913 int xIndex = i & INDEX_MASK;
684914 const int Y_SHIFT = 1;
@@ -688,24 +918,25 @@ bool HeightfieldColorAttribute::merge(void*& parent, void* children[], bool post · 3 unmodified lines
688918 }
689919 int Z_SHIFT = 2;
690920 int zIndex = (i >> Z_SHIFT) & INDEX_MASK;
691− char* dest = contents.data() + ((zIndex * halfSize * size) + (xIndex * halfSize)) * BYTES_PER_PIXEL;
921+ char* dest = contents.data() + ((zIndex * halfSize * size) + (xIndex * halfSize)) * HeightfieldData::COLOR_BYTES;
692922 uchar* src = (uchar*)childContents.data();
693− int childStride = childSize * BYTES_PER_PIXEL;
694− int stride = size * BYTES_PER_PIXEL;
923+ int childStride = childSize * HeightfieldData::COLOR_BYTES;
924+ int stride = size * HeightfieldData::COLOR_BYTES;
695925 int halfStride = stride / 2;
696− int childStep = 2 * BYTES_PER_PIXEL;
697− int redOffset3 = childStride + BYTES_PER_PIXEL;
698− int greenOffset1 = BYTES_PER_PIXEL + 1;
926+ int childStep = 2 * HeightfieldData::COLOR_BYTES;
927+ int redOffset3 = childStride + HeightfieldData::COLOR_BYTES;
928+ int greenOffset1 = HeightfieldData::COLOR_BYTES + 1;
699929 int greenOffset2 = childStride + 1;
700− int greenOffset3 = childStride + BYTES_PER_PIXEL + 1;
701− int blueOffset1 = BYTES_PER_PIXEL + 2;
930+ int greenOffset3 = childStride + HeightfieldData::COLOR_BYTES + 1;
931+ int blueOffset1 = HeightfieldData::COLOR_BYTES + 2;
702932 int blueOffset2 = childStride + 2;
703− int blueOffset3 = childStride + BYTES_PER_PIXEL + 2;
933+ int blueOffset3 = childStride + HeightfieldData::COLOR_BYTES + 2;
704934 if (childSize == size) {
705935 // simple case: one destination value for four child values
706936 for (int z = 0; z < halfSize; z++) {
707− for (char* end = dest + halfSize * BYTES_PER_PIXEL; dest != end; src += childStep) {
708− *dest++ = ((int)src[0] + (int)src[BYTES_PER_PIXEL] + (int)src[childStride] + (int)src[redOffset3]) >> 2;
937+ for (char* end = dest + halfSize * HeightfieldData::COLOR_BYTES; dest != end; src += childStep) {
938+ *dest++ = ((int)src[0] + (int)src[HeightfieldData::COLOR_BYTES] +
939+ (int)src[childStride] + (int)src[redOffset3]) >> 2;
709940 *dest++ = ((int)src[1] + (int)src[greenOffset1] + (int)src[greenOffset2] + (int)src[greenOffset3]) >> 2;
710941 *dest++ = ((int)src[2] + (int)src[blueOffset1] + (int)src[blueOffset2] + (int)src[blueOffset3]) >> 2;
711942 }
@@ -717,13 +948,14 @@ bool HeightfieldColorAttribute::merge(void*& parent, void* children[], bool post · 5 unmodified lines
717948 int halfChildSize = childSize / 2;
718949 int destPerSrc = size / childSize;
719950 for (int z = 0; z < halfChildSize; z++) {
720− for (uchar* end = src + childSize * BYTES_PER_PIXEL; src != end; src += childStep) {
721− *dest++ = ((int)src[0] + (int)src[BYTES_PER_PIXEL] + (int)src[childStride] + (int)src[redOffset3]) >> 2;
951+ for (uchar* end = src + childSize * HeightfieldData::COLOR_BYTES; src != end; src += childStep) {
952+ *dest++ = ((int)src[0] + (int)src[HeightfieldData::COLOR_BYTES] +
953+ (int)src[childStride] + (int)src[redOffset3]) >> 2;
722954 *dest++ = ((int)src[1] + (int)src[greenOffset1] + (int)src[greenOffset2] + (int)src[greenOffset3]) >> 2;
723955 *dest++ = ((int)src[2] + (int)src[blueOffset1] + (int)src[blueOffset2] + (int)src[blueOffset3]) >> 2;
724956 for (int j = 1; j < destPerSrc; j++) {
725− memcpy(dest, dest - BYTES_PER_PIXEL, BYTES_PER_PIXEL);
726− dest += BYTES_PER_PIXEL;
957+ memcpy(dest, dest - HeightfieldData::COLOR_BYTES, HeightfieldData::COLOR_BYTES);
958+ dest += HeightfieldData::COLOR_BYTES;
727959 }
728960 }
729961 dest += halfStride;
libraries/metavoxels/src/AttributeRegistry.h+22−4
@@ -28,6 +28,7 @@ class QScriptEngine; · 27 unmodified lines
2828 class QScriptValue;
2929
3030 class Attribute;
31+class HeightfieldData;
3132 class MetavoxelData;
3233 class MetavoxelLOD;
3334 class MetavoxelNode;
@@ -421,26 +422,37 @@ public: · 387 unmodified lines
421422 virtual AttributeValue inherit(const AttributeValue& parentValue) const;
422423 };
423424
425+typedef QExplicitlySharedDataPointer<HeightfieldData> HeightfieldDataPointer;
426+
424427 /// Contains a block of heightfield data.
425428 class HeightfieldData : public QSharedData {
426429 public:
427430
431+ static const int COLOR_BYTES = 3;
432+
428433 HeightfieldData(const QByteArray& contents);
429434 HeightfieldData(Bitstream& in, int bytes, bool color);
430−
435+ HeightfieldData(Bitstream& in, int bytes, const HeightfieldDataPointer& reference, bool color);
436+
431437 const QByteArray& getContents() const { return _contents; }
432438
433439 void write(Bitstream& out, bool color);
440+ void writeDelta(Bitstream& out, const HeightfieldDataPointer& reference, bool color);
434441
435442 private:
436443
444+ void read(Bitstream& in, int bytes, bool color);
445+ void set(const QImage& image, bool color);
446+
437447 QByteArray _contents;
438448 QByteArray _encoded;
439449 QMutex _encodedMutex;
450+
451+ HeightfieldDataPointer _deltaData;
452+ QByteArray _encodedDelta;
453+ QMutex _encodedDeltaMutex;
440454 };
441455
442−typedef QExplicitlySharedDataPointer<HeightfieldData> HeightfieldDataPointer;
443−
444456 /// An attribute that stores heightfield data.
445457 class HeightfieldAttribute : public InlineAttribute<HeightfieldDataPointer> {
446458 Q_OBJECT
@@ -451,7 +463,10 @@ public: · 4 unmodified lines
451463
452464 virtual void read(Bitstream& in, void*& value, bool isLeaf) const;
453465 virtual void write(Bitstream& out, void* value, bool isLeaf) const;
454−
466+
467+ virtual void readDelta(Bitstream& in, void*& value, void* reference, bool isLeaf) const;
468+ virtual void writeDelta(Bitstream& out, void* value, void* reference, bool isLeaf) const;
469+
455470 virtual bool merge(void*& parent, void* children[], bool postRead = false) const;
456471 };
457472
@@ -466,6 +481,9 @@ public: · 8 unmodified lines
466481 virtual void read(Bitstream& in, void*& value, bool isLeaf) const;
467482 virtual void write(Bitstream& out, void* value, bool isLeaf) const;
468483
484+ virtual void readDelta(Bitstream& in, void*& value, void* reference, bool isLeaf) const;
485+ virtual void writeDelta(Bitstream& out, void* value, void* reference, bool isLeaf) const;
486+
469487 virtual bool merge(void*& parent, void* children[], bool postRead = false) const;
470488 };
471489
libraries/metavoxels/src/MetavoxelMessages.cpp+174−0
@@ -318,3 +318,177 @@ SetDataEdit::SetDataEdit(const glm::vec3& minimum, const MetavoxelData& data, bo · 317 unmodified lines
318318 void SetDataEdit::apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const {
319319 data.set(minimum, this->data, blend);
320320 }
321+
322+PaintHeightfieldHeightEdit::PaintHeightfieldHeightEdit(const glm::vec3& position, float radius, float height) :
323+ position(position),
324+ radius(radius),
325+ height(height) {
326+}
327+
328+class PaintHeightfieldHeightEditVisitor : public MetavoxelVisitor {
329+public:
330+
331+ PaintHeightfieldHeightEditVisitor(const PaintHeightfieldHeightEdit& edit);
332+
333+ virtual int visit(MetavoxelInfo& info);
334+
335+private:
336+
337+ PaintHeightfieldHeightEdit _edit;
338+ Box _bounds;
339+};
340+
341+PaintHeightfieldHeightEditVisitor::PaintHeightfieldHeightEditVisitor(const PaintHeightfieldHeightEdit& edit) :
342+ MetavoxelVisitor(QVector<AttributePointer>() << AttributeRegistry::getInstance()->getHeightfieldAttribute(),
343+ QVector<AttributePointer>() << AttributeRegistry::getInstance()->getHeightfieldAttribute()),
344+ _edit(edit) {
345+
346+ glm::vec3 extents(_edit.radius, _edit.radius, _edit.radius);
347+ _bounds = Box(_edit.position - extents, _edit.position + extents);
348+}
349+
350+int PaintHeightfieldHeightEditVisitor::visit(MetavoxelInfo& info) {
351+ if (!info.getBounds().intersects(_bounds)) {
352+ return STOP_RECURSION;
353+ }
354+ if (!info.isLeaf) {
355+ return DEFAULT_ORDER;
356+ }
357+ HeightfieldDataPointer pointer = info.inputValues.at(0).getInlineValue<HeightfieldDataPointer>();
358+ if (!pointer) {
359+ return STOP_RECURSION;
360+ }
361+ QByteArray contents(pointer->getContents());
362+ int size = glm::sqrt((float)contents.size());
363+ int highest = size - 1;
364+ float heightScale = size / info.size;
365+
366+ glm::vec3 center = (_edit.position - info.minimum) * heightScale;
367+ float scaledRadius = _edit.radius * heightScale;
368+ glm::vec3 extents(scaledRadius, scaledRadius, scaledRadius);
369+
370+ glm::vec3 start = glm::floor(center - extents);
371+ glm::vec3 end = glm::ceil(center + extents);
372+
373+ // raise/lower all points within the radius
374+ float z = qMax(start.z, 0.0f);
375+ float startX = qMax(start.x, 0.0f), endX = qMin(end.x, (float)highest);
376+ uchar* lineDest = (uchar*)contents.data() + (int)z * size + (int)startX;
377+ float squaredRadius = scaledRadius * scaledRadius;
378+ float squaredRadiusReciprocal = 1.0f / squaredRadius;
379+ const int EIGHT_BIT_MAXIMUM = 255;
380+ float scaledHeight = _edit.height * EIGHT_BIT_MAXIMUM / info.size;
381+ bool changed = false;
382+ for (float endZ = qMin(end.z, (float)highest); z <= endZ; z += 1.0f) {
383+ uchar* dest = lineDest;
384+ for (float x = startX; x <= endX; x += 1.0f, dest++) {
385+ float dx = x - center.x, dz = z - center.z;
386+ float distanceSquared = dx * dx + dz * dz;
387+ if (distanceSquared <= squaredRadius) {
388+ // height falls off towards edges
389+ int value = *dest + scaledHeight * (squaredRadius - distanceSquared) * squaredRadiusReciprocal;
390+ if (value != *dest) {
391+ *dest = qMin(qMax(value, 0), EIGHT_BIT_MAXIMUM);
392+ changed = true;
393+ }
394+ }
395+ }
396+ lineDest += size;
397+ }
398+ if (changed) {
399+ HeightfieldDataPointer newPointer(new HeightfieldData(contents));
400+ info.outputValues[0] = AttributeValue(_outputs.at(0), encodeInline<HeightfieldDataPointer>(newPointer));
401+ }
402+ return STOP_RECURSION;
403+}
404+
405+void PaintHeightfieldHeightEdit::apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const {
406+ PaintHeightfieldHeightEditVisitor visitor(*this);
407+ data.guide(visitor);
408+}
409+
410+PaintHeightfieldColorEdit::PaintHeightfieldColorEdit(const glm::vec3& position, float radius, const QColor& color) :
411+ position(position),
412+ radius(radius),
413+ color(color) {
414+}
415+
416+class PaintHeightfieldColorEditVisitor : public MetavoxelVisitor {
417+public:
418+
419+ PaintHeightfieldColorEditVisitor(const PaintHeightfieldColorEdit& edit);
420+
421+ virtual int visit(MetavoxelInfo& info);
422+
423+private:
424+
425+ PaintHeightfieldColorEdit _edit;
426+ Box _bounds;
427+};
428+
429+PaintHeightfieldColorEditVisitor::PaintHeightfieldColorEditVisitor(const PaintHeightfieldColorEdit& edit) :
430+ MetavoxelVisitor(QVector<AttributePointer>() << AttributeRegistry::getInstance()->getHeightfieldColorAttribute(),
431+ QVector<AttributePointer>() << AttributeRegistry::getInstance()->getHeightfieldColorAttribute()),
432+ _edit(edit) {
433+
434+ glm::vec3 extents(_edit.radius, _edit.radius, _edit.radius);
435+ _bounds = Box(_edit.position - extents, _edit.position + extents);
436+}
437+
438+int PaintHeightfieldColorEditVisitor::visit(MetavoxelInfo& info) {
439+ if (!info.getBounds().intersects(_bounds)) {
440+ return STOP_RECURSION;
441+ }
442+ if (!info.isLeaf) {
443+ return DEFAULT_ORDER;
444+ }
445+ HeightfieldDataPointer pointer = info.inputValues.at(0).getInlineValue<HeightfieldDataPointer>();
446+ if (!pointer) {
447+ return STOP_RECURSION;
448+ }
449+ QByteArray contents(pointer->getContents());
450+ const int BYTES_PER_PIXEL = 3;
451+ int size = glm::sqrt((float)contents.size() / BYTES_PER_PIXEL);
452+ int highest = size - 1;
453+ float heightScale = size / info.size;
454+
455+ glm::vec3 center = (_edit.position - info.minimum) * heightScale;
456+ float scaledRadius = _edit.radius * heightScale;
457+ glm::vec3 extents(scaledRadius, scaledRadius, scaledRadius);
458+
459+ glm::vec3 start = glm::floor(center - extents);
460+ glm::vec3 end = glm::ceil(center + extents);
461+
462+ // paint all points within the radius
463+ float z = qMax(start.z, 0.0f);
464+ float startX = qMax(start.x, 0.0f), endX = qMin(end.x, (float)highest);
465+ int stride = size * BYTES_PER_PIXEL;
466+ char* lineDest = contents.data() + (int)z * stride + (int)startX * BYTES_PER_PIXEL;
467+ float squaredRadius = scaledRadius * scaledRadius;
468+ char red = _edit.color.red(), green = _edit.color.green(), blue = _edit.color.blue();
469+ bool changed = false;
470+ for (float endZ = qMin(end.z, (float)highest); z <= endZ; z += 1.0f) {
471+ char* dest = lineDest;
472+ for (float x = startX; x <= endX; x += 1.0f, dest += BYTES_PER_PIXEL) {
473+ float dx = x - center.x, dz = z - center.z;
474+ if (dx * dx + dz * dz <= squaredRadius) {
475+ dest[0] = red;
476+ dest[1] = green;
477+ dest[2] = blue;
478+ changed = true;
479+ }
480+ }
481+ lineDest += stride;
482+ }
483+ if (changed) {
484+ HeightfieldDataPointer newPointer(new HeightfieldData(contents));
485+ info.outputValues[0] = AttributeValue(_outputs.at(0), encodeInline<HeightfieldDataPointer>(newPointer));
486+ }
487+ return STOP_RECURSION;
488+}
489+
490+void PaintHeightfieldColorEdit::apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const {
491+ PaintHeightfieldColorEditVisitor visitor(*this);
492+ data.guide(visitor);
493+}
494+
libraries/metavoxels/src/MetavoxelMessages.h+34−0
@@ -207,4 +207,38 @@ public: · 206 unmodified lines
207207
208208 DECLARE_STREAMABLE_METATYPE(SetDataEdit)
209209
210+/// An edit that sets a region of a heightfield height.
211+class PaintHeightfieldHeightEdit : public MetavoxelEdit {
212+ STREAMABLE
213+
214+public:
215+
216+ STREAM glm::vec3 position;
217+ STREAM float radius;
218+ STREAM float height;
219+
220+ PaintHeightfieldHeightEdit(const glm::vec3& position = glm::vec3(), float radius = 0.0f, float height = 0.0f);
221+
222+ virtual void apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const;
223+};
224+
225+DECLARE_STREAMABLE_METATYPE(PaintHeightfieldHeightEdit)
226+
227+/// An edit that sets a region of a heightfield color.
228+class PaintHeightfieldColorEdit : public MetavoxelEdit {
229+ STREAMABLE
230+
231+public:
232+
233+ STREAM glm::vec3 position;
234+ STREAM float radius;
235+ STREAM QColor color;
236+
237+ PaintHeightfieldColorEdit(const glm::vec3& position = glm::vec3(), float radius = 0.0f, const QColor& color = QColor());
238+
239+ virtual void apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const;
240+};
241+
242+DECLARE_STREAMABLE_METATYPE(PaintHeightfieldColorEdit)
243+
210244 #endif // hifi_MetavoxelMessages_h
libraries/metavoxels/src/MetavoxelUtil.cpp+15−0
@@ -24,6 +24,8 @@ · 23 unmodified lines
2424 #include <QVBoxLayout>
2525 #include <QtDebug>
2626
27+#include <GLMHelpers.h>
28+
2729 #include "MetavoxelUtil.h"
2830 #include "ScriptCache.h"
2931 #include "StreamUtils.h"
@@ -162,6 +164,11 @@ Box::Box(const glm::vec3& minimum, const glm::vec3& maximum) : · 132 unmodified lines
162164 minimum(minimum), maximum(maximum) {
163165 }
164166
167+void Box::add(const Box& other) {
168+ minimum = glm::min(minimum, other.minimum);
169+ maximum = glm::max(maximum, other.maximum);
170+}
171+
165172 bool Box::contains(const glm::vec3& point) const {
166173 return point.x >= minimum.x && point.x <= maximum.x &&
167174 point.y >= minimum.y && point.y <= maximum.y &&
@@ -180,6 +187,14 @@ bool Box::intersects(const Box& other) const { · 12 unmodified lines
180187 other.maximum.z >= minimum.z && other.minimum.z <= maximum.z;
181188 }
182189
190+Box Box::getIntersection(const Box& other) const {
191+ return Box(glm::max(minimum, other.minimum), glm::min(maximum, other.maximum));
192+}
193+
194+bool Box::isEmpty() const {
195+ return minimum.x >= maximum.x || minimum.y >= maximum.y || minimum.z >= maximum.z;
196+}
197+
183198 const int X_MAXIMUM_FLAG = 1;
184199 const int Y_MAXIMUM_FLAG = 2;
185200 const int Z_MAXIMUM_FLAG = 4;
libraries/metavoxels/src/MetavoxelUtil.h+8−0
@@ -44,12 +44,18 @@ public: · 43 unmodified lines
4444
4545 explicit Box(const glm::vec3& minimum = glm::vec3(), const glm::vec3& maximum = glm::vec3());
4646
47+ void add(const Box& other);
48+
4749 bool contains(const glm::vec3& point) const;
4850
4951 bool contains(const Box& other) const;
5052
5153 bool intersects(const Box& other) const;
5254
55+ Box getIntersection(const Box& other) const;
56+
57+ bool isEmpty() const;
58+
5359 float getLongestSide() const { return qMax(qMax(maximum.x - minimum.x, maximum.y - minimum.y), maximum.z - minimum.z); }
5460
5561 glm::vec3 getVertex(int index) const;
@@ -138,6 +144,8 @@ public: · 82 unmodified lines
138144
139145 QColorEditor(QWidget* parent);
140146
147+ const QColor& getColor() const { return _color; }
148+
141149 signals:
142150
143151 void colorChanged(const QColor& color);
libraries/networking/CMakeLists.txt+9−16
@@ -1,21 +1,14 @@
1−set(ROOT_DIR ../..)
2−set(MACRO_DIR "${ROOT_DIR}/cmake/macros")
3−
41 set(TARGET_NAME networking)
5−project(${TARGET_NAME})
6−
7−find_package(Qt5 COMPONENTS Network)
82
9−include(${MACRO_DIR}/SetupHifiLibrary.cmake)
10−setup_hifi_library(${TARGET_NAME})
3+# use setup_hifi_library macro to setup our project and link appropriate Qt modules
4+setup_hifi_library(Network)
115
12−# include GLM
13−include(${MACRO_DIR}/IncludeGLM.cmake)
14−include_glm(${TARGET_NAME} "${ROOT_DIR}")
6+link_hifi_libraries(shared)
157
16−target_link_libraries(${TARGET_NAME} Qt5::Network)
17−
18−# add a definition for ssize_t so that windows doesn't bail
198 if (WIN32)
20− add_definitions(-Dssize_t=long)
21−endif ()
9+ # we need ws2_32.lib on windows, but it's static so we don't bubble it up
10+ target_link_libraries(${TARGET_NAME} ws2_32.lib)
11+endif ()
12+
13+# call macro to link our dependencies and bubble them up via a property on our target
14+link_shared_dependencies()
libraries/networking/src/DomainHandler.cpp+4−1
@@ -36,6 +36,7 @@ DomainHandler::DomainHandler(QObject* parent) : · 35 unmodified lines
3636 void DomainHandler::clearConnectionInfo() {
3737 _uuid = QUuid();
3838 _isConnected = false;
39+ emit disconnectedFromDomain();
3940
4041 if (_handshakeTimer) {
4142 _handshakeTimer->stop();
@@ -129,6 +130,8 @@ void DomainHandler::setIsConnected(bool isConnected) { · 87 unmodified lines
129130
130131 // we've connected to new domain - time to ask it for global settings
131132 requestDomainSettings();
133+ } else {
134+ emit disconnectedFromDomain();
132135 }
133136 }
134137 }
@@ -196,4 +199,4 @@ void DomainHandler::parseDTLSRequirementPacket(const QByteArray& dtlsRequirement · 61 unmodified lines
196199 _sockAddr.setPort(dtlsPort);
197200
198201 // initializeDTLSSession();
199−}
202+}
libraries/networking/src/DomainHandler.h+1−0
@@ -70,6 +70,7 @@ private slots: · 69 unmodified lines
7070 signals:
7171 void hostnameChanged(const QString& hostname);
7272 void connectedToDomain(const QString& hostname);
73+ void disconnectedFromDomain();
7374
7475 void settingsReceived(const QJsonObject& domainSettingsObject);
7576 void settingsReceiveFail();
libraries/networking/src/LimitedNodeList.cpp+25−12
@@ -82,8 +82,8 @@ LimitedNodeList::LimitedNodeList(unsigned short socketListenPort, unsigned short · 81 unmodified lines
8282 qDebug() << "NodeList DTLS socket is listening on" << _dtlsSocket->localPort();
8383 }
8484
85− const int LARGER_SNDBUF_SIZE = 1048576;
86− changeSendSocketBufferSize(LARGER_SNDBUF_SIZE);
85+ const int LARGER_BUFFER_SIZE = 1048576;
86+ changeSocketBufferSizes(LARGER_BUFFER_SIZE);
8787
8888 _packetStatTimer.start();
8989 }
@@ -129,7 +129,7 @@ QUdpSocket& LimitedNodeList::getDTLSSocket() { · 39 unmodified lines
129129 return *_dtlsSocket;
130130 }
131131
132−void LimitedNodeList::changeSendSocketBufferSize(int numSendBytes) {
132+void LimitedNodeList::changeSocketBufferSizes(int numBytes) {
133133 // change the socket send buffer size to be 1MB
134134 int oldBufferSize = 0;
135135
@@ -139,15 +139,28 @@ void LimitedNodeList::changeSendSocketBufferSize(int numSendBytes) { · 3 unmodified lines
139139 unsigned int sizeOfInt = sizeof(oldBufferSize);
140140 #endif
141141
142− getsockopt(_nodeSocket.socketDescriptor(), SOL_SOCKET, SO_SNDBUF, reinterpret_cast<char*>(&oldBufferSize), &sizeOfInt);
143−
144− setsockopt(_nodeSocket.socketDescriptor(), SOL_SOCKET, SO_SNDBUF, reinterpret_cast<const char*>(&numSendBytes),
145− sizeof(numSendBytes));
146−
147− int newBufferSize = 0;
148− getsockopt(_nodeSocket.socketDescriptor(), SOL_SOCKET, SO_SNDBUF, reinterpret_cast<char*>(&newBufferSize), &sizeOfInt);
149−
150− qDebug() << "Changed socket send buffer size from" << oldBufferSize << "to" << newBufferSize << "bytes";
142+ for (int i = 0; i < 2; i++) {
143+ int bufferOpt = (i == 0) ? SO_SNDBUF : SO_RCVBUF;
144+
145+ getsockopt(_nodeSocket.socketDescriptor(), SOL_SOCKET, bufferOpt, reinterpret_cast<char*>(&oldBufferSize), &sizeOfInt);
146+
147+ setsockopt(_nodeSocket.socketDescriptor(), SOL_SOCKET, bufferOpt, reinterpret_cast<const char*>(&numBytes),
148+ sizeof(numBytes));
149+
150+ QString bufferTypeString = (i == 0) ? "send" : "receive";
151+
152+ if (oldBufferSize < numBytes) {
153+ int newBufferSize = 0;
154+ getsockopt(_nodeSocket.socketDescriptor(), SOL_SOCKET, bufferOpt, reinterpret_cast<char*>(&newBufferSize), &sizeOfInt);
155+
156+ qDebug() << "Changed socket" << bufferTypeString << "buffer size from" << oldBufferSize << "to"
157+ << newBufferSize << "bytes";
158+ } else {
159+ // don't make the buffer smaller
160+ qDebug() << "Did not change socket" << bufferTypeString << "buffer size from" << oldBufferSize
161+ << "since it is larger than desired size of" << numBytes;
162+ }
163+ }
151164 }
152165
153166 bool LimitedNodeList::packetVersionAndHashMatch(const QByteArray& packet) {
libraries/networking/src/LimitedNodeList.h+1−1
@@ -123,7 +123,7 @@ protected: · 122 unmodified lines
123123 NodeHash::iterator killNodeAtHashIterator(NodeHash::iterator& nodeItemToKill);
124124
125125
126− void changeSendSocketBufferSize(int numSendBytes);
126+ void changeSocketBufferSizes(int numBytes);
127127
128128 QUuid _sessionUUID;
129129 NodeHash _nodeHash;
libraries/networking/src/PacketHeaders.cpp+2−0
@@ -80,6 +80,8 @@ PacketVersion versionForPacketType(PacketType type) { · 79 unmodified lines
8080 return 2;
8181 case PacketTypeAudioStreamStats:
8282 return 1;
83+ case PacketTypeMetavoxelData:
84+ return 1;
8385 default:
8486 return 0;
8587 }
libraries/networking/src/PacketSender.h+0−4
@@ -39,10 +39,6 @@ public: · 38 unmodified lines
3939 ~PacketSender();
4040
4141 /// Add packet to outbound queue.
42− /// \param HifiSockAddr& address the destination address
43− /// \param packetData pointer to data
44− /// \param ssize_t packetLength size of data
45− /// \thread any thread, typically the application thread
4642 void queuePacketForSending(const SharedNodePointer& destinationNode, const QByteArray& packet);
4743
4844 void setPacketsPerSecond(int packetsPerSecond);
libraries/networking/src/ReceivedPacketProcessor.h+0−4
@@ -24,10 +24,6 @@ public: · 23 unmodified lines
2424 ReceivedPacketProcessor() { }
2525
2626 /// Add packet from network receive thread to the processing queue.
27− /// \param sockaddr& senderAddress the address of the sender
28− /// \param packetData pointer to received data
29− /// \param ssize_t packetLength size of received data
30− /// \thread network receive thread
3127 void queueReceivedPacket(const SharedNodePointer& sendingNode, const QByteArray& packet);
3228
3329 /// Are there received packets waiting to be processed
libraries/networking/src/SequenceNumberStats.cpp+15−10
@@ -253,14 +253,19 @@ PacketStreamStats SequenceNumberStats::getStatsForHistoryWindow() const { · 252 unmodified lines
253253 }
254254
255255 // calculate difference between newest stats and oldest stats to get window stats
256− PacketStreamStats windowStats;
257− windowStats._received = newestStats->_received - oldestStats->_received;
258− windowStats._unreasonable = newestStats->_unreasonable - oldestStats->_unreasonable;
259− windowStats._early = newestStats->_early - oldestStats->_early;
260− windowStats._late = newestStats->_late - oldestStats->_late;
261− windowStats._lost = newestStats->_lost - oldestStats->_lost;
262− windowStats._recovered = newestStats->_recovered - oldestStats->_recovered;
263− windowStats._expectedReceived = newestStats->_expectedReceived - oldestStats->_expectedReceived;
264−
265− return windowStats;
256+ return *newestStats - *oldestStats;
266257 }
258+
259+PacketStreamStats SequenceNumberStats::getStatsForLastHistoryInterval() const {
260+
261+ const PacketStreamStats* newestStats = _statsHistory.getNewestEntry();
262+ const PacketStreamStats* secondNewestStats = _statsHistory.get(1);
263+
264+ // this catches cases where history is length 1 or 0 (both are NULL in case of 0)
265+ if (newestStats == NULL || secondNewestStats == NULL) {
266+ return PacketStreamStats();
267+ }
268+
269+ return *newestStats - *secondNewestStats;
270+}
271+
libraries/networking/src/SequenceNumberStats.h+13−0
@@ -31,6 +31,18 @@ public: · 30 unmodified lines
3131 _expectedReceived(0)
3232 {}
3333
34+ PacketStreamStats operator-(const PacketStreamStats& rhs) const {
35+ PacketStreamStats diff;
36+ diff._received = _received - rhs._received;
37+ diff._unreasonable = _unreasonable - rhs._unreasonable;
38+ diff._early = _early - rhs._early;
39+ diff._late = _late - rhs._late;
40+ diff._lost = _lost - rhs._lost;
41+ diff._recovered = _recovered - rhs._recovered;
42+ diff._expectedReceived = _expectedReceived - rhs._expectedReceived;
43+ return diff;
44+ }
45+
3446 float getLostRate() const { return (float)_lost / _expectedReceived; }
3547
3648 quint32 _received;
@@ -76,6 +88,7 @@ public: · 39 unmodified lines
7688
7789 const PacketStreamStats& getStats() const { return _stats; }
7890 PacketStreamStats getStatsForHistoryWindow() const;
91+ PacketStreamStats getStatsForLastHistoryInterval() const;
7992 const QSet<quint16>& getMissingSet() const { return _missingSet; }
8093
8194 private:
libraries/networking/src/ThreadedAssignment.cpp+20−3
@@ -11,6 +11,7 @@ · 10 unmodified lines
1111
1212 #include <QtCore/QCoreApplication>
1313 #include <QtCore/QJsonObject>
14+#include <QtCore/QThread>
1415 #include <QtCore/QTimer>
1516
1617 #include "Logging.h"
@@ -18,7 +19,8 @@ · 1 unmodified line
1819
1920 ThreadedAssignment::ThreadedAssignment(const QByteArray& packet) :
2021 Assignment(packet),
21− _isFinished(false)
22+ _isFinished(false),
23+ _datagramProcessingThread(NULL)
2224 {
2325
2426 }
@@ -28,10 +30,25 @@ void ThreadedAssignment::setFinished(bool isFinished) { · 3 unmodified lines
2830
2931 if (_isFinished) {
3032 aboutToFinish();
31− emit finished();
33+
34+ NodeList* nodeList = NodeList::getInstance();
35+
36+ // if we have a datagram processing thread, quit it and wait on it to make sure that
37+ // the node socket is back on the same thread as the NodeList
38+
39+ if (_datagramProcessingThread) {
40+ // tell the datagram processing thread to quit and wait until it is done, then return the node socket to the NodeList
41+ _datagramProcessingThread->quit();
42+ _datagramProcessingThread->wait();
43+
44+ // set node socket parent back to NodeList
45+ nodeList->getNodeSocket().setParent(nodeList);
46+ }
3247
3348 // move the NodeList back to the QCoreApplication instance's thread
34− NodeList::getInstance()->moveToThread(QCoreApplication::instance()->thread());
49+ nodeList->moveToThread(QCoreApplication::instance()->thread());
50+
51+ emit finished();
3552 }
3653 }
3754
libraries/networking/src/ThreadedAssignment.h+7−3
@@ -20,6 +20,7 @@ class ThreadedAssignment : public Assignment { · 19 unmodified lines
2020 Q_OBJECT
2121 public:
2222 ThreadedAssignment(const QByteArray& packet);
23+
2324 void setFinished(bool isFinished);
2425 virtual void aboutToFinish() { };
2526 void addPacketStatsAndSendStatsPacket(QJsonObject& statsObject);
@@ -29,15 +30,18 @@ public slots: · 3 unmodified lines
2930 virtual void run() = 0;
3031 virtual void readPendingDatagrams() = 0;
3132 virtual void sendStatsPacket();
32−
33+signals:
34+ void finished();
35+
3336 protected:
3437 bool readAvailableDatagram(QByteArray& destinationByteArray, HifiSockAddr& senderSockAddr);
3538 void commonInit(const QString& targetName, NodeType_t nodeType, bool shouldSendStats = true);
3639 bool _isFinished;
40+ QThread* _datagramProcessingThread;
41+
3742 private slots:
3843 void checkInWithDomainServerOrExit();
39−signals:
40− void finished();
44+
4145 };
4246
4347 typedef QSharedPointer<ThreadedAssignment> SharedAssignmentPointer;
libraries/octree/CMakeLists.txt+11−22
@@ -1,30 +1,19 @@
1−set(ROOT_DIR ../..)
2−set(MACRO_DIR "${ROOT_DIR}/cmake/macros")
3−
4−# setup for find modules
5−set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/../../cmake/modules/")
6−
71 set(TARGET_NAME octree)
82
9−find_package(Qt5Widgets REQUIRED)
3+# use setup_hifi_library macro to setup our project and link appropriate Qt modules
4+setup_hifi_library()
105
11−include(${MACRO_DIR}/SetupHifiLibrary.cmake)
12−setup_hifi_library(${TARGET_NAME})
6+include_glm()
137
14−include(${MACRO_DIR}/IncludeGLM.cmake)
15−include_glm(${TARGET_NAME} "${ROOT_DIR}")
8+link_hifi_libraries(shared networking)
169
17−include(${MACRO_DIR}/LinkHifiLibrary.cmake)
18−link_hifi_library(shared ${TARGET_NAME} "${ROOT_DIR}")
19−link_hifi_library(networking ${TARGET_NAME} "${ROOT_DIR}")
20−
21−# link ZLIB
22−find_package(ZLIB)
10+# find ZLIB
11+find_package(ZLIB REQUIRED)
2312
2413 include_directories(SYSTEM "${ZLIB_INCLUDE_DIRS}")
25−target_link_libraries(${TARGET_NAME} "${ZLIB_LIBRARIES}" Qt5::Widgets)
2614
27−# add a definition for ssize_t so that windows doesn't bail
28−if (WIN32)
29− add_definitions(-Dssize_t=long)
30−endif ()
15+# append ZLIB and OpenSSL to our list of libraries to link
16+list(APPEND ${TARGET_NAME}_LIBRARIES_TO_LINK "${ZLIB_LIBRARIES}")
17+
18+# call macro to link our dependencies and bubble them up via a property on our target
19+link_shared_dependencies()
libraries/octree/src/EditPacketBuffer.cpp+1−1
@@ -20,7 +20,7 @@ EditPacketBuffer::EditPacketBuffer() : · 19 unmodified lines
2020
2121 }
2222
23−EditPacketBuffer::EditPacketBuffer(PacketType type, unsigned char* buffer, ssize_t length, qint64 satoshiCost, QUuid nodeUUID) :
23+EditPacketBuffer::EditPacketBuffer(PacketType type, unsigned char* buffer, size_t length, qint64 satoshiCost, QUuid nodeUUID) :
2424 _nodeUUID(nodeUUID),
2525 _currentType(type),
2626 _currentSize(length),
libraries/octree/src/EditPacketBuffer.h+2−2
@@ -21,13 +21,13 @@ · 20 unmodified lines
2121 class EditPacketBuffer {
2222 public:
2323 EditPacketBuffer();
24− EditPacketBuffer(PacketType type, unsigned char* codeColorBuffer, ssize_t length,
24+ EditPacketBuffer(PacketType type, unsigned char* codeColorBuffer, size_t length,
2525 qint64 satoshiCost = 0, const QUuid nodeUUID = QUuid());
2626
2727 QUuid _nodeUUID;
2828 PacketType _currentType;
2929 unsigned char _currentBuffer[MAX_PACKET_SIZE];
30− ssize_t _currentSize;
30+ size_t _currentSize;
3131 qint64 _satoshiCost;
3232 };
3333
libraries/octree/src/JurisdictionListener.cpp+1−1
@@ -35,7 +35,7 @@ void JurisdictionListener::nodeKilled(SharedNodePointer node) { · 34 unmodified lines
3535 bool JurisdictionListener::queueJurisdictionRequest() {
3636 static unsigned char buffer[MAX_PACKET_SIZE];
3737 unsigned char* bufferOut = &buffer[0];
38− ssize_t sizeOut = populatePacketHeader(reinterpret_cast<char*>(bufferOut), PacketTypeJurisdictionRequest);
38+ int sizeOut = populatePacketHeader(reinterpret_cast<char*>(bufferOut), PacketTypeJurisdictionRequest);
3939 int nodeCount = 0;
4040
4141 NodeList* nodeList = NodeList::getInstance();
libraries/octree/src/JurisdictionListener.h+0−4
@@ -47,10 +47,6 @@ public slots: · 46 unmodified lines
4747 protected:
4848 /// Callback for processing of received packets. Will process any queued PacketType_JURISDICTION and update the
4949 /// jurisdiction map member variable
50− /// \param sockaddr& senderAddress the address of the sender
51− /// \param packetData pointer to received data
52− /// \param ssize_t packetLength size of received data
53− /// \thread "this" individual processing thread
5450 virtual void processPacket(const SharedNodePointer& sendingNode, const QByteArray& packet);
5551
5652 private:
libraries/octree/src/JurisdictionSender.cpp+1−1
@@ -47,7 +47,7 @@ bool JurisdictionSender::process() { · 46 unmodified lines
4747 // add our packet to our own queue, then let the PacketSender class do the rest of the work.
4848 static unsigned char buffer[MAX_PACKET_SIZE];
4949 unsigned char* bufferOut = &buffer[0];
50− ssize_t sizeOut = 0;
50+ int sizeOut = 0;
5151
5252 if (_jurisdictionMap) {
5353 sizeOut = _jurisdictionMap->packIntoMessage(bufferOut, MAX_PACKET_SIZE);
libraries/octree/src/OctreeEditPacketSender.cpp+3−3
@@ -83,7 +83,7 @@ bool OctreeEditPacketSender::serversExist() const { · 82 unmodified lines
8383 // This method is called when the edit packet layer has determined that it has a fully formed packet destined for
8484 // a known nodeID.
8585 void OctreeEditPacketSender::queuePacketToNode(const QUuid& nodeUUID, unsigned char* buffer,
86− ssize_t length, qint64 satoshiCost) {
86+ size_t length, qint64 satoshiCost) {
8787
8888 bool wantDebug = false;
8989 if (wantDebug) {
@@ -171,7 +171,7 @@ void OctreeEditPacketSender::processPreServerExistsPackets() { · 81 unmodified lines
171171 }
172172
173173 void OctreeEditPacketSender::queuePendingPacketToNodes(PacketType type, unsigned char* buffer,
174− ssize_t length, qint64 satoshiCost) {
174+ size_t length, qint64 satoshiCost) {
175175 // If we're asked to save messages while waiting for voxel servers to arrive, then do so...
176176
177177 if (_maxPendingMessages > 0) {
@@ -189,7 +189,7 @@ void OctreeEditPacketSender::queuePendingPacketToNodes(PacketType type, unsigned · 11 unmodified lines
189189 }
190190 }
191191
192−void OctreeEditPacketSender::queuePacketToNodes(unsigned char* buffer, ssize_t length, qint64 satoshiCost) {
192+void OctreeEditPacketSender::queuePacketToNodes(unsigned char* buffer, size_t length, qint64 satoshiCost) {
193193 if (!_shouldSend) {
194194 return; // bail early
195195 }
libraries/octree/src/OctreeEditPacketSender.h+5−5
@@ -30,7 +30,7 @@ public: · 29 unmodified lines
3030 /// Queues a single edit message. Will potentially send a pending multi-command packet. Determines which server
3131 /// node or nodes the packet should be sent to. Can be called even before servers are known, in which case up to
3232 /// MaxPendingMessages will be buffered and processed when servers are known.
33− void queueOctreeEditMessage(PacketType type, unsigned char* buffer, ssize_t length, qint64 satoshiCost = 0);
33+ void queueOctreeEditMessage(PacketType type, unsigned char* buffer, size_t length, qint64 satoshiCost = 0);
3434
3535 /// Releases all queued messages even if those messages haven't filled an MTU packet. This will move the packed message
3636 /// packets onto the send queue. If running in threaded mode, the caller does not need to do any further processing to
@@ -82,7 +82,7 @@ public: · 45 unmodified lines
8282 // you must override these...
8383 virtual char getMyNodeType() const = 0;
8484 virtual void adjustEditPacketForClockSkew(PacketType type,
85− unsigned char* editPacketBuffer, ssize_t length, int clockSkew) { };
85+ unsigned char* editPacketBuffer, size_t length, int clockSkew) { };
8686
8787 bool hasDestinationWalletUUID() const { return !_destinationWalletUUID.isNull(); }
8888 void setDestinationWalletUUID(const QUuid& destinationWalletUUID) { _destinationWalletUUID = destinationWalletUUID; }
@@ -98,9 +98,9 @@ signals: · 9 unmodified lines
9898
9999 protected:
100100 bool _shouldSend;
101− void queuePacketToNode(const QUuid& nodeID, unsigned char* buffer, ssize_t length, qint64 satoshiCost = 0);
102− void queuePendingPacketToNodes(PacketType type, unsigned char* buffer, ssize_t length, qint64 satoshiCost = 0);
103− void queuePacketToNodes(unsigned char* buffer, ssize_t length, qint64 satoshiCost = 0);
101+ void queuePacketToNode(const QUuid& nodeID, unsigned char* buffer, size_t length, qint64 satoshiCost = 0);
102+ void queuePendingPacketToNodes(PacketType type, unsigned char* buffer, size_t length, qint64 satoshiCost = 0);
103+ void queuePacketToNodes(unsigned char* buffer, size_t length, qint64 satoshiCost = 0);
104104 void initializePacket(EditPacketBuffer& packetBuffer, PacketType type);
105105 void releaseQueuedPacket(EditPacketBuffer& packetBuffer); // releases specific queued packet
106106
libraries/octree/src/OctreePacketData.cpp+2−0
@@ -9,7 +9,9 @@ · 8 unmodified lines
99 // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
1010 //
1111
12+#include <GLMHelpers.h>
1213 #include <PerfStat.h>
14+
1315 #include "OctreePacketData.h"
1416
1517 bool OctreePacketData::_debug = false;
libraries/octree/src/OctreeQuery.cpp+3−2
@@ -9,8 +9,9 @@ · 8 unmodified lines
99 // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
1010 //
1111
12−#include <PacketHeaders.h>
13−#include <SharedUtil.h>
12+//#include <PacketHeaders.h>
13+//#include <SharedUtil.h>
14+#include <GLMHelpers.h>
1415
1516 #include "OctreeConstants.h"
1617 #include "OctreeQuery.h"
libraries/particles/CMakeLists.txt+7−28
@@ -1,33 +1,12 @@
1−set(ROOT_DIR ../..)
2−set(MACRO_DIR "${ROOT_DIR}/cmake/macros")
3−
4−# setup for find modules
5−set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/../../cmake/modules/")
6−
71 set(TARGET_NAME particles)
82
9−find_package(Qt5Widgets REQUIRED)
10−
11−include(${MACRO_DIR}/SetupHifiLibrary.cmake)
12−setup_hifi_library(${TARGET_NAME})
13−
14−include(${MACRO_DIR}/IncludeGLM.cmake)
15−include_glm(${TARGET_NAME} "${ROOT_DIR}")
16−
17−include(${MACRO_DIR}/LinkHifiLibrary.cmake)
18−link_hifi_library(shared ${TARGET_NAME} "${ROOT_DIR}")
19−link_hifi_library(octree ${TARGET_NAME} "${ROOT_DIR}")
20−link_hifi_library(fbx ${TARGET_NAME} "${ROOT_DIR}")
21−link_hifi_library(networking ${TARGET_NAME} "${ROOT_DIR}")
22−link_hifi_library(animation ${TARGET_NAME} "${ROOT_DIR}")
3+# use setup_hifi_library macro to setup our project and link appropriate Qt modules
4+setup_hifi_library(Gui Network Script)
235
24−# link ZLIB
25−find_package(ZLIB)
6+include_glm()
267
27−include_directories(SYSTEM "${ZLIB_INCLUDE_DIRS}")
28−target_link_libraries(${TARGET_NAME} "${ZLIB_LIBRARIES}" Qt5::Widgets)
8+link_hifi_libraries(shared octree fbx networking animation)
9+include_hifi_library_headers(script-engine)
2910
30−# add a definition for ssize_t so that windows doesn't bail
31−if (WIN32)
32− add_definitions(-Dssize_t=long)
33−endif ()
11+# call macro to link our dependencies and bubble them up via a property on our target
12+link_shared_dependencies()
libraries/particles/src/Particle.cpp+3−3
@@ -13,7 +13,7 @@ · 12 unmodified lines
1313
1414 #include <Octree.h>
1515 #include <RegisteredMetaTypes.h>
16−#include <SharedUtil.h> // usecTimestampNow()
16+#include <GLMHelpers.h>
1717 #include <VoxelsScriptingInterface.h>
1818 #include <VoxelDetail.h>
1919
@@ -22,7 +22,7 @@ · 2 unmodified lines
2222 // I'm open to other potential solutions. Could we change cmake to allow libraries to reference each others
2323 // headers, but not link to each other, this is essentially what this construct is doing, but would be
2424 // better to add includes to the include path, but not link
25−#include "../../script-engine/src/ScriptEngine.h"
25+#include <ScriptEngine.h>
2626
2727 #include "ParticlesScriptingInterface.h"
2828 #include "Particle.h"
@@ -856,7 +856,7 @@ bool Particle::encodeParticleEditMessageDetails(PacketType command, ParticleID i · 827 unmodified lines
856856 }
857857
858858 // adjust any internal timestamps to fix clock skew for this server
859−void Particle::adjustEditPacketForClockSkew(unsigned char* codeColorBuffer, ssize_t length, int clockSkew) {
859+void Particle::adjustEditPacketForClockSkew(unsigned char* codeColorBuffer, size_t length, int clockSkew) {
860860 unsigned char* dataAt = codeColorBuffer;
861861 int octets = numberOfThreeBitSectionsInCode(dataAt);
862862 int lengthOfOctcode = bytesRequiredForCodeLength(octets);
libraries/particles/src/Particle.h+1−1
@@ -292,7 +292,7 @@ public: · 291 unmodified lines
292292 static bool encodeParticleEditMessageDetails(PacketType command, ParticleID id, const ParticleProperties& details,
293293 unsigned char* bufferOut, int sizeIn, int& sizeOut);
294294
295− static void adjustEditPacketForClockSkew(unsigned char* codeColorBuffer, ssize_t length, int clockSkew);
295+ static void adjustEditPacketForClockSkew(unsigned char* codeColorBuffer, size_t length, int clockSkew);
296296
297297 void applyHardCollision(const CollisionInfo& collisionInfo);
298298
libraries/particles/src/ParticleEditPacketSender.cpp+2−2
@@ -38,8 +38,8 @@ void ParticleEditPacketSender::sendEditParticleMessage(PacketType type, Particle · 37 unmodified lines
3838 }
3939 }
4040
41−void ParticleEditPacketSender::adjustEditPacketForClockSkew(PacketType type,
42− unsigned char* codeColorBuffer, ssize_t length, int clockSkew) {
41+void ParticleEditPacketSender::adjustEditPacketForClockSkew(PacketType type,
42+ unsigned char* codeColorBuffer, size_t length, int clockSkew) {
4343 Particle::adjustEditPacketForClockSkew(codeColorBuffer, length, clockSkew);
4444 }
4545
libraries/particles/src/ParticleEditPacketSender.h+1−1
@@ -31,6 +31,6 @@ public: · 30 unmodified lines
3131
3232 // My server type is the particle server
3333 virtual char getMyNodeType() const { return NodeType::ParticleServer; }
34− virtual void adjustEditPacketForClockSkew(PacketType type, unsigned char* codeColorBuffer, ssize_t length, int clockSkew);
34+ virtual void adjustEditPacketForClockSkew(PacketType type, unsigned char* codeColorBuffer, size_t length, int clockSkew);
3535 };
3636 #endif // hifi_ParticleEditPacketSender_h
libraries/script-engine/CMakeLists.txt+6−30
@@ -1,35 +1,11 @@
1−set(ROOT_DIR ../..)
2−set(MACRO_DIR "${ROOT_DIR}/cmake/macros")
3−
4−# setup for find modules
5−set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/../../cmake/modules/")
6−
71 set(TARGET_NAME script-engine)
82
9−find_package(Qt5Widgets REQUIRED)
10−
11−include(${MACRO_DIR}/SetupHifiLibrary.cmake)
12−setup_hifi_library(${TARGET_NAME})
13−
14−include(${MACRO_DIR}/IncludeGLM.cmake)
15−include_glm(${TARGET_NAME} "${ROOT_DIR}")
16−
17−include(${MACRO_DIR}/LinkHifiLibrary.cmake)
18−link_hifi_library(shared ${TARGET_NAME} "${ROOT_DIR}")
19−link_hifi_library(octree ${TARGET_NAME} "${ROOT_DIR}")
20−link_hifi_library(voxels ${TARGET_NAME} "${ROOT_DIR}")
21−link_hifi_library(fbx ${TARGET_NAME} "${ROOT_DIR}")
22−link_hifi_library(particles ${TARGET_NAME} "${ROOT_DIR}")
23−link_hifi_library(entities ${TARGET_NAME} "${ROOT_DIR}")
24−link_hifi_library(animation ${TARGET_NAME} "${ROOT_DIR}")
3+# use setup_hifi_library macro to setup our project and link appropriate Qt modules
4+setup_hifi_library(Gui Network Script Widgets)
255
26−# link ZLIB
27−find_package(ZLIB)
6+include_glm()
287
29−include_directories(SYSTEM "${ZLIB_INCLUDE_DIRS}")
30−target_link_libraries(${TARGET_NAME} "${ZLIB_LIBRARIES}" Qt5::Widgets)
8+link_hifi_libraries(shared octree voxels fbx particles entities animation)
319
32−# add a definition for ssize_t so that windows doesn't bail
33−if (WIN32)
34− add_definitions(-Dssize_t=long)
35−endif ()
10+# call macro to link our dependencies and bubble them up via a property on our target
11+link_shared_dependencies()
libraries/script-engine/src/Quat.cpp+1−1
@@ -14,7 +14,7 @@ · 13 unmodified lines
1414 #include <QDebug>
1515
1616 #include <OctreeConstants.h>
17−#include <SharedUtil.h>
17+#include <GLMHelpers.h>
1818 #include "Quat.h"
1919
2020
libraries/shared/CMakeLists.txt+4−30
@@ -1,33 +1,7 @@
1−set(ROOT_DIR ../..)
2−set(MACRO_DIR "${ROOT_DIR}/cmake/macros")
3−
41 set(TARGET_NAME shared)
5−project(${TARGET_NAME})
6−
7−find_package(Qt5 COMPONENTS Network Widgets Xml Script)
8−
9−include(${MACRO_DIR}/SetupHifiLibrary.cmake)
10−setup_hifi_library(${TARGET_NAME})
11−
12−# include GLM
13−include(${MACRO_DIR}/IncludeGLM.cmake)
14−include_glm(${TARGET_NAME} "${ROOT_DIR}")
15−
16−set(EXTERNAL_ROOT_DIR "${CMAKE_CURRENT_SOURCE_DIR}/external")
17−
18−if (WIN32)
19− # include headers for external libraries and InterfaceConfig.
20− include_directories("${EXTERNAL_ROOT_DIR}")
21−endif (WIN32)
22−
23−# link required libraries on UNIX
24−if (UNIX AND NOT APPLE)
25− find_package(Threads REQUIRED)
26− target_link_libraries(${TARGET_NAME} "${CMAKE_THREAD_LIBS_INIT}")
27−endif (UNIX AND NOT APPLE)
282
29−# There is something special (bug) about Qt5Scripts, that we have to explicitly add its include
30−# directory when Qt5 (5.2.1) is compiled from source and is not in a standard place.
31−include_directories(SYSTEM "${Qt5Script_INCLUDE_DIRS}")
3+# use setup_hifi_library macro to setup our project and link appropriate Qt modules
4+setup_hifi_library(Network Widgets)
325
33−target_link_libraries(${TARGET_NAME} Qt5::Network Qt5::Widgets)
6+# call macro to link our dependencies and bubble them up via a property on our target
7+link_shared_dependencies()
libraries/shared/external/pthread.h+0−1403
@@ -1,1403 +0,0 @@
1−/* This is an implementation of the threads API of POSIX 1003.1-2001.
2− *
3− * --------------------------------------------------------------------------
4− *
5− * Pthreads-win32 - POSIX Threads Library for Win32
6− * Copyright(C) 1998 John E. Bossom
7− * Copyright(C) 1999,2005 Pthreads-win32 contributors
8− *
9− * Contact Email: rpj@callisto.canberra.edu.au
10− *
11− * The current list of contributors is contained
12− * in the file CONTRIBUTORS included with the source
13− * code distribution. The list can also be seen at the
14− * following World Wide Web location:
15− * http://sources.redhat.com/pthreads-win32/contributors.html
16− *
17− * This library is free software; you can redistribute it and/or
18− * modify it under the terms of the GNU Lesser General Public
19− * License as published by the Free Software Foundation; either
20− * version 2 of the License, or (at your option) any later version.
21− *
22− * This library is distributed in the hope that it will be useful,
23− * but WITHOUT ANY WARRANTY; without even the implied warranty of
24− * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
25− * Lesser General Public License for more details.
26− *
27− * You should have received a copy of the GNU Lesser General Public
28− * License along with this library in the file COPYING.LIB;
29− * if not, write to the Free Software Foundation, Inc.,
30− * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
31− */
32−
33−#if !defined( PTHREAD_H )
34−#define PTHREAD_H
35−
36−#define PTW32_STATIC_LIB
37−
38−/*
39− * See the README file for an explanation of the pthreads-win32 version
40− * numbering scheme and how the DLL is named etc.
41− */
42−#define PTW32_VERSION 2,10,0,0
43−#define PTW32_VERSION_STRING "2, 10, 0, 0\0"
44−
45−/* There are three implementations of cancel cleanup.
46− * Note that pthread.h is included in both application
47− * compilation units and also internally for the library.
48− * The code here and within the library aims to work
49− * for all reasonable combinations of environments.
50− *
51− * The three implementations are:
52− *
53− * WIN32 SEH
54− * C
55− * C++
56− *
57− * Please note that exiting a push/pop block via
58− * "return", "exit", "break", or "continue" will
59− * lead to different behaviour amongst applications
60− * depending upon whether the library was built
61− * using SEH, C++, or C. For example, a library built
62− * with SEH will call the cleanup routine, while both
63− * C++ and C built versions will not.
64− */
65−
66−/*
67− * Define defaults for cleanup code.
68− * Note: Unless the build explicitly defines one of the following, then
69− * we default to standard C style cleanup. This style uses setjmp/longjmp
70− * in the cancellation and thread exit implementations and therefore won't
71− * do stack unwinding if linked to applications that have it (e.g.
72− * C++ apps). This is currently consistent with most/all commercial Unix
73− * POSIX threads implementations.
74− */
75−#if !defined( __CLEANUP_SEH ) && !defined( __CLEANUP_CXX ) && !defined( __CLEANUP_C )
76−/*
77− [i_a] fix for apps using pthreads-Win32: when they do not define __CLEANUP_SEH themselves,
78− they're screwed as they'll receive the '__CLEANUP_C' macros which do NOT work when
79− the pthreads library code itself has actually been build with __CLEANUP_SEH,
80− which is the case when building this stuff in MSVC.
81−
82− Hence this section is made to 'sensibly autodetect' the cleanup mode, when it hasn't
83− been hardwired in the makefiles / project files.
84−
85− After all, who expects he MUST define one of these __CLEANUP_XXX defines in his own
86− code when using pthreads-Win32, for whatever reason.
87− */
88−#if (defined(_MSC_VER) || defined(PTW32_RC_MSC))
89−#define __CLEANUP_SEH
90−#elif defined(__cplusplus)
91−#define __CLEANUP_CXX
92−#else
93−#define __CLEANUP_C
94−#endif
95−#endif
96−
97−#if defined( __CLEANUP_SEH ) && ( !defined( _MSC_VER ) && !defined(PTW32_RC_MSC))
98−#error ERROR [__FILE__, line __LINE__]: SEH is not supported for this compiler.
99−#endif
100−
101−/*
102− * Stop here if we are being included by the resource compiler.
103− */
104−#if !defined(RC_INVOKED)
105−
106−#undef PTW32_LEVEL
107−
108−#if defined(_POSIX_SOURCE)
109−#define PTW32_LEVEL 0
110−/* Early POSIX */
111−#endif
112−
113−#if defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 199309
114−#undef PTW32_LEVEL
115−#define PTW32_LEVEL 1
116−/* Include 1b, 1c and 1d */
117−#endif
118−
119−#if defined(INCLUDE_NP)
120−#undef PTW32_LEVEL
121−#define PTW32_LEVEL 2
122−/* Include Non-Portable extensions */
123−#endif
124−
125−#define PTW32_LEVEL_MAX 3
126−
127−#if ( defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 200112 ) || !defined(PTW32_LEVEL)
128−#define PTW32_LEVEL PTW32_LEVEL_MAX
129−/* Include everything */
130−#endif
131−
132−#if defined(_UWIN)
133−# define HAVE_STRUCT_TIMESPEC 1
134−# define HAVE_SIGNAL_H 1
135−# undef HAVE_PTW32_CONFIG_H
136−# pragma comment(lib, "pthread")
137−#endif
138−
139−#if defined(__MINGW32__) || defined(__MINGW64__)
140−# define PTW32_CONFIG_MINGW
141−#endif
142−#if defined(_MSC_VER)
143−# if _MSC_VER < 1300
144−# define PTW32_CONFIG_MSVC6
145−# endif
146−# if _MSC_VER < 1400
147−# define PTW32_CONFIG_MSVC7
148−# endif
149−#endif
150−
151−/*
152− * -------------------------------------------------------------
153− *
154− *
155− * Module: pthread.h
156− *
157− * Purpose:
158− * Provides an implementation of PThreads based upon the
159− * standard:
160− *
161− * POSIX 1003.1-2001
162− * and
163− * The Single Unix Specification version 3
164− *
165− * (these two are equivalent)
166− *
167− * in order to enhance code portability between Windows,
168− * various commercial Unix implementations, and Linux.
169− *
170− * See the ANNOUNCE file for a full list of conforming
171− * routines and defined constants, and a list of missing
172− * routines and constants not defined in this implementation.
173− *
174− * Authors:
175− * There have been many contributors to this library.
176− * The initial implementation was contributed by
177− * John Bossom, and several others have provided major
178− * sections or revisions of parts of the implementation.
179− * Often significant effort has been contributed to
180− * find and fix important bugs and other problems to
181− * improve the reliability of the library, which sometimes
182− * is not reflected in the amount of code which changed as
183− * result.
184− * As much as possible, the contributors are acknowledged
185− * in the ChangeLog file in the source code distribution
186− * where their changes are noted in detail.
187− *
188− * Contributors are listed in the CONTRIBUTORS file.
189− *
190− * As usual, all bouquets go to the contributors, and all
191− * brickbats go to the project maintainer.
192− *
193− * Maintainer:
194− * The code base for this project is coordinated and
195− * eventually pre-tested, packaged, and made available by
196− *
197− * Ross Johnson <rpj@callisto.canberra.edu.au>
198− *
199− * QA Testers:
200− * Ultimately, the library is tested in the real world by
201− * a host of competent and demanding scientists and
202− * engineers who report bugs and/or provide solutions
203− * which are then fixed or incorporated into subsequent
204− * versions of the library. Each time a bug is fixed, a
205− * test case is written to prove the fix and ensure
206− * that later changes to the code don't reintroduce the
207− * same error. The number of test cases is slowly growing
208− * and therefore so is the code reliability.
209− *
210− * Compliance:
211− * See the file ANNOUNCE for the list of implemented
212− * and not-implemented routines and defined options.
213− * Of course, these are all defined is this file as well.
214− *
215− * Web site:
216− * The source code and other information about this library
217− * are available from
218− *
219− * http://sources.redhat.com/pthreads-win32/
220− *
221− * -------------------------------------------------------------
222− */
223−
224−/* Try to avoid including windows.h */
225−#if defined(PTW32_CONFIG_MINGW) && defined(__cplusplus)
226−#define PTW32_INCLUDE_WINDOWS_H
227−#endif
228−
229−#if defined(PTW32_INCLUDE_WINDOWS_H)
230−#include <windows.h>
231−#endif
232−
233−#if defined(PTW32_CONFIG_MSVC6) || defined(__DMC__)
234−/*
235− * VC++6.0 or early compiler's header has no DWORD_PTR type.
236− */
237−typedef unsigned long DWORD_PTR;
238−typedef unsigned long ULONG_PTR;
239−#endif
240−/*
241− * -----------------
242− * autoconf switches
243− * -----------------
244− */
245−
246−#if defined(HAVE_PTW32_CONFIG_H)
247−#include "config.h"
248−#endif /* HAVE_PTW32_CONFIG_H */
249−
250−#if !defined(NEED_FTIME)
251−#include <time.h>
252−#else /* NEED_FTIME */
253−/* use native WIN32 time API */
254−#endif /* NEED_FTIME */
255−
256−#if defined(HAVE_SIGNAL_H)
257−#include <signal.h>
258−#endif /* HAVE_SIGNAL_H */
259−
260−#include <limits.h>
261−
262−/*
263− * Boolean values to make us independent of system includes.
264− */
265−enum {
266− PTW32_FALSE = 0,
267− PTW32_TRUE = (! PTW32_FALSE)
268−};
269−
270−/*
271− * This is a duplicate of what is in the autoconf config.h,
272− * which is only used when building the pthread-win32 libraries.
273− */
274−
275−#if !defined(PTW32_CONFIG_H)
276−# if defined(WINCE)
277−# define NEED_ERRNO
278−# define NEED_SEM
279−# endif
280−# if defined(__MINGW64__)
281−# define HAVE_STRUCT_TIMESPEC
282−# define HAVE_MODE_T
283−# elif defined(_UWIN) || defined(__MINGW32__)
284−# define HAVE_MODE_T
285−# endif
286−#endif
287−
288−/*
289− *
290− */
291−
292−#if PTW32_LEVEL >= PTW32_LEVEL_MAX
293−#if defined(NEED_ERRNO)
294−#include "need_errno.h"
295−#else
296−#include <errno.h>
297−#endif
298−#endif /* PTW32_LEVEL >= PTW32_LEVEL_MAX */
299−
300−/*
301− * Several systems don't define some error numbers.
302− */
303−#if !defined(ENOTSUP)
304−# define ENOTSUP 48 /* This is the value in Solaris. */
305−#endif
306−
307−#if !defined(ETIMEDOUT)
308−# define ETIMEDOUT 10060 /* Same as WSAETIMEDOUT */
309−#endif
310−
311−#if !defined(ENOSYS)
312−# define ENOSYS 140 /* Semi-arbitrary value */
313−#endif
314−
315−#if !defined(EDEADLK)
316−# if defined(EDEADLOCK)
317−# define EDEADLK EDEADLOCK
318−# else
319−# define EDEADLK 36 /* This is the value in MSVC. */
320−# endif
321−#endif
322−
323−/* POSIX 2008 - related to robust mutexes */
324−#if !defined(EOWNERDEAD)
325−# define EOWNERDEAD 43
326−#endif
327−#if !defined(ENOTRECOVERABLE)
328−# define ENOTRECOVERABLE 44
329−#endif
330−
331−#include <sched.h>
332−
333−/*
334− * To avoid including windows.h we define only those things that we
335− * actually need from it.
336− */
337−#if !defined(PTW32_INCLUDE_WINDOWS_H)
338−#if !defined(HANDLE)
339−# define PTW32__HANDLE_DEF
340−# define HANDLE void *
341−#endif
342−#if !defined(DWORD)
343−# define PTW32__DWORD_DEF
344−# define DWORD unsigned long
345−#endif
346−#endif
347−
348−#if !defined(HAVE_STRUCT_TIMESPEC)
349−#define HAVE_STRUCT_TIMESPEC
350−#if !defined(_TIMESPEC_DEFINED)
351−#define _TIMESPEC_DEFINED
352−struct timespec {
353− time_t tv_sec;
354− long tv_nsec;
355−};
356−#endif /* _TIMESPEC_DEFINED */
357−#endif /* HAVE_STRUCT_TIMESPEC */
358−
359−#if !defined(SIG_BLOCK)
360−#define SIG_BLOCK 0
361−#endif /* SIG_BLOCK */
362−
363−#if !defined(SIG_UNBLOCK)
364−#define SIG_UNBLOCK 1
365−#endif /* SIG_UNBLOCK */
366−
367−#if !defined(SIG_SETMASK)
368−#define SIG_SETMASK 2
369−#endif /* SIG_SETMASK */
370−
371−#if defined(__cplusplus)
372−extern "C"
373−{
374−#endif /* __cplusplus */
375−
376−/*
377− * -------------------------------------------------------------
378− *
379− * POSIX 1003.1-2001 Options
380− * =========================
381− *
382− * Options are normally set in <unistd.h>, which is not provided
383− * with pthreads-win32.
384− *
385− * For conformance with the Single Unix Specification (version 3), all of the
386− * options below are defined, and have a value of either -1 (not supported)
387− * or 200112L (supported).
388− *
389− * These options can neither be left undefined nor have a value of 0, because
390− * either indicates that sysconf(), which is not implemented, may be used at
391− * runtime to check the status of the option.
392− *
393− * _POSIX_THREADS (== 200112L)
394− * If == 200112L, you can use threads
395− *
396− * _POSIX_THREAD_ATTR_STACKSIZE (== 200112L)
397− * If == 200112L, you can control the size of a thread's
398− * stack
399− * pthread_attr_getstacksize
400− * pthread_attr_setstacksize
401− *
402− * _POSIX_THREAD_ATTR_STACKADDR (== -1)
403− * If == 200112L, you can allocate and control a thread's
404− * stack. If not supported, the following functions
405− * will return ENOSYS, indicating they are not
406− * supported:
407− * pthread_attr_getstackaddr
408− * pthread_attr_setstackaddr
409− *
410− * _POSIX_THREAD_PRIORITY_SCHEDULING (== -1)
411− * If == 200112L, you can use realtime scheduling.
412− * This option indicates that the behaviour of some
413− * implemented functions conforms to the additional TPS
414− * requirements in the standard. E.g. rwlocks favour
415− * writers over readers when threads have equal priority.
416− *
417− * _POSIX_THREAD_PRIO_INHERIT (== -1)
418− * If == 200112L, you can create priority inheritance
419− * mutexes.
420− * pthread_mutexattr_getprotocol +
421− * pthread_mutexattr_setprotocol +
422− *
423− * _POSIX_THREAD_PRIO_PROTECT (== -1)
424− * If == 200112L, you can create priority ceiling mutexes
425− * Indicates the availability of:
426− * pthread_mutex_getprioceiling
427− * pthread_mutex_setprioceiling
428− * pthread_mutexattr_getprioceiling
429− * pthread_mutexattr_getprotocol +
430− * pthread_mutexattr_setprioceiling
431− * pthread_mutexattr_setprotocol +
432− *
433− * _POSIX_THREAD_PROCESS_SHARED (== -1)
434− * If set, you can create mutexes and condition
435− * variables that can be shared with another
436− * process.If set, indicates the availability
437− * of:
438− * pthread_mutexattr_getpshared
439− * pthread_mutexattr_setpshared
440− * pthread_condattr_getpshared
441− * pthread_condattr_setpshared
442− *
443− * _POSIX_THREAD_SAFE_FUNCTIONS (== 200112L)
444− * If == 200112L you can use the special *_r library
445− * functions that provide thread-safe behaviour
446− *
447− * _POSIX_READER_WRITER_LOCKS (== 200112L)
448− * If == 200112L, you can use read/write locks
449− *
450− * _POSIX_SPIN_LOCKS (== 200112L)
451− * If == 200112L, you can use spin locks
452− *
453− * _POSIX_BARRIERS (== 200112L)
454− * If == 200112L, you can use barriers
455− *
456− * + These functions provide both 'inherit' and/or
457− * 'protect' protocol, based upon these macro
458− * settings.
459− *
460− * -------------------------------------------------------------
461− */
462−
463−/*
464− * POSIX Options
465− */
466−#undef _POSIX_THREADS
467−#define _POSIX_THREADS 200809L
468−
469−#undef _POSIX_READER_WRITER_LOCKS
470−#define _POSIX_READER_WRITER_LOCKS 200809L
471−
472−#undef _POSIX_SPIN_LOCKS
473−#define _POSIX_SPIN_LOCKS 200809L
474−
475−#undef _POSIX_BARRIERS
476−#define _POSIX_BARRIERS 200809L
477−
478−#undef _POSIX_THREAD_SAFE_FUNCTIONS
479−#define _POSIX_THREAD_SAFE_FUNCTIONS 200809L
480−
481−#undef _POSIX_THREAD_ATTR_STACKSIZE
482−#define _POSIX_THREAD_ATTR_STACKSIZE 200809L
483−
484−/*
485− * The following options are not supported
486− */
487−#undef _POSIX_THREAD_ATTR_STACKADDR
488−#define _POSIX_THREAD_ATTR_STACKADDR -1
489−
490−#undef _POSIX_THREAD_PRIO_INHERIT
491−#define _POSIX_THREAD_PRIO_INHERIT -1
492−
493−#undef _POSIX_THREAD_PRIO_PROTECT
494−#define _POSIX_THREAD_PRIO_PROTECT -1
495−
496−/* TPS is not fully supported. */
497−#undef _POSIX_THREAD_PRIORITY_SCHEDULING
498−#define _POSIX_THREAD_PRIORITY_SCHEDULING -1
499−
500−#undef _POSIX_THREAD_PROCESS_SHARED
501−#define _POSIX_THREAD_PROCESS_SHARED -1
502−
503−
504−/*
505− * POSIX 1003.1-2001 Limits
506− * ===========================
507− *
508− * These limits are normally set in <limits.h>, which is not provided with
509− * pthreads-win32.
510− *
511− * PTHREAD_DESTRUCTOR_ITERATIONS
512− * Maximum number of attempts to destroy
513− * a thread's thread-specific data on
514− * termination (must be at least 4)
515− *
516− * PTHREAD_KEYS_MAX
517− * Maximum number of thread-specific data keys
518− * available per process (must be at least 128)
519− *
520− * PTHREAD_STACK_MIN
521− * Minimum supported stack size for a thread
522− *
523− * PTHREAD_THREADS_MAX
524− * Maximum number of threads supported per
525− * process (must be at least 64).
526− *
527− * SEM_NSEMS_MAX
528− * The maximum number of semaphores a process can have.
529− * (must be at least 256)
530− *
531− * SEM_VALUE_MAX
532− * The maximum value a semaphore can have.
533− * (must be at least 32767)
534− *
535− */
536−#undef _POSIX_THREAD_DESTRUCTOR_ITERATIONS
537−#define _POSIX_THREAD_DESTRUCTOR_ITERATIONS 4
538−
539−#undef PTHREAD_DESTRUCTOR_ITERATIONS
540−#define PTHREAD_DESTRUCTOR_ITERATIONS _POSIX_THREAD_DESTRUCTOR_ITERATIONS
541−
542−#undef _POSIX_THREAD_KEYS_MAX
543−#define _POSIX_THREAD_KEYS_MAX 128
544−
545−#undef PTHREAD_KEYS_MAX
546−#define PTHREAD_KEYS_MAX _POSIX_THREAD_KEYS_MAX
547−
548−#undef PTHREAD_STACK_MIN
549−#define PTHREAD_STACK_MIN 0
550−
551−#undef _POSIX_THREAD_THREADS_MAX
552−#define _POSIX_THREAD_THREADS_MAX 64
553−
554− /* Arbitrary value */
555−#undef PTHREAD_THREADS_MAX
556−#define PTHREAD_THREADS_MAX 2019
557−
558−#undef _POSIX_SEM_NSEMS_MAX
559−#define _POSIX_SEM_NSEMS_MAX 256
560−
561− /* Arbitrary value */
562−#undef SEM_NSEMS_MAX
563−#define SEM_NSEMS_MAX 1024
564−
565−#undef _POSIX_SEM_VALUE_MAX
566−#define _POSIX_SEM_VALUE_MAX 32767
567−
568−#undef SEM_VALUE_MAX
569−#define SEM_VALUE_MAX INT_MAX
570−
571−
572−#if defined(__GNUC__) && !defined(__declspec)
573−# error Please upgrade your GNU compiler to one that supports __declspec.
574−#endif
575−
576−/*
577− * When building the library, you should define PTW32_BUILD so that
578− * the variables/functions are exported correctly. When using the library,
579− * do NOT define PTW32_BUILD, and then the variables/functions will
580− * be imported correctly.
581− */
582−#if !defined(PTW32_STATIC_LIB)
583−# if defined(PTW32_BUILD)
584−# define PTW32_DLLPORT __declspec (dllexport)
585−# else
586−# define PTW32_DLLPORT __declspec (dllimport)
587−# endif
588−#else
589−# define PTW32_DLLPORT
590−#endif
591−
592−/*
593− * The Open Watcom C/C++ compiler uses a non-standard calling convention
594− * that passes function args in registers unless __cdecl is explicitly specified
595− * in exposed function prototypes.
596− *
597− * We force all calls to cdecl even though this could slow Watcom code down
598− * slightly. If you know that the Watcom compiler will be used to build both
599− * the DLL and application, then you can probably define this as a null string.
600− * Remember that pthread.h (this file) is used for both the DLL and application builds.
601− */
602−#define PTW32_CDECL __cdecl
603−
604−#if defined(_UWIN) && PTW32_LEVEL >= PTW32_LEVEL_MAX
605−# include <sys/types.h>
606−#else
607−/*
608− * Generic handle type - intended to extend uniqueness beyond
609− * that available with a simple pointer. It should scale for either
610− * IA-32 or IA-64.
611− */
612−typedef struct {
613− void * p; /* Pointer to actual object */
614− unsigned int x; /* Extra information - reuse count etc */
615−} ptw32_handle_t;
616−
617−typedef ptw32_handle_t pthread_t;
618−typedef struct pthread_attr_t_ * pthread_attr_t;
619−typedef struct pthread_once_t_ pthread_once_t;
620−typedef struct pthread_key_t_ * pthread_key_t;
621−typedef struct pthread_mutex_t_ * pthread_mutex_t;
622−typedef struct pthread_mutexattr_t_ * pthread_mutexattr_t;
623−typedef struct pthread_cond_t_ * pthread_cond_t;
624−typedef struct pthread_condattr_t_ * pthread_condattr_t;
625−#endif
626−typedef struct pthread_rwlock_t_ * pthread_rwlock_t;
627−typedef struct pthread_rwlockattr_t_ * pthread_rwlockattr_t;
628−typedef struct pthread_spinlock_t_ * pthread_spinlock_t;
629−typedef struct pthread_barrier_t_ * pthread_barrier_t;
630−typedef struct pthread_barrierattr_t_ * pthread_barrierattr_t;
631−
632−/*
633− * ====================
634− * ====================
635− * POSIX Threads
636− * ====================
637− * ====================
638− */
639−
640−enum {
641−/*
642− * pthread_attr_{get,set}detachstate
643− */
644− PTHREAD_CREATE_JOINABLE = 0, /* Default */
645− PTHREAD_CREATE_DETACHED = 1,
646−
647−/*
648− * pthread_attr_{get,set}inheritsched
649− */
650− PTHREAD_INHERIT_SCHED = 0,
651− PTHREAD_EXPLICIT_SCHED = 1, /* Default */
652−
653−/*
654− * pthread_{get,set}scope
655− */
656− PTHREAD_SCOPE_PROCESS = 0,
657− PTHREAD_SCOPE_SYSTEM = 1, /* Default */
658−
659−/*
660− * pthread_setcancelstate paramters
661− */
662− PTHREAD_CANCEL_ENABLE = 0, /* Default */
663− PTHREAD_CANCEL_DISABLE = 1,
664−
665−/*
666− * pthread_setcanceltype parameters
667− */
668− PTHREAD_CANCEL_ASYNCHRONOUS = 0,
669− PTHREAD_CANCEL_DEFERRED = 1, /* Default */
670−
671−/*
672− * pthread_mutexattr_{get,set}pshared
673− * pthread_condattr_{get,set}pshared
674− */
675− PTHREAD_PROCESS_PRIVATE = 0,
676− PTHREAD_PROCESS_SHARED = 1,
677−
678−/*
679− * pthread_mutexattr_{get,set}robust
680− */
681− PTHREAD_MUTEX_STALLED = 0, /* Default */
682− PTHREAD_MUTEX_ROBUST = 1,
683−
684−/*
685− * pthread_barrier_wait
686− */
687− PTHREAD_BARRIER_SERIAL_THREAD = -1
688−};
689−
690−/*
691− * ====================
692− * ====================
693− * cancellation
694− * ====================
695− * ====================
696− */
697−#define PTHREAD_CANCELED ((void *)(size_t) -1)
698−
699−
700−/*
701− * ====================
702− * ====================
703− * Once Key
704− * ====================
705− * ====================
706− */
707−#define PTHREAD_ONCE_INIT { PTW32_FALSE, 0, 0, 0}
708−
709−struct pthread_once_t_
710−{
711− int done; /* indicates if user function has been executed */
712− void * lock;
713− int reserved1;
714− int reserved2;
715−};
716−
717−
718−/*
719− * ====================
720− * ====================
721− * Object initialisers
722− * ====================
723− * ====================
724− */
725−#define PTHREAD_MUTEX_INITIALIZER ((pthread_mutex_t)(size_t) -1)
726−#define PTHREAD_RECURSIVE_MUTEX_INITIALIZER ((pthread_mutex_t)(size_t) -2)
727−#define PTHREAD_ERRORCHECK_MUTEX_INITIALIZER ((pthread_mutex_t)(size_t) -3)
728−
729−/*
730− * Compatibility with LinuxThreads
731− */
732−#define PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP PTHREAD_RECURSIVE_MUTEX_INITIALIZER
733−#define PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP PTHREAD_ERRORCHECK_MUTEX_INITIALIZER
734−
735−#define PTHREAD_COND_INITIALIZER ((pthread_cond_t)(size_t) -1)
736−
737−#define PTHREAD_RWLOCK_INITIALIZER ((pthread_rwlock_t)(size_t) -1)
738−
739−#define PTHREAD_SPINLOCK_INITIALIZER ((pthread_spinlock_t)(size_t) -1)
740−
741−
742−/*
743− * Mutex types.
744− */
745−enum
746−{
747− /* Compatibility with LinuxThreads */
748− PTHREAD_MUTEX_FAST_NP,
749− PTHREAD_MUTEX_RECURSIVE_NP,
750− PTHREAD_MUTEX_ERRORCHECK_NP,
751− PTHREAD_MUTEX_TIMED_NP = PTHREAD_MUTEX_FAST_NP,
752− PTHREAD_MUTEX_ADAPTIVE_NP = PTHREAD_MUTEX_FAST_NP,
753− /* For compatibility with POSIX */
754− PTHREAD_MUTEX_NORMAL = PTHREAD_MUTEX_FAST_NP,
755− PTHREAD_MUTEX_RECURSIVE = PTHREAD_MUTEX_RECURSIVE_NP,
756− PTHREAD_MUTEX_ERRORCHECK = PTHREAD_MUTEX_ERRORCHECK_NP,
757− PTHREAD_MUTEX_DEFAULT = PTHREAD_MUTEX_NORMAL
758−};
759−
760−
761−typedef struct ptw32_cleanup_t ptw32_cleanup_t;
762−
763−#if defined(_MSC_VER)
764−/* Disable MSVC 'anachronism used' warning */
765−#pragma warning( disable : 4229 )
766−#endif
767−
768−typedef void (* PTW32_CDECL ptw32_cleanup_callback_t)(void *);
769−
770−#if defined(_MSC_VER)
771−#pragma warning( default : 4229 )
772−#endif
773−
774−struct ptw32_cleanup_t
775−{
776− ptw32_cleanup_callback_t routine;
777− void *arg;
778− struct ptw32_cleanup_t *prev;
779−};
780−
781−#if defined(__CLEANUP_SEH)
782− /*
783− * WIN32 SEH version of cancel cleanup.
784− */
785−
786−#define pthread_cleanup_push( _rout, _arg ) \
787− { \
788− ptw32_cleanup_t _cleanup; \
789− \
790− _cleanup.routine = (ptw32_cleanup_callback_t)(_rout); \
791− _cleanup.arg = (_arg); \
792− __try \
793− { \
794−
795−#define pthread_cleanup_pop( _execute ) \
796− } \
797− __finally \
798− { \
799− if( _execute || AbnormalTermination()) \
800− { \
801− (*(_cleanup.routine))( _cleanup.arg ); \
802− } \
803− } \
804− }
805−
806−#else /* __CLEANUP_SEH */
807−
808−#if defined(__CLEANUP_C)
809−
810− /*
811− * C implementation of PThreads cancel cleanup
812− */
813−
814−#define pthread_cleanup_push( _rout, _arg ) \
815− { \
816− ptw32_cleanup_t _cleanup; \
817− \
818− ptw32_push_cleanup( &_cleanup, (ptw32_cleanup_callback_t) (_rout), (_arg) ); \
819−
820−#define pthread_cleanup_pop( _execute ) \
821− (void) ptw32_pop_cleanup( _execute ); \
822− }
823−
824−#else /* __CLEANUP_C */
825−
826−#if defined(__CLEANUP_CXX)
827−
828− /*
829− * C++ version of cancel cleanup.
830− * - John E. Bossom.
831− */
832−
833− class PThreadCleanup {
834− /*
835− * PThreadCleanup
836− *
837− * Purpose
838− * This class is a C++ helper class that is
839− * used to implement pthread_cleanup_push/
840− * pthread_cleanup_pop.
841− * The destructor of this class automatically
842− * pops the pushed cleanup routine regardless
843− * of how the code exits the scope
844− * (i.e. such as by an exception)
845− */
846− ptw32_cleanup_callback_t cleanUpRout;
847− void * obj;
848− int executeIt;
849−
850− public:
851− PThreadCleanup() :
852− cleanUpRout( 0 ),
853− obj( 0 ),
854− executeIt( 0 )
855− /*
856− * No cleanup performed
857− */
858− {
859− }
860−
861− PThreadCleanup(
862− ptw32_cleanup_callback_t routine,
863− void * arg ) :
864− cleanUpRout( routine ),
865− obj( arg ),
866− executeIt( 1 )
867− /*
868− * Registers a cleanup routine for 'arg'
869− */
870− {
871− }
872−
873− ~PThreadCleanup()
874− {
875− if ( executeIt && ((void *) cleanUpRout != (void *) 0) )
876− {
877− (void) (*cleanUpRout)( obj );
878− }
879− }
880−
881− void execute( int exec )
882− {
883− executeIt = exec;
884− }
885− };
886−
887− /*
888− * C++ implementation of PThreads cancel cleanup;
889− * This implementation takes advantage of a helper
890− * class who's destructor automatically calls the
891− * cleanup routine if we exit our scope weirdly
892− */
893−#define pthread_cleanup_push( _rout, _arg ) \
894− { \
895− PThreadCleanup cleanup((ptw32_cleanup_callback_t)(_rout), \
896− (void *) (_arg) );
897−
898−#define pthread_cleanup_pop( _execute ) \
899− cleanup.execute( _execute ); \
900− }
901−
902−#else
903−
904−#error ERROR [__FILE__, line __LINE__]: Cleanup type undefined.
905−
906−#endif /* __CLEANUP_CXX */
907−
908−#endif /* __CLEANUP_C */
909−
910−#endif /* __CLEANUP_SEH */
911−
912−/*
913− * ===============
914− * ===============
915− * Methods
916− * ===============
917− * ===============
918− */
919−
920−/*
921− * PThread Attribute Functions
922− */
923−PTW32_DLLPORT int PTW32_CDECL pthread_attr_init (pthread_attr_t * attr);
924−
925−PTW32_DLLPORT int PTW32_CDECL pthread_attr_destroy (pthread_attr_t * attr);
926−
927−PTW32_DLLPORT int PTW32_CDECL pthread_attr_getdetachstate (const pthread_attr_t * attr,
928− int *detachstate);
929−
930−PTW32_DLLPORT int PTW32_CDECL pthread_attr_getstackaddr (const pthread_attr_t * attr,
931− void **stackaddr);
932−
933−PTW32_DLLPORT int PTW32_CDECL pthread_attr_getstacksize (const pthread_attr_t * attr,
934− size_t * stacksize);
935−
936−PTW32_DLLPORT int PTW32_CDECL pthread_attr_setdetachstate (pthread_attr_t * attr,
937− int detachstate);
938−
939−PTW32_DLLPORT int PTW32_CDECL pthread_attr_setstackaddr (pthread_attr_t * attr,
940− void *stackaddr);
941−
942−PTW32_DLLPORT int PTW32_CDECL pthread_attr_setstacksize (pthread_attr_t * attr,
943− size_t stacksize);
944−
945−PTW32_DLLPORT int PTW32_CDECL pthread_attr_getschedparam (const pthread_attr_t *attr,
946− struct sched_param *param);
947−
948−PTW32_DLLPORT int PTW32_CDECL pthread_attr_setschedparam (pthread_attr_t *attr,
949− const struct sched_param *param);
950−
951−PTW32_DLLPORT int PTW32_CDECL pthread_attr_setschedpolicy (pthread_attr_t *,
952− int);
953−
954−PTW32_DLLPORT int PTW32_CDECL pthread_attr_getschedpolicy (const pthread_attr_t *,
955− int *);
956−
957−PTW32_DLLPORT int PTW32_CDECL pthread_attr_setinheritsched(pthread_attr_t * attr,
958− int inheritsched);
959−
960−PTW32_DLLPORT int PTW32_CDECL pthread_attr_getinheritsched(const pthread_attr_t * attr,
961− int * inheritsched);
962−
963−PTW32_DLLPORT int PTW32_CDECL pthread_attr_setscope (pthread_attr_t *,
964− int);
965−
966−PTW32_DLLPORT int PTW32_CDECL pthread_attr_getscope (const pthread_attr_t *,
967− int *);
968−
969−/*
970− * PThread Functions
971− */
972−PTW32_DLLPORT int PTW32_CDECL pthread_create (pthread_t * tid,
973− const pthread_attr_t * attr,
974− void *(PTW32_CDECL *start) (void *),
975− void *arg);
976−
977−PTW32_DLLPORT int PTW32_CDECL pthread_detach (pthread_t tid);
978−
979−PTW32_DLLPORT int PTW32_CDECL pthread_equal (pthread_t t1,
980− pthread_t t2);
981−
982−PTW32_DLLPORT void PTW32_CDECL pthread_exit (void *value_ptr);
983−
984−PTW32_DLLPORT int PTW32_CDECL pthread_join (pthread_t thread,
985− void **value_ptr);
986−
987−PTW32_DLLPORT pthread_t PTW32_CDECL pthread_self (void);
988−
989−PTW32_DLLPORT int PTW32_CDECL pthread_cancel (pthread_t thread);
990−
991−PTW32_DLLPORT int PTW32_CDECL pthread_setcancelstate (int state,
992− int *oldstate);
993−
994−PTW32_DLLPORT int PTW32_CDECL pthread_setcanceltype (int type,
995− int *oldtype);
996−
997−PTW32_DLLPORT void PTW32_CDECL pthread_testcancel (void);
998−
999−PTW32_DLLPORT int PTW32_CDECL pthread_once (pthread_once_t * once_control,
1000− void (PTW32_CDECL *init_routine) (void));
1001−
1002−#if PTW32_LEVEL >= PTW32_LEVEL_MAX
1003−PTW32_DLLPORT ptw32_cleanup_t * PTW32_CDECL ptw32_pop_cleanup (int execute);
1004−
1005−PTW32_DLLPORT void PTW32_CDECL ptw32_push_cleanup (ptw32_cleanup_t * cleanup,
1006− ptw32_cleanup_callback_t routine,
1007− void *arg);
1008−#endif /* PTW32_LEVEL >= PTW32_LEVEL_MAX */
1009−
1010−/*
1011− * Thread Specific Data Functions
1012− */
1013−PTW32_DLLPORT int PTW32_CDECL pthread_key_create (pthread_key_t * key,
1014− void (PTW32_CDECL *destructor) (void *));
1015−
1016−PTW32_DLLPORT int PTW32_CDECL pthread_key_delete (pthread_key_t key);
1017−
1018−PTW32_DLLPORT int PTW32_CDECL pthread_setspecific (pthread_key_t key,
1019− const void *value);
1020−
1021−PTW32_DLLPORT void * PTW32_CDECL pthread_getspecific (pthread_key_t key);
1022−
1023−
1024−/*
1025− * Mutex Attribute Functions
1026− */
1027−PTW32_DLLPORT int PTW32_CDECL pthread_mutexattr_init (pthread_mutexattr_t * attr);
1028−
1029−PTW32_DLLPORT int PTW32_CDECL pthread_mutexattr_destroy (pthread_mutexattr_t * attr);
1030−
1031−PTW32_DLLPORT int PTW32_CDECL pthread_mutexattr_getpshared (const pthread_mutexattr_t
1032− * attr,
1033− int *pshared);
1034−
1035−PTW32_DLLPORT int PTW32_CDECL pthread_mutexattr_setpshared (pthread_mutexattr_t * attr,
1036− int pshared);
1037−
1038−PTW32_DLLPORT int PTW32_CDECL pthread_mutexattr_settype (pthread_mutexattr_t * attr, int kind);
1039−PTW32_DLLPORT int PTW32_CDECL pthread_mutexattr_gettype (const pthread_mutexattr_t * attr, int *kind);
1040−
1041−PTW32_DLLPORT int PTW32_CDECL pthread_mutexattr_setrobust(
1042− pthread_mutexattr_t *attr,
1043− int robust);
1044−PTW32_DLLPORT int PTW32_CDECL pthread_mutexattr_getrobust(
1045− const pthread_mutexattr_t * attr,
1046− int * robust);
1047−
1048−/*
1049− * Barrier Attribute Functions
1050− */
1051−PTW32_DLLPORT int PTW32_CDECL pthread_barrierattr_init (pthread_barrierattr_t * attr);
1052−
1053−PTW32_DLLPORT int PTW32_CDECL pthread_barrierattr_destroy (pthread_barrierattr_t * attr);
1054−
1055−PTW32_DLLPORT int PTW32_CDECL pthread_barrierattr_getpshared (const pthread_barrierattr_t
1056− * attr,
1057− int *pshared);
1058−
1059−PTW32_DLLPORT int PTW32_CDECL pthread_barrierattr_setpshared (pthread_barrierattr_t * attr,
1060− int pshared);
1061−
1062−/*
1063− * Mutex Functions
1064− */
1065−PTW32_DLLPORT int PTW32_CDECL pthread_mutex_init (pthread_mutex_t * mutex,
1066− const pthread_mutexattr_t * attr);
1067−
1068−PTW32_DLLPORT int PTW32_CDECL pthread_mutex_destroy (pthread_mutex_t * mutex);
1069−
1070−PTW32_DLLPORT int PTW32_CDECL pthread_mutex_lock (pthread_mutex_t * mutex);
1071−
1072−PTW32_DLLPORT int PTW32_CDECL pthread_mutex_timedlock(pthread_mutex_t * mutex,
1073− const struct timespec *abstime);
1074−
1075−PTW32_DLLPORT int PTW32_CDECL pthread_mutex_trylock (pthread_mutex_t * mutex);
1076−
1077−PTW32_DLLPORT int PTW32_CDECL pthread_mutex_unlock (pthread_mutex_t * mutex);
1078−
1079−PTW32_DLLPORT int PTW32_CDECL pthread_mutex_consistent (pthread_mutex_t * mutex);
1080−
1081−/*
1082− * Spinlock Functions
1083− */
1084−PTW32_DLLPORT int PTW32_CDECL pthread_spin_init (pthread_spinlock_t * lock, int pshared);
1085−
1086−PTW32_DLLPORT int PTW32_CDECL pthread_spin_destroy (pthread_spinlock_t * lock);
1087−
1088−PTW32_DLLPORT int PTW32_CDECL pthread_spin_lock (pthread_spinlock_t * lock);
1089−
1090−PTW32_DLLPORT int PTW32_CDECL pthread_spin_trylock (pthread_spinlock_t * lock);
1091−
1092−PTW32_DLLPORT int PTW32_CDECL pthread_spin_unlock (pthread_spinlock_t * lock);
1093−
1094−/*
1095− * Barrier Functions
1096− */
1097−PTW32_DLLPORT int PTW32_CDECL pthread_barrier_init (pthread_barrier_t * barrier,
1098− const pthread_barrierattr_t * attr,
1099− unsigned int count);
1100−
1101−PTW32_DLLPORT int PTW32_CDECL pthread_barrier_destroy (pthread_barrier_t * barrier);
1102−
1103−PTW32_DLLPORT int PTW32_CDECL pthread_barrier_wait (pthread_barrier_t * barrier);
1104−
1105−/*
1106− * Condition Variable Attribute Functions
1107− */
1108−PTW32_DLLPORT int PTW32_CDECL pthread_condattr_init (pthread_condattr_t * attr);
1109−
1110−PTW32_DLLPORT int PTW32_CDECL pthread_condattr_destroy (pthread_condattr_t * attr);
1111−
1112−PTW32_DLLPORT int PTW32_CDECL pthread_condattr_getpshared (const pthread_condattr_t * attr,
1113− int *pshared);
1114−
1115−PTW32_DLLPORT int PTW32_CDECL pthread_condattr_setpshared (pthread_condattr_t * attr,
1116− int pshared);
1117−
1118−/*
1119− * Condition Variable Functions
1120− */
1121−PTW32_DLLPORT int PTW32_CDECL pthread_cond_init (pthread_cond_t * cond,
1122− const pthread_condattr_t * attr);
1123−
1124−PTW32_DLLPORT int PTW32_CDECL pthread_cond_destroy (pthread_cond_t * cond);
1125−
1126−PTW32_DLLPORT int PTW32_CDECL pthread_cond_wait (pthread_cond_t * cond,
1127− pthread_mutex_t * mutex);
1128−
1129−PTW32_DLLPORT int PTW32_CDECL pthread_cond_timedwait (pthread_cond_t * cond,
1130− pthread_mutex_t * mutex,
1131− const struct timespec *abstime);
1132−
1133−PTW32_DLLPORT int PTW32_CDECL pthread_cond_signal (pthread_cond_t * cond);
1134−
1135−PTW32_DLLPORT int PTW32_CDECL pthread_cond_broadcast (pthread_cond_t * cond);
1136−
1137−/*
1138− * Scheduling
1139− */
1140−PTW32_DLLPORT int PTW32_CDECL pthread_setschedparam (pthread_t thread,
1141− int policy,
1142− const struct sched_param *param);
1143−
1144−PTW32_DLLPORT int PTW32_CDECL pthread_getschedparam (pthread_t thread,
1145− int *policy,
1146− struct sched_param *param);
1147−
1148−PTW32_DLLPORT int PTW32_CDECL pthread_setconcurrency (int);
1149−
1150−PTW32_DLLPORT int PTW32_CDECL pthread_getconcurrency (void);
1151−
1152−/*
1153− * Read-Write Lock Functions
1154− */
1155−PTW32_DLLPORT int PTW32_CDECL pthread_rwlock_init(pthread_rwlock_t *lock,
1156− const pthread_rwlockattr_t *attr);
1157−
1158−PTW32_DLLPORT int PTW32_CDECL pthread_rwlock_destroy(pthread_rwlock_t *lock);
1159−
1160−PTW32_DLLPORT int PTW32_CDECL pthread_rwlock_tryrdlock(pthread_rwlock_t *);
1161−
1162−PTW32_DLLPORT int PTW32_CDECL pthread_rwlock_trywrlock(pthread_rwlock_t *);
1163−
1164−PTW32_DLLPORT int PTW32_CDECL pthread_rwlock_rdlock(pthread_rwlock_t *lock);
1165−
1166−PTW32_DLLPORT int PTW32_CDECL pthread_rwlock_timedrdlock(pthread_rwlock_t *lock,
1167− const struct timespec *abstime);
1168−
1169−PTW32_DLLPORT int PTW32_CDECL pthread_rwlock_wrlock(pthread_rwlock_t *lock);
1170−
1171−PTW32_DLLPORT int PTW32_CDECL pthread_rwlock_timedwrlock(pthread_rwlock_t *lock,
1172− const struct timespec *abstime);
1173−
1174−PTW32_DLLPORT int PTW32_CDECL pthread_rwlock_unlock(pthread_rwlock_t *lock);
1175−
1176−PTW32_DLLPORT int PTW32_CDECL pthread_rwlockattr_init (pthread_rwlockattr_t * attr);
1177−
1178−PTW32_DLLPORT int PTW32_CDECL pthread_rwlockattr_destroy (pthread_rwlockattr_t * attr);
1179−
1180−PTW32_DLLPORT int PTW32_CDECL pthread_rwlockattr_getpshared (const pthread_rwlockattr_t * attr,
1181− int *pshared);
1182−
1183−PTW32_DLLPORT int PTW32_CDECL pthread_rwlockattr_setpshared (pthread_rwlockattr_t * attr,
1184− int pshared);
1185−
1186−#if PTW32_LEVEL >= PTW32_LEVEL_MAX - 1
1187−
1188−/*
1189− * Signal Functions. Should be defined in <signal.h> but MSVC and MinGW32
1190− * already have signal.h that don't define these.
1191− */
1192−PTW32_DLLPORT int PTW32_CDECL pthread_kill(pthread_t thread, int sig);
1193−
1194−/*
1195− * Non-portable functions
1196− */
1197−
1198−/*
1199− * Compatibility with Linux.
1200− */
1201−PTW32_DLLPORT int PTW32_CDECL pthread_mutexattr_setkind_np(pthread_mutexattr_t * attr,
1202− int kind);
1203−PTW32_DLLPORT int PTW32_CDECL pthread_mutexattr_getkind_np(pthread_mutexattr_t * attr,
1204− int *kind);
1205−PTW32_DLLPORT int PTW32_CDECL pthread_timedjoin_np(pthread_t thread,
1206− void **value_ptr,
1207− const struct timespec *abstime);
1208−
1209−/*
1210− * Possibly supported by other POSIX threads implementations
1211− */
1212−PTW32_DLLPORT int PTW32_CDECL pthread_delay_np (struct timespec * interval);
1213−PTW32_DLLPORT int PTW32_CDECL pthread_num_processors_np(void);
1214−PTW32_DLLPORT unsigned __int64 PTW32_CDECL pthread_getunique_np(pthread_t thread);
1215−
1216−/*
1217− * Useful if an application wants to statically link
1218− * the lib rather than load the DLL at run-time.
1219− */
1220−PTW32_DLLPORT int PTW32_CDECL pthread_win32_process_attach_np(void);
1221−PTW32_DLLPORT int PTW32_CDECL pthread_win32_process_detach_np(void);
1222−PTW32_DLLPORT int PTW32_CDECL pthread_win32_thread_attach_np(void);
1223−PTW32_DLLPORT int PTW32_CDECL pthread_win32_thread_detach_np(void);
1224−
1225−/*
1226− * Features that are auto-detected at load/run time.
1227− */
1228−PTW32_DLLPORT int PTW32_CDECL pthread_win32_test_features_np(int);
1229−enum ptw32_features {
1230− PTW32_SYSTEM_INTERLOCKED_COMPARE_EXCHANGE = 0x0001, /* System provides it. */
1231− PTW32_ALERTABLE_ASYNC_CANCEL = 0x0002 /* Can cancel blocked threads. */
1232−};
1233−
1234−/*
1235− * Register a system time change with the library.
1236− * Causes the library to perform various functions
1237− * in response to the change. Should be called whenever
1238− * the application's top level window receives a
1239− * WM_TIMECHANGE message. It can be passed directly to
1240− * pthread_create() as a new thread if desired.
1241− */
1242−PTW32_DLLPORT void * PTW32_CDECL pthread_timechange_handler_np(void *);
1243−
1244−#endif /*PTW32_LEVEL >= PTW32_LEVEL_MAX - 1 */
1245−
1246−#if PTW32_LEVEL >= PTW32_LEVEL_MAX
1247−
1248−/*
1249− * Returns the Win32 HANDLE for the POSIX thread.
1250− */
1251−PTW32_DLLPORT HANDLE PTW32_CDECL pthread_getw32threadhandle_np(pthread_t thread);
1252−/*
1253− * Returns the win32 thread ID for POSIX thread.
1254− */
1255−PTW32_DLLPORT DWORD PTW32_CDECL pthread_getw32threadid_np (pthread_t thread);
1256−
1257−
1258−/*
1259− * Protected Methods
1260− *
1261− * This function blocks until the given WIN32 handle
1262− * is signaled or pthread_cancel had been called.
1263− * This function allows the caller to hook into the
1264− * PThreads cancel mechanism. It is implemented using
1265− *
1266− * WaitForMultipleObjects
1267− *
1268− * on 'waitHandle' and a manually reset WIN32 Event
1269− * used to implement pthread_cancel. The 'timeout'
1270− * argument to TimedWait is simply passed to
1271− * WaitForMultipleObjects.
1272− */
1273−PTW32_DLLPORT int PTW32_CDECL pthreadCancelableWait (HANDLE waitHandle);
1274−PTW32_DLLPORT int PTW32_CDECL pthreadCancelableTimedWait (HANDLE waitHandle,
1275− DWORD timeout);
1276−
1277−#endif /* PTW32_LEVEL >= PTW32_LEVEL_MAX */
1278−
1279−/*
1280− * Thread-Safe C Runtime Library Mappings.
1281− */
1282−#if !defined(_UWIN)
1283−# if defined(NEED_ERRNO)
1284− PTW32_DLLPORT int * PTW32_CDECL _errno( void );
1285−# else
1286−# if !defined(errno)
1287−# if (defined(_MT) || defined(_DLL))
1288− __declspec(dllimport) extern int * __cdecl _errno(void);
1289−# define errno (*_errno())
1290−# endif
1291−# endif
1292−# endif
1293−#endif
1294−
1295−/*
1296− * Some compiler environments don't define some things.
1297− */
1298−#if defined(__BORLANDC__)
1299−# define _ftime ftime
1300−# define _timeb timeb
1301−#endif
1302−
1303−#if defined(__cplusplus)
1304−
1305−/*
1306− * Internal exceptions
1307− */
1308−class ptw32_exception {};
1309−class ptw32_exception_cancel : public ptw32_exception {};
1310−class ptw32_exception_exit : public ptw32_exception {};
1311−
1312−#endif
1313−
1314−#if PTW32_LEVEL >= PTW32_LEVEL_MAX
1315−
1316−/* FIXME: This is only required if the library was built using SEH */
1317−/*
1318− * Get internal SEH tag
1319− */
1320−PTW32_DLLPORT DWORD PTW32_CDECL ptw32_get_exception_services_code(void);
1321−
1322−#endif /* PTW32_LEVEL >= PTW32_LEVEL_MAX */
1323−
1324−#if !defined(PTW32_BUILD)
1325−
1326−#if defined(__CLEANUP_SEH)
1327−
1328−/*
1329− * Redefine the SEH __except keyword to ensure that applications
1330− * propagate our internal exceptions up to the library's internal handlers.
1331− */
1332−#define __except( E ) \
1333− __except( ( GetExceptionCode() == ptw32_get_exception_services_code() ) \
1334− ? EXCEPTION_CONTINUE_SEARCH : ( E ) )
1335−
1336−#endif /* __CLEANUP_SEH */
1337−
1338−#if defined(__CLEANUP_CXX)
1339−
1340−/*
1341− * Redefine the C++ catch keyword to ensure that applications
1342− * propagate our internal exceptions up to the library's internal handlers.
1343− */
1344−#if defined(_MSC_VER)
1345− /*
1346− * WARNING: Replace any 'catch( ... )' with 'PtW32CatchAll'
1347− * if you want Pthread-Win32 cancellation and pthread_exit to work.
1348− */
1349−
1350−#if !defined(PtW32NoCatchWarn)
1351−
1352−#pragma message("Specify \"/DPtW32NoCatchWarn\" compiler flag to skip this message.")
1353−#pragma message("------------------------------------------------------------------")
1354−#pragma message("When compiling applications with MSVC++ and C++ exception handling:")
1355−#pragma message(" Replace any 'catch( ... )' in routines called from POSIX threads")
1356−#pragma message(" with 'PtW32CatchAll' or 'CATCHALL' if you want POSIX thread")
1357−#pragma message(" cancellation and pthread_exit to work. For example:")
1358−#pragma message("")
1359−#pragma message(" #if defined(PtW32CatchAll)")
1360−#pragma message(" PtW32CatchAll")
1361−#pragma message(" #else")
1362−#pragma message(" catch(...)")
1363−#pragma message(" #endif")
1364−#pragma message(" {")
1365−#pragma message(" /* Catchall block processing */")
1366−#pragma message(" }")
1367−#pragma message("------------------------------------------------------------------")
1368−
1369−#endif
1370−
1371−#define PtW32CatchAll \
1372− catch( ptw32_exception & ) { throw; } \
1373− catch( ... )
1374−
1375−#else /* _MSC_VER */
1376−
1377−#define catch( E ) \
1378− catch( ptw32_exception & ) { throw; } \
1379− catch( E )
1380−
1381−#endif /* _MSC_VER */
1382−
1383−#endif /* __CLEANUP_CXX */
1384−
1385−#endif /* ! PTW32_BUILD */
1386−
1387−#if defined(__cplusplus)
1388−} /* End of extern "C" */
1389−#endif /* __cplusplus */
1390−
1391−#if defined(PTW32__HANDLE_DEF)
1392−# undef HANDLE
1393−#endif
1394−#if defined(PTW32__DWORD_DEF)
1395−# undef DWORD
1396−#endif
1397−
1398−#undef PTW32_LEVEL
1399−#undef PTW32_LEVEL_MAX
1400−
1401−#endif /* ! RC_INVOKED */
1402−
1403−#endif /* PTHREAD_H */
libraries/shared/external/sched.h+0−189
@@ -1,189 +0,0 @@
1−/*
2− * Module: sched.h
3− *
4− * Purpose:
5− * Provides an implementation of POSIX realtime extensions
6− * as defined in
7− *
8− * POSIX 1003.1b-1993 (POSIX.1b)
9− *
10− * --------------------------------------------------------------------------
11− *
12− * Pthreads-win32 - POSIX Threads Library for Win32
13− * Copyright(C) 1998 John E. Bossom
14− * Copyright(C) 1999,2005 Pthreads-win32 contributors
15− *
16− * Contact Email: rpj@callisto.canberra.edu.au
17− *
18− * The current list of contributors is contained
19− * in the file CONTRIBUTORS included with the source
20− * code distribution. The list can also be seen at the
21− * following World Wide Web location:
22− * http://sources.redhat.com/pthreads-win32/contributors.html
23− *
24− * This library is free software; you can redistribute it and/or
25− * modify it under the terms of the GNU Lesser General Public
26− * License as published by the Free Software Foundation; either
27− * version 2 of the License, or (at your option) any later version.
28− *
29− * This library is distributed in the hope that it will be useful,
30− * but WITHOUT ANY WARRANTY; without even the implied warranty of
31− * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
32− * Lesser General Public License for more details.
33− *
34− * You should have received a copy of the GNU Lesser General Public
35− * License along with this library in the file COPYING.LIB;
36− * if not, write to the Free Software Foundation, Inc.,
37− * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
38− */
39−#if !defined(_SCHED_H)
40−#define _SCHED_H
41−
42−#undef PTW32_SCHED_LEVEL
43−
44−#if defined(_POSIX_SOURCE)
45−#define PTW32_SCHED_LEVEL 0
46−/* Early POSIX */
47−#endif
48−
49−#if defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 199309
50−#undef PTW32_SCHED_LEVEL
51−#define PTW32_SCHED_LEVEL 1
52−/* Include 1b, 1c and 1d */
53−#endif
54−
55−#if defined(INCLUDE_NP)
56−#undef PTW32_SCHED_LEVEL
57−#define PTW32_SCHED_LEVEL 2
58−/* Include Non-Portable extensions */
59−#endif
60−
61−#define PTW32_SCHED_LEVEL_MAX 3
62−
63−#if ( defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 200112 ) || !defined(PTW32_SCHED_LEVEL)
64−#define PTW32_SCHED_LEVEL PTW32_SCHED_LEVEL_MAX
65−/* Include everything */
66−#endif
67−
68−
69−#if defined(__GNUC__) && !defined(__declspec)
70−# error Please upgrade your GNU compiler to one that supports __declspec.
71−#endif
72−
73−/*
74− * When building the library, you should define PTW32_BUILD so that
75− * the variables/functions are exported correctly. When using the library,
76− * do NOT define PTW32_BUILD, and then the variables/functions will
77− * be imported correctly.
78− */
79−#if !defined(PTW32_STATIC_LIB)
80−# if defined(PTW32_BUILD)
81−# define PTW32_DLLPORT __declspec (dllexport)
82−# else
83−# define PTW32_DLLPORT __declspec (dllimport)
84−# endif
85−#else
86−# define PTW32_DLLPORT
87−#endif
88−
89−/*
90− * This is a duplicate of what is in the autoconf config.h,
91− * which is only used when building the pthread-win32 libraries.
92− */
93−
94−#if !defined(PTW32_CONFIG_H)
95−# if defined(WINCE)
96−# define NEED_ERRNO
97−# define NEED_SEM
98−# endif
99−# if defined(__MINGW64__)
100−# define HAVE_STRUCT_TIMESPEC
101−# define HAVE_MODE_T
102−# elif defined(_UWIN) || defined(__MINGW32__)
103−# define HAVE_MODE_T
104−# endif
105−#endif
106−
107−/*
108− *
109− */
110−
111−#if PTW32_SCHED_LEVEL >= PTW32_SCHED_LEVEL_MAX
112−#if defined(NEED_ERRNO)
113−#include "need_errno.h"
114−#else
115−#include <errno.h>
116−#endif
117−#endif /* PTW32_SCHED_LEVEL >= PTW32_SCHED_LEVEL_MAX */
118−
119−#if (defined(__MINGW64__) || defined(__MINGW32__)) || defined(_UWIN)
120−# if PTW32_SCHED_LEVEL >= PTW32_SCHED_LEVEL_MAX
121−/* For pid_t */
122−# include <sys/types.h>
123−/* Required by Unix 98 */
124−# include <time.h>
125−# else
126− typedef int pid_t;
127−# endif
128−#else
129− /* [i_a] fix for using pthread_win32 with mongoose code, which #define's its own pid_t akin to typedef HANDLE pid_t; */
130− #undef pid_t
131−# if defined(_MSC_VER)
132− typedef void *pid_t;
133−# else
134− typedef int pid_t;
135−# endif
136−#endif
137−
138−/* Thread scheduling policies */
139−
140−enum {
141− SCHED_OTHER = 0,
142− SCHED_FIFO,
143− SCHED_RR,
144− SCHED_MIN = SCHED_OTHER,
145− SCHED_MAX = SCHED_RR
146−};
147−
148−struct sched_param {
149− int sched_priority;
150−};
151−
152−#if defined(__cplusplus)
153−extern "C"
154−{
155−#endif /* __cplusplus */
156−
157−PTW32_DLLPORT int __cdecl sched_yield (void);
158−
159−PTW32_DLLPORT int __cdecl sched_get_priority_min (int policy);
160−
161−PTW32_DLLPORT int __cdecl sched_get_priority_max (int policy);
162−
163−PTW32_DLLPORT int __cdecl sched_setscheduler (pid_t pid, int policy);
164−
165−PTW32_DLLPORT int __cdecl sched_getscheduler (pid_t pid);
166−
167−/*
168− * Note that this macro returns ENOTSUP rather than
169− * ENOSYS as might be expected. However, returning ENOSYS
170− * should mean that sched_get_priority_{min,max} are
171− * not implemented as well as sched_rr_get_interval.
172− * This is not the case, since we just don't support
173− * round-robin scheduling. Therefore I have chosen to
174− * return the same value as sched_setscheduler when
175− * SCHED_RR is passed to it.
176− */
177−#define sched_rr_get_interval(_pid, _interval) \
178− ( errno = ENOTSUP, (int) -1 )
179−
180−
181−#if defined(__cplusplus)
182−} /* End of extern "C" */
183−#endif /* __cplusplus */
184−
185−#undef PTW32_SCHED_LEVEL
186−#undef PTW32_SCHED_LEVEL_MAX
187−
188−#endif /* !_SCHED_H */
189−
libraries/shared/src/AngularConstraint.cpp+2−1
@@ -11,8 +11,9 @@ · 10 unmodified lines
1111
1212 #include <glm/gtx/norm.hpp>
1313
14+#include "GLMHelpers.h"
15+
1416 #include "AngularConstraint.h"
15−#include "SharedUtil.h"
1617
1718 // helper function
1819 /// \param angle radian angle to be clamped within angleMin and angleMax
libraries/shared/src/AngularConstraint.h+0−1
@@ -14,7 +14,6 @@ · 13 unmodified lines
1414
1515 #include <glm/glm.hpp>
1616
17−
1817 class AngularConstraint {
1918 public:
2019 /// \param minAngles minumum euler angles for the constraint
libraries/shared/src/ContactPoint.cpp+108−72
@@ -13,56 +13,128 @@ · 12 unmodified lines
1313 #include "Shape.h"
1414 #include "SharedUtil.h"
1515
16−ContactPoint::ContactPoint() : _lastFrame(0), _shapeA(NULL), _shapeB(NULL),
17− _offsetA(0.0f), _offsetB(0.0f), _normal(0.0f) {
16+// This parameter helps keep the actual point of contact slightly inside each shape
17+// which allows the collisions to happen almost every frame for more frequent updates.
18+const float CONTACT_PENETRATION_ALLOWANCE = 0.005f;
19+
20+ContactPoint::ContactPoint() :
21+ _lastFrame(0), _shapeA(NULL), _shapeB(NULL),
22+ _offsetA(0.0f), _offsetB(0.0f),
23+ _relativeMassA(0.5f), _relativeMassB(0.5f),
24+ _numPointsA(0), _numPoints(0), _normal(0.0f) {
1825 }
1926
20−ContactPoint::ContactPoint(const CollisionInfo& collision, quint32 frame) : _lastFrame(frame),
21− _shapeA(collision.getShapeA()), _shapeB(collision.getShapeB()), _offsetA(0.0f), _offsetB(0.0f),
27+ContactPoint::ContactPoint(const CollisionInfo& collision, quint32 frame) :
28+ _lastFrame(frame), _shapeA(collision.getShapeA()), _shapeB(collision.getShapeB()),
29+ _offsetA(0.0f), _offsetB(0.0f),
30+ _relativeMassA(0.5f), _relativeMassB(0.5f),
2231 _numPointsA(0), _numPoints(0), _normal(0.0f) {
2332
24− _contactPoint = collision._contactPoint - 0.5f * collision._penetration;
25− _offsetA = collision._contactPoint - _shapeA->getTranslation();
26− _offsetB = collision._contactPoint - collision._penetration - _shapeB->getTranslation();
33+ glm::vec3 pointA = collision._contactPoint;
34+ glm::vec3 pointB = collision._contactPoint - collision._penetration;
35+
2736 float pLength = glm::length(collision._penetration);
2837 if (pLength > EPSILON) {
2938 _normal = collision._penetration / pLength;
3039 }
31−
3240 if (_shapeA->getID() > _shapeB->getID()) {
3341 // swap so that _shapeA always has lower ID
3442 _shapeA = collision.getShapeB();
3543 _shapeB = collision.getShapeA();
36−
37− glm::vec3 temp = _offsetA;
38− _offsetA = _offsetB;
39− _offsetB = temp;
4044 _normal = - _normal;
45+ pointA = pointB;
46+ pointB = collision._contactPoint;
4147 }
4248
49+ // bring the contact points inside the shapes to help maintain collision updates
50+ pointA -= CONTACT_PENETRATION_ALLOWANCE * _normal;
51+ pointB += CONTACT_PENETRATION_ALLOWANCE * _normal;
52+
53+ _offsetA = pointA - _shapeA->getTranslation();
54+ _offsetB = pointB - _shapeB->getTranslation();
55+
4356 _shapeA->getVerletPoints(_points);
4457 _numPointsA = _points.size();
4558 _shapeB->getVerletPoints(_points);
4659 _numPoints = _points.size();
4760
61+ // compute and cache relative masses
62+ float massA = EPSILON;
63+ for (int i = 0; i < _numPointsA; ++i) {
64+ massA += _points[i]->getMass();
65+ }
66+ float massB = EPSILON;
67+ for (int i = _numPointsA; i < _numPoints; ++i) {
68+ massB += _points[i]->getMass();
69+ }
70+ float invTotalMass = 1.0f / (massA + massB);
71+ _relativeMassA = massA * invTotalMass;
72+ _relativeMassB = massB * invTotalMass;
73+
74+ // _contactPoint will be the weighted average of the two
75+ _contactPoint = _relativeMassA * pointA + _relativeMassB * pointB;
76+
4877 // compute offsets for shapeA
4978 for (int i = 0; i < _numPointsA; ++i) {
50− glm::vec3 offset = _points[i]->_position - collision._contactPoint;
79+ glm::vec3 offset = _points[i]->_position - pointA;
5180 _offsets.push_back(offset);
5281 _distances.push_back(glm::length(offset));
5382 }
5483 // compute offsets for shapeB
5584 for (int i = _numPointsA; i < _numPoints; ++i) {
56− glm::vec3 offset = _points[i]->_position - collision._contactPoint + collision._penetration;
85+ glm::vec3 offset = _points[i]->_position - pointB;
5786 _offsets.push_back(offset);
5887 _distances.push_back(glm::length(offset));
5988 }
6089 }
6190
62−// virtual
6391 float ContactPoint::enforce() {
64− int numPoints = _points.size();
65− for (int i = 0; i < numPoints; ++i) {
92+ glm::vec3 pointA = _shapeA->getTranslation() + _offsetA;
93+ glm::vec3 pointB = _shapeB->getTranslation() + _offsetB;
94+ glm::vec3 penetration = pointA - pointB;
95+ float pDotN = glm::dot(penetration, _normal);
96+ bool constraintViolation = (pDotN > CONTACT_PENETRATION_ALLOWANCE);
97+
98+ // the contact point will be the average of the two points on the shapes
99+ _contactPoint = 0.5f * (pointA + pointB);
100+
101+ if (constraintViolation) {
102+ for (int i = 0; i < _numPoints; ++i) {
103+ VerletPoint* point = _points[i];
104+ glm::vec3 offset = _offsets[i];
105+
106+ // split delta into parallel and perpendicular components
107+ glm::vec3 delta = _contactPoint + offset - point->_position;
108+ glm::vec3 paraDelta = glm::dot(delta, _normal) * _normal;
109+ glm::vec3 perpDelta = delta - paraDelta;
110+
111+ // use the relative sizes of the components to decide how much perpenducular delta to use
112+ // perpendicular < parallel ==> static friction ==> perpFactor = 1.0
113+ // perpendicular > parallel ==> dynamic friction ==> cap to length of paraDelta ==> perpFactor < 1.0
114+ float paraLength = glm::length(paraDelta);
115+ float perpLength = glm::length(perpDelta);
116+ float perpFactor = (perpLength > paraLength && perpLength > EPSILON) ? (paraLength / perpLength) : 1.0f;
117+
118+ // recombine the two components to get the final delta
119+ delta = paraDelta + perpFactor * perpDelta;
120+
121+ glm::vec3 targetPosition = point->_position + delta;
122+ _distances[i] = glm::distance(_contactPoint, targetPosition);
123+ point->_position += delta;
124+ }
125+ } else {
126+ for (int i = 0; i < _numPoints; ++i) {
127+ _distances[i] = glm::length(glm::length(_offsets[i]));
128+ }
129+ }
130+ return 0.0f;
131+}
132+
133+// virtual
134+void ContactPoint::applyFriction() {
135+ // TODO: Andrew to re-implement this in a different way
136+ /*
137+ for (int i = 0; i < _numPoints; ++i) {
66138 glm::vec3& position = _points[i]->_position;
67139 // TODO: use a fast distance approximation
68140 float newDistance = glm::distance(_contactPoint, position);
@@ -75,60 +147,17 @@ float ContactPoint::enforce() { · 6 unmodified lines
75147 position = center - (0.5f * constrainedDistance) * direction;
76148 }
77149 }
78− return 0.0f;
79−}
80−
81−void ContactPoint::buildConstraints() {
82− glm::vec3 pointA = _shapeA->getTranslation() + _offsetA;
83− glm::vec3 pointB = _shapeB->getTranslation() + _offsetB;
84− glm::vec3 penetration = pointA - pointB;
85− float pDotN = glm::dot(penetration, _normal);
86− bool actuallyMovePoints = (pDotN > EPSILON);
87−
88− // the contact point will be the average of the two points on the shapes
89− _contactPoint = 0.5f * (pointA + pointB);
90−
91− // TODO: Andrew to compute more correct lagrangian weights that provide a more realistic response.
92− //
93− // HACK: since the weights are naively equal for all points (which is what the above TODO is about) we
94− // don't want to use the full-strength delta because otherwise there can be annoying oscillations. We
95− // reduce this problem by in the short-term by attenuating the delta that is applied, the tradeoff is
96− // that this makes it easier for limbs to tunnel through during collisions.
97− const float HACK_STRENGTH = 0.5f;
98−
99− int numPoints = _points.size();
100− for (int i = 0; i < numPoints; ++i) {
101− VerletPoint* point = _points[i];
102− glm::vec3 offset = _offsets[i];
103−
104− // split delta into parallel and perpendicular components
105− glm::vec3 delta = _contactPoint + offset - point->_position;
106− glm::vec3 paraDelta = glm::dot(delta, _normal) * _normal;
107− glm::vec3 perpDelta = delta - paraDelta;
108−
109− // use the relative sizes of the components to decide how much perpenducular delta to use
110− // perpendicular < parallel ==> static friciton ==> perpFactor = 1.0
111− // perpendicular > parallel ==> dynamic friciton ==> cap to length of paraDelta ==> perpFactor < 1.0
112− float paraLength = glm::length(paraDelta);
113− float perpLength = glm::length(perpDelta);
114− float perpFactor = (perpLength > paraLength && perpLength > EPSILON) ? (paraLength / perpLength) : 1.0f;
115−
116− // recombine the two components to get the final delta
117− delta = paraDelta + perpFactor * perpDelta;
118−
119− glm::vec3 targetPosition = point->_position + delta;
120− _distances[i] = glm::distance(_contactPoint, targetPosition);
121− if (actuallyMovePoints) {
122− point->_position += HACK_STRENGTH * delta;
123− }
124− }
150+ */
125151 }
126152
127153 void ContactPoint::updateContact(const CollisionInfo& collision, quint32 frame) {
128154 _lastFrame = frame;
129− _contactPoint = collision._contactPoint - 0.5f * collision._penetration;
130− _offsetA = collision._contactPoint - collision._shapeA->getTranslation();
131− _offsetB = collision._contactPoint - collision._penetration - collision._shapeB->getTranslation();
155+
156+ // compute contact points on surface of each shape
157+ glm::vec3 pointA = collision._contactPoint;
158+ glm::vec3 pointB = pointA - collision._penetration;
159+
160+ // compute the normal (which points from A into B)
132161 float pLength = glm::length(collision._penetration);
133162 if (pLength > EPSILON) {
134163 _normal = collision._penetration / pLength;
@@ -138,19 +167,26 @@ void ContactPoint::updateContact(const CollisionInfo& collision, quint32 frame) · 3 unmodified lines
138167
139168 if (collision._shapeA->getID() > collision._shapeB->getID()) {
140169 // our _shapeA always has lower ID
141− glm::vec3 temp = _offsetA;
142− _offsetA = _offsetB;
143− _offsetB = temp;
144170 _normal = - _normal;
171+ pointA = pointB;
172+ pointB = collision._contactPoint;
145173 }
174+
175+ // bring the contact points inside the shapes to help maintain collision updates
176+ pointA -= CONTACT_PENETRATION_ALLOWANCE * _normal;
177+ pointB += CONTACT_PENETRATION_ALLOWANCE * _normal;
178+
179+ // compute relative offsets to per-shape contact points
180+ _offsetA = pointA - collision._shapeA->getTranslation();
181+ _offsetB = pointB - collision._shapeB->getTranslation();
146182
147183 // compute offsets for shapeA
148184 assert(_offsets.size() == _numPoints);
149185 for (int i = 0; i < _numPointsA; ++i) {
150− _offsets[i] = (_points[i]->_position - collision._contactPoint);
186+ _offsets[i] = _points[i]->_position - pointA;
151187 }
152188 // compute offsets for shapeB
153189 for (int i = _numPointsA; i < _numPoints; ++i) {
154− _offsets[i] = (_points[i]->_position - collision._contactPoint + collision._penetration);
190+ _offsets[i] = _points[i]->_position - pointB;
155191 }
156192 }
libraries/shared/src/ContactPoint.h+4−2
@@ -26,8 +26,8 @@ public: · 25 unmodified lines
2626 ContactPoint(const CollisionInfo& collision, quint32 frame);
2727
2828 virtual float enforce();
29−
30− void buildConstraints();
29+
30+ void applyFriction();
3131 void updateContact(const CollisionInfo& collision, quint32 frame);
3232 quint32 getLastFrame() const { return _lastFrame; }
3333
@@ -41,6 +41,8 @@ protected: · 7 unmodified lines
4141 glm::vec3 _offsetA; // contact point relative to A's center
4242 glm::vec3 _offsetB; // contact point relative to B's center
4343 glm::vec3 _contactPoint; // a "virtual" point that is added to the simulation
44+ float _relativeMassA; // massA / totalMass
45+ float _relativeMassB; // massB / totalMass
4446 int _numPointsA; // number of VerletPoints that belong to _shapeA
4547 int _numPoints; // total number of VerletPoints
4648 QVector<VerletPoint*> _points; // points that belong to colliding shapes
libraries/shared/src/GLMHelpers.cpp+299−0
@@ -0,0 +1,299 @@
1+//
2+// GLMHelpers.cpp
3+// libraries/shared/src
4+//
5+// Created by Stephen Birarda on 2014-08-07.
6+// Copyright 2014 High Fidelity, Inc.
7+//
8+// Distributed under the Apache License, Version 2.0.
9+// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
10+//
11+
12+#include "GLMHelpers.h"
13+
14+// Safe version of glm::mix; based on the code in Nick Bobick's article,
15+// http://www.gamasutra.com/features/19980703/quaternions_01.htm (via Clyde,
16+// https://github.com/threerings/clyde/blob/master/src/main/java/com/threerings/math/Quaternion.java)
17+glm::quat safeMix(const glm::quat& q1, const glm::quat& q2, float proportion) {
18+ float cosa = q1.x * q2.x + q1.y * q2.y + q1.z * q2.z + q1.w * q2.w;
19+ float ox = q2.x, oy = q2.y, oz = q2.z, ow = q2.w, s0, s1;
20+
21+ // adjust signs if necessary
22+ if (cosa < 0.0f) {
23+ cosa = -cosa;
24+ ox = -ox;
25+ oy = -oy;
26+ oz = -oz;
27+ ow = -ow;
28+ }
29+
30+ // calculate coefficients; if the angle is too close to zero, we must fall back
31+ // to linear interpolation
32+ if ((1.0f - cosa) > EPSILON) {
33+ float angle = acosf(cosa), sina = sinf(angle);
34+ s0 = sinf((1.0f - proportion) * angle) / sina;
35+ s1 = sinf(proportion * angle) / sina;
36+
37+ } else {
38+ s0 = 1.0f - proportion;
39+ s1 = proportion;
40+ }
41+
42+ return glm::normalize(glm::quat(s0 * q1.w + s1 * ow, s0 * q1.x + s1 * ox, s0 * q1.y + s1 * oy, s0 * q1.z + s1 * oz));
43+}
44+
45+// Allows sending of fixed-point numbers: radix 1 makes 15.1 number, radix 8 makes 8.8 number, etc
46+int packFloatScalarToSignedTwoByteFixed(unsigned char* buffer, float scalar, int radix) {
47+ int16_t outVal = (int16_t)(scalar * (float)(1 << radix));
48+ memcpy(buffer, &outVal, sizeof(uint16_t));
49+ return sizeof(uint16_t);
50+}
51+
52+int unpackFloatScalarFromSignedTwoByteFixed(const int16_t* byteFixedPointer, float* destinationPointer, int radix) {
53+ *destinationPointer = *byteFixedPointer / (float)(1 << radix);
54+ return sizeof(int16_t);
55+}
56+
57+int packFloatVec3ToSignedTwoByteFixed(unsigned char* destBuffer, const glm::vec3& srcVector, int radix) {
58+ const unsigned char* startPosition = destBuffer;
59+ destBuffer += packFloatScalarToSignedTwoByteFixed(destBuffer, srcVector.x, radix);
60+ destBuffer += packFloatScalarToSignedTwoByteFixed(destBuffer, srcVector.y, radix);
61+ destBuffer += packFloatScalarToSignedTwoByteFixed(destBuffer, srcVector.z, radix);
62+ return destBuffer - startPosition;
63+}
64+
65+int unpackFloatVec3FromSignedTwoByteFixed(const unsigned char* sourceBuffer, glm::vec3& destination, int radix) {
66+ const unsigned char* startPosition = sourceBuffer;
67+ sourceBuffer += unpackFloatScalarFromSignedTwoByteFixed((int16_t*) sourceBuffer, &(destination.x), radix);
68+ sourceBuffer += unpackFloatScalarFromSignedTwoByteFixed((int16_t*) sourceBuffer, &(destination.y), radix);
69+ sourceBuffer += unpackFloatScalarFromSignedTwoByteFixed((int16_t*) sourceBuffer, &(destination.z), radix);
70+ return sourceBuffer - startPosition;
71+}
72+
73+
74+int packFloatAngleToTwoByte(unsigned char* buffer, float degrees) {
75+ const float ANGLE_CONVERSION_RATIO = (std::numeric_limits<uint16_t>::max() / 360.f);
76+
77+ uint16_t angleHolder = floorf((degrees + 180.f) * ANGLE_CONVERSION_RATIO);
78+ memcpy(buffer, &angleHolder, sizeof(uint16_t));
79+
80+ return sizeof(uint16_t);
81+}
82+
83+int unpackFloatAngleFromTwoByte(const uint16_t* byteAnglePointer, float* destinationPointer) {
84+ *destinationPointer = (*byteAnglePointer / (float) std::numeric_limits<uint16_t>::max()) * 360.f - 180.f;
85+ return sizeof(uint16_t);
86+}
87+
88+int packOrientationQuatToBytes(unsigned char* buffer, const glm::quat& quatInput) {
89+ const float QUAT_PART_CONVERSION_RATIO = (std::numeric_limits<uint16_t>::max() / 2.f);
90+ uint16_t quatParts[4];
91+ quatParts[0] = floorf((quatInput.x + 1.f) * QUAT_PART_CONVERSION_RATIO);
92+ quatParts[1] = floorf((quatInput.y + 1.f) * QUAT_PART_CONVERSION_RATIO);
93+ quatParts[2] = floorf((quatInput.z + 1.f) * QUAT_PART_CONVERSION_RATIO);
94+ quatParts[3] = floorf((quatInput.w + 1.f) * QUAT_PART_CONVERSION_RATIO);
95+
96+ memcpy(buffer, &quatParts, sizeof(quatParts));
97+ return sizeof(quatParts);
98+}
99+
100+int unpackOrientationQuatFromBytes(const unsigned char* buffer, glm::quat& quatOutput) {
101+ uint16_t quatParts[4];
102+ memcpy(&quatParts, buffer, sizeof(quatParts));
103+
104+ quatOutput.x = ((quatParts[0] / (float) std::numeric_limits<uint16_t>::max()) * 2.f) - 1.f;
105+ quatOutput.y = ((quatParts[1] / (float) std::numeric_limits<uint16_t>::max()) * 2.f) - 1.f;
106+ quatOutput.z = ((quatParts[2] / (float) std::numeric_limits<uint16_t>::max()) * 2.f) - 1.f;
107+ quatOutput.w = ((quatParts[3] / (float) std::numeric_limits<uint16_t>::max()) * 2.f) - 1.f;
108+
109+ return sizeof(quatParts);
110+}
111+
112+// Safe version of glm::eulerAngles; uses the factorization method described in David Eberly's
113+// http://www.geometrictools.com/Documentation/EulerAngles.pdf (via Clyde,
114+// https://github.com/threerings/clyde/blob/master/src/main/java/com/threerings/math/Quaternion.java)
115+glm::vec3 safeEulerAngles(const glm::quat& q) {
116+ float sy = 2.0f * (q.y * q.w - q.x * q.z);
117+ glm::vec3 eulers;
118+ if (sy < 1.0f - EPSILON) {
119+ if (sy > -1.0f + EPSILON) {
120+ eulers = glm::vec3(
121+ atan2f(q.y * q.z + q.x * q.w, 0.5f - (q.x * q.x + q.y * q.y)),
122+ asinf(sy),
123+ atan2f(q.x * q.y + q.z * q.w, 0.5f - (q.y * q.y + q.z * q.z)));
124+
125+ } else {
126+ // not a unique solution; x + z = atan2(-m21, m11)
127+ eulers = glm::vec3(
128+ 0.0f,
129+ - PI_OVER_TWO,
130+ atan2f(q.x * q.w - q.y * q.z, 0.5f - (q.x * q.x + q.z * q.z)));
131+ }
132+ } else {
133+ // not a unique solution; x - z = atan2(-m21, m11)
134+ eulers = glm::vec3(
135+ 0.0f,
136+ PI_OVER_TWO,
137+ -atan2f(q.x * q.w - q.y * q.z, 0.5f - (q.x * q.x + q.z * q.z)));
138+ }
139+
140+ // adjust so that z, rather than y, is in [-pi/2, pi/2]
141+ if (eulers.z < -PI_OVER_TWO) {
142+ if (eulers.x < 0.0f) {
143+ eulers.x += PI;
144+ } else {
145+ eulers.x -= PI;
146+ }
147+ eulers.y = -eulers.y;
148+ if (eulers.y < 0.0f) {
149+ eulers.y += PI;
150+ } else {
151+ eulers.y -= PI;
152+ }
153+ eulers.z += PI;
154+
155+ } else if (eulers.z > PI_OVER_TWO) {
156+ if (eulers.x < 0.0f) {
157+ eulers.x += PI;
158+ } else {
159+ eulers.x -= PI;
160+ }
161+ eulers.y = -eulers.y;
162+ if (eulers.y < 0.0f) {
163+ eulers.y += PI;
164+ } else {
165+ eulers.y -= PI;
166+ }
167+ eulers.z -= PI;
168+ }
169+ return eulers;
170+}
171+
172+// Helper function returns the positive angle (in radians) between two 3D vectors
173+float angleBetween(const glm::vec3& v1, const glm::vec3& v2) {
174+ return acosf((glm::dot(v1, v2)) / (glm::length(v1) * glm::length(v2)));
175+}
176+
177+// Helper function return the rotation from the first vector onto the second
178+glm::quat rotationBetween(const glm::vec3& v1, const glm::vec3& v2) {
179+ float angle = angleBetween(v1, v2);
180+ if (glm::isnan(angle) || angle < EPSILON) {
181+ return glm::quat();
182+ }
183+ glm::vec3 axis;
184+ if (angle > 179.99f * RADIANS_PER_DEGREE) { // 180 degree rotation; must use another axis
185+ axis = glm::cross(v1, glm::vec3(1.0f, 0.0f, 0.0f));
186+ float axisLength = glm::length(axis);
187+ if (axisLength < EPSILON) { // parallel to x; y will work
188+ axis = glm::normalize(glm::cross(v1, glm::vec3(0.0f, 1.0f, 0.0f)));
189+ } else {
190+ axis /= axisLength;
191+ }
192+ } else {
193+ axis = glm::normalize(glm::cross(v1, v2));
194+ // It is possible for axis to be nan even when angle is not less than EPSILON.
195+ // For example when angle is small but not tiny but v1 and v2 and have very short lengths.
196+ if (glm::isnan(glm::dot(axis, axis))) {
197+ // set angle and axis to values that will generate an identity rotation
198+ angle = 0.0f;
199+ axis = glm::vec3(1.0f, 0.0f, 0.0f);
200+ }
201+ }
202+ return glm::angleAxis(angle, axis);
203+}
204+
205+glm::vec3 extractTranslation(const glm::mat4& matrix) {
206+ return glm::vec3(matrix[3][0], matrix[3][1], matrix[3][2]);
207+}
208+
209+void setTranslation(glm::mat4& matrix, const glm::vec3& translation) {
210+ matrix[3][0] = translation.x;
211+ matrix[3][1] = translation.y;
212+ matrix[3][2] = translation.z;
213+}
214+
215+glm::quat extractRotation(const glm::mat4& matrix, bool assumeOrthogonal) {
216+ // uses the iterative polar decomposition algorithm described by Ken Shoemake at
217+ // http://www.cs.wisc.edu/graphics/Courses/838-s2002/Papers/polar-decomp.pdf
218+ // code adapted from Clyde, https://github.com/threerings/clyde/blob/master/core/src/main/java/com/threerings/math/Matrix4f.java
219+ // start with the contents of the upper 3x3 portion of the matrix
220+ glm::mat3 upper = glm::mat3(matrix);
221+ if (!assumeOrthogonal) {
222+ for (int i = 0; i < 10; i++) {
223+ // store the results of the previous iteration
224+ glm::mat3 previous = upper;
225+
226+ // compute average of the matrix with its inverse transpose
227+ float sd00 = previous[1][1] * previous[2][2] - previous[2][1] * previous[1][2];
228+ float sd10 = previous[0][1] * previous[2][2] - previous[2][1] * previous[0][2];
229+ float sd20 = previous[0][1] * previous[1][2] - previous[1][1] * previous[0][2];
230+ float det = previous[0][0] * sd00 + previous[2][0] * sd20 - previous[1][0] * sd10;
231+ if (fabs(det) == 0.0f) {
232+ // determinant is zero; matrix is not invertible
233+ break;
234+ }
235+ float hrdet = 0.5f / det;
236+ upper[0][0] = +sd00 * hrdet + previous[0][0] * 0.5f;
237+ upper[1][0] = -sd10 * hrdet + previous[1][0] * 0.5f;
238+ upper[2][0] = +sd20 * hrdet + previous[2][0] * 0.5f;
239+
240+ upper[0][1] = -(previous[1][0] * previous[2][2] - previous[2][0] * previous[1][2]) * hrdet + previous[0][1] * 0.5f;
241+ upper[1][1] = +(previous[0][0] * previous[2][2] - previous[2][0] * previous[0][2]) * hrdet + previous[1][1] * 0.5f;
242+ upper[2][1] = -(previous[0][0] * previous[1][2] - previous[1][0] * previous[0][2]) * hrdet + previous[2][1] * 0.5f;
243+
244+ upper[0][2] = +(previous[1][0] * previous[2][1] - previous[2][0] * previous[1][1]) * hrdet + previous[0][2] * 0.5f;
245+ upper[1][2] = -(previous[0][0] * previous[2][1] - previous[2][0] * previous[0][1]) * hrdet + previous[1][2] * 0.5f;
246+ upper[2][2] = +(previous[0][0] * previous[1][1] - previous[1][0] * previous[0][1]) * hrdet + previous[2][2] * 0.5f;
247+
248+ // compute the difference; if it's small enough, we're done
249+ glm::mat3 diff = upper - previous;
250+ if (diff[0][0] * diff[0][0] + diff[1][0] * diff[1][0] + diff[2][0] * diff[2][0] + diff[0][1] * diff[0][1] +
251+ diff[1][1] * diff[1][1] + diff[2][1] * diff[2][1] + diff[0][2] * diff[0][2] + diff[1][2] * diff[1][2] +
252+ diff[2][2] * diff[2][2] < EPSILON) {
253+ break;
254+ }
255+ }
256+ }
257+
258+ // now that we have a nice orthogonal matrix, we can extract the rotation quaternion
259+ // using the method described in http://en.wikipedia.org/wiki/Rotation_matrix#Conversions
260+ float x2 = fabs(1.0f + upper[0][0] - upper[1][1] - upper[2][2]);
261+ float y2 = fabs(1.0f - upper[0][0] + upper[1][1] - upper[2][2]);
262+ float z2 = fabs(1.0f - upper[0][0] - upper[1][1] + upper[2][2]);
263+ float w2 = fabs(1.0f + upper[0][0] + upper[1][1] + upper[2][2]);
264+ return glm::normalize(glm::quat(0.5f * sqrtf(w2),
265+ 0.5f * sqrtf(x2) * (upper[1][2] >= upper[2][1] ? 1.0f : -1.0f),
266+ 0.5f * sqrtf(y2) * (upper[2][0] >= upper[0][2] ? 1.0f : -1.0f),
267+ 0.5f * sqrtf(z2) * (upper[0][1] >= upper[1][0] ? 1.0f : -1.0f)));
268+}
269+
270+glm::vec3 extractScale(const glm::mat4& matrix) {
271+ return glm::vec3(glm::length(matrix[0]), glm::length(matrix[1]), glm::length(matrix[2]));
272+}
273+
274+float extractUniformScale(const glm::mat4& matrix) {
275+ return extractUniformScale(extractScale(matrix));
276+}
277+
278+float extractUniformScale(const glm::vec3& scale) {
279+ return (scale.x + scale.y + scale.z) / 3.0f;
280+}
281+
282+QByteArray createByteArray(const glm::vec3& vector) {
283+ return QByteArray::number(vector.x) + ',' + QByteArray::number(vector.y) + ',' + QByteArray::number(vector.z);
284+}
285+
286+bool isSimilarOrientation(const glm::quat& orientionA, const glm::quat& orientionB, float similarEnough) {
287+ // Compute the angular distance between the two orientations
288+ float angleOrientation = orientionA == orientionB ? 0.0f : glm::degrees(glm::angle(orientionA * glm::inverse(orientionB)));
289+ if (isNaN(angleOrientation)) {
290+ angleOrientation = 0.0f;
291+ }
292+ return (angleOrientation <= similarEnough);
293+}
294+
295+bool isSimilarPosition(const glm::vec3& positionA, const glm::vec3& positionB, float similarEnough) {
296+ // Compute the distance between the two points
297+ float positionDistance = glm::distance(positionA, positionB);
298+ return (positionDistance <= similarEnough);
299+}
libraries/shared/src/GLMHelpers.h+89−0
@@ -0,0 +1,89 @@
1+//
2+// GLMHelpers.h
3+// libraries/shared/src
4+//
5+// Created by Stephen Birarda on 2014-08-07.
6+// Copyright 2014 High Fidelity, Inc.
7+//
8+// Distributed under the Apache License, Version 2.0.
9+// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
10+//
11+
12+#ifndef hifi_GLMHelpers_h
13+#define hifi_GLMHelpers_h
14+
15+#include <stdint.h>
16+
17+#include <glm/glm.hpp>
18+#include <glm/gtc/quaternion.hpp>
19+
20+#include <QtCore/QByteArray>
21+
22+#include "SharedUtil.h"
23+
24+glm::quat safeMix(const glm::quat& q1, const glm::quat& q2, float alpha);
25+
26+// These pack/unpack functions are designed to start specific known types in as efficient a manner
27+// as possible. Taking advantage of the known characteristics of the semantic types.
28+
29+// Angles are known to be between 0 and 360 degrees, this allows us to encode in 16bits with great accuracy
30+int packFloatAngleToTwoByte(unsigned char* buffer, float degrees);
31+int unpackFloatAngleFromTwoByte(const uint16_t* byteAnglePointer, float* destinationPointer);
32+
33+// Orientation Quats are known to have 4 normalized components be between -1.0 and 1.0
34+// this allows us to encode each component in 16bits with great accuracy
35+int packOrientationQuatToBytes(unsigned char* buffer, const glm::quat& quatInput);
36+int unpackOrientationQuatFromBytes(const unsigned char* buffer, glm::quat& quatOutput);
37+
38+// Ratios need the be highly accurate when less than 10, but not very accurate above 10, and they
39+// are never greater than 1000 to 1, this allows us to encode each component in 16bits
40+int packFloatRatioToTwoByte(unsigned char* buffer, float ratio);
41+int unpackFloatRatioFromTwoByte(const unsigned char* buffer, float& ratio);
42+
43+// Near/Far Clip values need the be highly accurate when less than 10, but only integer accuracy above 10 and
44+// they are never greater than 16,000, this allows us to encode each component in 16bits
45+int packClipValueToTwoByte(unsigned char* buffer, float clipValue);
46+int unpackClipValueFromTwoByte(const unsigned char* buffer, float& clipValue);
47+
48+// Positive floats that don't need to be very precise
49+int packFloatToByte(unsigned char* buffer, float value, float scaleBy);
50+int unpackFloatFromByte(const unsigned char* buffer, float& value, float scaleBy);
51+
52+// Allows sending of fixed-point numbers: radix 1 makes 15.1 number, radix 8 makes 8.8 number, etc
53+int packFloatScalarToSignedTwoByteFixed(unsigned char* buffer, float scalar, int radix);
54+int unpackFloatScalarFromSignedTwoByteFixed(const int16_t* byteFixedPointer, float* destinationPointer, int radix);
55+
56+// A convenience for sending vec3's as fixed-point floats
57+int packFloatVec3ToSignedTwoByteFixed(unsigned char* destBuffer, const glm::vec3& srcVector, int radix);
58+int unpackFloatVec3FromSignedTwoByteFixed(const unsigned char* sourceBuffer, glm::vec3& destination, int radix);
59+
60+/// \return vec3 with euler angles in radians
61+glm::vec3 safeEulerAngles(const glm::quat& q);
62+
63+float angleBetween(const glm::vec3& v1, const glm::vec3& v2);
64+
65+glm::quat rotationBetween(const glm::vec3& v1, const glm::vec3& v2);
66+
67+glm::vec3 extractTranslation(const glm::mat4& matrix);
68+
69+void setTranslation(glm::mat4& matrix, const glm::vec3& translation);
70+
71+glm::quat extractRotation(const glm::mat4& matrix, bool assumeOrthogonal = false);
72+
73+glm::vec3 extractScale(const glm::mat4& matrix);
74+
75+float extractUniformScale(const glm::mat4& matrix);
76+
77+float extractUniformScale(const glm::vec3& scale);
78+
79+QByteArray createByteArray(const glm::vec3& vector);
80+
81+/// \return bool are two orientations similar to each other
82+const float ORIENTATION_SIMILAR_ENOUGH = 5.0f; // 10 degrees in any direction
83+bool isSimilarOrientation(const glm::quat& orientionA, const glm::quat& orientionB,
84+ float similarEnough = ORIENTATION_SIMILAR_ENOUGH);
85+const float POSITION_SIMILAR_ENOUGH = 0.1f; // 0.1 meter
86+bool isSimilarPosition(const glm::vec3& positionA, const glm::vec3& positionB, float similarEnough = POSITION_SIMILAR_ENOUGH);
87+
88+
89+#endif // hifi_GLMHelpers_h
libraries/shared/src/MovingMinMaxAvg.h+1−0
@@ -12,6 +12,7 @@ · 11 unmodified lines
1212 #ifndef hifi_MovingMinMaxAvg_h
1313 #define hifi_MovingMinMaxAvg_h
1414
15+#include <cassert>
1516 #include <limits>
1617
1718 #include "RingBufferHistory.h"
libraries/shared/src/PhysicsSimulation.cpp+20−20
@@ -47,12 +47,12 @@ PhysicsSimulation::~PhysicsSimulation() { · 46 unmodified lines
4747 void PhysicsSimulation::setRagdoll(Ragdoll* ragdoll) {
4848 if (_ragdoll != ragdoll) {
4949 if (_ragdoll) {
50− _ragdoll->_ragdollSimulation = NULL;
50+ _ragdoll->_simulation = NULL;
5151 }
5252 _ragdoll = ragdoll;
5353 if (_ragdoll) {
54− assert(!(_ragdoll->_ragdollSimulation));
55− _ragdoll->_ragdollSimulation = this;
54+ assert(!(_ragdoll->_simulation));
55+ _ragdoll->_simulation = this;
5656 }
5757 }
5858 }
@@ -144,7 +144,7 @@ bool PhysicsSimulation::addRagdoll(Ragdoll* doll) { · 85 unmodified lines
144144 // list is full
145145 return false;
146146 }
147− if (doll->_ragdollSimulation == this) {
147+ if (doll->_simulation == this) {
148148 for (int i = 0; i < numDolls; ++i) {
149149 if (doll == _otherRagdolls[i]) {
150150 // already in list
@@ -153,8 +153,8 @@ bool PhysicsSimulation::addRagdoll(Ragdoll* doll) { · 2 unmodified lines
153153 }
154154 }
155155 // add to list
156− assert(!(doll->_ragdollSimulation));
157− doll->_ragdollSimulation = this;
156+ assert(!(doll->_simulation));
157+ doll->_simulation = this;
158158 _otherRagdolls.push_back(doll);
159159
160160 // set the massScale of otherRagdolls artificially high
@@ -164,7 +164,7 @@ bool PhysicsSimulation::addRagdoll(Ragdoll* doll) { · 3 unmodified lines
164164
165165 void PhysicsSimulation::removeRagdoll(Ragdoll* doll) {
166166 int numDolls = _otherRagdolls.size();
167− if (doll->_ragdollSimulation != this) {
167+ if (doll->_simulation != this) {
168168 return;
169169 }
170170 for (int i = 0; i < numDolls; ++i) {
@@ -178,7 +178,7 @@ void PhysicsSimulation::removeRagdoll(Ragdoll* doll) { · 7 unmodified lines
178178 _otherRagdolls.pop_back();
179179 _otherRagdolls[i] = lastDoll;
180180 }
181− doll->_ragdollSimulation = NULL;
181+ doll->_simulation = NULL;
182182 doll->setMassScale(1.0f);
183183 break;
184184 }
@@ -195,13 +195,13 @@ void PhysicsSimulation::stepForward(float deltaTime, float minError, int maxIter · 10 unmodified lines
195195 quint64 expiry = startTime + maxUsec;
196196
197197 moveRagdolls(deltaTime);
198− buildContactConstraints();
198+ enforceContacts();
199199 int numDolls = _otherRagdolls.size();
200200 {
201201 PerformanceTimer perfTimer("enforce");
202− _ragdoll->enforceRagdollConstraints();
202+ _ragdoll->enforceConstraints();
203203 for (int i = 0; i < numDolls; ++i) {
204− _otherRagdolls[i]->enforceRagdollConstraints();
204+ _otherRagdolls[i]->enforceConstraints();
205205 }
206206 }
207207
@@ -214,12 +214,12 @@ void PhysicsSimulation::stepForward(float deltaTime, float minError, int maxIter · 6 unmodified lines
214214
215215 { // enforce constraints
216216 PerformanceTimer perfTimer("enforce");
217− error = _ragdoll->enforceRagdollConstraints();
217+ error = _ragdoll->enforceConstraints();
218218 for (int i = 0; i < numDolls; ++i) {
219− error = glm::max(error, _otherRagdolls[i]->enforceRagdollConstraints());
219+ error = glm::max(error, _otherRagdolls[i]->enforceConstraints());
220220 }
221221 }
222− enforceContactConstraints();
222+ applyContactFriction();
223223 ++iterations;
224224
225225 now = usecTimestampNow();
@@ -230,10 +230,10 @@ void PhysicsSimulation::stepForward(float deltaTime, float minError, int maxIter · 4 unmodified lines
230230
231231 void PhysicsSimulation::moveRagdolls(float deltaTime) {
232232 PerformanceTimer perfTimer("integrate");
233− _ragdoll->stepRagdollForward(deltaTime);
233+ _ragdoll->stepForward(deltaTime);
234234 int numDolls = _otherRagdolls.size();
235235 for (int i = 0; i < numDolls; ++i) {
236− _otherRagdolls[i]->stepRagdollForward(deltaTime);
236+ _otherRagdolls[i]->stepForward(deltaTime);
237237 }
238238 }
239239
@@ -288,20 +288,20 @@ void PhysicsSimulation::resolveCollisions() { · 48 unmodified lines
288288 }
289289 }
290290
291−void PhysicsSimulation::buildContactConstraints() {
291+void PhysicsSimulation::enforceContacts() {
292292 PerformanceTimer perfTimer("contacts");
293293 QMap<quint64, ContactPoint>::iterator itr = _contacts.begin();
294294 while (itr != _contacts.end()) {
295− itr.value().buildConstraints();
295+ itr.value().enforce();
296296 ++itr;
297297 }
298298 }
299299
300−void PhysicsSimulation::enforceContactConstraints() {
300+void PhysicsSimulation::applyContactFriction() {
301301 PerformanceTimer perfTimer("contacts");
302302 QMap<quint64, ContactPoint>::iterator itr = _contacts.begin();
303303 while (itr != _contacts.end()) {
304− itr.value().enforce();
304+ itr.value().applyFriction();
305305 ++itr;
306306 }
307307 }
libraries/shared/src/PhysicsSimulation.h+2−2
@@ -56,8 +56,8 @@ protected: · 55 unmodified lines
5656 void computeCollisions();
5757 void resolveCollisions();
5858
59− void buildContactConstraints();
60− void enforceContactConstraints();
59+ void enforceContacts();
60+ void applyContactFriction();
6161 void updateContacts();
6262 void pruneContacts();
6363
libraries/shared/src/Ragdoll.cpp+23−20
@@ -19,24 +19,27 @@ · 18 unmodified lines
1919 #include "PhysicsSimulation.h"
2020 #include "SharedUtil.h" // for EPSILON
2121
22−Ragdoll::Ragdoll() : _massScale(1.0f), _ragdollTranslation(0.0f), _translationInSimulationFrame(0.0f), _ragdollSimulation(NULL) {
22+Ragdoll::Ragdoll() : _massScale(1.0f), _translation(0.0f), _translationInSimulationFrame(0.0f), _simulation(NULL) {
2323 }
2424
2525 Ragdoll::~Ragdoll() {
26− clearRagdollConstraintsAndPoints();
26+ clearConstraintsAndPoints();
27+ if (_simulation) {
28+ _simulation->removeRagdoll(this);
29+ }
2730 }
2831
29−void Ragdoll::stepRagdollForward(float deltaTime) {
30− if (_ragdollSimulation) {
31− updateSimulationTransforms(_ragdollTranslation - _ragdollSimulation->getTranslation(), _ragdollRotation);
32+void Ragdoll::stepForward(float deltaTime) {
33+ if (_simulation) {
34+ updateSimulationTransforms(_translation - _simulation->getTranslation(), _rotation);
3235 }
33− int numPoints = _ragdollPoints.size();
36+ int numPoints = _points.size();
3437 for (int i = 0; i < numPoints; ++i) {
35− _ragdollPoints[i].integrateForward();
38+ _points[i].integrateForward();
3639 }
3740 }
3841
39−void Ragdoll::clearRagdollConstraintsAndPoints() {
42+void Ragdoll::clearConstraintsAndPoints() {
4043 int numConstraints = _boneConstraints.size();
4144 for (int i = 0; i < numConstraints; ++i) {
4245 delete _boneConstraints[i];
@@ -47,10 +50,10 @@ void Ragdoll::clearRagdollConstraintsAndPoints() { · 4 unmodified lines
4750 delete _fixedConstraints[i];
4851 }
4952 _fixedConstraints.clear();
50− _ragdollPoints.clear();
53+ _points.clear();
5154 }
5255
53−float Ragdoll::enforceRagdollConstraints() {
56+float Ragdoll::enforceConstraints() {
5457 float maxDistance = 0.0f;
5558 // enforce the bone constraints first
5659 int numConstraints = _boneConstraints.size();
@@ -65,16 +68,16 @@ float Ragdoll::enforceRagdollConstraints() { · 8 unmodified lines
6568 return maxDistance;
6669 }
6770
68−void Ragdoll::initRagdollTransform() {
69− _ragdollTranslation = glm::vec3(0.0f);
70− _ragdollRotation = glm::quat();
71+void Ragdoll::initTransform() {
72+ _translation = glm::vec3(0.0f);
73+ _rotation = glm::quat();
7174 _translationInSimulationFrame = glm::vec3(0.0f);
7275 _rotationInSimulationFrame = glm::quat();
7376 }
7477
75−void Ragdoll::setRagdollTransform(const glm::vec3& translation, const glm::quat& rotation) {
76− _ragdollTranslation = translation;
77− _ragdollRotation = rotation;
78+void Ragdoll::setTransform(const glm::vec3& translation, const glm::quat& rotation) {
79+ _translation = translation;
80+ _rotation = rotation;
7881 }
7982
8083 void Ragdoll::updateSimulationTransforms(const glm::vec3& translation, const glm::quat& rotation) {
@@ -90,9 +93,9 @@ void Ragdoll::updateSimulationTransforms(const glm::vec3& translation, const glm · 9 unmodified lines
9093 glm::quat deltaRotation = rotation * glm::inverse(_rotationInSimulationFrame);
9194
9295 // apply the deltas to all ragdollPoints
93− int numPoints = _ragdollPoints.size();
96+ int numPoints = _points.size();
9497 for (int i = 0; i < numPoints; ++i) {
95− _ragdollPoints[i].move(deltaPosition, deltaRotation, _translationInSimulationFrame);
98+ _points[i].move(deltaPosition, deltaRotation, _translationInSimulationFrame);
9699 }
97100
98101 // remember the current transform
@@ -106,9 +109,9 @@ void Ragdoll::setMassScale(float scale) { · 7 unmodified lines
106109 scale = glm::clamp(glm::abs(scale), MIN_SCALE, MAX_SCALE);
107110 if (scale != _massScale) {
108111 float rescale = scale / _massScale;
109− int numPoints = _ragdollPoints.size();
112+ int numPoints = _points.size();
110113 for (int i = 0; i < numPoints; ++i) {
111− _ragdollPoints[i].setMass(rescale * _ragdollPoints[i].getMass());
114+ _points[i].setMass(rescale * _points[i].getMass());
112115 }
113116 _massScale = scale;
114117 }
libraries/shared/src/Ragdoll.h+14−14
@@ -33,44 +33,44 @@ public: · 32 unmodified lines
3333 Ragdoll();
3434 virtual ~Ragdoll();
3535
36− virtual void stepRagdollForward(float deltaTime);
36+ virtual void stepForward(float deltaTime);
3737
3838 /// \return max distance of point movement
39− float enforceRagdollConstraints();
39+ float enforceConstraints();
4040
4141 // both const and non-const getPoints()
42− const QVector<VerletPoint>& getRagdollPoints() const { return _ragdollPoints; }
43− QVector<VerletPoint>& getRagdollPoints() { return _ragdollPoints; }
42+ const QVector<VerletPoint>& getPoints() const { return _points; }
43+ QVector<VerletPoint>& getPoints() { return _points; }
4444
45− void initRagdollTransform();
45+ void initTransform();
4646
4747 /// set the translation and rotation of the Ragdoll and adjust all VerletPoints.
48− void setRagdollTransform(const glm::vec3& translation, const glm::quat& rotation);
48+ void setTransform(const glm::vec3& translation, const glm::quat& rotation);
4949
5050 const glm::vec3& getTranslationInSimulationFrame() const { return _translationInSimulationFrame; }
5151
5252 void setMassScale(float scale);
5353 float getMassScale() const { return _massScale; }
5454
55−protected:
56− void clearRagdollConstraintsAndPoints();
57− virtual void initRagdollPoints() = 0;
58− virtual void buildRagdollConstraints() = 0;
55+ void clearConstraintsAndPoints();
56+ virtual void initPoints() = 0;
57+ virtual void buildConstraints() = 0;
5958
59+protected:
6060 float _massScale;
61− glm::vec3 _ragdollTranslation; // world-frame
62− glm::quat _ragdollRotation; // world-frame
61+ glm::vec3 _translation; // world-frame
62+ glm::quat _rotation; // world-frame
6363 glm::vec3 _translationInSimulationFrame;
6464 glm::quat _rotationInSimulationFrame;
6565
66− QVector<VerletPoint> _ragdollPoints;
66+ QVector<VerletPoint> _points;
6767 QVector<DistanceConstraint*> _boneConstraints;
6868 QVector<FixedConstraint*> _fixedConstraints;
6969 private:
7070 void updateSimulationTransforms(const glm::vec3& translation, const glm::quat& rotation);
7171
7272 friend class PhysicsSimulation;
73− PhysicsSimulation* _ragdollSimulation;
73+ PhysicsSimulation* _simulation;
7474 };
7575
7676 #endif // hifi_Ragdoll_h
libraries/shared/src/SharedUtil.cpp+1−290
@@ -9,6 +9,7 @@ · 8 unmodified lines
99 // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
1010 //
1111
12+#include <cassert>
1213 #include <cstdlib>
1314 #include <cstdio>
1415 #include <cstring>
@@ -79,40 +80,6 @@ bool shouldDo(float desiredInterval, float deltaTime) { · 64 unmodified lines
7980 return randFloat() < deltaTime / desiredInterval;
8081 }
8182
82−// Safe version of glm::mix; based on the code in Nick Bobick's article,
83−// http://www.gamasutra.com/features/19980703/quaternions_01.htm (via Clyde,
84−// https://github.com/threerings/clyde/blob/master/src/main/java/com/threerings/math/Quaternion.java)
85−glm::quat safeMix(const glm::quat& q1, const glm::quat& q2, float proportion) {
86− float cosa = q1.x * q2.x + q1.y * q2.y + q1.z * q2.z + q1.w * q2.w;
87− float ox = q2.x, oy = q2.y, oz = q2.z, ow = q2.w, s0, s1;
88−
89− // adjust signs if necessary
90− if (cosa < 0.0f) {
91− cosa = -cosa;
92− ox = -ox;
93− oy = -oy;
94− oz = -oz;
95− ow = -ow;
96− }
97−
98− // calculate coefficients; if the angle is too close to zero, we must fall back
99− // to linear interpolation
100− if ((1.0f - cosa) > EPSILON) {
101− float angle = acosf(cosa), sina = sinf(angle);
102− s0 = sinf((1.0f - proportion) * angle) / sina;
103− s1 = sinf(proportion * angle) / sina;
104−
105− } else {
106− s0 = 1.0f - proportion;
107− s1 = proportion;
108− }
109−
110− return glm::normalize(glm::quat(s0 * q1.w + s1 * ow, s0 * q1.x + s1 * ox, s0 * q1.y + s1 * oy, s0 * q1.z + s1 * oz));
111−}
112−
113−
114−
115−
11683 void outputBufferBits(const unsigned char* buffer, int length, QDebug* continuedDebug) {
11784 for (int i = 0; i < length; i++) {
11885 outputBits(buffer[i], continuedDebug);
@@ -489,73 +456,6 @@ int removeFromSortedArrays(void* value, void** valueArray, float* keyArray, int* · 370 unmodified lines
489456 return -1; // error case
490457 }
491458
492−// Allows sending of fixed-point numbers: radix 1 makes 15.1 number, radix 8 makes 8.8 number, etc
493−int packFloatScalarToSignedTwoByteFixed(unsigned char* buffer, float scalar, int radix) {
494− int16_t outVal = (int16_t)(scalar * (float)(1 << radix));
495− memcpy(buffer, &outVal, sizeof(uint16_t));
496− return sizeof(uint16_t);
497−}
498−
499−int unpackFloatScalarFromSignedTwoByteFixed(const int16_t* byteFixedPointer, float* destinationPointer, int radix) {
500− *destinationPointer = *byteFixedPointer / (float)(1 << radix);
501− return sizeof(int16_t);
502−}
503−
504−int packFloatVec3ToSignedTwoByteFixed(unsigned char* destBuffer, const glm::vec3& srcVector, int radix) {
505− const unsigned char* startPosition = destBuffer;
506− destBuffer += packFloatScalarToSignedTwoByteFixed(destBuffer, srcVector.x, radix);
507− destBuffer += packFloatScalarToSignedTwoByteFixed(destBuffer, srcVector.y, radix);
508− destBuffer += packFloatScalarToSignedTwoByteFixed(destBuffer, srcVector.z, radix);
509− return destBuffer - startPosition;
510−}
511−
512−int unpackFloatVec3FromSignedTwoByteFixed(const unsigned char* sourceBuffer, glm::vec3& destination, int radix) {
513− const unsigned char* startPosition = sourceBuffer;
514− sourceBuffer += unpackFloatScalarFromSignedTwoByteFixed((int16_t*) sourceBuffer, &(destination.x), radix);
515− sourceBuffer += unpackFloatScalarFromSignedTwoByteFixed((int16_t*) sourceBuffer, &(destination.y), radix);
516− sourceBuffer += unpackFloatScalarFromSignedTwoByteFixed((int16_t*) sourceBuffer, &(destination.z), radix);
517− return sourceBuffer - startPosition;
518−}
519−
520−
521−int packFloatAngleToTwoByte(unsigned char* buffer, float degrees) {
522− const float ANGLE_CONVERSION_RATIO = (std::numeric_limits<uint16_t>::max() / 360.f);
523−
524− uint16_t angleHolder = floorf((degrees + 180.f) * ANGLE_CONVERSION_RATIO);
525− memcpy(buffer, &angleHolder, sizeof(uint16_t));
526−
527− return sizeof(uint16_t);
528−}
529−
530−int unpackFloatAngleFromTwoByte(const uint16_t* byteAnglePointer, float* destinationPointer) {
531− *destinationPointer = (*byteAnglePointer / (float) std::numeric_limits<uint16_t>::max()) * 360.f - 180.f;
532− return sizeof(uint16_t);
533−}
534−
535−int packOrientationQuatToBytes(unsigned char* buffer, const glm::quat& quatInput) {
536− const float QUAT_PART_CONVERSION_RATIO = (std::numeric_limits<uint16_t>::max() / 2.f);
537− uint16_t quatParts[4];
538− quatParts[0] = floorf((quatInput.x + 1.f) * QUAT_PART_CONVERSION_RATIO);
539− quatParts[1] = floorf((quatInput.y + 1.f) * QUAT_PART_CONVERSION_RATIO);
540− quatParts[2] = floorf((quatInput.z + 1.f) * QUAT_PART_CONVERSION_RATIO);
541− quatParts[3] = floorf((quatInput.w + 1.f) * QUAT_PART_CONVERSION_RATIO);
542−
543− memcpy(buffer, &quatParts, sizeof(quatParts));
544− return sizeof(quatParts);
545−}
546−
547−int unpackOrientationQuatFromBytes(const unsigned char* buffer, glm::quat& quatOutput) {
548− uint16_t quatParts[4];
549− memcpy(&quatParts, buffer, sizeof(quatParts));
550−
551− quatOutput.x = ((quatParts[0] / (float) std::numeric_limits<uint16_t>::max()) * 2.f) - 1.f;
552− quatOutput.y = ((quatParts[1] / (float) std::numeric_limits<uint16_t>::max()) * 2.f) - 1.f;
553− quatOutput.z = ((quatParts[2] / (float) std::numeric_limits<uint16_t>::max()) * 2.f) - 1.f;
554− quatOutput.w = ((quatParts[3] / (float) std::numeric_limits<uint16_t>::max()) * 2.f) - 1.f;
555−
556− return sizeof(quatParts);
557−}
558−
559459 float SMALL_LIMIT = 10.f;
560460 float LARGE_LIMIT = 1000.f;
561461
@@ -651,199 +551,10 @@ void debug::checkDeadBeef(void* memoryVoid, int size) { · 89 unmodified lines
651551 assert(memcmp((unsigned char*)memoryVoid, DEADBEEF, std::min(size, DEADBEEF_SIZE)) != 0);
652552 }
653553
654−// Safe version of glm::eulerAngles; uses the factorization method described in David Eberly's
655−// http://www.geometrictools.com/Documentation/EulerAngles.pdf (via Clyde,
656−// https://github.com/threerings/clyde/blob/master/src/main/java/com/threerings/math/Quaternion.java)
657−glm::vec3 safeEulerAngles(const glm::quat& q) {
658− float sy = 2.0f * (q.y * q.w - q.x * q.z);
659− glm::vec3 eulers;
660− if (sy < 1.0f - EPSILON) {
661− if (sy > -1.0f + EPSILON) {
662− eulers = glm::vec3(
663− atan2f(q.y * q.z + q.x * q.w, 0.5f - (q.x * q.x + q.y * q.y)),
664− asinf(sy),
665− atan2f(q.x * q.y + q.z * q.w, 0.5f - (q.y * q.y + q.z * q.z)));
666−
667− } else {
668− // not a unique solution; x + z = atan2(-m21, m11)
669− eulers = glm::vec3(
670− 0.0f,
671− - PI_OVER_TWO,
672− atan2f(q.x * q.w - q.y * q.z, 0.5f - (q.x * q.x + q.z * q.z)));
673− }
674− } else {
675− // not a unique solution; x - z = atan2(-m21, m11)
676− eulers = glm::vec3(
677− 0.0f,
678− PI_OVER_TWO,
679− -atan2f(q.x * q.w - q.y * q.z, 0.5f - (q.x * q.x + q.z * q.z)));
680− }
681−
682− // adjust so that z, rather than y, is in [-pi/2, pi/2]
683− if (eulers.z < -PI_OVER_TWO) {
684− if (eulers.x < 0.0f) {
685− eulers.x += PI;
686− } else {
687− eulers.x -= PI;
688− }
689− eulers.y = -eulers.y;
690− if (eulers.y < 0.0f) {
691− eulers.y += PI;
692− } else {
693− eulers.y -= PI;
694− }
695− eulers.z += PI;
696−
697− } else if (eulers.z > PI_OVER_TWO) {
698− if (eulers.x < 0.0f) {
699− eulers.x += PI;
700− } else {
701− eulers.x -= PI;
702− }
703− eulers.y = -eulers.y;
704− if (eulers.y < 0.0f) {
705− eulers.y += PI;
706− } else {
707− eulers.y -= PI;
708− }
709− eulers.z -= PI;
710− }
711− return eulers;
712−}
713−
714−// Helper function returns the positive angle (in radians) between two 3D vectors
715−float angleBetween(const glm::vec3& v1, const glm::vec3& v2) {
716− return acosf((glm::dot(v1, v2)) / (glm::length(v1) * glm::length(v2)));
717−}
718−
719−// Helper function return the rotation from the first vector onto the second
720−glm::quat rotationBetween(const glm::vec3& v1, const glm::vec3& v2) {
721− float angle = angleBetween(v1, v2);
722− if (glm::isnan(angle) || angle < EPSILON) {
723− return glm::quat();
724− }
725− glm::vec3 axis;
726− if (angle > 179.99f * RADIANS_PER_DEGREE) { // 180 degree rotation; must use another axis
727− axis = glm::cross(v1, glm::vec3(1.0f, 0.0f, 0.0f));
728− float axisLength = glm::length(axis);
729− if (axisLength < EPSILON) { // parallel to x; y will work
730− axis = glm::normalize(glm::cross(v1, glm::vec3(0.0f, 1.0f, 0.0f)));
731− } else {
732− axis /= axisLength;
733− }
734− } else {
735− axis = glm::normalize(glm::cross(v1, v2));
736− // It is possible for axis to be nan even when angle is not less than EPSILON.
737− // For example when angle is small but not tiny but v1 and v2 and have very short lengths.
738− if (glm::isnan(glm::dot(axis, axis))) {
739− // set angle and axis to values that will generate an identity rotation
740− angle = 0.0f;
741− axis = glm::vec3(1.0f, 0.0f, 0.0f);
742− }
743− }
744− return glm::angleAxis(angle, axis);
745−}
746−
747−glm::vec3 extractTranslation(const glm::mat4& matrix) {
748− return glm::vec3(matrix[3][0], matrix[3][1], matrix[3][2]);
749−}
750−
751−void setTranslation(glm::mat4& matrix, const glm::vec3& translation) {
752− matrix[3][0] = translation.x;
753− matrix[3][1] = translation.y;
754− matrix[3][2] = translation.z;
755−}
756−
757−glm::quat extractRotation(const glm::mat4& matrix, bool assumeOrthogonal) {
758− // uses the iterative polar decomposition algorithm described by Ken Shoemake at
759− // http://www.cs.wisc.edu/graphics/Courses/838-s2002/Papers/polar-decomp.pdf
760− // code adapted from Clyde, https://github.com/threerings/clyde/blob/master/core/src/main/java/com/threerings/math/Matrix4f.java
761− // start with the contents of the upper 3x3 portion of the matrix
762− glm::mat3 upper = glm::mat3(matrix);
763− if (!assumeOrthogonal) {
764− for (int i = 0; i < 10; i++) {
765− // store the results of the previous iteration
766− glm::mat3 previous = upper;
767−
768− // compute average of the matrix with its inverse transpose
769− float sd00 = previous[1][1] * previous[2][2] - previous[2][1] * previous[1][2];
770− float sd10 = previous[0][1] * previous[2][2] - previous[2][1] * previous[0][2];
771− float sd20 = previous[0][1] * previous[1][2] - previous[1][1] * previous[0][2];
772− float det = previous[0][0] * sd00 + previous[2][0] * sd20 - previous[1][0] * sd10;
773− if (fabs(det) == 0.0f) {
774− // determinant is zero; matrix is not invertible
775− break;
776− }
777− float hrdet = 0.5f / det;
778− upper[0][0] = +sd00 * hrdet + previous[0][0] * 0.5f;
779− upper[1][0] = -sd10 * hrdet + previous[1][0] * 0.5f;
780− upper[2][0] = +sd20 * hrdet + previous[2][0] * 0.5f;
781−
782− upper[0][1] = -(previous[1][0] * previous[2][2] - previous[2][0] * previous[1][2]) * hrdet + previous[0][1] * 0.5f;
783− upper[1][1] = +(previous[0][0] * previous[2][2] - previous[2][0] * previous[0][2]) * hrdet + previous[1][1] * 0.5f;
784− upper[2][1] = -(previous[0][0] * previous[1][2] - previous[1][0] * previous[0][2]) * hrdet + previous[2][1] * 0.5f;
785−
786− upper[0][2] = +(previous[1][0] * previous[2][1] - previous[2][0] * previous[1][1]) * hrdet + previous[0][2] * 0.5f;
787− upper[1][2] = -(previous[0][0] * previous[2][1] - previous[2][0] * previous[0][1]) * hrdet + previous[1][2] * 0.5f;
788− upper[2][2] = +(previous[0][0] * previous[1][1] - previous[1][0] * previous[0][1]) * hrdet + previous[2][2] * 0.5f;
789−
790− // compute the difference; if it's small enough, we're done
791− glm::mat3 diff = upper - previous;
792− if (diff[0][0] * diff[0][0] + diff[1][0] * diff[1][0] + diff[2][0] * diff[2][0] + diff[0][1] * diff[0][1] +
793− diff[1][1] * diff[1][1] + diff[2][1] * diff[2][1] + diff[0][2] * diff[0][2] + diff[1][2] * diff[1][2] +
794− diff[2][2] * diff[2][2] < EPSILON) {
795− break;
796− }
797− }
798− }
799−
800− // now that we have a nice orthogonal matrix, we can extract the rotation quaternion
801− // using the method described in http://en.wikipedia.org/wiki/Rotation_matrix#Conversions
802− float x2 = fabs(1.0f + upper[0][0] - upper[1][1] - upper[2][2]);
803− float y2 = fabs(1.0f - upper[0][0] + upper[1][1] - upper[2][2]);
804− float z2 = fabs(1.0f - upper[0][0] - upper[1][1] + upper[2][2]);
805− float w2 = fabs(1.0f + upper[0][0] + upper[1][1] + upper[2][2]);
806− return glm::normalize(glm::quat(0.5f * sqrtf(w2),
807− 0.5f * sqrtf(x2) * (upper[1][2] >= upper[2][1] ? 1.0f : -1.0f),
808− 0.5f * sqrtf(y2) * (upper[2][0] >= upper[0][2] ? 1.0f : -1.0f),
809− 0.5f * sqrtf(z2) * (upper[0][1] >= upper[1][0] ? 1.0f : -1.0f)));
810−}
811−
812−glm::vec3 extractScale(const glm::mat4& matrix) {
813− return glm::vec3(glm::length(matrix[0]), glm::length(matrix[1]), glm::length(matrix[2]));
814−}
815−
816−float extractUniformScale(const glm::mat4& matrix) {
817− return extractUniformScale(extractScale(matrix));
818−}
819−
820−float extractUniformScale(const glm::vec3& scale) {
821− return (scale.x + scale.y + scale.z) / 3.0f;
822−}
823−
824554 bool isNaN(float value) {
825555 return value != value;
826556 }
827557
828−bool isSimilarOrientation(const glm::quat& orientionA, const glm::quat& orientionB, float similarEnough) {
829− // Compute the angular distance between the two orientations
830− float angleOrientation = orientionA == orientionB ? 0.0f : glm::degrees(glm::angle(orientionA * glm::inverse(orientionB)));
831− if (isNaN(angleOrientation)) {
832− angleOrientation = 0.0f;
833− }
834− return (angleOrientation <= similarEnough);
835−}
836−
837−bool isSimilarPosition(const glm::vec3& positionA, const glm::vec3& positionB, float similarEnough) {
838− // Compute the distance between the two points
839− float positionDistance = glm::distance(positionA, positionB);
840− return (positionDistance <= similarEnough);
841−}
842−
843−QByteArray createByteArray(const glm::vec3& vector) {
844− return QByteArray::number(vector.x) + ',' + QByteArray::number(vector.y) + ',' + QByteArray::number(vector.z);
845−}
846−
847558 QString formatUsecTime(float usecs, int prec) {
848559 static const quint64 SECONDS_PER_MINUTE = 60;
849560 static const quint64 USECS_PER_MINUTE = USECS_PER_SECOND * SECONDS_PER_MINUTE;
libraries/shared/src/SharedUtil.h+0−69
@@ -19,9 +19,6 @@ · 18 unmodified lines
1919 #include <unistd.h> // not on windows, not needed for mac or windows
2020 #endif
2121
22−#include <glm/glm.hpp>
23−#include <glm/gtc/quaternion.hpp>
24−
2522 #include <QtCore/QDebug>
2623
2724 const int BYTES_PER_COLOR = 3;
@@ -71,8 +68,6 @@ float randomSign(); /// \return -1.0 or 1.0 · 43 unmodified lines
7168 unsigned char randomColorValue(int minimum = 0);
7269 bool randomBoolean();
7370
74−glm::quat safeMix(const glm::quat& q1, const glm::quat& q2, float alpha);
75−
7671 bool shouldDo(float desiredInterval, float deltaTime);
7772
7873 void outputBufferBits(const unsigned char* buffer, int length, QDebug* continuedDebug = NULL);
@@ -108,8 +103,6 @@ int insertIntoSortedArrays(void* value, float key, int originalIndex, · 29 unmodified lines
108103 int removeFromSortedArrays(void* value, void** valueArray, float* keyArray, int* originalIndexArray,
109104 int currentCount, int maxCount);
110105
111−
112−
113106 // Helper Class for debugging
114107 class debug {
115108 public:
@@ -124,71 +117,9 @@ private: · 8 unmodified lines
124117 bool isBetween(int64_t value, int64_t max, int64_t min);
125118
126119
127−// These pack/unpack functions are designed to start specific known types in as efficient a manner
128−// as possible. Taking advantage of the known characteristics of the semantic types.
129−
130−// Angles are known to be between 0 and 360 degrees, this allows us to encode in 16bits with great accuracy
131−int packFloatAngleToTwoByte(unsigned char* buffer, float degrees);
132−int unpackFloatAngleFromTwoByte(const uint16_t* byteAnglePointer, float* destinationPointer);
133−
134−// Orientation Quats are known to have 4 normalized components be between -1.0 and 1.0
135−// this allows us to encode each component in 16bits with great accuracy
136−int packOrientationQuatToBytes(unsigned char* buffer, const glm::quat& quatInput);
137−int unpackOrientationQuatFromBytes(const unsigned char* buffer, glm::quat& quatOutput);
138−
139−// Ratios need the be highly accurate when less than 10, but not very accurate above 10, and they
140−// are never greater than 1000 to 1, this allows us to encode each component in 16bits
141−int packFloatRatioToTwoByte(unsigned char* buffer, float ratio);
142−int unpackFloatRatioFromTwoByte(const unsigned char* buffer, float& ratio);
143−
144−// Near/Far Clip values need the be highly accurate when less than 10, but only integer accuracy above 10 and
145−// they are never greater than 16,000, this allows us to encode each component in 16bits
146−int packClipValueToTwoByte(unsigned char* buffer, float clipValue);
147−int unpackClipValueFromTwoByte(const unsigned char* buffer, float& clipValue);
148−
149−// Positive floats that don't need to be very precise
150−int packFloatToByte(unsigned char* buffer, float value, float scaleBy);
151−int unpackFloatFromByte(const unsigned char* buffer, float& value, float scaleBy);
152−
153−// Allows sending of fixed-point numbers: radix 1 makes 15.1 number, radix 8 makes 8.8 number, etc
154−int packFloatScalarToSignedTwoByteFixed(unsigned char* buffer, float scalar, int radix);
155−int unpackFloatScalarFromSignedTwoByteFixed(const int16_t* byteFixedPointer, float* destinationPointer, int radix);
156−
157−// A convenience for sending vec3's as fixed-point floats
158−int packFloatVec3ToSignedTwoByteFixed(unsigned char* destBuffer, const glm::vec3& srcVector, int radix);
159−int unpackFloatVec3FromSignedTwoByteFixed(const unsigned char* sourceBuffer, glm::vec3& destination, int radix);
160−
161−/// \return vec3 with euler angles in radians
162−glm::vec3 safeEulerAngles(const glm::quat& q);
163−
164−float angleBetween(const glm::vec3& v1, const glm::vec3& v2);
165−
166−glm::quat rotationBetween(const glm::vec3& v1, const glm::vec3& v2);
167−
168−glm::vec3 extractTranslation(const glm::mat4& matrix);
169−
170−void setTranslation(glm::mat4& matrix, const glm::vec3& translation);
171−
172−glm::quat extractRotation(const glm::mat4& matrix, bool assumeOrthogonal = false);
173−
174−glm::vec3 extractScale(const glm::mat4& matrix);
175−
176−float extractUniformScale(const glm::mat4& matrix);
177−
178−float extractUniformScale(const glm::vec3& scale);
179−
180−/// \return bool are two orientations similar to each other
181−const float ORIENTATION_SIMILAR_ENOUGH = 5.0f; // 10 degrees in any direction
182−bool isSimilarOrientation(const glm::quat& orientionA, const glm::quat& orientionB,
183− float similarEnough = ORIENTATION_SIMILAR_ENOUGH);
184−const float POSITION_SIMILAR_ENOUGH = 0.1f; // 0.1 meter
185−bool isSimilarPosition(const glm::vec3& positionA, const glm::vec3& positionB, float similarEnough = POSITION_SIMILAR_ENOUGH);
186−
187120 /// \return bool is the float NaN
188121 bool isNaN(float value);
189122
190−QByteArray createByteArray(const glm::vec3& vector);
191−
192123 QString formatUsecTime(float usecs, int prec = 3);
193124
194125
libraries/voxels/CMakeLists.txt+11−24
@@ -1,31 +1,18 @@
1−set(ROOT_DIR ../..)
2−set(MACRO_DIR "${ROOT_DIR}/cmake/macros")
3−
4−# setup for find modules
5−set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/../../cmake/modules/")
6−
71 set(TARGET_NAME voxels)
82
9−find_package(Qt5 COMPONENTS Widgets Script)
10−
11−include(${MACRO_DIR}/SetupHifiLibrary.cmake)
12−setup_hifi_library(${TARGET_NAME})
3+# use setup_hifi_library macro to setup our project and link appropriate Qt modules
4+setup_hifi_library(Widgets Script)
135
14−include(${MACRO_DIR}/IncludeGLM.cmake)
15−include_glm(${TARGET_NAME} "${ROOT_DIR}")
6+include_glm()
167
17−include(${MACRO_DIR}/LinkHifiLibrary.cmake)
18−link_hifi_library(shared ${TARGET_NAME} "${ROOT_DIR}")
19−link_hifi_library(octree ${TARGET_NAME} "${ROOT_DIR}")
20−link_hifi_library(networking ${TARGET_NAME} "${ROOT_DIR}")
21−
22−# link ZLIB
23−find_package(ZLIB)
8+link_hifi_libraries(shared octree networking)
249
10+# find ZLIB
11+find_package(ZLIB REQUIRED)
2512 include_directories(SYSTEM "${ZLIB_INCLUDE_DIRS}")
26−target_link_libraries(${TARGET_NAME} "${ZLIB_LIBRARIES}" Qt5::Widgets Qt5::Script)
2713
28−# add a definition for ssize_t so that windows doesn't bail
29−if (WIN32)
30− add_definitions(-Dssize_t=long)
31−endif ()
14+# add it to our list of libraries to link
15+list(APPEND ${TARGET}_LIBRARIES_TO_LINK "${ZLIB_LIBRARIES}")
16+
17+# call macro to link our dependencies and bubble them up via a property on our target
18+link_shared_dependencies()
libraries/voxels/src/VoxelEditPacketSender.h+1−1
@@ -28,7 +28,7 @@ public: · 27 unmodified lines
2828 /// Queues a single voxel edit message. Will potentially send a pending multi-command packet. Determines which voxel-server
2929 /// node or nodes the packet should be sent to. Can be called even before voxel servers are known, in which case up to
3030 /// MaxPendingMessages will be buffered and processed when voxel servers are known.
31− void queueVoxelEditMessage(PacketType type, unsigned char* codeColorBuffer, ssize_t length) {
31+ void queueVoxelEditMessage(PacketType type, unsigned char* codeColorBuffer, size_t length) {
3232 queueOctreeEditMessage(type, codeColorBuffer, length);
3333 }
3434
tests/audio/CMakeLists.txt+4−28
@@ -1,34 +1,10 @@
11 set(TARGET_NAME audio-tests)
22
3−set(ROOT_DIR ../..)
4−set(MACRO_DIR ${ROOT_DIR}/cmake/macros)
3+setup_hifi_project()
54
6−# setup for find modules
7−set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/../../cmake/modules/")
8−
9−#find_package(Qt5Network REQUIRED)
10−#find_package(Qt5Script REQUIRED)
11−#find_package(Qt5Widgets REQUIRED)
12−
13−include(${MACRO_DIR}/SetupHifiProject.cmake)
14−setup_hifi_project(${TARGET_NAME} TRUE)
15−
16−include(${MACRO_DIR}/AutoMTC.cmake)
17−auto_mtc(${TARGET_NAME} ${ROOT_DIR})
18−
19−#qt5_use_modules(${TARGET_NAME} Network Script Widgets)
20−
21−#include glm
22−include(${MACRO_DIR}/IncludeGLM.cmake)
23−include_glm(${TARGET_NAME} ${ROOT_DIR})
5+include_glm()
246
257 # link in the shared libraries
26−include(${MACRO_DIR}/LinkHifiLibrary.cmake)
27−link_hifi_library(shared ${TARGET_NAME} ${ROOT_DIR})
28−link_hifi_library(audio ${TARGET_NAME} ${ROOT_DIR})
29−link_hifi_library(networking ${TARGET_NAME} ${ROOT_DIR})
30−
31−IF (WIN32)
32− target_link_libraries(${TARGET_NAME} Winmm Ws2_32)
33−ENDIF(WIN32)
8+link_hifi_libraries(shared audio networking)
349
10+link_shared_dependencies()
tests/jitter/CMakeLists.txt+3−28
@@ -1,33 +1,8 @@
11 set(TARGET_NAME jitter-tests)
22
3−set(ROOT_DIR ../..)
4−set(MACRO_DIR ${ROOT_DIR}/cmake/macros)
5−
6−# setup for find modules
7−set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/../../cmake/modules/")
8−
9−#find_package(Qt5Network REQUIRED)
10−#find_package(Qt5Script REQUIRED)
11−#find_package(Qt5Widgets REQUIRED)
12−
13−include(${MACRO_DIR}/SetupHifiProject.cmake)
14−setup_hifi_project(${TARGET_NAME} TRUE)
15−
16−#include(${MACRO_DIR}/AutoMTC.cmake)
17−#auto_mtc(${TARGET_NAME} ${ROOT_DIR})
18−
19−#qt5_use_modules(${TARGET_NAME} Network Script Widgets)
20−
21−#include glm - because it's a dependency of shared utils...
22−include(${MACRO_DIR}/IncludeGLM.cmake)
23−include_glm(${TARGET_NAME} ${ROOT_DIR})
3+setup_hifi_project()
244
255 # link in the shared libraries
26−include(${MACRO_DIR}/LinkHifiLibrary.cmake)
27−link_hifi_library(shared ${TARGET_NAME} ${ROOT_DIR})
28−link_hifi_library(networking ${TARGET_NAME} ${ROOT_DIR})
29−
30−IF (WIN32)
31− target_link_libraries(${TARGET_NAME} Winmm Ws2_32)
32−ENDIF(WIN32)
6+link_hifi_libraries(shared networking)
337
8+link_shared_dependencies()
tests/jitter/src/main.cpp+258−50
@@ -13,12 +13,17 @@ · 12 unmodified lines
1313 #include <sys/socket.h>
1414 #include <arpa/inet.h>
1515 #endif
16+#include <cerrno>
1617 #include <stdio.h>
1718
18−#include <MovingMinMaxAvg.h> // for MovingMinMaxAvg
19+#include <MovingMinMaxAvg.h>
20+#include <SequenceNumberStats.h>
1921 #include <SharedUtil.h> // for usecTimestampNow
22+#include <SimpleMovingAverage.h>
23+#include <StdDev.h>
2024
2125 const quint64 MSEC_TO_USEC = 1000;
26+const quint64 LARGE_STATS_TIME = 500; // we don't expect stats calculation to take more than this many usecs
2227
2328 void runSend(const char* addressOption, int port, int gap, int size, int report);
2429 void runReceive(const char* addressOption, int port, int gap, int size, int report);
@@ -56,112 +61,315 @@ int main(int argc, const char * argv[]) { · 31 unmodified lines
5661 void runSend(const char* addressOption, int port, int gap, int size, int report) {
5762 std::cout << "runSend...\n";
5863
64+#ifdef _WIN32
65+ WSADATA wsaData;
66+ if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {
67+ printf("WSAStartup failed with error %d\n", WSAGetLastError());
68+ return;
69+ }
70+#endif
71+
5972 int sockfd;
6073 struct sockaddr_in servaddr;
61−
62− char* outputBuffer = new char[size];
63− memset(outputBuffer, 0, size);
64−
65− sockfd=socket(AF_INET,SOCK_DGRAM,0);
66−
74+
75+ sockfd = socket(AF_INET, SOCK_DGRAM, 0);
76+
6777 memset(&servaddr, 0, sizeof(servaddr));
6878 servaddr.sin_family = AF_INET;
69− servaddr.sin_addr.s_addr=inet_addr(addressOption);
70− servaddr.sin_port=htons(port);
71−
72− const int SAMPLES_FOR_30_SECONDS = 30 * 1000000 / gap;
79+ servaddr.sin_addr.s_addr = inet_addr(addressOption);
80+ servaddr.sin_port = htons(port);
7381
82+ const int SAMPLES_FOR_SECOND = 1000000 / gap;
83+ std::cout << "SAMPLES_FOR_SECOND:" << SAMPLES_FOR_SECOND << "\n";
84+ const int INTERVALS_PER_30_SECONDS = 30;
85+ std::cout << "INTERVALS_PER_30_SECONDS:" << INTERVALS_PER_30_SECONDS << "\n";
86+ const int SAMPLES_FOR_30_SECONDS = 30 * SAMPLES_FOR_SECOND;
7487 std::cout << "SAMPLES_FOR_30_SECONDS:" << SAMPLES_FOR_30_SECONDS << "\n";
88+ const int REPORTS_FOR_30_SECONDS = 30 * MSECS_PER_SECOND / report;
89+ std::cout << "REPORTS_FOR_30_SECONDS:" << REPORTS_FOR_30_SECONDS << "\n";
90+
91+ int intervalsPerReport = report / MSEC_TO_USEC;
92+ if (intervalsPerReport < 1) {
93+ intervalsPerReport = 1;
94+ }
95+ std::cout << "intervalsPerReport:" << intervalsPerReport << "\n";
96+ MovingMinMaxAvg<int> timeGaps(SAMPLES_FOR_SECOND, INTERVALS_PER_30_SECONDS);
97+ MovingMinMaxAvg<int> timeGapsPerReport(SAMPLES_FOR_SECOND, intervalsPerReport);
98+
99+ char* outputBuffer = new char[size];
100+ memset(outputBuffer, 0, size);
101+
102+ quint16 outgoingSequenceNumber = 0;
103+
104+
105+ StDev stDevReportInterval;
106+ StDev stDev30s;
107+ StDev stDev;
108+
109+ SimpleMovingAverage averageNetworkTime(SAMPLES_FOR_30_SECONDS);
110+ SimpleMovingAverage averageStatsCalcultionTime(SAMPLES_FOR_30_SECONDS);
111+ float lastStatsCalculationTime = 0.0f; // we add out stats calculation time in the next calculation window
112+ bool hasStatsCalculationTime = false;
75113
76− MovingMinMaxAvg<int> timeGaps(1, SAMPLES_FOR_30_SECONDS); // stats
77−
78114 quint64 last = usecTimestampNow();
79115 quint64 lastReport = 0;
80−
116+
81117 while (true) {
82118
83119 quint64 now = usecTimestampNow();
84120 int actualGap = now - last;
85−
86−
121+
122+
87123 if (actualGap >= gap) {
88− sendto(sockfd, outputBuffer, size, 0, (struct sockaddr *)&servaddr, sizeof(servaddr));
89−
124+
125+ // pack seq num
126+ memcpy(outputBuffer, &outgoingSequenceNumber, sizeof(quint16));
127+
128+ quint64 networkStart = usecTimestampNow();
129+ int n = sendto(sockfd, outputBuffer, size, 0, (struct sockaddr *)&servaddr, sizeof(servaddr));
130+ quint64 networkEnd = usecTimestampNow();
131+ float networkElapsed = (float)(networkEnd - networkStart);
132+
133+ if (n < 0) {
134+ std::cout << "Send error: " << strerror(errno) << "\n";
135+ }
136+ outgoingSequenceNumber++;
137+
138+ quint64 statsCalcultionStart = usecTimestampNow();
139+
90140 int gapDifferece = actualGap - gap;
141+
91142 timeGaps.update(gapDifferece);
143+ timeGapsPerReport.update(gapDifferece);
144+ stDev.addValue(gapDifferece);
145+ stDev30s.addValue(gapDifferece);
146+ stDevReportInterval.addValue(gapDifferece);
92147 last = now;
148+
149+ // track out network time and stats calculation times
150+ averageNetworkTime.updateAverage(networkElapsed);
151+
152+ // for our stats calculation time, we actually delay the updating by one sample.
153+ // we do this so that the calculation of the average timing for the stats calculation
154+ // happen inside of the calculation processing. This ensures that tracking stats on
155+ // stats calculation doesn't side effect the remaining running time.
156+ if (hasStatsCalculationTime) {
157+ averageStatsCalcultionTime.updateAverage(lastStatsCalculationTime);
158+ }
93159
94160 if (now - lastReport >= (report * MSEC_TO_USEC)) {
95− std::cout << "SEND gap Difference From Expected "
96− << "min: " << timeGaps.getMin() << " usecs, "
97− << "max: " << timeGaps.getMax() << " usecs, "
98− << "avg: " << timeGaps.getAverage() << " usecs, "
99− << "min last 30: " << timeGaps.getWindowMin() << " usecs, "
100− << "max last 30: " << timeGaps.getWindowMax() << " usecs, "
101− << "avg last 30: " << timeGaps.getWindowAverage() << " usecs "
102− << "\n";
161+
162+ std::cout << "\n"
163+ << "SEND gap Difference From Expected\n"
164+ << "Overall:\n"
165+ << "min: " << timeGaps.getMin() << " usecs, "
166+ << "max: " << timeGaps.getMax() << " usecs, "
167+ << "avg: " << timeGaps.getAverage() << " usecs, "
168+ << "stdev: " << stDev.getStDev() << " usecs\n"
169+ << "Last 30s:\n"
170+ << "min: " << timeGaps.getWindowMin() << " usecs, "
171+ << "max: " << timeGaps.getWindowMax() << " usecs, "
172+ << "avg: " << timeGaps.getWindowAverage() << " usecs, "
173+ << "stdev: " << stDev30s.getStDev() << " usecs\n"
174+ << "Last report interval:\n"
175+ << "min: " << timeGapsPerReport.getWindowMin() << " usecs, "
176+ << "max: " << timeGapsPerReport.getWindowMax() << " usecs, "
177+ << "avg: " << timeGapsPerReport.getWindowAverage() << " usecs, "
178+ << "stdev: " << stDevReportInterval.getStDev() << " usecs\n"
179+ << "Average Execution Times Last 30s:\n"
180+ << " network: " << averageNetworkTime.getAverage() << " usecs average\n"
181+ << " stats: " << averageStatsCalcultionTime.getAverage() << " usecs average"
182+ << "\n";
183+
184+ stDevReportInterval.reset();
185+ if (stDev30s.getSamples() > SAMPLES_FOR_30_SECONDS) {
186+ stDev30s.reset();
187+ }
188+
103189 lastReport = now;
104190 }
191+
192+ quint64 statsCalcultionEnd = usecTimestampNow();
193+ lastStatsCalculationTime = (float)(statsCalcultionEnd - statsCalcultionStart);
194+ if (lastStatsCalculationTime > LARGE_STATS_TIME) {
195+ qDebug() << "WARNING -- unexpectedly large lastStatsCalculationTime=" << lastStatsCalculationTime;
196+ }
197+ hasStatsCalculationTime = true;
198+
105199 }
106200 }
201+ delete[] outputBuffer;
202+
203+#ifdef _WIN32
204+ WSACleanup();
205+#endif
107206 }
108207
109208 void runReceive(const char* addressOption, int port, int gap, int size, int report) {
110209 std::cout << "runReceive...\n";
111210
211+#ifdef _WIN32
212+ WSADATA wsaData;
213+ if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {
214+ printf("WSAStartup failed with error %d\n", WSAGetLastError());
215+ return;
216+ }
217+#endif
112218
113− int sockfd,n;
219+ int sockfd, n;
114220 struct sockaddr_in myaddr;
115−
116− char* inputBuffer = new char[size];
117− memset(inputBuffer, 0, size);
118−
119− sockfd=socket(AF_INET, SOCK_DGRAM, 0);
120−
221+
222+ sockfd = socket(AF_INET, SOCK_DGRAM, 0);
223+
121224 memset(&myaddr, 0, sizeof(myaddr));
122225 myaddr.sin_family = AF_INET;
123− myaddr.sin_addr.s_addr=htonl(INADDR_ANY);
124− myaddr.sin_port=htons(port);
226+ myaddr.sin_addr.s_addr = htonl(INADDR_ANY);
227+ myaddr.sin_port = htons(port);
125228
126− const int SAMPLES_FOR_30_SECONDS = 30 * 1000000 / gap;
127229
230+ const int SAMPLES_FOR_SECOND = 1000000 / gap;
231+ std::cout << "SAMPLES_FOR_SECOND:" << SAMPLES_FOR_SECOND << "\n";
232+ const int INTERVALS_PER_30_SECONDS = 30;
233+ std::cout << "INTERVALS_PER_30_SECONDS:" << INTERVALS_PER_30_SECONDS << "\n";
234+ const int SAMPLES_FOR_30_SECONDS = 30 * SAMPLES_FOR_SECOND;
128235 std::cout << "SAMPLES_FOR_30_SECONDS:" << SAMPLES_FOR_30_SECONDS << "\n";
236+ const int REPORTS_FOR_30_SECONDS = 30 * MSECS_PER_SECOND / report;
237+ std::cout << "REPORTS_FOR_30_SECONDS:" << REPORTS_FOR_30_SECONDS << "\n";
238+
239+ int intervalsPerReport = report / MSEC_TO_USEC;
240+ if (intervalsPerReport < 1) {
241+ intervalsPerReport = 1;
242+ }
243+ std::cout << "intervalsPerReport:" << intervalsPerReport << "\n";
244+ MovingMinMaxAvg<int> timeGaps(SAMPLES_FOR_SECOND, INTERVALS_PER_30_SECONDS);
245+ MovingMinMaxAvg<int> timeGapsPerReport(SAMPLES_FOR_SECOND, intervalsPerReport);
246+
247+ char* inputBuffer = new char[size];
248+ memset(inputBuffer, 0, size);
249+
250+
251+ SequenceNumberStats seqStats(REPORTS_FOR_30_SECONDS);
252+
253+ StDev stDevReportInterval;
254+ StDev stDev30s;
255+ StDev stDev;
256+
257+ SimpleMovingAverage averageNetworkTime(SAMPLES_FOR_30_SECONDS);
258+ SimpleMovingAverage averageStatsCalcultionTime(SAMPLES_FOR_30_SECONDS);
259+ float lastStatsCalculationTime = 0.0f; // we add out stats calculation time in the next calculation window
260+ bool hasStatsCalculationTime = false;
129261
130− MovingMinMaxAvg<int> timeGaps(1, SAMPLES_FOR_30_SECONDS); // stats
131−
132262 if (bind(sockfd, (struct sockaddr *)&myaddr, sizeof(myaddr)) < 0) {
133263 std::cout << "bind failed\n";
134264 return;
135− }
136−
265+ }
266+
137267 quint64 last = 0; // first case
138268 quint64 lastReport = 0;
139269
140270 while (true) {
271+
272+ quint64 networkStart = usecTimestampNow();
141273 n = recvfrom(sockfd, inputBuffer, size, 0, NULL, NULL); // we don't care about where it came from
142−
274+ quint64 networkEnd = usecTimestampNow();
275+ float networkElapsed = (float)(networkEnd - networkStart);
276+
277+ if (n < 0) {
278+ std::cout << "Receive error: " << strerror(errno) << "\n";
279+ }
280+
281+ // parse seq num
282+ quint16 incomingSequenceNumber = *(reinterpret_cast<quint16*>(inputBuffer));
283+ seqStats.sequenceNumberReceived(incomingSequenceNumber);
284+
143285 if (last == 0) {
144286 last = usecTimestampNow();
145287 std::cout << "first packet received\n";
146288 } else {
289+
290+ quint64 statsCalcultionStart = usecTimestampNow();
147291 quint64 now = usecTimestampNow();
148292 int actualGap = now - last;
293+
149294 int gapDifferece = actualGap - gap;
150295 timeGaps.update(gapDifferece);
296+ timeGapsPerReport.update(gapDifferece);
297+ stDev.addValue(gapDifferece);
298+ stDev30s.addValue(gapDifferece);
299+ stDevReportInterval.addValue(gapDifferece);
151300 last = now;
152301
302+ // track out network time and stats calculation times
303+ averageNetworkTime.updateAverage(networkElapsed);
304+
305+ // for our stats calculation time, we actually delay the updating by one sample.
306+ // we do this so that the calculation of the average timing for the stats calculation
307+ // happen inside of the calculation processing. This ensures that tracking stats on
308+ // stats calculation doesn't side effect the remaining running time.
309+ if (hasStatsCalculationTime) {
310+ averageStatsCalcultionTime.updateAverage(lastStatsCalculationTime);
311+ }
312+
153313 if (now - lastReport >= (report * MSEC_TO_USEC)) {
154− std::cout << "RECEIVE gap Difference From Expected "
155− << "min: " << timeGaps.getMin() << " usecs, "
156− << "max: " << timeGaps.getMax() << " usecs, "
157− << "avg: " << timeGaps.getAverage() << " usecs, "
158− << "min last 30: " << timeGaps.getWindowMin() << " usecs, "
159− << "max last 30: " << timeGaps.getWindowMax() << " usecs, "
160− << "avg last 30: " << timeGaps.getWindowAverage() << " usecs "
161− << "\n";
314+
315+ seqStats.pushStatsToHistory();
316+
317+ std::cout << "RECEIVE gap Difference From Expected\n"
318+ << "Overall:\n"
319+ << "min: " << timeGaps.getMin() << " usecs, "
320+ << "max: " << timeGaps.getMax() << " usecs, "
321+ << "avg: " << timeGaps.getAverage() << " usecs, "
322+ << "stdev: " << stDev.getStDev() << " usecs\n"
323+ << "Last 30s:\n"
324+ << "min: " << timeGaps.getWindowMin() << " usecs, "
325+ << "max: " << timeGaps.getWindowMax() << " usecs, "
326+ << "avg: " << timeGaps.getWindowAverage() << " usecs, "
327+ << "stdev: " << stDev30s.getStDev() << " usecs\n"
328+ << "Last report interval:\n"
329+ << "min: " << timeGapsPerReport.getWindowMin() << " usecs, "
330+ << "max: " << timeGapsPerReport.getWindowMax() << " usecs, "
331+ << "avg: " << timeGapsPerReport.getWindowAverage() << " usecs, "
332+ << "stdev: " << stDevReportInterval.getStDev() << " usecs\n"
333+ << "Average Execution Times Last 30s:\n"
334+ << " network: " << averageNetworkTime.getAverage() << " usecs average\n"
335+ << " stats: " << averageStatsCalcultionTime.getAverage() << " usecs average"
336+ << "\n";
337+ stDevReportInterval.reset();
338+
339+ if (stDev30s.getSamples() > SAMPLES_FOR_30_SECONDS) {
340+ stDev30s.reset();
341+ }
342+
343+ PacketStreamStats packetStatsLast30s = seqStats.getStatsForHistoryWindow();
344+ PacketStreamStats packetStatsLastReportInterval = seqStats.getStatsForLastHistoryInterval();
345+
346+ std::cout << "RECEIVE Packet Stats\n"
347+ << "Overall:\n"
348+ << "lost: " << seqStats.getLost() << ", "
349+ << "lost %: " << seqStats.getStats().getLostRate() * 100.0f << "%\n"
350+ << "Last 30s:\n"
351+ << "lost: " << packetStatsLast30s._lost << ", "
352+ << "lost %: " << packetStatsLast30s.getLostRate() * 100.0f << "%\n"
353+ << "Last report interval:\n"
354+ << "lost: " << packetStatsLastReportInterval._lost << ", "
355+ << "lost %: " << packetStatsLastReportInterval.getLostRate() * 100.0f << "%\n"
356+ << "\n\n";
357+
162358 lastReport = now;
163359 }
360+
361+ quint64 statsCalcultionEnd = usecTimestampNow();
362+
363+ lastStatsCalculationTime = (float)(statsCalcultionEnd - statsCalcultionStart);
364+ if (lastStatsCalculationTime > LARGE_STATS_TIME) {
365+ qDebug() << "WARNING -- unexpectedly large lastStatsCalculationTime=" << lastStatsCalculationTime;
366+ }
367+ hasStatsCalculationTime = true;
164368 }
165369 }
166−}
370+ delete[] inputBuffer;
167371
372+#ifdef _WIN32
373+ WSACleanup();
374+#endif
375+}
tests/metavoxels/CMakeLists.txt+5−25
@@ -1,32 +1,12 @@
11 set(TARGET_NAME metavoxel-tests)
22
3−set(ROOT_DIR ../..)
4−set(MACRO_DIR "${ROOT_DIR}/cmake/macros")
3+auto_mtc()
54
6−# setup for find modules
7−set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/../../cmake/modules/")
5+include_glm()
86
9−find_package(Qt5 COMPONENTS Network Script Widgets)
10−
11−include(${MACRO_DIR}/AutoMTC.cmake)
12−auto_mtc(${TARGET_NAME} "${ROOT_DIR}")
13−
14−include(${MACRO_DIR}/SetupHifiProject.cmake)
15−setup_hifi_project(${TARGET_NAME} TRUE "${AUTOMTC_SRC}")
16−
17−#include glm
18−include(${MACRO_DIR}/IncludeGLM.cmake)
19−include_glm(${TARGET_NAME} "${ROOT_DIR}")
7+setup_hifi_project(Network Script Widgets)
208
219 # link in the shared libraries
22−include(${MACRO_DIR}/LinkHifiLibrary.cmake)
23−link_hifi_library(metavoxels ${TARGET_NAME} "${ROOT_DIR}")
24−link_hifi_library(networking ${TARGET_NAME} "${ROOT_DIR}")
25−link_hifi_library(shared ${TARGET_NAME} "${ROOT_DIR}")
26−
27−IF (WIN32)
28− target_link_libraries(${TARGET_NAME} Winmm Ws2_32)
29−ENDIF(WIN32)
30−
31−target_link_libraries(${TARGET_NAME} Qt5::Network Qt5::Widgets Qt5::Script)
10+link_hifi_libraries(metavoxels networking shared)
3211
12+link_shared_dependencies()
tests/networking/CMakeLists.txt+3−28
@@ -1,33 +1,8 @@
11 set(TARGET_NAME networking-tests)
22
3−set(ROOT_DIR ../..)
4−set(MACRO_DIR ${ROOT_DIR}/cmake/macros)
5−
6−# setup for find modules
7−set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/../../cmake/modules/")
8−
9−#find_package(Qt5Network REQUIRED)
10−#find_package(Qt5Script REQUIRED)
11−#find_package(Qt5Widgets REQUIRED)
12−
13−include(${MACRO_DIR}/SetupHifiProject.cmake)
14−setup_hifi_project(${TARGET_NAME} TRUE)
15−
16−include(${MACRO_DIR}/AutoMTC.cmake)
17−auto_mtc(${TARGET_NAME} ${ROOT_DIR})
18−
19−#qt5_use_modules(${TARGET_NAME} Network Script Widgets)
20−
21−#include glm
22−include(${MACRO_DIR}/IncludeGLM.cmake)
23−include_glm(${TARGET_NAME} ${ROOT_DIR})
3+setup_hifi_project()
244
255 # link in the shared libraries
26−include(${MACRO_DIR}/LinkHifiLibrary.cmake)
27−link_hifi_library(shared ${TARGET_NAME} ${ROOT_DIR})
28−link_hifi_library(networking ${TARGET_NAME} ${ROOT_DIR})
29−
30−IF (WIN32)
31− target_link_libraries(${TARGET_NAME} Winmm Ws2_32)
32−ENDIF(WIN32)
6+link_hifi_libraries(shared networking)
337
8+link_shared_dependencies()
tests/networking/src/SequenceNumberStatsTests.cpp+4−3
@@ -9,11 +9,12 @@ · 8 unmodified lines
99 // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
1010 //
1111
12−#include "SequenceNumberStatsTests.h"
13−
14−#include "SharedUtil.h"
12+#include <cassert>
1513 #include <limits>
1614
15+#include <SharedUtil.h>
16+
17+#include "SequenceNumberStatsTests.h"
1718
1819 void SequenceNumberStatsTests::runAllTests() {
1920 rolloverTest();
tests/octree/CMakeLists.txt+14−39
@@ -1,44 +1,19 @@
11 set(TARGET_NAME octree-tests)
22
3−set(ROOT_DIR ../..)
4−set(MACRO_DIR ${ROOT_DIR}/cmake/macros)
3+setup_hifi_project(Script Network)
54
6−# setup for find modules
7−set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/../../cmake/modules/")
8−
9−find_package(Qt5Network REQUIRED)
10−find_package(Qt5Script REQUIRED)
11−find_package(Qt5Widgets REQUIRED)
12−
13−include(${MACRO_DIR}/SetupHifiProject.cmake)
14−setup_hifi_project(${TARGET_NAME} TRUE)
15−
16−include(${MACRO_DIR}/AutoMTC.cmake)
17−auto_mtc(${TARGET_NAME} ${ROOT_DIR})
18−
19−qt5_use_modules(${TARGET_NAME} Network Script Widgets)
20−
21−#include glm
22−include(${MACRO_DIR}/IncludeGLM.cmake)
23−include_glm(${TARGET_NAME} ${ROOT_DIR})
5+include_glm()
246
257 # link in the shared libraries
26−include(${MACRO_DIR}/LinkHifiLibrary.cmake)
27−link_hifi_library(entities ${TARGET_NAME} ${ROOT_DIR})
28−link_hifi_library(octree ${TARGET_NAME} ${ROOT_DIR})
29−link_hifi_library(audio ${TARGET_NAME} ${ROOT_DIR})
30−link_hifi_library(networking ${TARGET_NAME} ${ROOT_DIR})
31−link_hifi_library(animation ${TARGET_NAME} ${ROOT_DIR})
32−link_hifi_library(fbx ${TARGET_NAME} ${ROOT_DIR})
33−link_hifi_library(shared ${TARGET_NAME} ${ROOT_DIR})
34−link_hifi_library(voxels ${TARGET_NAME} ${ROOT_DIR})
35−
36−IF (WIN32)
37− # add a definition for ssize_t so that windows doesn't bail
38− add_definitions(-Dssize_t=long)
39−
40− #target_link_libraries(${TARGET_NAME} Winmm Ws2_32)
41− target_link_libraries(${TARGET_NAME} wsock32.lib)
42−ENDIF(WIN32)
43−
44−
8+//link_hifi_library(entities ${TARGET_NAME} ${ROOT_DIR})
9+//link_hifi_library(octree ${TARGET_NAME} ${ROOT_DIR})
10+//link_hifi_library(audio ${TARGET_NAME} ${ROOT_DIR})
11+//link_hifi_library(networking ${TARGET_NAME} ${ROOT_DIR})
12+//link_hifi_library(animation ${TARGET_NAME} ${ROOT_DIR})
13+//link_hifi_library(fbx ${TARGET_NAME} ${ROOT_DIR})
14+//link_hifi_library(shared ${TARGET_NAME} ${ROOT_DIR})
15+//link_hifi_library(voxels ${TARGET_NAME} ${ROOT_DIR})
16+
17+link_hifi_libraries(animation fbx entities networking octree shared)
18+
19+link_shared_dependencies()
tests/physics/CMakeLists.txt+4−26
@@ -1,32 +1,10 @@
11 set(TARGET_NAME physics-tests)
22
3−set(ROOT_DIR ../..)
4−set(MACRO_DIR ${ROOT_DIR}/cmake/macros)
3+setup_hifi_project()
54
6−# setup for find modules
7−set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/../../cmake/modules/")
8−
9−#find_package(Qt5Network REQUIRED)
10−#find_package(Qt5Script REQUIRED)
11−#find_package(Qt5Widgets REQUIRED)
12−
13−include(${MACRO_DIR}/SetupHifiProject.cmake)
14−setup_hifi_project(${TARGET_NAME} TRUE)
15−
16−include(${MACRO_DIR}/AutoMTC.cmake)
17−auto_mtc(${TARGET_NAME} ${ROOT_DIR})
18−
19−#qt5_use_modules(${TARGET_NAME} Network Script Widgets)
20−
21−#include glm
22−include(${MACRO_DIR}/IncludeGLM.cmake)
23−include_glm(${TARGET_NAME} ${ROOT_DIR})
5+include_glm()
246
257 # link in the shared libraries
26−include(${MACRO_DIR}/LinkHifiLibrary.cmake)
27−link_hifi_library(shared ${TARGET_NAME} ${ROOT_DIR})
28−
29−IF (WIN32)
30− #target_link_libraries(${TARGET_NAME} Winmm Ws2_32)
31−ENDIF(WIN32)
8+link_hifi_libraries(shared)
329
10+link_shared_dependencies()
tests/shared/CMakeLists.txt+4−26
@@ -1,32 +1,10 @@
11 set(TARGET_NAME shared-tests)
22
3−set(ROOT_DIR ../..)
4−set(MACRO_DIR ${ROOT_DIR}/cmake/macros)
3+setup_hifi_project()
54
6−# setup for find modules
7−set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/../../cmake/modules/")
8−
9−#find_package(Qt5Network REQUIRED)
10−#find_package(Qt5Script REQUIRED)
11−#find_package(Qt5Widgets REQUIRED)
12−
13−include(${MACRO_DIR}/SetupHifiProject.cmake)
14−setup_hifi_project(${TARGET_NAME} TRUE)
15−
16−include(${MACRO_DIR}/AutoMTC.cmake)
17−auto_mtc(${TARGET_NAME} ${ROOT_DIR})
18−
19−#qt5_use_modules(${TARGET_NAME} Network Script Widgets)
20−
21−#include glm
22−include(${MACRO_DIR}/IncludeGLM.cmake)
23−include_glm(${TARGET_NAME} ${ROOT_DIR})
5+include_glm()
246
257 # link in the shared libraries
26−include(${MACRO_DIR}/LinkHifiLibrary.cmake)
27−link_hifi_library(shared ${TARGET_NAME} ${ROOT_DIR})
28−
29−IF (WIN32)
30− #target_link_libraries(${TARGET_NAME} Winmm Ws2_32)
31−ENDIF(WIN32)
8+link_hifi_libraries(shared)
329
10+link_shared_dependencies()
tools/CMakeLists.txt+0−1
@@ -1,4 +1,3 @@
1−
21 # add the tool directories
32 add_subdirectory(bitstream2json)
43 add_subdirectory(json2bitstream)
tools/bitstream2json/CMakeLists.txt+4−21
@@ -1,25 +1,8 @@
11 set(TARGET_NAME bitstream2json)
2+setup_hifi_project(Widgets Script)
23
3−set(ROOT_DIR ../..)
4−set(MACRO_DIR "${ROOT_DIR}/cmake/macros")
4+include_glm()
55
6−# setup for find modules
7−set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/../../cmake/modules/")
6+link_hifi_libraries(metavoxels)
87
9−find_package(Qt5 COMPONENTS Network Script Widgets)
10−
11−include(${MACRO_DIR}/SetupHifiProject.cmake)
12−setup_hifi_project(${TARGET_NAME} TRUE)
13−
14−link_hifi_library(metavoxels ${TARGET_NAME} "${ROOT_DIR}")
15−link_hifi_library(networking ${TARGET_NAME} "${ROOT_DIR}")
16−link_hifi_library(shared ${TARGET_NAME} "${ROOT_DIR}")
17−
18−include(${MACRO_DIR}/IncludeGLM.cmake)
19−include_glm(${TARGET_NAME} "${ROOT_DIR}")
20−
21−IF (WIN32)
22− target_link_libraries(${TARGET_NAME} Winmm Ws2_32)
23−ENDIF(WIN32)
24−
25−target_link_libraries(${TARGET_NAME} Qt5::Network Qt5::Widgets Qt5::Script)
8+link_shared_dependencies()
tools/json2bitstream/CMakeLists.txt+4−21
@@ -1,25 +1,8 @@
11 set(TARGET_NAME json2bitstream)
2+setup_hifi_project(Widgets Script)
23
3−set(ROOT_DIR ../..)
4−set(MACRO_DIR "${ROOT_DIR}/cmake/macros")
4+include_glm()
55
6−# setup for find modules
7−set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/../../cmake/modules/")
6+link_hifi_libraries(metavoxels)
87
9−find_package(Qt5 COMPONENTS Network Script Widgets)
10−
11−include(${MACRO_DIR}/SetupHifiProject.cmake)
12−setup_hifi_project(${TARGET_NAME} TRUE)
13−
14−link_hifi_library(metavoxels ${TARGET_NAME} "${ROOT_DIR}")
15−link_hifi_library(networking ${TARGET_NAME} "${ROOT_DIR}")
16−link_hifi_library(shared ${TARGET_NAME} "${ROOT_DIR}")
17−
18−include(${MACRO_DIR}/IncludeGLM.cmake)
19−include_glm(${TARGET_NAME} "${ROOT_DIR}")
20−
21−IF (WIN32)
22− target_link_libraries(${TARGET_NAME} Winmm Ws2_32)
23−ENDIF(WIN32)
24−
25−target_link_libraries(${TARGET_NAME} Qt5::Network Qt5::Widgets Qt5::Script)
8+link_shared_dependencies()
tools/mtc/CMakeLists.txt+2−5
@@ -1,7 +1,4 @@
11 set(TARGET_NAME mtc)
2+setup_hifi_project()
23
3−set(ROOT_DIR ../..)
4−set(MACRO_DIR "${ROOT_DIR}/cmake/macros")
5−
6−include(${MACRO_DIR}/SetupHifiProject.cmake)
7−setup_hifi_project(${TARGET_NAME} TRUE)
4+link_shared_dependencies()
voxel-edit/CMakeLists.txt+4−35
@@ -1,40 +1,9 @@
11 set(TARGET_NAME voxel-edit)
22
3−set(ROOT_DIR ..)
4−set(MACRO_DIR "${ROOT_DIR}/cmake/macros")
3+setup_hifi_project()
54
6−# setup for find modules
7−set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/../cmake/modules/")
5+include_glm()
86
9−# set up the external glm library
10−include(${MACRO_DIR}/IncludeGLM.cmake)
11−include_glm(${TARGET_NAME} "${ROOT_DIR}")
7+link_hifi_libraries(networking octree shared voxels)
128
13−find_package(Qt5Script REQUIRED)
14−
15−include(${MACRO_DIR}/SetupHifiProject.cmake)
16−setup_hifi_project(${TARGET_NAME} TRUE)
17−
18−# link in the shared library
19−include(${MACRO_DIR}/LinkHifiLibrary.cmake)
20−link_hifi_library(shared ${TARGET_NAME} "${ROOT_DIR}")
21−
22−# link in the hifi octree library
23−link_hifi_library(octree ${TARGET_NAME} "${ROOT_DIR}")
24−
25−# link in the hifi voxels library
26−link_hifi_library(voxels ${TARGET_NAME} "${ROOT_DIR}")
27−
28−# link in the hifi networking library
29−link_hifi_library(networking ${TARGET_NAME} "${ROOT_DIR}")
30−
31−IF (WIN32)
32− target_link_libraries(${TARGET_NAME} Winmm Ws2_32)
33−ENDIF(WIN32)
34−
35−target_link_libraries(${TARGET_NAME} Qt5::Script)
36−
37−# add a definition for ssize_t so that windows doesn't bail
38−if (WIN32)
39− add_definitions(-Dssize_t=long)
40−endif ()
9+link_shared_dependencies()

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t