Unified · Split
| @@ -44,6 +44,18 @@ set(CMAKE_INCLUDE_CURRENT_DIR ON) · 43 unmodified lines | ||
| 44 | 44 | # Instruct CMake to run moc automatically when needed. |
| 45 | 45 | set(CMAKE_AUTOMOC ON) |
| 46 | 46 | |
| 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 | + | |
| 47 | 59 | # targets on all platforms |
| 48 | 60 | add_subdirectory(assignment-client) |
| 49 | 61 | add_subdirectory(domain-server) |
| @@ -1,47 +1,17 @@ | ||
| 1 | 1 | set(TARGET_NAME assignment-client) |
| 2 | 2 | |
| 3 | −set(ROOT_DIR ..) | |
| 4 | −set(MACRO_DIR "${ROOT_DIR}/cmake/macros") | |
| 3 | +setup_hifi_project(Core Gui Network Script Widgets) | |
| 5 | 4 | |
| 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() | |
| 17 | 6 | |
| 18 | 7 | # 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 | +) | |
| 33 | 12 | |
| 34 | 13 | if (UNIX) |
| 35 | − target_link_libraries(${TARGET_NAME} ${CMAKE_DL_LIBS}) | |
| 14 | + list(APPEND ${TARGET_NAME}_LIBRARIES_TO_LINK ${CMAKE_DL_LIBS}) | |
| 36 | 15 | endif (UNIX) |
| 37 | 16 | |
| 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() | |
| @@ -115,7 +115,7 @@ void Agent::readPendingDatagrams() { · 114 unmodified lines | ||
| 115 | 115 | sourceNode->setLastHeardMicrostamp(usecTimestampNow()); |
| 116 | 116 | |
| 117 | 117 | QByteArray mutablePacket = receivedPacket; |
| 118 | − ssize_t messageLength = mutablePacket.size(); | |
| 118 | + int messageLength = mutablePacket.size(); | |
| 119 | 119 | |
| 120 | 120 | if (datagramPacketType == PacketTypeOctreeStats) { |
| 121 | 121 | |
| @@ -37,6 +37,7 @@ · 36 unmodified lines | ||
| 37 | 37 | #include <QtCore/QJsonDocument> |
| 38 | 38 | #include <QtCore/QJsonObject> |
| 39 | 39 | #include <QtCore/QJsonValue> |
| 40 | +#include <QtCore/QThread> | |
| 40 | 41 | #include <QtCore/QTimer> |
| 41 | 42 | #include <QtNetwork/QNetworkRequest> |
| 42 | 43 | #include <QtNetwork/QNetworkReply> |
| @@ -52,6 +53,7 @@ · 9 unmodified lines | ||
| 52 | 53 | |
| 53 | 54 | #include "AudioRingBuffer.h" |
| 54 | 55 | #include "AudioMixerClientData.h" |
| 56 | +#include "AudioMixerDatagramProcessor.h" | |
| 55 | 57 | #include "AvatarAudioStream.h" |
| 56 | 58 | #include "InjectedAudioStream.h" |
| 57 | 59 | |
| @@ -71,6 +73,8 @@ bool AudioMixer::_useDynamicJitterBuffers = false; · 13 unmodified lines | ||
| 71 | 73 | int AudioMixer::_staticDesiredJitterBufferFrames = 0; |
| 72 | 74 | int AudioMixer::_maxFramesOverDesired = 0; |
| 73 | 75 | |
| 76 | +bool AudioMixer::_printStreamStats = false; | |
| 77 | + | |
| 74 | 78 | AudioMixer::AudioMixer(const QByteArray& packet) : |
| 75 | 79 | ThreadedAssignment(packet), |
| 76 | 80 | _trailingSleepRatio(1.0f), |
| @@ -295,38 +299,34 @@ void AudioMixer::prepareMixForListeningNode(Node* node) { · 218 unmodified lines | ||
| 295 | 299 | } |
| 296 | 300 | } |
| 297 | 301 | |
| 298 | −void AudioMixer::readPendingDatagrams() { | |
| 299 | − QByteArray receivedPacket; | |
| 300 | − HifiSockAddr senderSockAddr; | |
| 302 | +void AudioMixer::readPendingDatagram(const QByteArray& receivedPacket, const HifiSockAddr& senderSockAddr) { | |
| 301 | 303 | NodeList* nodeList = NodeList::getInstance(); |
| 302 | 304 | |
| 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); | |
| 322 | 322 | } |
| 323 | − | |
| 324 | − } else { | |
| 325 | − // let processNodeData handle it. | |
| 326 | − nodeList->processNodeData(senderSockAddr, receivedPacket); | |
| 327 | 323 | } |
| 324 | + | |
| 325 | + } else { | |
| 326 | + // let processNodeData handle it. | |
| 327 | + nodeList->processNodeData(senderSockAddr, receivedPacket); | |
| 328 | 328 | } |
| 329 | − } | |
| 329 | + } | |
| 330 | 330 | } |
| 331 | 331 | |
| 332 | 332 | void AudioMixer::sendStatsPacket() { |
| @@ -390,7 +390,35 @@ void AudioMixer::run() { · 57 unmodified lines | ||
| 390 | 390 | ThreadedAssignment::commonInit(AUDIO_MIXER_LOGGING_TARGET_NAME, NodeType::AudioMixer); |
| 391 | 391 | |
| 392 | 392 | 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 | + | |
| 394 | 422 | nodeList->addNodeTypeToInterestSet(NodeType::Agent); |
| 395 | 423 | |
| 396 | 424 | nodeList->linkedDataCreateCallback = attachNewNodeDataToNode; |
| @@ -448,7 +476,11 @@ void AudioMixer::run() { · 51 unmodified lines | ||
| 448 | 476 | } |
| 449 | 477 | qDebug() << "Max frames over desired:" << _maxFramesOverDesired; |
| 450 | 478 | |
| 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 | + } | |
| 452 | 484 | |
| 453 | 485 | const QString UNATTENUATED_ZONE_KEY = "D-unattenuated-zone"; |
| 454 | 486 | |
| @@ -547,6 +579,7 @@ void AudioMixer::run() { · 92 unmodified lines | ||
| 547 | 579 | sendAudioStreamStats = true; |
| 548 | 580 | } |
| 549 | 581 | |
| 582 | + bool streamStatsPrinted = false; | |
| 550 | 583 | foreach (const SharedNodePointer& node, nodeList->getNodeHash()) { |
| 551 | 584 | if (node->getLinkedData()) { |
| 552 | 585 | AudioMixerClientData* nodeData = (AudioMixerClientData*)node->getLinkedData(); |
| @@ -581,12 +614,21 @@ void AudioMixer::run() { · 28 unmodified lines | ||
| 581 | 614 | // send an audio stream stats packet if it's time |
| 582 | 615 | if (sendAudioStreamStats) { |
| 583 | 616 | 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 | + } | |
| 584 | 623 | } |
| 585 | 624 | |
| 586 | 625 | ++_sumListeners; |
| 587 | 626 | } |
| 588 | 627 | } |
| 589 | 628 | } |
| 629 | + if (streamStatsPrinted) { | |
| 630 | + printf("\n----------------------------------------------------------------\n"); | |
| 631 | + } | |
| 590 | 632 | |
| 591 | 633 | ++_numStatFrames; |
| 592 | 634 | |
| @@ -33,7 +33,8 @@ public slots: · 32 unmodified lines | ||
| 33 | 33 | /// threaded run of assignment |
| 34 | 34 | void run(); |
| 35 | 35 | |
| 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); | |
| 37 | 38 | |
| 38 | 39 | void sendStatsPacket(); |
| 39 | 40 | |
| @@ -66,6 +67,8 @@ private: · 26 unmodified lines | ||
| 66 | 67 | static int _staticDesiredJitterBufferFrames; |
| 67 | 68 | static int _maxFramesOverDesired; |
| 68 | 69 | |
| 70 | + static bool _printStreamStats; | |
| 71 | + | |
| 69 | 72 | quint64 _lastSendAudioStreamStatsTime; |
| 70 | 73 | }; |
| 71 | 74 | |
| @@ -22,7 +22,8 @@ · 21 unmodified lines | ||
| 22 | 22 | |
| 23 | 23 | AudioMixerClientData::AudioMixerClientData() : |
| 24 | 24 | _audioStreams(), |
| 25 | − _outgoingMixedAudioSequenceNumber(0) | |
| 25 | + _outgoingMixedAudioSequenceNumber(0), | |
| 26 | + _downstreamAudioStreamStats() | |
| 26 | 27 | { |
| 27 | 28 | } |
| 28 | 29 | |
| @@ -263,3 +264,45 @@ QString AudioMixerClientData::getAudioStreamStatsString() const { · 234 unmodified lines | ||
| 263 | 264 | } |
| 264 | 265 | return result; |
| 265 | 266 | } |
| 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 | +} | |
| @@ -38,6 +38,11 @@ public: · 37 unmodified lines | ||
| 38 | 38 | void incrementOutgoingMixedAudioSequenceNumber() { _outgoingMixedAudioSequenceNumber++; } |
| 39 | 39 | quint16 getOutgoingSequenceNumber() const { return _outgoingMixedAudioSequenceNumber; } |
| 40 | 40 | |
| 41 | + void printUpstreamDownstreamStats() const; | |
| 42 | + | |
| 43 | +private: | |
| 44 | + void printAudioStreamStats(const AudioStreamStats& streamStats) const; | |
| 45 | + | |
| 41 | 46 | private: |
| 42 | 47 | QHash<QUuid, PositionalAudioStream*> _audioStreams; // mic stream stored under key of null UUID |
| 43 | 48 | |
| @@ -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 | +} | |
| @@ -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 | |
| @@ -11,6 +11,8 @@ · 10 unmodified lines | ||
| 11 | 11 | |
| 12 | 12 | #include <QThread> |
| 13 | 13 | |
| 14 | +#include <GLMHelpers.h> | |
| 15 | + | |
| 14 | 16 | #include "ScriptableAvatar.h" |
| 15 | 17 | |
| 16 | 18 | ScriptableAvatar::ScriptableAvatar(ScriptEngine* scriptEngine) : _scriptEngine(scriptEngine), _animation(NULL) { |
| @@ -8,8 +8,8 @@ · 7 unmodified lines | ||
| 8 | 8 | # See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html |
| 9 | 9 | # |
| 10 | 10 | |
| 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) | |
| 13 | 13 | |
| 14 | 14 | file(GLOB INCLUDE_FILES src/*.h) |
| 15 | 15 | |
| @@ -7,7 +7,7 @@ · 6 unmodified lines | ||
| 7 | 7 | # See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html |
| 8 | 8 | # |
| 9 | 9 | |
| 10 | −macro(INCLUDE_GLM TARGET ROOT_DIR) | |
| 10 | +macro(INCLUDE_GLM) | |
| 11 | 11 | |
| 12 | 12 | find_package(GLM REQUIRED) |
| 13 | 13 | include_directories("${GLM_INCLUDE_DIRS}") |
| @@ -16,4 +16,4 @@ macro(INCLUDE_GLM TARGET ROOT_DIR) · 2 unmodified lines | ||
| 16 | 16 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -isystem ${GLM_INCLUDE_DIRS}") |
| 17 | 17 | endif () |
| 18 | 18 | |
| 19 | −endmacro(INCLUDE_GLM _target _root_dir) | |
| 19 | +endmacro(INCLUDE_GLM) | |
| @@ -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) | |
| @@ -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) | |
| @@ -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) | |
| @@ -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) | |
| @@ -7,20 +7,27 @@ · 6 unmodified lines | ||
| 7 | 7 | # See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html |
| 8 | 8 | # |
| 9 | 9 | |
| 10 | −macro(SETUP_HIFI_LIBRARY TARGET) | |
| 10 | +macro(SETUP_HIFI_LIBRARY) | |
| 11 | 11 | |
| 12 | − project(${TARGET}) | |
| 12 | + project(${TARGET_NAME}) | |
| 13 | 13 | |
| 14 | 14 | # grab the implemenation and header files |
| 15 | 15 | file(GLOB LIB_SRCS src/*.h src/*.cpp) |
| 16 | − set(LIB_SRCS ${LIB_SRCS} ${WRAPPED_SRCS}) | |
| 16 | + set(LIB_SRCS ${LIB_SRCS}) | |
| 17 | 17 | |
| 18 | 18 | # 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}) | |
| 20 | 20 | |
| 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) | |
| @@ -7,8 +7,8 @@ · 6 unmodified lines | ||
| 7 | 7 | # See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html |
| 8 | 8 | # |
| 9 | 9 | |
| 10 | −macro(SETUP_HIFI_PROJECT TARGET INCLUDE_QT) | |
| 11 | − project(${TARGET}) | |
| 10 | +macro(SETUP_HIFI_PROJECT) | |
| 11 | + project(${TARGET_NAME}) | |
| 12 | 12 | |
| 13 | 13 | # grab the implemenation and header files |
| 14 | 14 | file(GLOB TARGET_SRCS src/*) |
| @@ -23,12 +23,19 @@ macro(SETUP_HIFI_PROJECT TARGET INCLUDE_QT) · 8 unmodified lines | ||
| 23 | 23 | endforeach() |
| 24 | 24 | |
| 25 | 25 | # add the executable, include additional optional sources |
| 26 | − add_executable(${TARGET} ${TARGET_SRCS} ${ARGN}) | |
| 26 | + add_executable(${TARGET_NAME} ${TARGET_SRCS} "${AUTOMTC_SRC}") | |
| 27 | 27 | |
| 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() | |
| 32 | 40 | |
| 33 | − target_link_libraries(${TARGET} ${QT_LIBRARIES}) | |
| 34 | 41 | endmacro() |
| @@ -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) | |
| @@ -19,6 +19,6 @@ if (WIN32) · 18 unmodified lines | ||
| 19 | 19 | endif (WIN32) |
| 20 | 20 | |
| 21 | 21 | 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) | |
| 23 | 23 | |
| 24 | 24 | mark_as_advanced(FACEPLUS_INCLUDE_DIRS FACEPLUS_LIBRARIES) |
| @@ -40,6 +40,6 @@ select_library_configurations(FACESHIFT) · 39 unmodified lines | ||
| 40 | 40 | set(FACESHIFT_LIBRARIES ${FACESHIFT_LIBRARY}) |
| 41 | 41 | |
| 42 | 42 | 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) | |
| 44 | 44 | |
| 45 | 45 | mark_as_advanced(FACESHIFT_INCLUDE_DIRS FACESHIFT_LIBRARIES FACESHIFT_SEARCH_DIRS) |
| @@ -30,6 +30,6 @@ select_library_configurations(LEAPMOTION) · 29 unmodified lines | ||
| 30 | 30 | set(LEAPMOTION_LIBRARIES "${LEAPMOTION_LIBRARY}") |
| 31 | 31 | |
| 32 | 32 | 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) | |
| 34 | 34 | |
| 35 | 35 | mark_as_advanced(LEAPMOTION_INCLUDE_DIRS LEAPMOTION_LIBRARIES LEAPMOTION_SEARCH_DIRS) |
| @@ -19,16 +19,18 @@ · 18 unmodified lines | ||
| 19 | 19 | # |
| 20 | 20 | |
| 21 | 21 | include("${MACRO_DIR}/HifiLibrarySearchHints.cmake") |
| 22 | −hifi_library_search_hints("oculus") | |
| 22 | +hifi_library_search_hints("libovr") | |
| 23 | 23 | |
| 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}) | |
| 26 | 26 | |
| 27 | 27 | include(SelectLibraryConfigurations) |
| 28 | 28 | |
| 29 | 29 | 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) | |
| 32 | 34 | elseif (UNIX) |
| 33 | 35 | find_library(UDEV_LIBRARY_RELEASE udev /usr/lib/x86_64-linux-gnu/) |
| 34 | 36 | find_library(XINERAMA_LIBRARY_RELEASE Xinerama /usr/lib/x86_64-linux-gnu/) |
| @@ -39,29 +41,36 @@ elseif (UNIX) · 4 unmodified lines | ||
| 39 | 41 | set(LINUX_ARCH_DIR "x86_64") |
| 40 | 42 | endif() |
| 41 | 43 | |
| 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}) | |
| 44 | 46 | |
| 45 | 47 | select_library_configurations(UDEV) |
| 46 | 48 | select_library_configurations(XINERAMA) |
| 47 | 49 | |
| 48 | 50 | 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) | |
| 51 | 54 | endif () |
| 52 | 55 | |
| 53 | 56 | select_library_configurations(LIBOVR) |
| 54 | −set(LIBOVR_LIBRARIES "${LIBOVR_LIBRARY}") | |
| 57 | +set(LIBOVR_LIBRARIES ${LIBOVR_LIBRARY}) | |
| 55 | 58 | |
| 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) | |
| 58 | 70 | endif () |
| 59 | 71 | |
| 60 | 72 | 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 () | |
| 66 | 73 | |
| 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) | |
| @@ -19,6 +19,6 @@ if (WIN32) · 18 unmodified lines | ||
| 19 | 19 | endif (WIN32) |
| 20 | 20 | |
| 21 | 21 | 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) | |
| 23 | 23 | |
| 24 | 24 | mark_as_advanced(PRIOVR_INCLUDE_DIRS PRIOVR_LIBRARIES) |
| @@ -26,12 +26,14 @@ find_path(QXMPP_INCLUDE_DIRS QXmppClient.h PATH_SUFFIXES include/qxmpp HINTS ${Q · 25 unmodified lines | ||
| 26 | 26 | find_library(QXMPP_LIBRARY_RELEASE NAMES qxmpp PATH_SUFFIXES lib HINTS ${QXMPP_SEARCH_DIRS}) |
| 27 | 27 | find_library(QXMPP_LIBRARY_DEBUG NAMES qxmpp_d PATH_SUFFIXES lib HINTS ${QXMPP_SEARCH_DIRS}) |
| 28 | 28 | |
| 29 | +find_package(Qt5 COMPONENTS Xml REQUIRED) | |
| 30 | + | |
| 29 | 31 | include(SelectLibraryConfigurations) |
| 30 | 32 | select_library_configurations(QXMPP) |
| 31 | 33 | |
| 32 | −set(QXMPP_LIBRARIES "${QXMPP_LIBRARY}") | |
| 34 | +set(QXMPP_LIBRARIES "${QXMPP_LIBRARY}" Qt5::Xml) | |
| 33 | 35 | |
| 34 | 36 | 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) | |
| 36 | 38 | |
| 37 | 39 | mark_as_advanced(QXMPP_INCLUDE_DIRS QXMPP_LIBRARIES QXMPP_SEARCH_DIRS) |
| @@ -25,6 +25,6 @@ find_path(RTMIDI_INCLUDE_DIRS RtMidi.h PATH_SUFFIXES include HINTS ${RTMIDI_SEAR · 24 unmodified lines | ||
| 25 | 25 | find_library(RTMIDI_LIBRARIES NAMES rtmidi PATH_SUFFIXES lib HINTS ${RTMIDI_SEARCH_DIRS}) |
| 26 | 26 | |
| 27 | 27 | 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) | |
| 29 | 29 | |
| 30 | 30 | mark_as_advanced(RTMIDI_INCLUDE_DIRS RTMIDI_LIBRARIES RTMIDI_SEARCH_DIRS) |
| @@ -40,6 +40,6 @@ select_library_configurations(SIXENSE) · 39 unmodified lines | ||
| 40 | 40 | set(SIXENSE_LIBRARIES "${SIXENSE_LIBRARY}") |
| 41 | 41 | |
| 42 | 42 | 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) | |
| 44 | 44 | |
| 45 | 45 | mark_as_advanced(SIXENSE_LIBRARIES SIXENSE_INCLUDE_DIRS SIXENSE_SEARCH_DIRS) |
| @@ -32,6 +32,9 @@ if (APPLE) · 31 unmodified lines | ||
| 32 | 32 | find_library(VISAGE_VISION_LIBRARY NAME vsvision PATH_SUFFIXES lib HINTS ${VISAGE_SEARCH_DIRS}) |
| 33 | 33 | find_library(VISAGE_OPENCV_LIBRARY NAME OpenCV PATH_SUFFIXES dependencies/OpenCV_MacOSX/lib HINTS ${VISAGE_SEARCH_DIRS}) |
| 34 | 34 | |
| 35 | + find_library(AppKit AppKit) | |
| 36 | + find_library(QuartzCore QuartzCore) | |
| 37 | + find_library(QTKit QTKit) | |
| 35 | 38 | elseif (WIN32) |
| 36 | 39 | find_path(VISAGE_XML_INCLUDE_DIR libxml/xmlreader.h PATH_SUFFIXES dependencies/libxml2/include HINTS ${VISAGE_SEARCH_DIRS}) |
| 37 | 40 | 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 | ||
| 43 | 46 | endif () |
| 44 | 47 | |
| 45 | 48 | 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}) | |
| 50 | 59 | |
| 51 | 60 | set(VISAGE_INCLUDE_DIRS "${VISAGE_XML_INCLUDE_DIR}" "${VISAGE_OPENCV_INCLUDE_DIR}" "${VISAGE_OPENCV2_INCLUDE_DIR}" "${VISAGE_BASE_INCLUDE_DIR}") |
| 52 | 61 | set(VISAGE_LIBRARIES "${VISAGE_CORE_LIBRARY}" "${VISAGE_VISION_LIBRARY}" "${VISAGE_OPENCV_LIBRARY}") |
| 53 | 62 | |
| 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) | |
| @@ -1,24 +1,7 @@ | ||
| 1 | −if (WIN32) | |
| 2 | − cmake_policy (SET CMP0020 NEW) | |
| 3 | −endif (WIN32) | |
| 4 | − | |
| 5 | 1 | set(TARGET_NAME domain-server) |
| 6 | 2 | |
| 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) | |
| 22 | 5 | |
| 23 | 6 | # remove and then copy the files for the webserver |
| 24 | 7 | add_custom_command(TARGET ${TARGET_NAME} POST_BUILD |
| @@ -29,20 +12,7 @@ add_custom_command(TARGET ${TARGET_NAME} POST_BUILD · 4 unmodified lines | ||
| 29 | 12 | "${PROJECT_SOURCE_DIR}/resources/web" |
| 30 | 13 | $<TARGET_FILE_DIR:${TARGET_NAME}>/resources/web) |
| 31 | 14 | |
| 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) | |
| 44 | 17 | |
| 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() | |
| @@ -21,6 +21,12 @@ · 20 unmodified lines | ||
| 21 | 21 | "placeholder": "10", |
| 22 | 22 | "default": "10" |
| 23 | 23 | }, |
| 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 | + }, | |
| 24 | 30 | "D-unattenuated-zone": { |
| 25 | 31 | "label": "Unattenuated Zone", |
| 26 | 32 | "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)", |
| @@ -24,6 +24,7 @@ function setupMenus() { · 23 unmodified lines | ||
| 24 | 24 | Menu.removeMenuItem("Edit", "Paste"); |
| 25 | 25 | Menu.removeMenuItem("Edit", "Delete"); |
| 26 | 26 | Menu.removeMenuItem("Edit", "Nudge"); |
| 27 | + Menu.removeMenuItem("Edit", "Replace from File"); | |
| 27 | 28 | Menu.removeMenuItem("File", "Export Voxels"); |
| 28 | 29 | Menu.removeMenuItem("File", "Import Voxels"); |
| 29 | 30 | |
| @@ -32,6 +33,7 @@ function setupMenus() { · 2 unmodified lines | ||
| 32 | 33 | Menu.addMenuItem({ menuName: "Edit", menuItemName: "Copy", shortcutKey: "CTRL+C", afterItem: "Cut" }); |
| 33 | 34 | Menu.addMenuItem({ menuName: "Edit", menuItemName: "Paste", shortcutKey: "CTRL+V", afterItem: "Copy" }); |
| 34 | 35 | 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" }); | |
| 35 | 37 | Menu.addMenuItem({ menuName: "Edit", menuItemName: "Delete", shortcutKeyEvent: { text: "backspace" }, afterItem: "Nudge" }); |
| 36 | 38 | Menu.addMenuItem({ menuName: "File", menuItemName: "Export Voxels", shortcutKey: "CTRL+E", afterItem: "Voxels" }); |
| 37 | 39 | Menu.addMenuItem({ menuName: "File", menuItemName: "Import Voxels", shortcutKey: "CTRL+I", afterItem: "Export Voxels" }); |
| @@ -60,7 +62,6 @@ function menuItemEvent(menuItem) { · 22 unmodified lines | ||
| 60 | 62 | print("deleting..."); |
| 61 | 63 | Clipboard.deleteVoxel(selectedVoxel.x, selectedVoxel.y, selectedVoxel.z, selectedVoxel.s); |
| 62 | 64 | } |
| 63 | − | |
| 64 | 65 | if (menuItem == "Export Voxels") { |
| 65 | 66 | print("export"); |
| 66 | 67 | Clipboard.exportVoxel(selectedVoxel.x, selectedVoxel.y, selectedVoxel.z, selectedVoxel.s); |
| @@ -73,6 +74,12 @@ function menuItemEvent(menuItem) { · 6 unmodified lines | ||
| 73 | 74 | print("nudge"); |
| 74 | 75 | Clipboard.nudgeVoxel(selectedVoxel.x, selectedVoxel.y, selectedVoxel.z, selectedVoxel.s, { x: -1, y: 0, z: 0 }); |
| 75 | 76 | } |
| 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 | + } | |
| 76 | 83 | } |
| 77 | 84 | |
| 78 | 85 | var selectCube = Overlays.addOverlay("cube", { |
| @@ -1,18 +1,13 @@ | ||
| 1 | −set(ROOT_DIR ..) | |
| 2 | −set(MACRO_DIR "${ROOT_DIR}/cmake/macros") | |
| 3 | − | |
| 4 | 1 | set(TARGET_NAME interface) |
| 5 | 2 | project(${TARGET_NAME}) |
| 6 | 3 | |
| 7 | −# setup for find modules | |
| 8 | −set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/../cmake/modules/") | |
| 9 | − | |
| 10 | 4 | # 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") | |
| 12 | 6 | 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}") | |
| 16 | 11 | endif () |
| 17 | 12 | endforeach() |
| 18 | 13 | |
| @@ -38,8 +33,7 @@ elseif (WIN32) · 19 unmodified lines | ||
| 38 | 33 | endif () |
| 39 | 34 | |
| 40 | 35 | # set up the external glm library |
| 41 | −include("${MACRO_DIR}/IncludeGLM.cmake") | |
| 42 | −include_glm(${TARGET_NAME} "${ROOT_DIR}") | |
| 36 | +include_glm() | |
| 43 | 37 | |
| 44 | 38 | # create the InterfaceConfig.h file based on GL_HEADERS above |
| 45 | 39 | 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 | ||
| 52 | 46 | set(INTERFACE_SRCS ${INTERFACE_SRCS} "${SUBDIR_SRCS}") |
| 53 | 47 | endforeach(SUBDIR) |
| 54 | 48 | |
| 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) | |
| 56 | 50 | |
| 57 | 51 | # grab the ui files in resources/ui |
| 58 | 52 | file (GLOB_RECURSE QT_UI_FILES ui/*.ui) |
| @@ -97,60 +91,36 @@ endif() · 38 unmodified lines | ||
| 97 | 91 | # create the executable, make it a bundle on OS X |
| 98 | 92 | add_executable(${TARGET_NAME} MACOSX_BUNDLE ${INTERFACE_SRCS} ${QM}) |
| 99 | 93 | |
| 100 | −# link in the hifi shared library | |
| 101 | −include(${MACRO_DIR}/LinkHifiLibrary.cmake) | |
| 102 | − | |
| 103 | 94 | # 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) | |
| 116 | 96 | |
| 117 | 97 | # 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) | |
| 129 | 99 | find_package(OpenSSL REQUIRED) |
| 130 | 100 | |
| 131 | 101 | # perform standard include and linking for found externals |
| 132 | 102 | foreach(EXTERNAL ${OPTIONAL_EXTERNALS}) |
| 133 | − string(TOUPPER ${EXTERNAL} UPPER_EXTERNAL) | |
| 134 | 103 | |
| 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}) | |
| 138 | 108 | endif () |
| 139 | 109 | |
| 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}) | |
| 142 | 112 | |
| 143 | 113 | # include the library directories (ignoring warnings) |
| 144 | − include_directories(SYSTEM ${${UPPER_EXTERNAL}_INCLUDE_DIRS}) | |
| 114 | + include_directories(SYSTEM ${${${EXTERNAL}_UPPERCASE}_INCLUDE_DIRS}) | |
| 145 | 115 | |
| 146 | 116 | # perform the system include hack for OS X to ignore warnings |
| 147 | 117 | if (APPLE) |
| 148 | − foreach(EXTERNAL_INCLUDE_DIR ${${UPPER_EXTERNAL}_INCLUDE_DIRS}) | |
| 118 | + foreach(EXTERNAL_INCLUDE_DIR ${${${EXTERNAL}_UPPERCASE}_INCLUDE_DIRS}) | |
| 149 | 119 | SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -isystem ${EXTERNAL_INCLUDE_DIR}") |
| 150 | 120 | endforeach() |
| 151 | 121 | endif () |
| 152 | 122 | |
| 153 | − target_link_libraries(${TARGET_NAME} ${${UPPER_EXTERNAL}_LIBRARIES}) | |
| 123 | + target_link_libraries(${TARGET_NAME} ${${${EXTERNAL}_UPPERCASE}_LIBRARIES}) | |
| 154 | 124 | |
| 155 | 125 | endif () |
| 156 | 126 | endforeach() |
| @@ -179,17 +149,11 @@ endif () · 22 unmodified lines | ||
| 179 | 149 | |
| 180 | 150 | # include headers for interface and InterfaceConfig. |
| 181 | 151 | 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}") | |
| 186 | 153 | |
| 187 | 154 | 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 | |
| 193 | 157 | ) |
| 194 | 158 | |
| 195 | 159 | # assume we are using a Qt build without bearer management |
| @@ -197,30 +161,12 @@ add_definitions(-DQT_NO_BEARERMANAGEMENT) · 1 unmodified line | ||
| 197 | 161 | |
| 198 | 162 | if (APPLE) |
| 199 | 163 | # link in required OS X frameworks and include the right GL headers |
| 200 | − find_library(AppKit AppKit) | |
| 201 | 164 | find_library(CoreAudio CoreAudio) |
| 202 | − find_library(CoreServices CoreServices) | |
| 203 | − find_library(Carbon Carbon) | |
| 204 | − find_library(Foundation Foundation) | |
| 165 | + find_library(CoreFoundation CoreFoundation) | |
| 205 | 166 | find_library(GLUT GLUT) |
| 206 | 167 | find_library(OpenGL OpenGL) |
| 207 | − find_library(IOKit IOKit) | |
| 208 | − find_library(QTKit QTKit) | |
| 209 | − find_library(QuartzCore QuartzCore) | |
| 210 | 168 | |
| 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}) | |
| 224 | 170 | |
| 225 | 171 | # install command for OS X bundle |
| 226 | 172 | INSTALL(TARGETS ${TARGET_NAME} |
| @@ -254,9 +200,9 @@ else (APPLE) · 27 unmodified lines | ||
| 254 | 200 | # we're using static GLEW, so define GLEW_STATIC |
| 255 | 201 | add_definitions(-DGLEW_STATIC) |
| 256 | 202 | |
| 257 | − # add a definition for ssize_t so that windows doesn't bail | |
| 258 | − add_definitions(-Dssize_t=long) | |
| 259 | − | |
| 260 | 203 | target_link_libraries(${TARGET_NAME} "${GLEW_LIBRARIES}" wsock32.lib opengl32.lib) |
| 261 | 204 | endif() |
| 262 | 205 | endif (APPLE) |
| 206 | + | |
| 207 | +# link any dependencies bubbled up from our linked dependencies | |
| 208 | +link_shared_dependencies() | |
File renamed without changes.
| @@ -245,28 +245,28 @@ · 244 unmodified lines | ||
| 245 | 245 | <context> |
| 246 | 246 | <name>QObject</name> |
| 247 | 247 | <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"/> | |
| 250 | 250 | <source>Import Voxels</source> |
| 251 | 251 | <translation type="unfinished"></translation> |
| 252 | 252 | </message> |
| 253 | 253 | <message> |
| 254 | − <location filename="src/ui/ImportDialog.cpp" line="24"/> | |
| 254 | + <location filename="src/ui/VoxelImportDialog.cpp" line="24"/> | |
| 255 | 255 | <source>Loading ...</source> |
| 256 | 256 | <translation type="unfinished"></translation> |
| 257 | 257 | </message> |
| 258 | 258 | <message> |
| 259 | − <location filename="src/ui/ImportDialog.cpp" line="25"/> | |
| 259 | + <location filename="src/ui/VoxelImportDialog.cpp" line="25"/> | |
| 260 | 260 | <source>Place voxels</source> |
| 261 | 261 | <translation type="unfinished"></translation> |
| 262 | 262 | </message> |
| 263 | 263 | <message> |
| 264 | − <location filename="src/ui/ImportDialog.cpp" line="26"/> | |
| 264 | + <location filename="src/ui/VoxelImportDialog.cpp" line="26"/> | |
| 265 | 265 | <source><b>Import</b> %1 as voxels</source> |
| 266 | 266 | <translation type="unfinished"></translation> |
| 267 | 267 | </message> |
| 268 | 268 | <message> |
| 269 | − <location filename="src/ui/ImportDialog.cpp" line="27"/> | |
| 269 | + <location filename="src/ui/VoxelImportDialog.cpp" line="27"/> | |
| 270 | 270 | <source>Cancel</source> |
| 271 | 271 | <translation type="unfinished"></translation> |
| 272 | 272 | </message> |
| @@ -17,6 +17,12 @@ uniform sampler2D heightMap; · 16 unmodified lines | ||
| 17 | 17 | // the distance between height points in texture space |
| 18 | 18 | uniform float heightScale; |
| 19 | 19 | |
| 20 | +// the scale between height and color textures | |
| 21 | +uniform float colorScale; | |
| 22 | + | |
| 23 | +// the interpolated position | |
| 24 | +varying vec4 position; | |
| 25 | + | |
| 20 | 26 | // the interpolated normal |
| 21 | 27 | varying vec4 normal; |
| 22 | 28 | |
| @@ -29,13 +35,17 @@ void main(void) { · 6 unmodified lines | ||
| 29 | 35 | texture2D(heightMap, heightCoord + vec2(0.0, heightScale)).r; |
| 30 | 36 | normal = normalize(gl_ModelViewMatrix * vec4(deltaX, heightScale, deltaZ, 0.0)); |
| 31 | 37 | |
| 32 | − // pass along the texture coordinates | |
| 33 | − gl_TexCoord[0] = gl_MultiTexCoord0; | |
| 34 | − | |
| 35 | 38 | // add the height to the position |
| 36 | 39 | 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; | |
| 38 | 42 | |
| 39 | 43 | // the zero height should be invisible |
| 40 | 44 | 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); | |
| 41 | 51 | } |
| @@ -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 | +} | |
| @@ -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 | +} | |
| @@ -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 | +} | |
| @@ -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 | +} | |
| @@ -137,7 +137,8 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) : · 136 unmodified lines | ||
| 137 | 137 | _frameCount(0), |
| 138 | 138 | _fps(60.0f), |
| 139 | 139 | _justStarted(true), |
| 140 | − _voxelImporter(NULL), | |
| 140 | + _voxelImportDialog(NULL), | |
| 141 | + _voxelImporter(), | |
| 141 | 142 | _importSucceded(false), |
| 142 | 143 | _sharedVoxelSystem(TREE_SCALE, DEFAULT_MAX_VOXELS_PER_SYSTEM, &_clipboard), |
| 143 | 144 | _entityClipboardRenderer(), |
| @@ -242,6 +243,8 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) : · 98 unmodified lines | ||
| 242 | 243 | |
| 243 | 244 | connect(&domainHandler, SIGNAL(hostnameChanged(const QString&)), SLOT(domainChanged(const QString&))); |
| 244 | 245 | 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())); | |
| 245 | 248 | connect(&domainHandler, &DomainHandler::settingsReceived, this, &Application::domainSettingsReceived); |
| 246 | 249 | |
| 247 | 250 | // hookup VoxelEditSender to PaymentManager so we can pay for octree edits |
| @@ -430,7 +433,7 @@ Application::~Application() { · 182 unmodified lines | ||
| 430 | 433 | delete idleTimer; |
| 431 | 434 | |
| 432 | 435 | _sharedVoxelSystem.changeTree(new VoxelTree); |
| 433 | − delete _voxelImporter; | |
| 436 | + delete _voxelImportDialog; | |
| 434 | 437 | |
| 435 | 438 | // let the avatar mixer know we're out |
| 436 | 439 | MyAvatar::sendKillAvatar(); |
| @@ -465,8 +468,8 @@ void Application::saveSettings() { · 28 unmodified lines | ||
| 465 | 468 | Menu::getInstance()->saveSettings(); |
| 466 | 469 | _rearMirrorTools->saveSettings(_settings); |
| 467 | 470 | |
| 468 | − if (_voxelImporter) { | |
| 469 | − _voxelImporter->saveSettings(_settings); | |
| 471 | + if (_voxelImportDialog) { | |
| 472 | + _voxelImportDialog->saveSettings(_settings); | |
| 470 | 473 | } |
| 471 | 474 | _settings->sync(); |
| 472 | 475 | _numChangedSettings = 0; |
| @@ -599,7 +602,7 @@ void Application::paintGL() { · 126 unmodified lines | ||
| 599 | 602 | |
| 600 | 603 | if (_myCamera.getMode() == CAMERA_MODE_FIRST_PERSON) { |
| 601 | 604 | _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()); | |
| 603 | 606 | _myCamera.setTargetRotation(_myAvatar->getHead()->getCameraOrientation()); |
| 604 | 607 | |
| 605 | 608 | } else if (_myCamera.getMode() == CAMERA_MODE_THIRD_PERSON) { |
| @@ -621,7 +624,7 @@ void Application::paintGL() { · 15 unmodified lines | ||
| 621 | 624 | _myCamera.setTargetPosition(_myAvatar->getHead()->getEyePosition() + glm::vec3(0, _raiseMirror * _myAvatar->getScale(), 0)); |
| 622 | 625 | } else { |
| 623 | 626 | _myCamera.setTightness(0.0f); |
| 624 | − glm::vec3 eyePosition = _myAvatar->getHead()->getFilteredEyePosition(); | |
| 627 | + glm::vec3 eyePosition = _myAvatar->getHead()->getEyePosition(); | |
| 625 | 628 | float headHeight = eyePosition.y - _myAvatar->getPosition().y; |
| 626 | 629 | _myCamera.setDistance(MIRROR_FULLSCREEN_DISTANCE * _scaleMirror); |
| 627 | 630 | _myCamera.setTargetPosition(_myAvatar->getPosition() + glm::vec3(0, headHeight + (_raiseMirror * _myAvatar->getScale()), 0)); |
| @@ -814,9 +817,8 @@ bool Application::event(QEvent* event) { · 186 unmodified lines | ||
| 814 | 817 | QFileOpenEvent* fileEvent = static_cast<QFileOpenEvent*>(event); |
| 815 | 818 | bool isHifiSchemeURL = !fileEvent->url().isEmpty() && fileEvent->url().toLocalFile().startsWith(CUSTOM_URL_SCHEME); |
| 816 | 819 | if (isHifiSchemeURL) { |
| 817 | − Menu::getInstance()->goTo(fileEvent->url().toString()); | |
| 820 | + Menu::getInstance()->goToURL(fileEvent->url().toLocalFile()); | |
| 818 | 821 | } |
| 819 | − | |
| 820 | 822 | return false; |
| 821 | 823 | } |
| 822 | 824 | return QApplication::event(event); |
| @@ -1440,6 +1442,15 @@ void Application::setEnable3DTVMode(bool enable3DTVMode) { · 617 unmodified lines | ||
| 1440 | 1442 | } |
| 1441 | 1443 | |
| 1442 | 1444 | 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 | + | |
| 1443 | 1454 | resizeGL(_glWidget->width(), _glWidget->height()); |
| 1444 | 1455 | } |
| 1445 | 1456 | |
| @@ -1599,17 +1610,17 @@ void Application::exportVoxels(const VoxelDetail& sourceVoxel) { · 153 unmodified lines | ||
| 1599 | 1610 | void Application::importVoxels() { |
| 1600 | 1611 | _importSucceded = false; |
| 1601 | 1612 | |
| 1602 | − if (!_voxelImporter) { | |
| 1603 | − _voxelImporter = new VoxelImporter(_window); | |
| 1604 | − _voxelImporter->loadSettings(_settings); | |
| 1613 | + if (!_voxelImportDialog) { | |
| 1614 | + _voxelImportDialog = new VoxelImportDialog(_window); | |
| 1615 | + _voxelImportDialog->loadSettings(_settings); | |
| 1605 | 1616 | } |
| 1606 | 1617 | |
| 1607 | − if (!_voxelImporter->exec()) { | |
| 1618 | + if (!_voxelImportDialog->exec()) { | |
| 1608 | 1619 | qDebug() << "Import succeeded." << endl; |
| 1609 | 1620 | _importSucceded = true; |
| 1610 | 1621 | } else { |
| 1611 | 1622 | qDebug() << "Import failed." << endl; |
| 1612 | − if (_sharedVoxelSystem.getTree() == _voxelImporter->getVoxelTree()) { | |
| 1623 | + if (_sharedVoxelSystem.getTree() == _voxelImporter.getVoxelTree()) { | |
| 1613 | 1624 | _sharedVoxelSystem.killLocalVoxels(); |
| 1614 | 1625 | _sharedVoxelSystem.changeTree(&_clipboard); |
| 1615 | 1626 | } |
| @@ -1724,7 +1735,7 @@ void Application::init() { · 108 unmodified lines | ||
| 1724 | 1735 | // Cleanup of the original shared tree |
| 1725 | 1736 | _sharedVoxelSystem.init(); |
| 1726 | 1737 | |
| 1727 | − _voxelImporter = new VoxelImporter(_window); | |
| 1738 | + _voxelImportDialog = new VoxelImportDialog(_window); | |
| 1728 | 1739 | |
| 1729 | 1740 | _environment.init(); |
| 1730 | 1741 | |
| @@ -3322,9 +3333,10 @@ void Application::updateWindowTitle(){ · 1591 unmodified lines | ||
| 3322 | 3333 | QString buildVersion = " (build " + applicationVersion() + ")"; |
| 3323 | 3334 | NodeList* nodeList = NodeList::getInstance(); |
| 3324 | 3335 | |
| 3336 | + QString connectionStatus = nodeList->getDomainHandler().isConnected() ? "" : " (NOT CONNECTED) "; | |
| 3325 | 3337 | QString username = AccountManager::getInstance().getAccountInfo().getUsername(); |
| 3326 | 3338 | QString title = QString() + (!username.isEmpty() ? username + " @ " : QString()) |
| 3327 | − + nodeList->getDomainHandler().getHostname() + buildVersion; | |
| 3339 | + + nodeList->getDomainHandler().getHostname() + connectionStatus + buildVersion; | |
| 3328 | 3340 | |
| 3329 | 3341 | AccountManager& accountManager = AccountManager::getInstance(); |
| 3330 | 3342 | if (accountManager.getAccountInfo().hasBalance()) { |
| @@ -87,6 +87,7 @@ · 86 unmodified lines | ||
| 87 | 87 | #include "ui/overlays/Overlays.h" |
| 88 | 88 | #include "ui/ApplicationOverlay.h" |
| 89 | 89 | #include "ui/RunningScriptsWidget.h" |
| 90 | +#include "ui/VoxelImportDialog.h" | |
| 90 | 91 | #include "voxels/VoxelFade.h" |
| 91 | 92 | #include "voxels/VoxelHideShowThread.h" |
| 92 | 93 | #include "voxels/VoxelImporter.h" |
| @@ -193,6 +194,7 @@ public: · 100 unmodified lines | ||
| 193 | 194 | Camera* getCamera() { return &_myCamera; } |
| 194 | 195 | ViewFrustum* getViewFrustum() { return &_viewFrustum; } |
| 195 | 196 | ViewFrustum* getShadowViewFrustum() { return &_shadowViewFrustum; } |
| 197 | + VoxelImporter* getVoxelImporter() { return &_voxelImporter; } | |
| 196 | 198 | VoxelSystem* getVoxels() { return &_voxels; } |
| 197 | 199 | VoxelTree* getVoxelTree() { return _voxels.getTree(); } |
| 198 | 200 | const OctreePacketProcessor& getOctreePacketProcessor() const { return _octreeProcessor; } |
| @@ -463,7 +465,8 @@ private: · 264 unmodified lines | ||
| 463 | 465 | |
| 464 | 466 | VoxelSystem _voxels; |
| 465 | 467 | VoxelTree _clipboard; // if I copy/paste |
| 466 | − VoxelImporter* _voxelImporter; | |
| 468 | + VoxelImportDialog* _voxelImportDialog; | |
| 469 | + VoxelImporter _voxelImporter; | |
| 467 | 470 | bool _importSucceded; |
| 468 | 471 | VoxelSystem _sharedVoxelSystem; |
| 469 | 472 | ViewFrustum _sharedVoxelSystemViewFrustum; |
| @@ -56,7 +56,6 @@ static const int MUTE_ICON_SIZE = 24; · 55 unmodified lines | ||
| 56 | 56 | |
| 57 | 57 | static const int RECEIVED_AUDIO_STREAM_CAPACITY_FRAMES = 100; |
| 58 | 58 | |
| 59 | − | |
| 60 | 59 | Audio::Audio(QObject* parent) : |
| 61 | 60 | AbstractAudioInterface(parent), |
| 62 | 61 | _audioInput(NULL), |
| @@ -83,6 +82,7 @@ Audio::Audio(QObject* parent) : · 20 unmodified lines | ||
| 83 | 82 | _noiseGateSampleCounter(0), |
| 84 | 83 | _noiseGateOpen(false), |
| 85 | 84 | _noiseGateEnabled(true), |
| 85 | + _peqEnabled(false), | |
| 86 | 86 | _toneInjectionEnabled(false), |
| 87 | 87 | _noiseGateFramesToClose(0), |
| 88 | 88 | _totalInputAudioSamples(0), |
| @@ -132,6 +132,7 @@ void Audio::init(QGLWidget *parent) { · 43 unmodified lines | ||
| 132 | 132 | void Audio::reset() { |
| 133 | 133 | _receivedAudioStream.reset(); |
| 134 | 134 | resetStats(); |
| 135 | + _peq.reset(); | |
| 135 | 136 | } |
| 136 | 137 | |
| 137 | 138 | void Audio::resetStats() { |
| @@ -418,9 +419,15 @@ void Audio::start() { · 280 unmodified lines | ||
| 418 | 419 | if (!outputFormatSupported) { |
| 419 | 420 | qDebug() << "Unable to set up audio output because of a problem with output format."; |
| 420 | 421 | } |
| 422 | + | |
| 423 | + _peq.initialize( _inputFormat.sampleRate(), _audioInput->bufferSize() ); | |
| 424 | + | |
| 421 | 425 | } |
| 422 | 426 | |
| 423 | 427 | void Audio::stop() { |
| 428 | + | |
| 429 | + _peq.finalize(); | |
| 430 | + | |
| 424 | 431 | // "switch" to invalid devices in order to shut down the state |
| 425 | 432 | switchInputToAudioDevice(QAudioDeviceInfo()); |
| 426 | 433 | switchOutputToAudioDevice(QAudioDeviceInfo()); |
| @@ -462,7 +469,15 @@ void Audio::handleAudioInput() { · 35 unmodified lines | ||
| 462 | 469 | int inputSamplesRequired = (int)((float)NETWORK_BUFFER_LENGTH_SAMPLES_PER_CHANNEL * inputToNetworkInputRatio); |
| 463 | 470 | |
| 464 | 471 | 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 | + | |
| 466 | 481 | if (Menu::getInstance()->isOptionChecked(MenuOption::EchoLocalAudio) && !_muted && _audioOutput) { |
| 467 | 482 | // if this person wants local loopback add that to the locally injected audio |
| 468 | 483 | |
| @@ -1172,6 +1187,31 @@ void Audio::renderToolBox(int x, int y, bool boxed) { · 703 unmodified lines | ||
| 1172 | 1187 | glDisable(GL_TEXTURE_2D); |
| 1173 | 1188 | } |
| 1174 | 1189 | |
| 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 | + | |
| 1175 | 1215 | void Audio::toggleScope() { |
| 1176 | 1216 | _scopeEnabled = !_scopeEnabled; |
| 1177 | 1217 | if (_scopeEnabled) { |
| @@ -19,6 +19,7 @@ · 18 unmodified lines | ||
| 19 | 19 | #include "AudioStreamStats.h" |
| 20 | 20 | #include "RingBufferHistory.h" |
| 21 | 21 | #include "MovingMinMaxAvg.h" |
| 22 | +#include "AudioFilter.h" | |
| 22 | 23 | |
| 23 | 24 | #include <QAudio> |
| 24 | 25 | #include <QAudioInput> |
| @@ -125,7 +126,12 @@ public slots: · 100 unmodified lines | ||
| 125 | 126 | void selectAudioScopeFiveFrames(); |
| 126 | 127 | void selectAudioScopeTwentyFrames(); |
| 127 | 128 | void selectAudioScopeFiftyFrames(); |
| 128 | − | |
| 129 | + void toggleAudioFilter(); | |
| 130 | + void selectAudioFilterFlat(); | |
| 131 | + void selectAudioFilterTrebleCut(); | |
| 132 | + void selectAudioFilterBassCut(); | |
| 133 | + void selectAudioFilterSmiley(); | |
| 134 | + | |
| 129 | 135 | virtual void handleAudioByteArray(const QByteArray& audioByteArray); |
| 130 | 136 | |
| 131 | 137 | void sendDownstreamAudioStatsPacket(); |
| @@ -252,12 +258,12 @@ private: · 120 unmodified lines | ||
| 252 | 258 | // Audio scope methods for rendering |
| 253 | 259 | void renderBackground(const float* color, int x, int y, int width, int height); |
| 254 | 260 | 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); | |
| 256 | 262 | |
| 257 | 263 | // audio stats methods for rendering |
| 258 | 264 | void renderAudioStreamStats(const AudioStreamStats& streamStats, int horizontalOffset, int& verticalOffset, |
| 259 | 265 | float scale, float rotation, int font, const float* color, bool isDownstreamStats = false); |
| 260 | − | |
| 266 | + | |
| 261 | 267 | // Audio scope data |
| 262 | 268 | static const unsigned int NETWORK_SAMPLES_PER_FRAME = NETWORK_BUFFER_LENGTH_SAMPLES_PER_CHANNEL; |
| 263 | 269 | static const unsigned int DEFAULT_FRAMES_PER_SCOPE = 5; |
| @@ -270,6 +276,11 @@ private: · 6 unmodified lines | ||
| 270 | 276 | int _scopeOutputOffset; |
| 271 | 277 | int _framesPerScope; |
| 272 | 278 | int _samplesPerScope; |
| 279 | + | |
| 280 | + // Multi-band parametric EQ | |
| 281 | + bool _peqEnabled; | |
| 282 | + AudioFilterPEQ3 _peq; | |
| 283 | + | |
| 273 | 284 | QMutex _guard; |
| 274 | 285 | QByteArray* _scopeInput; |
| 275 | 286 | QByteArray* _scopeOutputLeft; |
| @@ -486,6 +486,48 @@ Menu::Menu() : · 485 unmodified lines | ||
| 486 | 486 | true, |
| 487 | 487 | appInstance->getAudio(), |
| 488 | 488 | 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 | + | |
| 489 | 531 | addCheckableActionToQMenuAndActionHash(audioDebugMenu, MenuOption::EchoServerAudio); |
| 490 | 532 | addCheckableActionToQMenuAndActionHash(audioDebugMenu, MenuOption::EchoLocalAudio); |
| 491 | 533 | addCheckableActionToQMenuAndActionHash(audioDebugMenu, MenuOption::StereoAudio, 0, false, |
| @@ -1111,8 +1153,8 @@ void Menu::goTo() { · 619 unmodified lines | ||
| 1111 | 1153 | } |
| 1112 | 1154 | |
| 1113 | 1155 | 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); | |
| 1116 | 1158 | |
| 1117 | 1159 | if (urlParts.count() > 1) { |
| 1118 | 1160 | // if url has 2 or more parts, the first one is domain name |
| @@ -311,6 +311,11 @@ namespace MenuOption { · 310 unmodified lines | ||
| 311 | 311 | const QString Animations = "Animations..."; |
| 312 | 312 | const QString Atmosphere = "Atmosphere"; |
| 313 | 313 | 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"; | |
| 314 | 319 | const QString AudioNoiseReduction = "Audio Noise Reduction"; |
| 315 | 320 | const QString AudioScope = "Audio Scope"; |
| 316 | 321 | const QString AudioScopeFiftyFrames = "Fifty"; |
| @@ -37,6 +37,9 @@ void MetavoxelSystem::init() { · 36 unmodified lines | ||
| 37 | 37 | _pointBufferAttribute = AttributeRegistry::getInstance()->registerAttribute(new BufferDataAttribute("pointBuffer")); |
| 38 | 38 | _heightfieldBufferAttribute = AttributeRegistry::getInstance()->registerAttribute( |
| 39 | 39 | new BufferDataAttribute("heightfieldBuffer")); |
| 40 | + | |
| 41 | + _heightfieldBufferAttribute->setLODThresholdMultiplier( | |
| 42 | + AttributeRegistry::getInstance()->getHeightfieldAttribute()->getLODThresholdMultiplier()); | |
| 40 | 43 | } |
| 41 | 44 | |
| 42 | 45 | MetavoxelLOD MetavoxelSystem::getLOD() { |
| @@ -118,6 +121,334 @@ void MetavoxelSystem::render() { · 75 unmodified lines | ||
| 118 | 121 | guideToAugmented(renderVisitor); |
| 119 | 122 | } |
| 120 | 123 | |
| 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 | + | |
| 121 | 452 | void MetavoxelSystem::deleteTextures(int heightID, int colorID) { |
| 122 | 453 | glDeleteTextures(1, (GLuint*)&heightID); |
| 123 | 454 | glDeleteTextures(1, (GLuint*)&colorID); |
| @@ -239,7 +570,7 @@ PointBuffer::PointBuffer(const BufferPointVector& points) : · 115 unmodified lines | ||
| 239 | 570 | _points(points) { |
| 240 | 571 | } |
| 241 | 572 | |
| 242 | −void PointBuffer::render() { | |
| 573 | +void PointBuffer::render(bool cursor) { | |
| 243 | 574 | // initialize buffer, etc. on first render |
| 244 | 575 | if (!_buffer.isCreated()) { |
| 245 | 576 | _buffer.setUsagePattern(QOpenGLBuffer::StaticDraw); |
| @@ -265,16 +596,32 @@ void PointBuffer::render() { · 19 unmodified lines | ||
| 265 | 596 | _buffer.release(); |
| 266 | 597 | } |
| 267 | 598 | |
| 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 | + | |
| 268 | 603 | HeightfieldBuffer::HeightfieldBuffer(const glm::vec3& translation, float scale, |
| 269 | − const QByteArray& height, const QByteArray& color, bool clearAfterLoading) : | |
| 604 | + const QByteArray& height, const QByteArray& color) : | |
| 270 | 605 | _translation(translation), |
| 271 | 606 | _scale(scale), |
| 607 | + _heightBounds(translation, translation + glm::vec3(scale, scale, scale)), | |
| 608 | + _colorBounds(_heightBounds), | |
| 272 | 609 | _height(height), |
| 273 | 610 | _color(color), |
| 274 | − _clearAfterLoading(clearAfterLoading), | |
| 275 | 611 | _heightTextureID(0), |
| 276 | 612 | _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; | |
| 278 | 625 | } |
| 279 | 626 | |
| 280 | 627 | HeightfieldBuffer::~HeightfieldBuffer() { |
| @@ -288,25 +635,49 @@ HeightfieldBuffer::~HeightfieldBuffer() { · 7 unmodified lines | ||
| 288 | 635 | } |
| 289 | 636 | } |
| 290 | 637 | |
| 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 | + | |
| 291 | 664 | class HeightfieldPoint { |
| 292 | 665 | public: |
| 293 | 666 | glm::vec2 textureCoord; |
| 294 | 667 | glm::vec3 vertex; |
| 295 | 668 | }; |
| 296 | 669 | |
| 297 | −void HeightfieldBuffer::render() { | |
| 670 | +void HeightfieldBuffer::render(bool cursor) { | |
| 298 | 671 | // initialize textures, etc. on first render |
| 299 | 672 | if (_heightTextureID == 0) { |
| 300 | 673 | glGenTextures(1, &_heightTextureID); |
| 301 | 674 | glBindTexture(GL_TEXTURE_2D, _heightTextureID); |
| 675 | + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); | |
| 302 | 676 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); |
| 303 | 677 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); |
| 304 | 678 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); |
| 305 | 679 | glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, _heightSize, _heightSize, 0, |
| 306 | 680 | GL_LUMINANCE, GL_UNSIGNED_BYTE, _height.constData()); |
| 307 | − if (_clearAfterLoading) { | |
| 308 | − _height.clear(); | |
| 309 | − } | |
| 310 | 681 | |
| 311 | 682 | glGenTextures(1, &_colorTextureID); |
| 312 | 683 | glBindTexture(GL_TEXTURE_2D, _colorTextureID); |
| @@ -318,32 +689,32 @@ void HeightfieldBuffer::render() { · 5 unmodified lines | ||
| 318 | 689 | glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 1, 1, 0, GL_RGB, GL_UNSIGNED_BYTE, WHITE_COLOR); |
| 319 | 690 | |
| 320 | 691 | } else { |
| 321 | − int colorSize = glm::sqrt(_color.size() / 3); | |
| 692 | + int colorSize = glm::sqrt(_color.size() / HeightfieldData::COLOR_BYTES); | |
| 322 | 693 | glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, colorSize, colorSize, 0, GL_RGB, GL_UNSIGNED_BYTE, _color.constData()); |
| 323 | − if (_clearAfterLoading) { | |
| 324 | − _color.clear(); | |
| 325 | − } | |
| 326 | 694 | } |
| 327 | 695 | } |
| 328 | 696 | // 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; | |
| 333 | 701 | BufferPair& bufferPair = _bufferPairs[_heightSize]; |
| 334 | 702 | if (!bufferPair.first.isCreated()) { |
| 335 | 703 | QVector<HeightfieldPoint> vertices(vertexCount); |
| 336 | 704 | HeightfieldPoint* point = vertices.data(); |
| 337 | 705 | |
| 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; | |
| 342 | 713 | 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); | |
| 347 | 718 | } |
| 348 | 719 | } |
| 349 | 720 | |
| @@ -355,13 +726,16 @@ void HeightfieldBuffer::render() { · 5 unmodified lines | ||
| 355 | 726 | QVector<int> indices(indexCount); |
| 356 | 727 | int* index = indices.data(); |
| 357 | 728 | 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; | |
| 360 | 731 | for (int j = 0; j < rows; j++) { |
| 361 | 732 | *index++ = lineIndex + j; |
| 362 | 733 | *index++ = nextLineIndex + j; |
| 734 | + *index++ = nextLineIndex + j + 1; | |
| 735 | + | |
| 363 | 736 | *index++ = nextLineIndex + j + 1; |
| 364 | 737 | *index++ = lineIndex + j + 1; |
| 738 | + *index++ = lineIndex + j; | |
| 365 | 739 | } |
| 366 | 740 | } |
| 367 | 741 | |
| @@ -385,17 +759,33 @@ void HeightfieldBuffer::render() { · 17 unmodified lines | ||
| 385 | 759 | |
| 386 | 760 | glBindTexture(GL_TEXTURE_2D, _heightTextureID); |
| 387 | 761 | |
| 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 | + } | |
| 390 | 781 | |
| 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); | |
| 395 | 783 | |
| 396 | − glBindTexture(GL_TEXTURE_2D, 0); | |
| 784 | + if (!cursor) { | |
| 785 | + glBindTexture(GL_TEXTURE_2D, 0); | |
| 786 | + glActiveTexture(GL_TEXTURE0); | |
| 787 | + } | |
| 397 | 788 | |
| 398 | − glActiveTexture(GL_TEXTURE0); | |
| 399 | 789 | glBindTexture(GL_TEXTURE_2D, 0); |
| 400 | 790 | |
| 401 | 791 | glPopMatrix(); |
| @@ -444,17 +834,19 @@ BufferDataAttribute::BufferDataAttribute(const QString& name) : · 42 unmodified lines | ||
| 444 | 834 | } |
| 445 | 835 | |
| 446 | 836 | 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])) { | |
| 451 | 840 | return false; |
| 452 | 841 | } |
| 453 | 842 | } |
| 454 | − *(BufferDataPointer*)&parent = firstChild; | |
| 455 | 843 | return true; |
| 456 | 844 | } |
| 457 | 845 | |
| 846 | +AttributeValue BufferDataAttribute::inherit(const AttributeValue& parentValue) const { | |
| 847 | + return AttributeValue(parentValue.getAttribute()); | |
| 848 | +} | |
| 849 | + | |
| 458 | 850 | void DefaultMetavoxelRendererImplementation::init() { |
| 459 | 851 | if (!_pointProgram.isLinked()) { |
| 460 | 852 | _pointProgram.addShaderFromSourceFile(QGLShader::Vertex, Application::resourcesPath() + "shaders/metavoxel_point.vert"); |
| @@ -474,7 +866,47 @@ void DefaultMetavoxelRendererImplementation::init() { · 13 unmodified lines | ||
| 474 | 866 | _heightfieldProgram.setUniformValue("heightMap", 0); |
| 475 | 867 | _heightfieldProgram.setUniformValue("diffuseMap", 1); |
| 476 | 868 | _heightScaleLocation = _heightfieldProgram.uniformLocation("heightScale"); |
| 869 | + _colorScaleLocation = _heightfieldProgram.uniformLocation("colorScale"); | |
| 477 | 870 | _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(); | |
| 478 | 910 | } |
| 479 | 911 | } |
| 480 | 912 | |
| @@ -549,33 +981,279 @@ bool PointAugmentVisitor::postVisit(MetavoxelInfo& info) { · 68 unmodified lines | ||
| 549 | 981 | return true; |
| 550 | 982 | } |
| 551 | 983 | |
| 552 | −class HeightfieldAugmentVisitor : public MetavoxelVisitor { | |
| 984 | +class HeightfieldFetchVisitor : public MetavoxelVisitor { | |
| 553 | 985 | 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); | |
| 554 | 992 | |
| 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); | |
| 556 | 1127 | |
| 557 | 1128 | 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; | |
| 558 | 1136 | }; |
| 559 | 1137 | |
| 560 | −HeightfieldAugmentVisitor::HeightfieldAugmentVisitor(const MetavoxelLOD& lod) : | |
| 1138 | +HeightfieldRegionVisitor::HeightfieldRegionVisitor(const MetavoxelLOD& lod) : | |
| 561 | 1139 | 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; | |
| 574 | 1166 | } |
| 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()) { | |
| 576 | 1249 | return STOP_RECURSION; |
| 577 | 1250 | } |
| 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; | |
| 579 | 1257 | } |
| 580 | 1258 | |
| 581 | 1259 | void DefaultMetavoxelRendererImplementation::augment(MetavoxelData& data, const MetavoxelData& previous, |
| @@ -602,8 +1280,12 @@ void DefaultMetavoxelRendererImplementation::augment(MetavoxelData& data, const · 20 unmodified lines | ||
| 602 | 1280 | PointAugmentVisitor pointAugmentVisitor(lod); |
| 603 | 1281 | data.guideToDifferent(expandedPrevious, pointAugmentVisitor); |
| 604 | 1282 | |
| 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); | |
| 607 | 1289 | } |
| 608 | 1290 | |
| 609 | 1291 | class SpannerSimulateVisitor : public SpannerVisitor { |
| @@ -675,7 +1357,7 @@ bool SpannerRenderVisitor::visit(Spanner* spanner, const glm::vec3& clipMinimum, · 65 unmodified lines | ||
| 675 | 1357 | class BufferRenderVisitor : public MetavoxelVisitor { |
| 676 | 1358 | public: |
| 677 | 1359 | |
| 678 | − BufferRenderVisitor(const AttributePointer& attribute, const MetavoxelLOD& lod); | |
| 1360 | + BufferRenderVisitor(const AttributePointer& attribute); | |
| 679 | 1361 | |
| 680 | 1362 | virtual int visit(MetavoxelInfo& info); |
| 681 | 1363 | |
| @@ -685,8 +1367,8 @@ private: · 3 unmodified lines | ||
| 685 | 1367 | int _containmentDepth; |
| 686 | 1368 | }; |
| 687 | 1369 | |
| 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), | |
| 690 | 1372 | _order(encodeOrder(Application::getInstance()->getViewFrustum()->getDirection())), |
| 691 | 1373 | _containmentDepth(INT_MAX) { |
| 692 | 1374 | } |
| @@ -700,11 +1382,14 @@ int BufferRenderVisitor::visit(MetavoxelInfo& info) { · 7 unmodified lines | ||
| 700 | 1382 | } |
| 701 | 1383 | _containmentDepth = (intersection == Frustum::CONTAINS_INTERSECTION) ? _depth : INT_MAX; |
| 702 | 1384 | } |
| 1385 | + if (!info.isLeaf) { | |
| 1386 | + return _order; | |
| 1387 | + } | |
| 703 | 1388 | BufferDataPointer buffer = info.inputValues.at(0).getInlineValue<BufferDataPointer>(); |
| 704 | 1389 | if (buffer) { |
| 705 | 1390 | buffer->render(); |
| 706 | 1391 | } |
| 707 | − return info.isLeaf ? STOP_RECURSION : _order; | |
| 1392 | + return STOP_RECURSION; | |
| 708 | 1393 | } |
| 709 | 1394 | |
| 710 | 1395 | void DefaultMetavoxelRendererImplementation::render(MetavoxelData& data, MetavoxelInfo& info, const MetavoxelLOD& lod) { |
| @@ -733,7 +1418,7 @@ void DefaultMetavoxelRendererImplementation::render(MetavoxelData& data, Metavox · 22 unmodified lines | ||
| 733 | 1418 | |
| 734 | 1419 | glDisable(GL_BLEND); |
| 735 | 1420 | |
| 736 | − BufferRenderVisitor pointRenderVisitor(Application::getInstance()->getMetavoxels()->getPointBufferAttribute(), lod); | |
| 1421 | + BufferRenderVisitor pointRenderVisitor(Application::getInstance()->getMetavoxels()->getPointBufferAttribute()); | |
| 737 | 1422 | data.guide(pointRenderVisitor); |
| 738 | 1423 | |
| 739 | 1424 | glDisable(GL_VERTEX_PROGRAM_POINT_SIZE_ARB); |
| @@ -749,16 +1434,34 @@ void DefaultMetavoxelRendererImplementation::render(MetavoxelData& data, Metavox · 9 unmodified lines | ||
| 749 | 1434 | |
| 750 | 1435 | glColor4f(1.0f, 1.0f, 1.0f, 1.0f); |
| 751 | 1436 | |
| 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(); | |
| 753 | 1453 | |
| 754 | 1454 | glEnableClientState(GL_TEXTURE_COORD_ARRAY); |
| 755 | 1455 | |
| 756 | − BufferRenderVisitor heightfieldRenderVisitor(Application::getInstance()->getMetavoxels()->getHeightfieldBufferAttribute(), | |
| 757 | − lod); | |
| 1456 | + BufferRenderVisitor heightfieldRenderVisitor(Application::getInstance()->getMetavoxels()->getHeightfieldBufferAttribute()); | |
| 758 | 1457 | data.guide(heightfieldRenderVisitor); |
| 759 | 1458 | |
| 760 | − _heightfieldProgram.release(); | |
| 1459 | + program->release(); | |
| 761 | 1460 | |
| 1461 | + glActiveTexture(GL_TEXTURE2); | |
| 1462 | + glBindTexture(GL_TEXTURE_2D, 0); | |
| 1463 | + glActiveTexture(GL_TEXTURE0); | |
| 1464 | + | |
| 762 | 1465 | glDisableClientState(GL_TEXTURE_COORD_ARRAY); |
| 763 | 1466 | glDisableClientState(GL_VERTEX_ARRAY); |
| 764 | 1467 | |
| @@ -771,6 +1474,15 @@ ProgramObject DefaultMetavoxelRendererImplementation::_pointProgram; · 6 unmodified lines | ||
| 771 | 1474 | int DefaultMetavoxelRendererImplementation::_pointScaleLocation; |
| 772 | 1475 | ProgramObject DefaultMetavoxelRendererImplementation::_heightfieldProgram; |
| 773 | 1476 | 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; | |
| 774 | 1486 | |
| 775 | 1487 | static void enableClipPlane(GLenum plane, float x, float y, float z, float w) { |
| 776 | 1488 | GLdouble coefficients[] = { x, y, z, w }; |
| @@ -43,6 +43,12 @@ public: · 42 unmodified lines | ||
| 43 | 43 | void simulate(float deltaTime); |
| 44 | 44 | void render(); |
| 45 | 45 | |
| 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 | + | |
| 46 | 52 | Q_INVOKABLE void deleteTextures(int heightID, int colorID); |
| 47 | 53 | |
| 48 | 54 | protected: |
| @@ -105,7 +111,7 @@ public: · 56 unmodified lines | ||
| 105 | 111 | |
| 106 | 112 | virtual ~BufferData(); |
| 107 | 113 | |
| 108 | − virtual void render() = 0; | |
| 114 | + virtual void render(bool cursor = false) = 0; | |
| 109 | 115 | }; |
| 110 | 116 | |
| 111 | 117 | typedef QExplicitlySharedDataPointer<BufferData> BufferDataPointer; |
| @@ -116,7 +122,7 @@ public: · 4 unmodified lines | ||
| 116 | 122 | |
| 117 | 123 | PointBuffer(const BufferPointVector& points); |
| 118 | 124 | |
| 119 | − virtual void render(); | |
| 125 | + virtual void render(bool cursor = false); | |
| 120 | 126 | |
| 121 | 127 | private: |
| 122 | 128 | |
| @@ -129,29 +135,50 @@ private: · 6 unmodified lines | ||
| 129 | 135 | class HeightfieldBuffer : public BufferData { |
| 130 | 136 | public: |
| 131 | 137 | |
| 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); | |
| 136 | 143 | ~HeightfieldBuffer(); |
| 137 | 144 | |
| 138 | 145 | 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; } | |
| 139 | 150 | |
| 151 | + QByteArray& getHeight() { return _height; } | |
| 140 | 152 | const QByteArray& getHeight() const { return _height; } |
| 153 | + | |
| 154 | + QByteArray& getColor() { return _color; } | |
| 141 | 155 | const QByteArray& getColor() const { return _color; } |
| 142 | 156 | |
| 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); | |
| 144 | 167 | |
| 145 | 168 | private: |
| 146 | 169 | |
| 147 | 170 | glm::vec3 _translation; |
| 148 | 171 | float _scale; |
| 172 | + Box _heightBounds; | |
| 173 | + Box _colorBounds; | |
| 149 | 174 | QByteArray _height; |
| 150 | 175 | QByteArray _color; |
| 151 | − bool _clearAfterLoading; | |
| 152 | 176 | GLuint _heightTextureID; |
| 153 | 177 | GLuint _colorTextureID; |
| 154 | 178 | int _heightSize; |
| 179 | + float _heightIncrement; | |
| 180 | + int _colorSize; | |
| 181 | + float _colorIncrement; | |
| 155 | 182 | |
| 156 | 183 | typedef QPair<QOpenGLBuffer, QOpenGLBuffer> BufferPair; |
| 157 | 184 | static QHash<int, BufferPair> _bufferPairs; |
| @@ -180,6 +207,8 @@ public: · 22 unmodified lines | ||
| 180 | 207 | Q_INVOKABLE BufferDataAttribute(const QString& name = QString()); |
| 181 | 208 | |
| 182 | 209 | virtual bool merge(void*& parent, void* children[], bool postRead = false) const; |
| 210 | + | |
| 211 | + virtual AttributeValue inherit(const AttributeValue& parentValue) const; | |
| 183 | 212 | }; |
| 184 | 213 | |
| 185 | 214 | /// Renders metavoxels as points. |
| @@ -192,7 +221,18 @@ public: · 6 unmodified lines | ||
| 192 | 221 | |
| 193 | 222 | static ProgramObject& getHeightfieldProgram() { return _heightfieldProgram; } |
| 194 | 223 | 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 | + | |
| 196 | 236 | Q_INVOKABLE DefaultMetavoxelRendererImplementation(); |
| 197 | 237 | |
| 198 | 238 | virtual void augment(MetavoxelData& data, const MetavoxelData& previous, MetavoxelInfo& info, const MetavoxelLOD& lod); |
| @@ -206,6 +246,18 @@ private: · 7 unmodified lines | ||
| 206 | 246 | |
| 207 | 247 | static ProgramObject _heightfieldProgram; |
| 208 | 248 | 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; | |
| 209 | 261 | }; |
| 210 | 262 | |
| 211 | 263 | /// Base class for spanner renderers; provides clipping. |
| @@ -163,9 +163,6 @@ void Head::simulate(float deltaTime, bool isMine, bool billboard) { · 162 unmodified lines | ||
| 163 | 163 | } |
| 164 | 164 | _eyePosition = calculateAverageEyePosition(); |
| 165 | 165 | |
| 166 | − float velocityFilter = glm::clamp(1.0f - glm::length(_filteredEyePosition - _eyePosition), 0.0f, 1.0f); | |
| 167 | − _filteredEyePosition = velocityFilter * _filteredEyePosition + (1.0f - velocityFilter) * _eyePosition; | |
| 168 | − | |
| 169 | 166 | } |
| 170 | 167 | |
| 171 | 168 | void Head::relaxLean(float deltaTime) { |
| @@ -88,7 +88,6 @@ public: · 87 unmodified lines | ||
| 88 | 88 | |
| 89 | 89 | const bool getReturnToCenter() const { return _returnHeadToCenter; } // Do you want head to try to return to center (depends on interface detected) |
| 90 | 90 | float getAverageLoudness() const { return _averageLoudness; } |
| 91 | − glm::vec3 getFilteredEyePosition() const { return _filteredEyePosition; } | |
| 92 | 91 | /// \return the point about which scaling occurs. |
| 93 | 92 | glm::vec3 getScalePivot() const; |
| 94 | 93 | |
| @@ -121,7 +120,6 @@ private: · 26 unmodified lines | ||
| 121 | 120 | glm::vec3 _leftEyePosition; |
| 122 | 121 | glm::vec3 _rightEyePosition; |
| 123 | 122 | glm::vec3 _eyePosition; |
| 124 | − glm::vec3 _filteredEyePosition; // velocity filtered world space eye position | |
| 125 | 123 | |
| 126 | 124 | float _scale; |
| 127 | 125 | float _lastLoudness; |
| @@ -83,10 +83,11 @@ MyAvatar::MyAvatar() : · 82 unmodified lines | ||
| 83 | 83 | for (int i = 0; i < MAX_DRIVE_KEYS; i++) { |
| 84 | 84 | _driveKeys[i] = 0.0f; |
| 85 | 85 | } |
| 86 | − _skeletonModel.setEnableShapes(true); | |
| 87 | − // The skeleton is both a PhysicsEntity and Ragdoll, so we add it to the simulation once for each type. | |
| 88 | 86 | _physicsSimulation.setEntity(&_skeletonModel); |
| 89 | − _physicsSimulation.setRagdoll(&_skeletonModel); | |
| 87 | + | |
| 88 | + _skeletonModel.setEnableShapes(true); | |
| 89 | + Ragdoll* ragdoll = _skeletonModel.buildRagdoll(); | |
| 90 | + _physicsSimulation.setRagdoll(ragdoll); | |
| 90 | 91 | } |
| 91 | 92 | |
| 92 | 93 | MyAvatar::~MyAvatar() { |
| @@ -206,9 +207,9 @@ void MyAvatar::simulate(float deltaTime) { · 113 unmodified lines | ||
| 206 | 207 | { |
| 207 | 208 | PerformanceTimer perfTimer("ragdoll"); |
| 208 | 209 | 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; | |
| 212 | 213 | _physicsSimulation.setTranslation(_position); |
| 213 | 214 | _physicsSimulation.stepForward(deltaTime, minError, maxIterations, maxUsec); |
| 214 | 215 | } else { |
| @@ -255,6 +256,12 @@ void MyAvatar::updateFromTrackers(float deltaTime) { · 40 unmodified lines | ||
| 255 | 256 | estimatedRotation.x *= -1.0f; |
| 256 | 257 | estimatedRotation.z *= -1.0f; |
| 257 | 258 | |
| 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; | |
| 258 | 265 | } else { |
| 259 | 266 | FaceTracker* tracker = Application::getInstance()->getActiveFaceTracker(); |
| 260 | 267 | if (tracker) { |
| @@ -1598,10 +1605,10 @@ void MyAvatar::updateCollisionWithAvatars(float deltaTime) { · 1337 unmodified lines | ||
| 1598 | 1605 | if (simulation != &(_physicsSimulation)) { |
| 1599 | 1606 | skeleton->setEnableShapes(true); |
| 1600 | 1607 | _physicsSimulation.addEntity(skeleton); |
| 1601 | − _physicsSimulation.addRagdoll(skeleton); | |
| 1608 | + _physicsSimulation.addRagdoll(skeleton->getRagdoll()); | |
| 1602 | 1609 | } |
| 1603 | 1610 | } else if (simulation == &(_physicsSimulation)) { |
| 1604 | − _physicsSimulation.removeRagdoll(skeleton); | |
| 1611 | + _physicsSimulation.removeRagdoll(skeleton->getRagdoll()); | |
| 1605 | 1612 | _physicsSimulation.removeEntity(skeleton); |
| 1606 | 1613 | skeleton->setEnableShapes(false); |
| 1607 | 1614 | } |
| @@ -23,13 +23,19 @@ · 22 unmodified lines | ||
| 23 | 23 | #include "Menu.h" |
| 24 | 24 | #include "MuscleConstraint.h" |
| 25 | 25 | #include "SkeletonModel.h" |
| 26 | +#include "SkeletonRagdoll.h" | |
| 26 | 27 | |
| 27 | 28 | SkeletonModel::SkeletonModel(Avatar* owningAvatar, QObject* parent) : |
| 28 | 29 | Model(parent), |
| 29 | − Ragdoll(), | |
| 30 | 30 | _owningAvatar(owningAvatar), |
| 31 | 31 | _boundingShape(), |
| 32 | − _boundingShapeLocalOffset(0.0f) { | |
| 32 | + _boundingShapeLocalOffset(0.0f), | |
| 33 | + _ragdoll(NULL) { | |
| 34 | +} | |
| 35 | + | |
| 36 | +SkeletonModel::~SkeletonModel() { | |
| 37 | + delete _ragdoll; | |
| 38 | + _ragdoll = NULL; | |
| 33 | 39 | } |
| 34 | 40 | |
| 35 | 41 | void SkeletonModel::setJointStates(QVector<JointState> states) { |
| @@ -155,9 +161,6 @@ void SkeletonModel::getBodyShapes(QVector<const Shape*>& shapes) const { · 119 unmodified lines | ||
| 155 | 161 | void SkeletonModel::renderIKConstraints() { |
| 156 | 162 | renderJointConstraints(getRightHandJointIndex()); |
| 157 | 163 | renderJointConstraints(getLeftHandJointIndex()); |
| 158 | − //if (isActive() && _owningAvatar->isMyAvatar()) { | |
| 159 | − // renderRagdoll(); | |
| 160 | − //} | |
| 161 | 164 | } |
| 162 | 165 | |
| 163 | 166 | class IndexValue { |
| @@ -432,6 +435,10 @@ bool SkeletonModel::getHeadPosition(glm::vec3& headPosition) const { · 268 unmodified lines | ||
| 432 | 435 | } |
| 433 | 436 | |
| 434 | 437 | 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 | + } | |
| 435 | 442 | return isActive() && getJointPositionInWorldFrame(_geometry->getFBXGeometry().neckJointIndex, neckPosition); |
| 436 | 443 | } |
| 437 | 444 | |
| @@ -445,11 +452,16 @@ bool SkeletonModel::getNeckParentRotationFromDefaultOrientation(glm::quat& neckP · 7 unmodified lines | ||
| 445 | 452 | } |
| 446 | 453 | int parentIndex = geometry.joints.at(geometry.neckJointIndex).parentIndex; |
| 447 | 454 | 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) { | |
| 449 | 462 | neckParentRotation = worldFrameRotation * _jointStates[parentIndex].getFBXJoint().inverseDefaultRotation; |
| 450 | − return true; | |
| 451 | 463 | } |
| 452 | − return false; | |
| 464 | + return success; | |
| 453 | 465 | } |
| 454 | 466 | |
| 455 | 467 | 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 | ||
| 480 | 492 | } |
| 481 | 493 | |
| 482 | 494 | void SkeletonModel::renderRagdoll() { |
| 495 | + if (!_ragdoll) { | |
| 496 | + return; | |
| 497 | + } | |
| 498 | + const QVector<VerletPoint>& points = _ragdoll->getPoints(); | |
| 483 | 499 | const int BALL_SUBDIVISIONS = 6; |
| 484 | 500 | glDisable(GL_DEPTH_TEST); |
| 485 | 501 | glDisable(GL_LIGHTING); |
| 486 | 502 | glPushMatrix(); |
| 487 | 503 | |
| 488 | 504 | Application::getInstance()->loadTranslatedViewMatrix(_translation); |
| 489 | − int numPoints = _ragdollPoints.size(); | |
| 505 | + int numPoints = points.size(); | |
| 490 | 506 | float alpha = 0.3f; |
| 491 | 507 | float radius1 = 0.008f; |
| 492 | 508 | float radius2 = 0.01f; |
| 493 | − glm::vec3 simulationTranslation = getTranslationInSimulationFrame(); | |
| 509 | + glm::vec3 simulationTranslation = _ragdoll->getTranslationInSimulationFrame(); | |
| 494 | 510 | for (int i = 0; i < numPoints; ++i) { |
| 495 | 511 | glPushMatrix(); |
| 496 | 512 | // 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; | |
| 498 | 514 | glTranslatef(position.x, position.y, position.z); |
| 499 | 515 | // draw each point as a yellow hexagon with black border |
| 500 | 516 | glColor4f(0.0f, 0.0f, 0.0f, alpha); |
| @@ -508,109 +524,18 @@ void SkeletonModel::renderRagdoll() { · 7 unmodified lines | ||
| 508 | 524 | glEnable(GL_LIGHTING); |
| 509 | 525 | } |
| 510 | 526 | |
| 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 | − | |
| 603 | 527 | void SkeletonModel::updateVisibleJointStates() { |
| 604 | − if (_showTrueJointTransforms) { | |
| 528 | + if (_showTrueJointTransforms || !_ragdoll) { | |
| 605 | 529 | // no need to update visible transforms |
| 606 | 530 | return; |
| 607 | 531 | } |
| 532 | + const QVector<VerletPoint>& ragdollPoints = _ragdoll->getPoints(); | |
| 608 | 533 | QVector<glm::vec3> points; |
| 609 | 534 | points.reserve(_jointStates.size()); |
| 610 | 535 | glm::quat invRotation = glm::inverse(_rotation); |
| 611 | 536 | for (int i = 0; i < _jointStates.size(); i++) { |
| 612 | 537 | JointState& state = _jointStates[i]; |
| 613 | − points.push_back(_ragdollPoints[i]._position); | |
| 538 | + points.push_back(ragdollPoints[i]._position); | |
| 614 | 539 | |
| 615 | 540 | // get the parent state (this is the state that we want to rotate) |
| 616 | 541 | int parentIndex = state.getParentIndex(); |
| @@ -644,15 +569,14 @@ void SkeletonModel::updateVisibleJointStates() { · 27 unmodified lines | ||
| 644 | 569 | } |
| 645 | 570 | } |
| 646 | 571 | |
| 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 | + } | |
| 655 | 578 | } |
| 579 | + return _ragdoll; | |
| 656 | 580 | } |
| 657 | 581 | |
| 658 | 582 | float DENSITY_OF_WATER = 1000.0f; // kg/m^3 |
| @@ -670,8 +594,13 @@ void SkeletonModel::buildShapes() { · 11 unmodified lines | ||
| 670 | 594 | return; |
| 671 | 595 | } |
| 672 | 596 | |
| 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(); | |
| 675 | 604 | |
| 676 | 605 | float uniformScale = extractUniformScale(_scale); |
| 677 | 606 | const int numStates = _jointStates.size(); |
| @@ -691,14 +620,14 @@ void SkeletonModel::buildShapes() { · 13 unmodified lines | ||
| 691 | 620 | Shape* shape = NULL; |
| 692 | 621 | int parentIndex = joint.parentIndex; |
| 693 | 622 | if (type == Shape::SPHERE_SHAPE) { |
| 694 | − shape = new VerletSphereShape(radius, &(_ragdollPoints[i])); | |
| 623 | + shape = new VerletSphereShape(radius, &(points[i])); | |
| 695 | 624 | 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())); | |
| 697 | 626 | } else if (type == Shape::CAPSULE_SHAPE) { |
| 698 | 627 | assert(parentIndex != -1); |
| 699 | − shape = new VerletCapsuleShape(radius, &(_ragdollPoints[parentIndex]), &(_ragdollPoints[i])); | |
| 628 | + shape = new VerletCapsuleShape(radius, &(points[parentIndex]), &(points[i])); | |
| 700 | 629 | 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())); | |
| 702 | 631 | } |
| 703 | 632 | if (parentIndex != -1) { |
| 704 | 633 | // always disable collisions between joint and its parent |
| @@ -708,7 +637,7 @@ void SkeletonModel::buildShapes() { · 3 unmodified lines | ||
| 708 | 637 | } else { |
| 709 | 638 | // give the base joint a very large mass since it doesn't actually move |
| 710 | 639 | // in the local-frame simulation (it defines the origin) |
| 711 | − _ragdollPoints[i].setMass(VERY_BIG_MASS); | |
| 640 | + points[i].setMass(VERY_BIG_MASS); | |
| 712 | 641 | } |
| 713 | 642 | _shapes.push_back(shape); |
| 714 | 643 | } |
| @@ -720,17 +649,11 @@ void SkeletonModel::buildShapes() { · 5 unmodified lines | ||
| 720 | 649 | // joints that are currently colliding. |
| 721 | 650 | disableCurrentSelfCollisions(); |
| 722 | 651 | |
| 723 | − buildRagdollConstraints(); | |
| 652 | + _ragdoll->buildConstraints(); | |
| 724 | 653 | |
| 725 | 654 | // ... 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(); | |
| 734 | 657 | } |
| 735 | 658 | |
| 736 | 659 | void SkeletonModel::moveShapesTowardJoints(float deltaTime) { |
| @@ -738,8 +661,9 @@ void SkeletonModel::moveShapesTowardJoints(float deltaTime) { · 1 unmodified line | ||
| 738 | 661 | // unravel a skelton that has become tangled in its constraints. So let's keep this |
| 739 | 662 | // around for a while just in case. |
| 740 | 663 | 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) { | |
| 743 | 667 | return; |
| 744 | 668 | } |
| 745 | 669 | |
| @@ -748,32 +672,22 @@ void SkeletonModel::moveShapesTowardJoints(float deltaTime) { · 2 unmodified lines | ||
| 748 | 672 | float fraction = glm::clamp(deltaTime / RAGDOLL_FOLLOWS_JOINTS_TIMESCALE, 0.0f, 1.0f); |
| 749 | 673 | |
| 750 | 674 | float oneMinusFraction = 1.0f - fraction; |
| 751 | − glm::vec3 simulationTranslation = getTranslationInSimulationFrame(); | |
| 675 | + glm::vec3 simulationTranslation = _ragdoll->getTranslationInSimulationFrame(); | |
| 752 | 676 | for (int i = 0; i < numStates; ++i) { |
| 753 | 677 | // 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 + | |
| 755 | 679 | fraction * (simulationTranslation + _rotation * (_jointStates.at(i).getPosition()))); |
| 756 | 680 | } |
| 757 | 681 | } |
| 758 | 682 | |
| 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 | − | |
| 771 | 683 | void SkeletonModel::computeBoundingShape(const FBXGeometry& geometry) { |
| 772 | 684 | // compute default joint transforms |
| 773 | 685 | int numStates = _jointStates.size(); |
| 774 | 686 | QVector<glm::mat4> transforms; |
| 775 | 687 | transforms.fill(glm::mat4(), numStates); |
| 776 | 688 | |
| 689 | + QVector<VerletPoint>& ragdollPoints = _ragdoll->getPoints(); | |
| 690 | + | |
| 777 | 691 | // compute the default transforms and slam the ragdoll positions accordingly |
| 778 | 692 | // (which puts the shapes where we want them) |
| 779 | 693 | for (int i = 0; i < numStates; i++) { |
| @@ -782,7 +696,7 @@ void SkeletonModel::computeBoundingShape(const FBXGeometry& geometry) { · 2 unmodified lines | ||
| 782 | 696 | int parentIndex = joint.parentIndex; |
| 783 | 697 | if (parentIndex == -1) { |
| 784 | 698 | transforms[i] = _jointStates[i].getTransform(); |
| 785 | − _ragdollPoints[i].initPosition(extractTranslation(transforms[i])); | |
| 699 | + ragdollPoints[i].initPosition(extractTranslation(transforms[i])); | |
| 786 | 700 | continue; |
| 787 | 701 | } |
| 788 | 702 | |
| @@ -790,7 +704,7 @@ void SkeletonModel::computeBoundingShape(const FBXGeometry& geometry) { · 1 unmodified line | ||
| 790 | 704 | transforms[i] = transforms[parentIndex] * glm::translate(joint.translation) |
| 791 | 705 | * joint.preTransform * glm::mat4_cast(modifiedRotation) * joint.postTransform; |
| 792 | 706 | // 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])); | |
| 794 | 708 | } |
| 795 | 709 | |
| 796 | 710 | // compute bounding box that encloses all shapes |
| @@ -909,9 +823,12 @@ const int BALL_SUBDIVISIONS = 10; · 112 unmodified lines | ||
| 909 | 823 | |
| 910 | 824 | // virtual |
| 911 | 825 | void SkeletonModel::renderJointCollisionShapes(float alpha) { |
| 826 | + if (!_ragdoll) { | |
| 827 | + return; | |
| 828 | + } | |
| 912 | 829 | glPushMatrix(); |
| 913 | 830 | Application::getInstance()->loadTranslatedViewMatrix(_translation); |
| 914 | − glm::vec3 simulationTranslation = getTranslationInSimulationFrame(); | |
| 831 | + glm::vec3 simulationTranslation = _ragdoll->getTranslationInSimulationFrame(); | |
| 915 | 832 | for (int i = 0; i < _shapes.size(); i++) { |
| 916 | 833 | Shape* shape = _shapes[i]; |
| 917 | 834 | if (!shape) { |
| @@ -15,18 +15,20 @@ · 14 unmodified lines | ||
| 15 | 15 | #include "renderer/Model.h" |
| 16 | 16 | |
| 17 | 17 | #include <CapsuleShape.h> |
| 18 | −#include <Ragdoll.h> | |
| 18 | +#include "SkeletonRagdoll.h" | |
| 19 | 19 | |
| 20 | 20 | class Avatar; |
| 21 | 21 | class MuscleConstraint; |
| 22 | +class SkeletonRagdoll; | |
| 22 | 23 | |
| 23 | 24 | /// A skeleton loaded from a model. |
| 24 | −class SkeletonModel : public Model, public Ragdoll { | |
| 25 | +class SkeletonModel : public Model { | |
| 25 | 26 | Q_OBJECT |
| 26 | 27 | |
| 27 | 28 | public: |
| 28 | 29 | |
| 29 | 30 | SkeletonModel(Avatar* owningAvatar, QObject* parent = NULL); |
| 31 | + ~SkeletonModel(); | |
| 30 | 32 | |
| 31 | 33 | void setJointStates(QVector<JointState> states); |
| 32 | 34 | |
| @@ -96,12 +98,11 @@ public: · 63 unmodified lines | ||
| 96 | 98 | bool getEyePositions(glm::vec3& firstEyePosition, glm::vec3& secondEyePosition) const; |
| 97 | 99 | |
| 98 | 100 | virtual void updateVisibleJointStates(); |
| 99 | − | |
| 100 | − // virtual overrride from Ragdoll | |
| 101 | − virtual void stepRagdollForward(float deltaTime); | |
| 102 | 101 | |
| 102 | + SkeletonRagdoll* buildRagdoll(); | |
| 103 | + SkeletonRagdoll* getRagdoll() { return _ragdoll; } | |
| 104 | + | |
| 103 | 105 | void moveShapesTowardJoints(float fraction); |
| 104 | − void updateMuscles(); | |
| 105 | 106 | |
| 106 | 107 | void computeBoundingShape(const FBXGeometry& geometry); |
| 107 | 108 | void renderBoundingCollisionShapes(float alpha); |
| @@ -115,10 +116,6 @@ public: · 7 unmodified lines | ||
| 115 | 116 | |
| 116 | 117 | protected: |
| 117 | 118 | |
| 118 | − // virtual overrrides from Ragdoll | |
| 119 | − void initRagdollPoints(); | |
| 120 | − void buildRagdollConstraints(); | |
| 121 | − | |
| 122 | 119 | void buildShapes(); |
| 123 | 120 | |
| 124 | 121 | /// \param jointIndex index of joint in model |
| @@ -147,7 +144,7 @@ private: · 22 unmodified lines | ||
| 147 | 144 | |
| 148 | 145 | CapsuleShape _boundingShape; |
| 149 | 146 | glm::vec3 _boundingShapeLocalOffset; |
| 150 | − QVector<MuscleConstraint*> _muscleConstraints; | |
| 147 | + SkeletonRagdoll* _ragdoll; | |
| 151 | 148 | }; |
| 152 | 149 | |
| 153 | 150 | #endif // hifi_SkeletonModel_h |
| @@ -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 | +} | |
| @@ -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 | |
| @@ -10,7 +10,7 @@ · 9 unmodified lines | ||
| 10 | 10 | // |
| 11 | 11 | |
| 12 | 12 | #include "CaraFaceTracker.h" |
| 13 | −#include <SharedUtil.h> | |
| 13 | +#include <GLMHelpers.h> | |
| 14 | 14 | |
| 15 | 15 | //qt |
| 16 | 16 | #include <QJsonDocument> |
| @@ -178,6 +178,31 @@ float rescaleCoef(float ddeCoef) { · 177 unmodified lines | ||
| 178 | 178 | return (ddeCoef - DDE_MIN_RANGE) / (DDE_MAX_RANGE - DDE_MIN_RANGE); |
| 179 | 179 | } |
| 180 | 180 | |
| 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 | + | |
| 181 | 206 | void DdeFaceTracker::decodePacket(const QByteArray& buffer) { |
| 182 | 207 | if(buffer.size() > MIN_PACKET_SIZE) { |
| 183 | 208 | Packet packet; |
| @@ -189,14 +214,17 @@ void DdeFaceTracker::decodePacket(const QByteArray& buffer) { · 5 unmodified lines | ||
| 189 | 214 | memcpy(&translation, packet.translation, sizeof(packet.translation)); |
| 190 | 215 | glm::quat rotation; |
| 191 | 216 | memcpy(&rotation, &packet.rotation, sizeof(packet.rotation)); |
| 192 | − if (_reset) { | |
| 217 | + if (_reset || (_lastReceiveTimestamp == 0)) { | |
| 193 | 218 | memcpy(&_referenceTranslation, &translation, sizeof(glm::vec3)); |
| 194 | 219 | memcpy(&_referenceRotation, &rotation, sizeof(glm::quat)); |
| 220 | + | |
| 221 | + resetCoefficient(_rightEye, packet.expressions[0]); | |
| 222 | + resetCoefficient(_leftEye, packet.expressions[1]); | |
| 195 | 223 | _reset = false; |
| 196 | 224 | } |
| 197 | 225 | |
| 198 | 226 | // Compute relative translation |
| 199 | − float LEAN_DAMPING_FACTOR = 40; | |
| 227 | + float LEAN_DAMPING_FACTOR = 200.0f; | |
| 200 | 228 | translation -= _referenceTranslation; |
| 201 | 229 | translation /= LEAN_DAMPING_FACTOR; |
| 202 | 230 | translation.x *= -1; |
| @@ -208,10 +236,19 @@ void DdeFaceTracker::decodePacket(const QByteArray& buffer) { · 5 unmodified lines | ||
| 208 | 236 | _headTranslation = translation; |
| 209 | 237 | _headRotation = rotation; |
| 210 | 238 | |
| 239 | + if (_lastReceiveTimestamp == 0) { | |
| 240 | + // On first packet, reset coefficients | |
| 241 | + } | |
| 242 | + | |
| 211 | 243 | // 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]; | |
| 215 | 252 | |
| 216 | 253 | float leftBrow = 1.0f - rescaleCoef(packet.expressions[14]); |
| 217 | 254 | if (leftBrow < 0.5f) { |
| @@ -84,6 +84,10 @@ private: · 83 unmodified lines | ||
| 84 | 84 | int _mouthSmileRightIndex; |
| 85 | 85 | |
| 86 | 86 | int _jawOpenIndex; |
| 87 | + | |
| 88 | + float _leftEye[3]; | |
| 89 | + float _rightEye[3]; | |
| 90 | + | |
| 87 | 91 | }; |
| 88 | 92 | |
| 89 | 93 | #endif // hifi_DdeFaceTracker_h |
| @@ -44,7 +44,7 @@ ovrFovPort OculusManager::_eyeFov[ovrEye_Count]; · 43 unmodified lines | ||
| 44 | 44 | ovrEyeRenderDesc OculusManager::_eyeRenderDesc[ovrEye_Count]; |
| 45 | 45 | ovrSizei OculusManager::_renderTargetSize; |
| 46 | 46 | ovrVector2f OculusManager::_UVScaleOffset[ovrEye_Count][2]; |
| 47 | −GLuint OculusManager::_vertices[ovrEye_Count] = { 0, 0 }; | |
| 47 | +GLuint OculusManager::_vertices[ovrEye_Count] = { 0, 0 }; | |
| 48 | 48 | GLuint OculusManager::_indices[ovrEye_Count] = { 0, 0 }; |
| 49 | 49 | GLsizei OculusManager::_meshSize[ovrEye_Count] = { 0, 0 }; |
| 50 | 50 | ovrFrameTiming OculusManager::_hmdFrameTiming; |
| @@ -66,12 +66,14 @@ void OculusManager::connect() { · 15 unmodified lines | ||
| 66 | 66 | UserActivityLogger::getInstance().connectedDevice("hmd", "oculus"); |
| 67 | 67 | } |
| 68 | 68 | _isConnected = true; |
| 69 | − | |
| 69 | +#if defined(__APPLE__) || defined(_WIN32) | |
| 70 | + _eyeFov[0] = _ovrHmd->DefaultEyeFov[0]; | |
| 71 | + _eyeFov[1] = _ovrHmd->DefaultEyeFov[1]; | |
| 72 | +#else | |
| 70 | 73 | ovrHmd_GetDesc(_ovrHmd, &_ovrHmdDesc); |
| 71 | − | |
| 72 | 74 | _eyeFov[0] = _ovrHmdDesc.DefaultEyeFov[0]; |
| 73 | 75 | _eyeFov[1] = _ovrHmdDesc.DefaultEyeFov[1]; |
| 74 | − | |
| 76 | +#endif | |
| 75 | 77 | //Get texture size |
| 76 | 78 | ovrSizei recommendedTex0Size = ovrHmd_GetFovTextureSize(_ovrHmd, ovrEye_Left, |
| 77 | 79 | _eyeFov[0], 1.0f); |
| @@ -86,11 +88,21 @@ void OculusManager::connect() { · 8 unmodified lines | ||
| 86 | 88 | _eyeRenderDesc[0] = ovrHmd_GetRenderDesc(_ovrHmd, ovrEye_Left, _eyeFov[0]); |
| 87 | 89 | _eyeRenderDesc[1] = ovrHmd_GetRenderDesc(_ovrHmd, ovrEye_Right, _eyeFov[1]); |
| 88 | 90 | |
| 91 | +#if defined(__APPLE__) || defined(_WIN32) | |
| 92 | + ovrHmd_SetEnabledCaps(_ovrHmd, ovrHmdCap_LowPersistence); | |
| 93 | +#else | |
| 89 | 94 | ovrHmd_SetEnabledCaps(_ovrHmd, ovrHmdCap_LowPersistence | ovrHmdCap_LatencyTest); |
| 95 | +#endif | |
| 90 | 96 | |
| 97 | +#if defined(__APPLE__) || defined(_WIN32) | |
| 98 | + ovrHmd_ConfigureTracking(_ovrHmd, ovrTrackingCap_Orientation | ovrTrackingCap_Position | | |
| 99 | + ovrTrackingCap_MagYawCorrection, | |
| 100 | + ovrTrackingCap_Orientation); | |
| 101 | +#else | |
| 91 | 102 | ovrHmd_StartSensor(_ovrHmd, ovrSensorCap_Orientation | ovrSensorCap_YawCorrection | |
| 92 | 103 | ovrSensorCap_Position, |
| 93 | 104 | ovrSensorCap_Orientation); |
| 105 | +#endif | |
| 94 | 106 | |
| 95 | 107 | if (!_camera) { |
| 96 | 108 | _camera = new Camera; |
| @@ -123,6 +135,10 @@ void OculusManager::connect() { · 26 unmodified lines | ||
| 123 | 135 | |
| 124 | 136 | } else { |
| 125 | 137 | _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 | + | |
| 126 | 142 | ovrHmd_Destroy(_ovrHmd); |
| 127 | 143 | ovr_Shutdown(); |
| 128 | 144 | } |
| @@ -183,6 +199,16 @@ void OculusManager::generateDistortionMesh() { · 54 unmodified lines | ||
| 183 | 199 | DistortionVertex* v = pVBVerts; |
| 184 | 200 | ovrDistortionVertex* ov = meshData.pVertexData; |
| 185 | 201 | 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 | |
| 186 | 212 | v->pos.x = ov->Pos.x; |
| 187 | 213 | v->pos.y = ov->Pos.y; |
| 188 | 214 | v->texR.x = ov->TexR.x; |
| @@ -191,6 +217,7 @@ void OculusManager::generateDistortionMesh() { · 2 unmodified lines | ||
| 191 | 217 | v->texG.y = ov->TexG.y; |
| 192 | 218 | v->texB.x = ov->TexB.x; |
| 193 | 219 | v->texB.y = ov->TexB.y; |
| 220 | +#endif | |
| 194 | 221 | v->color.r = v->color.g = v->color.b = (GLubyte)(ov->VignetteFactor * 255.99f); |
| 195 | 222 | v->color.a = (GLubyte)(ov->TimeWarpFactor * 255.99f); |
| 196 | 223 | v++; |
| @@ -291,12 +318,23 @@ void OculusManager::display(const glm::quat &bodyOrientation, const glm::vec3 &p · 94 unmodified lines | ||
| 291 | 318 | glPushMatrix(); |
| 292 | 319 | |
| 293 | 320 | 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 | + | |
| 295 | 331 | //Render each eye into an fbo |
| 296 | 332 | for (int eyeIndex = 0; eyeIndex < ovrEye_Count; eyeIndex++) { |
| 297 | − | |
| 333 | +#if defined(__APPLE__) || defined(_WIN32) | |
| 334 | + ovrEyeType eye = _ovrHmd->EyeRenderOrder[eyeIndex]; | |
| 335 | +#else | |
| 298 | 336 | ovrEyeType eye = _ovrHmdDesc.EyeRenderOrder[eyeIndex]; |
| 299 | − | |
| 337 | +#endif | |
| 300 | 338 | //Set the camera rotation for this eye |
| 301 | 339 | eyeRenderPose[eye] = ovrHmd_GetEyePose(_ovrHmd, eye); |
| 302 | 340 | orientation.x = eyeRenderPose[eye].Orientation.x; |
| @@ -305,7 +343,8 @@ void OculusManager::display(const glm::quat &bodyOrientation, const glm::vec3 &p · 2 unmodified lines | ||
| 305 | 343 | orientation.w = eyeRenderPose[eye].Orientation.w; |
| 306 | 344 | |
| 307 | 345 | _camera->setTargetRotation(bodyOrientation * orientation); |
| 308 | − _camera->setTargetPosition(position); | |
| 346 | + _camera->setTargetPosition(position + trackerPosition); | |
| 347 | + | |
| 309 | 348 | _camera->update(1.0f / Application::getInstance()->getFps()); |
| 310 | 349 | |
| 311 | 350 | 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 | ||
| 425 | 464 | //Tries to reconnect to the sensors |
| 426 | 465 | void OculusManager::reset() { |
| 427 | 466 | #ifdef HAVE_LIBOVR |
| 428 | − disconnect(); | |
| 429 | − connect(); | |
| 467 | + if (_isConnected) { | |
| 468 | + ovrHmd_RecenterPose(_ovrHmd); | |
| 469 | + } | |
| 430 | 470 | #endif |
| 431 | 471 | } |
| 432 | 472 | |
| 433 | 473 | //Gets the current predicted angles from the oculus sensors |
| 434 | 474 | void OculusManager::getEulerAngles(float& yaw, float& pitch, float& roll) { |
| 435 | 475 | #ifdef HAVE_LIBOVR |
| 476 | +#if defined(__APPLE__) || defined(_WIN32) | |
| 477 | + ovrTrackingState ts = ovrHmd_GetTrackingState(_ovrHmd, ovr_GetTimeInSeconds()); | |
| 478 | +#else | |
| 436 | 479 | 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 | |
| 438 | 484 | 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); | |
| 441 | 493 | orientation.GetEulerAngles<Axis_Y, Axis_X, Axis_Z, Rotate_CCW, Handed_R>(&yaw, &pitch, &roll); |
| 442 | 494 | } else { |
| 443 | 495 | yaw = 0.0f; |
| @@ -450,6 +502,18 @@ void OculusManager::getEulerAngles(float& yaw, float& pitch, float& roll) { · 6 unmodified lines | ||
| 450 | 502 | roll = 0.0f; |
| 451 | 503 | #endif |
| 452 | 504 | } |
| 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 | +} | |
| 453 | 517 | |
| 454 | 518 | //Used to set the size of the glow framebuffers |
| 455 | 519 | QSize OculusManager::getRenderTargetSize() { |
| @@ -40,6 +40,7 @@ public: · 39 unmodified lines | ||
| 40 | 40 | /// param \pitch[out] pitch in radians |
| 41 | 41 | /// param \roll[out] roll in radians |
| 42 | 42 | static void getEulerAngles(float& yaw, float& pitch, float& roll); |
| 43 | + static glm::vec3 getRelativePosition(); | |
| 43 | 44 | static QSize getRenderTargetSize(); |
| 44 | 45 | |
| 45 | 46 | private: |
| @@ -19,6 +19,7 @@ · 18 unmodified lines | ||
| 19 | 19 | #include "UserActivityLogger.h" |
| 20 | 20 | |
| 21 | 21 | #ifdef HAVE_SIXENSE |
| 22 | + | |
| 22 | 23 | const int CALIBRATION_STATE_IDLE = 0; |
| 23 | 24 | const int CALIBRATION_STATE_X = 1; |
| 24 | 25 | const int CALIBRATION_STATE_Y = 2; |
| @@ -41,8 +42,9 @@ SixenseManager::SixenseManager() { · 16 unmodified lines | ||
| 41 | 42 | // By default we assume the _neckBase (in orb frame) is as high above the orb |
| 42 | 43 | // as the "torso" is below it. |
| 43 | 44 | _neckBase = glm::vec3(NECK_X, -NECK_Y, NECK_Z); |
| 44 | − | |
| 45 | + | |
| 45 | 46 | sixenseInit(); |
| 47 | + | |
| 46 | 48 | #endif |
| 47 | 49 | _hydrasConnected = false; |
| 48 | 50 | _triggerPressed[0] = false; |
| @@ -71,7 +71,7 @@ private: · 70 unmodified lines | ||
| 71 | 71 | glm::vec3 _reachUp; |
| 72 | 72 | glm::vec3 _reachForward; |
| 73 | 73 | float _lastDistance; |
| 74 | − | |
| 74 | + | |
| 75 | 75 | #endif |
| 76 | 76 | bool _hydrasConnected; |
| 77 | 77 | quint64 _lastMovement; |
| @@ -16,6 +16,7 @@ · 15 unmodified lines | ||
| 16 | 16 | #include <glm/gtx/quaternion.hpp> |
| 17 | 17 | #include <glm/gtx/transform.hpp> |
| 18 | 18 | |
| 19 | +#include <GLMHelpers.h> | |
| 19 | 20 | #include <FBXReader.h> |
| 20 | 21 | |
| 21 | 22 | class AngularConstraint; |
| @@ -1449,9 +1449,11 @@ void Model::renderMeshes(float alpha, RenderMode mode, bool translucent, bool re · 1448 unmodified lines | ||
| 1449 | 1449 | if (cascadedShadows) { |
| 1450 | 1450 | activeProgram->setUniform(activeLocations->shadowDistances, Application::getInstance()->getShadowDistances()); |
| 1451 | 1451 | } |
| 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 | + | |
| 1455 | 1457 | if (mesh.blendshapes.isEmpty()) { |
| 1456 | 1458 | if (!(mesh.tangents.isEmpty() || mode == SHADOW_RENDER_MODE)) { |
| 1457 | 1459 | activeProgram->setAttributeBuffer(activeLocations->tangent, GL_FLOAT, vertexCount * 2 * sizeof(glm::vec3), 3); |
| @@ -164,6 +164,9 @@ public: · 163 unmodified lines | ||
| 164 | 164 | const QVector<LocalLight>& getLocalLights() const { return _localLights; } |
| 165 | 165 | |
| 166 | 166 | void setShowTrueJointTransforms(bool show) { _showTrueJointTransforms = show; } |
| 167 | + | |
| 168 | + QVector<JointState>& getJointStates() { return _jointStates; } | |
| 169 | + const QVector<JointState>& getJointStates() const { return _jointStates; } | |
| 167 | 170 | |
| 168 | 171 | protected: |
| 169 | 172 | QSharedPointer<NetworkGeometry> _geometry; |
| @@ -87,6 +87,37 @@ bool ClipboardScriptingInterface::importVoxels() { · 86 unmodified lines | ||
| 87 | 87 | return Application::getInstance()->getImportSucceded(); |
| 88 | 88 | } |
| 89 | 89 | |
| 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 | + | |
| 90 | 121 | void ClipboardScriptingInterface::nudgeVoxel(const VoxelDetail& sourceVoxel, const glm::vec3& nudgeVec) { |
| 91 | 122 | nudgeVoxel(sourceVoxel.x, sourceVoxel.y, sourceVoxel.z, sourceVoxel.s, nudgeVec); |
| 92 | 123 | } |
| @@ -39,6 +39,9 @@ public slots: · 38 unmodified lines | ||
| 39 | 39 | void exportVoxel(float x, float y, float z, float s); |
| 40 | 40 | |
| 41 | 41 | 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); | |
| 42 | 45 | |
| 43 | 46 | void nudgeVoxel(const VoxelDetail& sourceVoxel, const glm::vec3& nudgeVec); |
| 44 | 47 | void nudgeVoxel(float x, float y, float z, float s, const glm::vec3& nudgeVec); |
| @@ -57,6 +57,9 @@ ApplicationOverlay::~ApplicationOverlay() { · 56 unmodified lines | ||
| 57 | 57 | const float WHITE_TEXT[] = { 0.93f, 0.93f, 0.93f }; |
| 58 | 58 | const float RETICLE_COLOR[] = { 0.0f, 198.0f / 255.0f, 244.0f / 255.0f }; |
| 59 | 59 | |
| 60 | +const float CONNECTION_STATUS_BORDER_COLOR[] = { 1.0f, 0.0f, 0.0f }; | |
| 61 | +const float CONNECTION_STATUS_BORDER_LINE_WIDTH = 4.0f; | |
| 62 | + | |
| 60 | 63 | // Renders the overlays either to a texture or to the screen |
| 61 | 64 | void ApplicationOverlay::renderOverlay(bool renderToTexture) { |
| 62 | 65 | |
| @@ -115,6 +118,8 @@ void ApplicationOverlay::renderOverlay(bool renderToTexture) { · 52 unmodified lines | ||
| 115 | 118 | |
| 116 | 119 | renderPointers(); |
| 117 | 120 | |
| 121 | + renderDomainConnectionStatusBorder(); | |
| 122 | + | |
| 118 | 123 | glPopMatrix(); |
| 119 | 124 | |
| 120 | 125 | |
| @@ -1234,6 +1239,30 @@ void ApplicationOverlay::renderTexturedHemisphere() { · 1113 unmodified lines | ||
| 1234 | 1239 | |
| 1235 | 1240 | } |
| 1236 | 1241 | |
| 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 | + | |
| 1237 | 1266 | QOpenGLFramebufferObject* ApplicationOverlay::getFramebufferObject() { |
| 1238 | 1267 | QSize size = Application::getInstance()->getGLWidget()->size(); |
| 1239 | 1268 | if (!_framebufferObject || _framebufferObject->size() != size) { |
| @@ -56,6 +56,7 @@ private: · 55 unmodified lines | ||
| 56 | 56 | void renderAudioMeter(); |
| 57 | 57 | void renderStatsAndLogs(); |
| 58 | 58 | void renderTexturedHemisphere(); |
| 59 | + void renderDomainConnectionStatusBorder(); | |
| 59 | 60 | |
| 60 | 61 | QOpenGLFramebufferObject* _framebufferObject; |
| 61 | 62 | float _trailingAudioLoudness; |
| @@ -76,4 +77,4 @@ private: · 14 unmodified lines | ||
| 76 | 77 | GLuint _crosshairTexture; |
| 77 | 78 | }; |
| 78 | 79 | |
| 79 | −#endif // hifi_ApplicationOverlay_h | |
| 80 | +#endif // hifi_ApplicationOverlay_h | |
| @@ -118,6 +118,8 @@ MetavoxelEditor::MetavoxelEditor() : · 117 unmodified lines | ||
| 118 | 118 | addTool(new SetSpannerTool(this)); |
| 119 | 119 | addTool(new ImportHeightfieldTool(this)); |
| 120 | 120 | addTool(new EraseHeightfieldTool(this)); |
| 121 | + addTool(new HeightfieldHeightBrushTool(this)); | |
| 122 | + addTool(new HeightfieldColorBrushTool(this)); | |
| 121 | 123 | |
| 122 | 124 | updateAttributes(); |
| 123 | 125 | |
| @@ -954,11 +956,11 @@ void ImportHeightfieldTool::apply() { · 830 unmodified lines | ||
| 954 | 956 | HeightfieldBuffer* buffer = static_cast<HeightfieldBuffer*>(bufferData.data()); |
| 955 | 957 | MetavoxelData data; |
| 956 | 958 | data.setSize(scale); |
| 957 | − HeightfieldDataPointer heightPointer(new HeightfieldData(buffer->getHeight())); | |
| 959 | + HeightfieldDataPointer heightPointer(new HeightfieldData(buffer->getUnextendedHeight())); | |
| 958 | 960 | data.setRoot(AttributeRegistry::getInstance()->getHeightfieldAttribute(), new MetavoxelNode(AttributeValue( |
| 959 | 961 | AttributeRegistry::getInstance()->getHeightfieldAttribute(), encodeInline(heightPointer)))); |
| 960 | 962 | if (!buffer->getColor().isEmpty()) { |
| 961 | − HeightfieldDataPointer colorPointer(new HeightfieldData(buffer->getColor())); | |
| 963 | + HeightfieldDataPointer colorPointer(new HeightfieldData(buffer->getUnextendedColor())); | |
| 962 | 964 | data.setRoot(AttributeRegistry::getInstance()->getHeightfieldColorAttribute(), new MetavoxelNode(AttributeValue( |
| 963 | 965 | AttributeRegistry::getInstance()->getHeightfieldColorAttribute(), encodeInline(colorPointer)))); |
| 964 | 966 | } |
| @@ -1001,34 +1003,38 @@ void ImportHeightfieldTool::updatePreview() { · 36 unmodified lines | ||
| 1001 | 1003 | if (_heightImage.width() > 0 && _heightImage.height() > 0) { |
| 1002 | 1004 | float z = 0.0f; |
| 1003 | 1005 | 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++) { | |
| 1006 | 1009 | 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); | |
| 1012 | 1018 | 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; | |
| 1015 | 1021 | for (int x = 0; x < columns; x++) { |
| 1016 | 1022 | *dest++ = *src; |
| 1017 | − src += BYTES_PER_COLOR; | |
| 1023 | + src += HeightfieldData::COLOR_BYTES; | |
| 1018 | 1024 | } |
| 1019 | 1025 | } |
| 1020 | − | |
| 1021 | 1026 | QByteArray color; |
| 1022 | 1027 | 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)); | |
| 1026 | 1031 | 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); | |
| 1029 | 1035 | } |
| 1030 | 1036 | } |
| 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))); | |
| 1032 | 1038 | } |
| 1033 | 1039 | } |
| 1034 | 1040 | } |
| @@ -1080,3 +1086,72 @@ void EraseHeightfieldTool::apply() { · 45 unmodified lines | ||
| 1080 | 1086 | message.edit = QVariant::fromValue(edit); |
| 1081 | 1087 | Application::getInstance()->getMetavoxels()->applyEdit(message, true); |
| 1082 | 1088 | } |
| 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 | +} | |
| @@ -18,6 +18,7 @@ · 17 unmodified lines | ||
| 18 | 18 | #include "MetavoxelSystem.h" |
| 19 | 19 | #include "renderer/ProgramObject.h" |
| 20 | 20 | |
| 21 | +class QColorEditor; | |
| 21 | 22 | class QComboBox; |
| 22 | 23 | class QDoubleSpinBox; |
| 23 | 24 | class QGroupBox; |
| @@ -282,7 +283,7 @@ private: · 258 unmodified lines | ||
| 282 | 283 | HeightfieldPreview _preview; |
| 283 | 284 | }; |
| 284 | 285 | |
| 285 | −// Allows clearing heighfield blocks. | |
| 286 | +/// Allows clearing heighfield blocks. | |
| 286 | 287 | class EraseHeightfieldTool : public HeightfieldTool { |
| 287 | 288 | Q_OBJECT |
| 288 | 289 | |
| @@ -302,4 +303,60 @@ private: · 13 unmodified lines | ||
| 302 | 303 | QSpinBox* _length; |
| 303 | 304 | }; |
| 304 | 305 | |
| 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 | + | |
| 305 | 362 | #endif // hifi_MetavoxelEditor_h |
| @@ -1,5 +1,5 @@ | ||
| 1 | 1 | // |
| 2 | −// ImportDialog.cpp | |
| 2 | +// VoxelImportDialog.cpp | |
| 3 | 3 | // interface/src/ui |
| 4 | 4 | // |
| 5 | 5 | // Created by Clement Brisset on 8/12/13. |
| @@ -20,7 +20,11 @@ · 14 unmodified lines | ||
| 20 | 20 | |
| 21 | 21 | #include "Application.h" |
| 22 | 22 | |
| 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"; | |
| 24 | 28 | |
| 25 | 29 | const QString WINDOW_NAME = QObject::tr("Import Voxels"); |
| 26 | 30 | const QString IMPORT_BUTTON_NAME = QObject::tr("Import Voxels"); |
| @@ -97,12 +101,14 @@ QString HiFiIconProvider::type(const QFileInfo &info) const { · 70 unmodified lines | ||
| 97 | 101 | return QFileIconProvider::type(info); |
| 98 | 102 | } |
| 99 | 103 | |
| 100 | −ImportDialog::ImportDialog(QWidget* parent) : | |
| 104 | +VoxelImportDialog::VoxelImportDialog(QWidget* parent) : | |
| 101 | 105 | QFileDialog(parent, WINDOW_NAME, DOWNLOAD_LOCATION, NULL), |
| 102 | − _progressBar(this), | |
| 103 | − _importButton(IMPORT_BUTTON_NAME, this), | |
| 104 | 106 | _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) { | |
| 106 | 112 | |
| 107 | 113 | setOption(QFileDialog::DontUseNativeDialog, true); |
| 108 | 114 | setFileMode(QFileDialog::ExistingFile); |
| @@ -113,41 +119,54 @@ ImportDialog::ImportDialog(QWidget* parent) : · 4 unmodified lines | ||
| 113 | 119 | |
| 114 | 120 | _progressBar.setRange(0, 100); |
| 115 | 121 | |
| 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())); | |
| 118 | 124 | connect(this, SIGNAL(currentChanged(QString)), SLOT(saveCurrentFile(QString))); |
| 119 | 125 | } |
| 120 | 126 | |
| 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() { | |
| 122 | 160 | setMode(importMode); |
| 123 | − _progressBar.setValue(0); | |
| 161 | + _didImport = false; | |
| 124 | 162 | } |
| 125 | 163 | |
| 126 | −void ImportDialog::setMode(dialogMode mode) { | |
| 164 | +void VoxelImportDialog::setMode(dialogMode mode) { | |
| 165 | + dialogMode previousMode = _mode; | |
| 127 | 166 | _mode = mode; |
| 128 | 167 | |
| 129 | 168 | 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; | |
| 149 | 169 | case importMode: |
| 150 | − default: | |
| 151 | 170 | _progressBar.setValue(0); |
| 152 | 171 | _importButton.setEnabled(true); |
| 153 | 172 | _importButton.setText(IMPORT_BUTTON_NAME); |
| @@ -157,22 +176,60 @@ void ImportDialog::setMode(dialogMode mode) { · 3 unmodified lines | ||
| 157 | 176 | findChild<QWidget*>("forwardButton")->setEnabled(true); |
| 158 | 177 | findChild<QWidget*>("toParentButton")->setEnabled(true); |
| 159 | 178 | 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; | |
| 160 | 200 | } |
| 161 | 201 | } |
| 162 | 202 | |
| 163 | −void ImportDialog::setProgressBarValue(int value) { | |
| 203 | +void VoxelImportDialog::setProgressBarValue(int value) { | |
| 164 | 204 | _progressBar.setValue(value); |
| 165 | 205 | } |
| 166 | 206 | |
| 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(); | |
| 169 | 226 | } |
| 170 | 227 | |
| 171 | −void ImportDialog::saveCurrentFile(QString filename) { | |
| 228 | +void VoxelImportDialog::saveCurrentFile(QString filename) { | |
| 172 | 229 | _currentFile = QFileInfo(filename).isFile() ? filename : ""; |
| 173 | 230 | } |
| 174 | 231 | |
| 175 | −void ImportDialog::setLayout() { | |
| 232 | +void VoxelImportDialog::setLayout() { | |
| 176 | 233 | QGridLayout* gridLayout = (QGridLayout*) layout(); |
| 177 | 234 | gridLayout->addWidget(&_progressBar, 2, 0, 2, 1); |
| 178 | 235 | gridLayout->addWidget(&_cancelButton, 2, 1, 2, 1); |
| @@ -258,7 +315,7 @@ void ImportDialog::setLayout() { · 79 unmodified lines | ||
| 258 | 315 | |
| 259 | 316 | } |
| 260 | 317 | |
| 261 | −void ImportDialog::setImportTypes() { | |
| 318 | +void VoxelImportDialog::setImportTypes() { | |
| 262 | 319 | QFile config(Application::resourcesPath() + "config/config.json"); |
| 263 | 320 | config.open(QFile::ReadOnly | QFile::Text); |
| 264 | 321 | QJsonDocument document = QJsonDocument::fromJson(config.readAll()); |
| @@ -1,5 +1,5 @@ | ||
| 1 | 1 | // |
| 2 | −// ImportDialog.h | |
| 2 | +// VoxelImportDialog.h | |
| 3 | 3 | // interface/src/ui |
| 4 | 4 | // |
| 5 | 5 | // Created by Clement Brisset on 8/12/13. |
| @@ -9,8 +9,8 @@ · 3 unmodified lines | ||
| 9 | 9 | // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html |
| 10 | 10 | // |
| 11 | 11 | |
| 12 | −#ifndef hifi_ImportDialog_h | |
| 13 | −#define hifi_ImportDialog_h | |
| 12 | +#ifndef hifi_VoxelImportDialog_h | |
| 13 | +#define hifi_VoxelImportDialog_h | |
| 14 | 14 | |
| 15 | 15 | #include "InterfaceConfig.h" |
| 16 | 16 | |
| @@ -23,6 +23,8 @@ · 6 unmodified lines | ||
| 23 | 23 | |
| 24 | 24 | #include <SharedUtil.h> |
| 25 | 25 | |
| 26 | +#include "voxels/VoxelImporter.h" | |
| 27 | + | |
| 26 | 28 | class HiFiIconProvider : public QFileIconProvider { |
| 27 | 29 | public: |
| 28 | 30 | HiFiIconProvider(const QHash<QString, QString> map) { iconsMap = map; }; |
| @@ -35,39 +37,45 @@ public: · 6 unmodified lines | ||
| 35 | 37 | enum dialogMode { |
| 36 | 38 | importMode, |
| 37 | 39 | loadingMode, |
| 38 | − placeMode | |
| 40 | + finishedMode | |
| 39 | 41 | }; |
| 40 | 42 | |
| 41 | −class ImportDialog : public QFileDialog { | |
| 43 | +class VoxelImportDialog : public QFileDialog { | |
| 42 | 44 | Q_OBJECT |
| 43 | 45 | |
| 44 | 46 | public: |
| 45 | − ImportDialog(QWidget* parent = NULL); | |
| 46 | − void reset(); | |
| 47 | + VoxelImportDialog(QWidget* parent = NULL); | |
| 47 | 48 | |
| 48 | 49 | QString getCurrentFile() const { return _currentFile; } |
| 49 | 50 | dialogMode getMode() const { return _mode; } |
| 51 | + | |
| 50 | 52 | void setMode(dialogMode mode); |
| 53 | + void reset(); | |
| 54 | + bool prompt(); | |
| 55 | + void loadSettings(QSettings* settings); | |
| 56 | + void saveSettings(QSettings* settings); | |
| 51 | 57 | |
| 52 | 58 | signals: |
| 53 | 59 | void canceled(); |
| 54 | − | |
| 55 | −public slots: | |
| 56 | − void setProgressBarValue(int value); | |
| 57 | 60 | |
| 58 | 61 | private slots: |
| 62 | + void setProgressBarValue(int value); | |
| 59 | 63 | void accept(); |
| 64 | + void cancel(); | |
| 60 | 65 | void saveCurrentFile(QString filename); |
| 66 | + void afterImport(); | |
| 61 | 67 | |
| 62 | 68 | private: |
| 69 | + QPushButton _cancelButton; | |
| 63 | 70 | QString _currentFile; |
| 64 | − QProgressBar _progressBar; | |
| 65 | 71 | QPushButton _importButton; |
| 66 | − QPushButton _cancelButton; | |
| 72 | + VoxelImporter* _importer; | |
| 67 | 73 | dialogMode _mode; |
| 74 | + QProgressBar _progressBar; | |
| 75 | + bool _didImport; | |
| 68 | 76 | |
| 69 | 77 | void setLayout(); |
| 70 | 78 | void setImportTypes(); |
| 71 | 79 | }; |
| 72 | 80 | |
| 73 | −#endif // hifi_ImportDialog_h | |
| 81 | +#endif // hifi_VoxelImportDialog_h | |
| @@ -11,6 +11,8 @@ · 10 unmodified lines | ||
| 11 | 11 | #ifndef hifi_Base3DOverlay_h |
| 12 | 12 | #define hifi_Base3DOverlay_h |
| 13 | 13 | |
| 14 | +#include <glm/glm.hpp> | |
| 15 | + | |
| 14 | 16 | #include "Overlay.h" |
| 15 | 17 | |
| 16 | 18 | class Base3DOverlay : public Overlay { |
| @@ -26,7 +26,7 @@ void OctreePacketProcessor::processPacket(const SharedNodePointer& sendingNode, · 25 unmodified lines | ||
| 26 | 26 | if (packetsToProcessCount() > WAY_BEHIND && Application::getInstance()->getLogger()->extraDebugging()) { |
| 27 | 27 | qDebug("OctreePacketProcessor::processPacket() packets to process=%d", packetsToProcessCount()); |
| 28 | 28 | } |
| 29 | − ssize_t messageLength = mutablePacket.size(); | |
| 29 | + int messageLength = mutablePacket.size(); | |
| 30 | 30 | |
| 31 | 31 | Application* app = Application::getInstance(); |
| 32 | 32 | bool wasStatsPacket = false; |
| @@ -20,8 +20,7 @@ · 19 unmodified lines | ||
| 20 | 20 | |
| 21 | 21 | #include "voxels/VoxelImporter.h" |
| 22 | 22 | |
| 23 | −const QString SETTINGS_GROUP_NAME = "VoxelImport"; | |
| 24 | −const QString IMPORT_DIALOG_SETTINGS_KEY = "ImportDialogSettings"; | |
| 23 | +const QStringList SUPPORTED_EXTENSIONS = QStringList() << "png" << "svo" << "schematic"; | |
| 25 | 24 | |
| 26 | 25 | class ImportTask : public QObject, public QRunnable { |
| 27 | 26 | public: |
| @@ -32,104 +31,46 @@ private: · 4 unmodified lines | ||
| 32 | 31 | QString _filename; |
| 33 | 32 | }; |
| 34 | 33 | |
| 35 | −VoxelImporter::VoxelImporter(QWidget* parent) : | |
| 36 | − QObject(parent), | |
| 34 | +VoxelImporter::VoxelImporter() : | |
| 37 | 35 | _voxelTree(true), |
| 38 | − _importDialog(parent), | |
| 39 | − _task(NULL), | |
| 40 | − _didImport(false) | |
| 36 | + _task(NULL) | |
| 41 | 37 | { |
| 42 | 38 | LocalVoxelsList::getInstance()->addPersistantTree(IMPORT_TREE_NAME, &_voxelTree); |
| 43 | 39 | |
| 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))); | |
| 59 | 41 | } |
| 60 | 42 | |
| 61 | 43 | VoxelImporter::~VoxelImporter() { |
| 62 | 44 | cleanupTask(); |
| 63 | 45 | } |
| 64 | 46 | |
| 47 | +void VoxelImporter::cancel() { | |
| 48 | + if (_task) { | |
| 49 | + disconnect(_task, 0, 0, 0); | |
| 50 | + } | |
| 51 | + reset(); | |
| 52 | +} | |
| 53 | + | |
| 65 | 54 | void VoxelImporter::reset() { |
| 66 | 55 | _voxelTree.eraseAllOctreeElements(); |
| 67 | − _importDialog.reset(); | |
| 68 | − | |
| 69 | 56 | cleanupTask(); |
| 70 | 57 | } |
| 71 | 58 | |
| 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) { | |
| 78 | 62 | cleanupTask(); |
| 79 | − return 1; | |
| 80 | − } else { | |
| 81 | − _didImport = false; | |
| 82 | − return 0; | |
| 83 | 63 | } |
| 84 | −} | |
| 85 | 64 | |
| 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); | |
| 117 | 68 | } |
| 118 | −} | |
| 119 | 69 | |
| 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); | |
| 133 | 74 | } |
| 134 | 75 | |
| 135 | 76 | void VoxelImporter::cleanupTask() { |
| @@ -140,6 +81,16 @@ void VoxelImporter::cleanupTask() { · 4 unmodified lines | ||
| 140 | 81 | } |
| 141 | 82 | } |
| 142 | 83 | |
| 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 | + | |
| 143 | 94 | ImportTask::ImportTask(const QString &filename) |
| 144 | 95 | : _filename(filename) |
| 145 | 96 | { |
| @@ -151,7 +102,7 @@ void ImportTask::run() { · 5 unmodified lines | ||
| 151 | 102 | // We start by cleaning up the shared voxel system just in case |
| 152 | 103 | voxelSystem->killLocalVoxels(); |
| 153 | 104 | |
| 154 | − // Then we call the righ method for the job | |
| 105 | + // Then we call the right method for the job | |
| 155 | 106 | if (_filename.endsWith(".png", Qt::CaseInsensitive)) { |
| 156 | 107 | voxelSystem->getTree()->readFromSquareARGB32Pixels(_filename.toLocal8Bit().data()); |
| 157 | 108 | } else if (_filename.endsWith(".svo", Qt::CaseInsensitive)) { |
| @@ -163,6 +114,6 @@ void ImportTask::run() { · 5 unmodified lines | ||
| 163 | 114 | qDebug() << "[ERROR] Invalid file extension." << endl; |
| 164 | 115 | } |
| 165 | 116 | |
| 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 | |
| 167 | 118 | voxelSystem->getTree()->reaverageOctreeElements(); |
| 168 | 119 | } |
| @@ -14,8 +14,8 @@ · 13 unmodified lines | ||
| 14 | 14 | |
| 15 | 15 | #include <QThread> |
| 16 | 16 | #include <QRunnable> |
| 17 | +#include <QStringList> | |
| 17 | 18 | |
| 18 | −#include "ui/ImportDialog.h" | |
| 19 | 19 | #include "voxels/VoxelSystem.h" |
| 20 | 20 | |
| 21 | 21 | class ImportTask; |
| @@ -23,28 +23,29 @@ class ImportTask; · 1 unmodified line | ||
| 23 | 23 | class VoxelImporter : public QObject { |
| 24 | 24 | Q_OBJECT |
| 25 | 25 | public: |
| 26 | − VoxelImporter(QWidget* parent = NULL); | |
| 26 | + VoxelImporter(); | |
| 27 | 27 | ~VoxelImporter(); |
| 28 | 28 | |
| 29 | 29 | void reset(); |
| 30 | − void loadSettings(QSettings* settings); | |
| 31 | − void saveSettings(QSettings* settings); | |
| 32 | − | |
| 30 | + void cancel(); | |
| 33 | 31 | VoxelTree* getVoxelTree() { return &_voxelTree; } |
| 32 | + bool validImportFile(const QString& filename); | |
| 34 | 33 | |
| 35 | 34 | 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); | |
| 39 | 40 | |
| 40 | 41 | private: |
| 41 | 42 | VoxelTree _voxelTree; |
| 42 | − ImportDialog _importDialog; | |
| 43 | − | |
| 44 | 43 | ImportTask* _task; |
| 45 | − bool _didImport; | |
| 46 | 44 | |
| 47 | 45 | void cleanupTask(); |
| 46 | + | |
| 47 | +private slots: | |
| 48 | + void finishImport(); | |
| 48 | 49 | }; |
| 49 | 50 | |
| 50 | 51 | #endif // hifi_VoxelImporter_h |
| @@ -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 | − | |
| 7 | 1 | set(TARGET_NAME animation) |
| 8 | 2 | |
| 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) | |
| 23 | 5 | |
| 24 | −target_link_libraries(${TARGET_NAME} "${ZLIB_LIBRARIES}" Qt5::Widgets) | |
| 6 | +link_hifi_libraries(shared fbx) | |
| 25 | 7 | |
| 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() | |
| @@ -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 | − | |
| 7 | 1 | set(TARGET_NAME audio) |
| 8 | 2 | |
| 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) | |
| 15 | 5 | |
| 16 | −include(${MACRO_DIR}/IncludeGLM.cmake) | |
| 17 | −include_glm(${TARGET_NAME} "${ROOT_DIR}") | |
| 6 | +include_glm() | |
| 18 | 7 | |
| 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) | |
| 22 | 9 | |
| 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() | |
| @@ -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 | +}; | |
| @@ -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 | |
| @@ -15,9 +15,9 @@ · 14 unmodified lines | ||
| 15 | 15 | |
| 16 | 16 | #include <QtCore/QDebug> |
| 17 | 17 | |
| 18 | −#include "PacketHeaders.h" | |
| 19 | −#include "AudioRingBuffer.h" | |
| 18 | +#include <PacketHeaders.h> | |
| 20 | 19 | |
| 20 | +#include "AudioRingBuffer.h" | |
| 21 | 21 | |
| 22 | 22 | AudioRingBuffer::AudioRingBuffer(int numFrameSamples, bool randomAccessMode, int numFramesCapacity) : |
| 23 | 23 | _frameCapacity(numFramesCapacity), |
| @@ -15,12 +15,10 @@ · 14 unmodified lines | ||
| 15 | 15 | #include <limits> |
| 16 | 16 | #include <stdint.h> |
| 17 | 17 | |
| 18 | −#include <glm/glm.hpp> | |
| 19 | − | |
| 20 | 18 | #include <QtCore/QIODevice> |
| 21 | 19 | |
| 22 | −#include "NodeData.h" | |
| 23 | −#include "SharedUtil.h" | |
| 20 | +#include <SharedUtil.h> | |
| 21 | +#include <NodeData.h> | |
| 24 | 22 | |
| 25 | 23 | const int SAMPLE_RATE = 24000; |
| 26 | 24 | |
| @@ -9,6 +9,8 @@ · 8 unmodified lines | ||
| 9 | 9 | // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html |
| 10 | 10 | // |
| 11 | 11 | |
| 12 | +#include <glm/glm.hpp> | |
| 13 | + | |
| 12 | 14 | #include "InboundAudioStream.h" |
| 13 | 15 | #include "PacketHeaders.h" |
| 14 | 16 | |
| @@ -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 | − | |
| 7 | 1 | set(TARGET_NAME avatars) |
| 8 | 2 | |
| 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) | |
| 19 | 5 | |
| 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() | |
| 24 | 7 | |
| 25 | −target_link_libraries(${TARGET_NAME} Qt5::Script) | |
| 8 | +link_hifi_libraries(shared octree voxels networking) | |
| 9 | +include_hifi_library_headers(fbx) | |
| 26 | 10 | |
| 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() | |
| @@ -22,7 +22,7 @@ · 21 unmodified lines | ||
| 22 | 22 | #include <NetworkAccessManager.h> |
| 23 | 23 | #include <NodeList.h> |
| 24 | 24 | #include <PacketHeaders.h> |
| 25 | −#include <SharedUtil.h> | |
| 25 | +#include <GLMHelpers.h> | |
| 26 | 26 | #include <StreamUtils.h> |
| 27 | 27 | #include <UUID.h> |
| 28 | 28 | #include <VoxelConstants.h> |
| @@ -11,13 +11,13 @@ · 10 unmodified lines | ||
| 11 | 11 | |
| 12 | 12 | #include <glm/gtx/quaternion.hpp> |
| 13 | 13 | |
| 14 | +#include <FBXReader.h> | |
| 15 | +#include <GLMHelpers.h> | |
| 14 | 16 | #include <OctreeConstants.h> |
| 15 | 17 | |
| 16 | 18 | #include "AvatarData.h" |
| 17 | 19 | #include "HeadData.h" |
| 18 | 20 | |
| 19 | −#include "../fbx/src/FBXReader.h" | |
| 20 | − | |
| 21 | 21 | HeadData::HeadData(AvatarData* owningAvatar) : |
| 22 | 22 | _baseYaw(0.0f), |
| 23 | 23 | _basePitch(0.0f), |
| @@ -9,7 +9,7 @@ · 8 unmodified lines | ||
| 9 | 9 | // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html |
| 10 | 10 | // |
| 11 | 11 | |
| 12 | −#include <SharedUtil.h> | |
| 12 | +#include <GLMHelpers.h> | |
| 13 | 13 | |
| 14 | 14 | #include "AvatarData.h" |
| 15 | 15 | #include "Referential.h" |
| @@ -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 | − | |
| 7 | 1 | set(TARGET_NAME embedded-webserver) |
| 8 | 2 | |
| 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) | |
| 13 | 5 | |
| 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() | |
| @@ -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 | − | |
| 7 | 1 | set(TARGET_NAME entities) |
| 8 | 2 | |
| 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) | |
| 29 | 5 | |
| 30 | −# link ZLIB | |
| 31 | −find_package(ZLIB) | |
| 6 | +include_glm() | |
| 32 | 7 | |
| 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) | |
| 35 | 9 | |
| 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() | |
| @@ -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 | − | |
| 7 | 1 | set(TARGET_NAME fbx) |
| 8 | 2 | |
| 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() | |
| 14 | 5 | |
| 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() | |
| 20 | 7 | |
| 21 | −# link ZLIB | |
| 22 | −find_package(ZLIB) | |
| 8 | +link_hifi_libraries(shared networking octree voxels) | |
| 23 | 9 | |
| 10 | +find_package(ZLIB REQUIRED) | |
| 24 | 11 | 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}") | |
| 26 | 13 | |
| 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() | |
| @@ -23,9 +23,9 @@ · 22 unmodified lines | ||
| 23 | 23 | #include <glm/gtx/transform.hpp> |
| 24 | 24 | |
| 25 | 25 | #include <GeometryUtil.h> |
| 26 | +#include <GLMHelpers.h> | |
| 26 | 27 | #include <OctalCode.h> |
| 27 | 28 | #include <Shape.h> |
| 28 | −#include <SharedUtil.h> | |
| 29 | 29 | |
| 30 | 30 | #include <VoxelTree.h> |
| 31 | 31 | |
| @@ -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 | − | |
| 7 | 1 | set(TARGET_NAME metavoxels) |
| 8 | 2 | |
| 9 | −find_package(Qt5 COMPONENTS Network Script Widgets) | |
| 3 | +auto_mtc() | |
| 10 | 4 | |
| 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) | |
| 16 | 7 | |
| 17 | 8 | # 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) | |
| 23 | 10 | |
| 24 | −target_link_libraries(${TARGET_NAME} Qt5::Network Qt5::Widgets Qt5::Script) | |
| 11 | +include_glm() | |
| 25 | 12 | |
| 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() | |
| @@ -489,22 +489,92 @@ HeightfieldData::HeightfieldData(const QByteArray& contents) : · 488 unmodified lines | ||
| 489 | 489 | _contents(contents) { |
| 490 | 490 | } |
| 491 | 491 | |
| 492 | −const int BYTES_PER_PIXEL = 3; | |
| 492 | +HeightfieldData::HeightfieldData(Bitstream& in, int bytes, bool color) { | |
| 493 | + read(in, bytes, color); | |
| 494 | +} | |
| 493 | 495 | |
| 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()); | |
| 501 | 511 | |
| 502 | 512 | } 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; | |
| 508 | 578 | } |
| 509 | 579 | } |
| 510 | 580 | } |
| @@ -514,7 +584,7 @@ void HeightfieldData::write(Bitstream& out, bool color) { · 3 unmodified lines | ||
| 514 | 584 | if (_encoded.isEmpty()) { |
| 515 | 585 | QImage image; |
| 516 | 586 | if (color) { |
| 517 | − int size = glm::sqrt(_contents.size() / (float)BYTES_PER_PIXEL); | |
| 587 | + int size = glm::sqrt(_contents.size() / (float)COLOR_BYTES); | |
| 518 | 588 | image = QImage((uchar*)_contents.data(), size, size, QImage::Format_RGB888); |
| 519 | 589 | } else { |
| 520 | 590 | int size = glm::sqrt((float)_contents.size()); |
| @@ -526,38 +596,170 @@ void HeightfieldData::write(Bitstream& out, bool color) { · 5 unmodified lines | ||
| 526 | 596 | *dest++ = *src; |
| 527 | 597 | } |
| 528 | 598 | } |
| 529 | − QBuffer buffer(&_encoded); | |
| 530 | − buffer.open(QIODevice::WriteOnly); | |
| 531 | − image.save(&buffer, "JPG"); | |
| 599 | + _encoded = encodeHeightfieldImage(image); | |
| 532 | 600 | } |
| 533 | 601 | out << _encoded.size(); |
| 534 | 602 | out.writeAligned(_encoded); |
| 535 | 603 | } |
| 536 | 604 | |
| 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 | + | |
| 537 | 711 | HeightfieldAttribute::HeightfieldAttribute(const QString& name) : |
| 538 | 712 | InlineAttribute<HeightfieldDataPointer>(name) { |
| 539 | 713 | } |
| 540 | 714 | |
| 541 | 715 | 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)); | |
| 550 | 725 | } |
| 551 | 726 | } |
| 552 | 727 | |
| 553 | 728 | 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; | |
| 561 | 763 | } |
| 562 | 764 | } |
| 563 | 765 | |
| @@ -635,25 +837,53 @@ HeightfieldColorAttribute::HeightfieldColorAttribute(const QString& name) : · 71 unmodified lines | ||
| 635 | 837 | } |
| 636 | 838 | |
| 637 | 839 | 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)); | |
| 646 | 849 | } |
| 647 | 850 | } |
| 648 | 851 | |
| 649 | 852 | 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; | |
| 657 | 887 | } |
| 658 | 888 | } |
| 659 | 889 | |
| @@ -669,8 +899,8 @@ bool HeightfieldColorAttribute::merge(void*& parent, void* children[], bool post · 9 unmodified lines | ||
| 669 | 899 | *(HeightfieldDataPointer*)&parent = HeightfieldDataPointer(); |
| 670 | 900 | return true; |
| 671 | 901 | } |
| 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); | |
| 674 | 904 | int halfSize = size / 2; |
| 675 | 905 | for (int i = 0; i < MERGE_COUNT; i++) { |
| 676 | 906 | HeightfieldDataPointer child = decodeInline<HeightfieldDataPointer>(children[i]); |
| @@ -678,7 +908,7 @@ bool HeightfieldColorAttribute::merge(void*& parent, void* children[], bool post · 1 unmodified line | ||
| 678 | 908 | continue; |
| 679 | 909 | } |
| 680 | 910 | 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); | |
| 682 | 912 | const int INDEX_MASK = 1; |
| 683 | 913 | int xIndex = i & INDEX_MASK; |
| 684 | 914 | const int Y_SHIFT = 1; |
| @@ -688,24 +918,25 @@ bool HeightfieldColorAttribute::merge(void*& parent, void* children[], bool post · 3 unmodified lines | ||
| 688 | 918 | } |
| 689 | 919 | int Z_SHIFT = 2; |
| 690 | 920 | 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; | |
| 692 | 922 | 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; | |
| 695 | 925 | 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; | |
| 699 | 929 | 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; | |
| 702 | 932 | int blueOffset2 = childStride + 2; |
| 703 | − int blueOffset3 = childStride + BYTES_PER_PIXEL + 2; | |
| 933 | + int blueOffset3 = childStride + HeightfieldData::COLOR_BYTES + 2; | |
| 704 | 934 | if (childSize == size) { |
| 705 | 935 | // simple case: one destination value for four child values |
| 706 | 936 | 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; | |
| 709 | 940 | *dest++ = ((int)src[1] + (int)src[greenOffset1] + (int)src[greenOffset2] + (int)src[greenOffset3]) >> 2; |
| 710 | 941 | *dest++ = ((int)src[2] + (int)src[blueOffset1] + (int)src[blueOffset2] + (int)src[blueOffset3]) >> 2; |
| 711 | 942 | } |
| @@ -717,13 +948,14 @@ bool HeightfieldColorAttribute::merge(void*& parent, void* children[], bool post · 5 unmodified lines | ||
| 717 | 948 | int halfChildSize = childSize / 2; |
| 718 | 949 | int destPerSrc = size / childSize; |
| 719 | 950 | 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; | |
| 722 | 954 | *dest++ = ((int)src[1] + (int)src[greenOffset1] + (int)src[greenOffset2] + (int)src[greenOffset3]) >> 2; |
| 723 | 955 | *dest++ = ((int)src[2] + (int)src[blueOffset1] + (int)src[blueOffset2] + (int)src[blueOffset3]) >> 2; |
| 724 | 956 | 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; | |
| 727 | 959 | } |
| 728 | 960 | } |
| 729 | 961 | dest += halfStride; |
| @@ -28,6 +28,7 @@ class QScriptEngine; · 27 unmodified lines | ||
| 28 | 28 | class QScriptValue; |
| 29 | 29 | |
| 30 | 30 | class Attribute; |
| 31 | +class HeightfieldData; | |
| 31 | 32 | class MetavoxelData; |
| 32 | 33 | class MetavoxelLOD; |
| 33 | 34 | class MetavoxelNode; |
| @@ -421,26 +422,37 @@ public: · 387 unmodified lines | ||
| 421 | 422 | virtual AttributeValue inherit(const AttributeValue& parentValue) const; |
| 422 | 423 | }; |
| 423 | 424 | |
| 425 | +typedef QExplicitlySharedDataPointer<HeightfieldData> HeightfieldDataPointer; | |
| 426 | + | |
| 424 | 427 | /// Contains a block of heightfield data. |
| 425 | 428 | class HeightfieldData : public QSharedData { |
| 426 | 429 | public: |
| 427 | 430 | |
| 431 | + static const int COLOR_BYTES = 3; | |
| 432 | + | |
| 428 | 433 | HeightfieldData(const QByteArray& contents); |
| 429 | 434 | HeightfieldData(Bitstream& in, int bytes, bool color); |
| 430 | − | |
| 435 | + HeightfieldData(Bitstream& in, int bytes, const HeightfieldDataPointer& reference, bool color); | |
| 436 | + | |
| 431 | 437 | const QByteArray& getContents() const { return _contents; } |
| 432 | 438 | |
| 433 | 439 | void write(Bitstream& out, bool color); |
| 440 | + void writeDelta(Bitstream& out, const HeightfieldDataPointer& reference, bool color); | |
| 434 | 441 | |
| 435 | 442 | private: |
| 436 | 443 | |
| 444 | + void read(Bitstream& in, int bytes, bool color); | |
| 445 | + void set(const QImage& image, bool color); | |
| 446 | + | |
| 437 | 447 | QByteArray _contents; |
| 438 | 448 | QByteArray _encoded; |
| 439 | 449 | QMutex _encodedMutex; |
| 450 | + | |
| 451 | + HeightfieldDataPointer _deltaData; | |
| 452 | + QByteArray _encodedDelta; | |
| 453 | + QMutex _encodedDeltaMutex; | |
| 440 | 454 | }; |
| 441 | 455 | |
| 442 | −typedef QExplicitlySharedDataPointer<HeightfieldData> HeightfieldDataPointer; | |
| 443 | − | |
| 444 | 456 | /// An attribute that stores heightfield data. |
| 445 | 457 | class HeightfieldAttribute : public InlineAttribute<HeightfieldDataPointer> { |
| 446 | 458 | Q_OBJECT |
| @@ -451,7 +463,10 @@ public: · 4 unmodified lines | ||
| 451 | 463 | |
| 452 | 464 | virtual void read(Bitstream& in, void*& value, bool isLeaf) const; |
| 453 | 465 | 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 | + | |
| 455 | 470 | virtual bool merge(void*& parent, void* children[], bool postRead = false) const; |
| 456 | 471 | }; |
| 457 | 472 | |
| @@ -466,6 +481,9 @@ public: · 8 unmodified lines | ||
| 466 | 481 | virtual void read(Bitstream& in, void*& value, bool isLeaf) const; |
| 467 | 482 | virtual void write(Bitstream& out, void* value, bool isLeaf) const; |
| 468 | 483 | |
| 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 | + | |
| 469 | 487 | virtual bool merge(void*& parent, void* children[], bool postRead = false) const; |
| 470 | 488 | }; |
| 471 | 489 | |
| @@ -318,3 +318,177 @@ SetDataEdit::SetDataEdit(const glm::vec3& minimum, const MetavoxelData& data, bo · 317 unmodified lines | ||
| 318 | 318 | void SetDataEdit::apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const { |
| 319 | 319 | data.set(minimum, this->data, blend); |
| 320 | 320 | } |
| 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 | + | |
| @@ -207,4 +207,38 @@ public: · 206 unmodified lines | ||
| 207 | 207 | |
| 208 | 208 | DECLARE_STREAMABLE_METATYPE(SetDataEdit) |
| 209 | 209 | |
| 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 | + | |
| 210 | 244 | #endif // hifi_MetavoxelMessages_h |
| @@ -24,6 +24,8 @@ · 23 unmodified lines | ||
| 24 | 24 | #include <QVBoxLayout> |
| 25 | 25 | #include <QtDebug> |
| 26 | 26 | |
| 27 | +#include <GLMHelpers.h> | |
| 28 | + | |
| 27 | 29 | #include "MetavoxelUtil.h" |
| 28 | 30 | #include "ScriptCache.h" |
| 29 | 31 | #include "StreamUtils.h" |
| @@ -162,6 +164,11 @@ Box::Box(const glm::vec3& minimum, const glm::vec3& maximum) : · 132 unmodified lines | ||
| 162 | 164 | minimum(minimum), maximum(maximum) { |
| 163 | 165 | } |
| 164 | 166 | |
| 167 | +void Box::add(const Box& other) { | |
| 168 | + minimum = glm::min(minimum, other.minimum); | |
| 169 | + maximum = glm::max(maximum, other.maximum); | |
| 170 | +} | |
| 171 | + | |
| 165 | 172 | bool Box::contains(const glm::vec3& point) const { |
| 166 | 173 | return point.x >= minimum.x && point.x <= maximum.x && |
| 167 | 174 | point.y >= minimum.y && point.y <= maximum.y && |
| @@ -180,6 +187,14 @@ bool Box::intersects(const Box& other) const { · 12 unmodified lines | ||
| 180 | 187 | other.maximum.z >= minimum.z && other.minimum.z <= maximum.z; |
| 181 | 188 | } |
| 182 | 189 | |
| 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 | + | |
| 183 | 198 | const int X_MAXIMUM_FLAG = 1; |
| 184 | 199 | const int Y_MAXIMUM_FLAG = 2; |
| 185 | 200 | const int Z_MAXIMUM_FLAG = 4; |
| @@ -44,12 +44,18 @@ public: · 43 unmodified lines | ||
| 44 | 44 | |
| 45 | 45 | explicit Box(const glm::vec3& minimum = glm::vec3(), const glm::vec3& maximum = glm::vec3()); |
| 46 | 46 | |
| 47 | + void add(const Box& other); | |
| 48 | + | |
| 47 | 49 | bool contains(const glm::vec3& point) const; |
| 48 | 50 | |
| 49 | 51 | bool contains(const Box& other) const; |
| 50 | 52 | |
| 51 | 53 | bool intersects(const Box& other) const; |
| 52 | 54 | |
| 55 | + Box getIntersection(const Box& other) const; | |
| 56 | + | |
| 57 | + bool isEmpty() const; | |
| 58 | + | |
| 53 | 59 | float getLongestSide() const { return qMax(qMax(maximum.x - minimum.x, maximum.y - minimum.y), maximum.z - minimum.z); } |
| 54 | 60 | |
| 55 | 61 | glm::vec3 getVertex(int index) const; |
| @@ -138,6 +144,8 @@ public: · 82 unmodified lines | ||
| 138 | 144 | |
| 139 | 145 | QColorEditor(QWidget* parent); |
| 140 | 146 | |
| 147 | + const QColor& getColor() const { return _color; } | |
| 148 | + | |
| 141 | 149 | signals: |
| 142 | 150 | |
| 143 | 151 | void colorChanged(const QColor& color); |
| @@ -1,21 +1,14 @@ | ||
| 1 | −set(ROOT_DIR ../..) | |
| 2 | −set(MACRO_DIR "${ROOT_DIR}/cmake/macros") | |
| 3 | − | |
| 4 | 1 | set(TARGET_NAME networking) |
| 5 | −project(${TARGET_NAME}) | |
| 6 | − | |
| 7 | −find_package(Qt5 COMPONENTS Network) | |
| 8 | 2 | |
| 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) | |
| 11 | 5 | |
| 12 | −# include GLM | |
| 13 | −include(${MACRO_DIR}/IncludeGLM.cmake) | |
| 14 | −include_glm(${TARGET_NAME} "${ROOT_DIR}") | |
| 6 | +link_hifi_libraries(shared) | |
| 15 | 7 | |
| 16 | −target_link_libraries(${TARGET_NAME} Qt5::Network) | |
| 17 | − | |
| 18 | −# add a definition for ssize_t so that windows doesn't bail | |
| 19 | 8 | 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() | |
| @@ -36,6 +36,7 @@ DomainHandler::DomainHandler(QObject* parent) : · 35 unmodified lines | ||
| 36 | 36 | void DomainHandler::clearConnectionInfo() { |
| 37 | 37 | _uuid = QUuid(); |
| 38 | 38 | _isConnected = false; |
| 39 | + emit disconnectedFromDomain(); | |
| 39 | 40 | |
| 40 | 41 | if (_handshakeTimer) { |
| 41 | 42 | _handshakeTimer->stop(); |
| @@ -129,6 +130,8 @@ void DomainHandler::setIsConnected(bool isConnected) { · 87 unmodified lines | ||
| 129 | 130 | |
| 130 | 131 | // we've connected to new domain - time to ask it for global settings |
| 131 | 132 | requestDomainSettings(); |
| 133 | + } else { | |
| 134 | + emit disconnectedFromDomain(); | |
| 132 | 135 | } |
| 133 | 136 | } |
| 134 | 137 | } |
| @@ -196,4 +199,4 @@ void DomainHandler::parseDTLSRequirementPacket(const QByteArray& dtlsRequirement · 61 unmodified lines | ||
| 196 | 199 | _sockAddr.setPort(dtlsPort); |
| 197 | 200 | |
| 198 | 201 | // initializeDTLSSession(); |
| 199 | −} | |
| 202 | +} | |
| @@ -70,6 +70,7 @@ private slots: · 69 unmodified lines | ||
| 70 | 70 | signals: |
| 71 | 71 | void hostnameChanged(const QString& hostname); |
| 72 | 72 | void connectedToDomain(const QString& hostname); |
| 73 | + void disconnectedFromDomain(); | |
| 73 | 74 | |
| 74 | 75 | void settingsReceived(const QJsonObject& domainSettingsObject); |
| 75 | 76 | void settingsReceiveFail(); |
| @@ -82,8 +82,8 @@ LimitedNodeList::LimitedNodeList(unsigned short socketListenPort, unsigned short · 81 unmodified lines | ||
| 82 | 82 | qDebug() << "NodeList DTLS socket is listening on" << _dtlsSocket->localPort(); |
| 83 | 83 | } |
| 84 | 84 | |
| 85 | − const int LARGER_SNDBUF_SIZE = 1048576; | |
| 86 | − changeSendSocketBufferSize(LARGER_SNDBUF_SIZE); | |
| 85 | + const int LARGER_BUFFER_SIZE = 1048576; | |
| 86 | + changeSocketBufferSizes(LARGER_BUFFER_SIZE); | |
| 87 | 87 | |
| 88 | 88 | _packetStatTimer.start(); |
| 89 | 89 | } |
| @@ -129,7 +129,7 @@ QUdpSocket& LimitedNodeList::getDTLSSocket() { · 39 unmodified lines | ||
| 129 | 129 | return *_dtlsSocket; |
| 130 | 130 | } |
| 131 | 131 | |
| 132 | −void LimitedNodeList::changeSendSocketBufferSize(int numSendBytes) { | |
| 132 | +void LimitedNodeList::changeSocketBufferSizes(int numBytes) { | |
| 133 | 133 | // change the socket send buffer size to be 1MB |
| 134 | 134 | int oldBufferSize = 0; |
| 135 | 135 | |
| @@ -139,15 +139,28 @@ void LimitedNodeList::changeSendSocketBufferSize(int numSendBytes) { · 3 unmodified lines | ||
| 139 | 139 | unsigned int sizeOfInt = sizeof(oldBufferSize); |
| 140 | 140 | #endif |
| 141 | 141 | |
| 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 | + } | |
| 151 | 164 | } |
| 152 | 165 | |
| 153 | 166 | bool LimitedNodeList::packetVersionAndHashMatch(const QByteArray& packet) { |
| @@ -123,7 +123,7 @@ protected: · 122 unmodified lines | ||
| 123 | 123 | NodeHash::iterator killNodeAtHashIterator(NodeHash::iterator& nodeItemToKill); |
| 124 | 124 | |
| 125 | 125 | |
| 126 | − void changeSendSocketBufferSize(int numSendBytes); | |
| 126 | + void changeSocketBufferSizes(int numBytes); | |
| 127 | 127 | |
| 128 | 128 | QUuid _sessionUUID; |
| 129 | 129 | NodeHash _nodeHash; |
| @@ -80,6 +80,8 @@ PacketVersion versionForPacketType(PacketType type) { · 79 unmodified lines | ||
| 80 | 80 | return 2; |
| 81 | 81 | case PacketTypeAudioStreamStats: |
| 82 | 82 | return 1; |
| 83 | + case PacketTypeMetavoxelData: | |
| 84 | + return 1; | |
| 83 | 85 | default: |
| 84 | 86 | return 0; |
| 85 | 87 | } |
| @@ -39,10 +39,6 @@ public: · 38 unmodified lines | ||
| 39 | 39 | ~PacketSender(); |
| 40 | 40 | |
| 41 | 41 | /// 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 | |
| 46 | 42 | void queuePacketForSending(const SharedNodePointer& destinationNode, const QByteArray& packet); |
| 47 | 43 | |
| 48 | 44 | void setPacketsPerSecond(int packetsPerSecond); |
| @@ -24,10 +24,6 @@ public: · 23 unmodified lines | ||
| 24 | 24 | ReceivedPacketProcessor() { } |
| 25 | 25 | |
| 26 | 26 | /// 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 | |
| 31 | 27 | void queueReceivedPacket(const SharedNodePointer& sendingNode, const QByteArray& packet); |
| 32 | 28 | |
| 33 | 29 | /// Are there received packets waiting to be processed |
| @@ -253,14 +253,19 @@ PacketStreamStats SequenceNumberStats::getStatsForHistoryWindow() const { · 252 unmodified lines | ||
| 253 | 253 | } |
| 254 | 254 | |
| 255 | 255 | // 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; | |
| 266 | 257 | } |
| 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 | + | |
| @@ -31,6 +31,18 @@ public: · 30 unmodified lines | ||
| 31 | 31 | _expectedReceived(0) |
| 32 | 32 | {} |
| 33 | 33 | |
| 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 | + | |
| 34 | 46 | float getLostRate() const { return (float)_lost / _expectedReceived; } |
| 35 | 47 | |
| 36 | 48 | quint32 _received; |
| @@ -76,6 +88,7 @@ public: · 39 unmodified lines | ||
| 76 | 88 | |
| 77 | 89 | const PacketStreamStats& getStats() const { return _stats; } |
| 78 | 90 | PacketStreamStats getStatsForHistoryWindow() const; |
| 91 | + PacketStreamStats getStatsForLastHistoryInterval() const; | |
| 79 | 92 | const QSet<quint16>& getMissingSet() const { return _missingSet; } |
| 80 | 93 | |
| 81 | 94 | private: |
| @@ -11,6 +11,7 @@ · 10 unmodified lines | ||
| 11 | 11 | |
| 12 | 12 | #include <QtCore/QCoreApplication> |
| 13 | 13 | #include <QtCore/QJsonObject> |
| 14 | +#include <QtCore/QThread> | |
| 14 | 15 | #include <QtCore/QTimer> |
| 15 | 16 | |
| 16 | 17 | #include "Logging.h" |
| @@ -18,7 +19,8 @@ · 1 unmodified line | ||
| 18 | 19 | |
| 19 | 20 | ThreadedAssignment::ThreadedAssignment(const QByteArray& packet) : |
| 20 | 21 | Assignment(packet), |
| 21 | − _isFinished(false) | |
| 22 | + _isFinished(false), | |
| 23 | + _datagramProcessingThread(NULL) | |
| 22 | 24 | { |
| 23 | 25 | |
| 24 | 26 | } |
| @@ -28,10 +30,25 @@ void ThreadedAssignment::setFinished(bool isFinished) { · 3 unmodified lines | ||
| 28 | 30 | |
| 29 | 31 | if (_isFinished) { |
| 30 | 32 | 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 | + } | |
| 32 | 47 | |
| 33 | 48 | // 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(); | |
| 35 | 52 | } |
| 36 | 53 | } |
| 37 | 54 | |
| @@ -20,6 +20,7 @@ class ThreadedAssignment : public Assignment { · 19 unmodified lines | ||
| 20 | 20 | Q_OBJECT |
| 21 | 21 | public: |
| 22 | 22 | ThreadedAssignment(const QByteArray& packet); |
| 23 | + | |
| 23 | 24 | void setFinished(bool isFinished); |
| 24 | 25 | virtual void aboutToFinish() { }; |
| 25 | 26 | void addPacketStatsAndSendStatsPacket(QJsonObject& statsObject); |
| @@ -29,15 +30,18 @@ public slots: · 3 unmodified lines | ||
| 29 | 30 | virtual void run() = 0; |
| 30 | 31 | virtual void readPendingDatagrams() = 0; |
| 31 | 32 | virtual void sendStatsPacket(); |
| 32 | − | |
| 33 | +signals: | |
| 34 | + void finished(); | |
| 35 | + | |
| 33 | 36 | protected: |
| 34 | 37 | bool readAvailableDatagram(QByteArray& destinationByteArray, HifiSockAddr& senderSockAddr); |
| 35 | 38 | void commonInit(const QString& targetName, NodeType_t nodeType, bool shouldSendStats = true); |
| 36 | 39 | bool _isFinished; |
| 40 | + QThread* _datagramProcessingThread; | |
| 41 | + | |
| 37 | 42 | private slots: |
| 38 | 43 | void checkInWithDomainServerOrExit(); |
| 39 | −signals: | |
| 40 | − void finished(); | |
| 44 | + | |
| 41 | 45 | }; |
| 42 | 46 | |
| 43 | 47 | typedef QSharedPointer<ThreadedAssignment> SharedAssignmentPointer; |
| @@ -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 | − | |
| 7 | 1 | set(TARGET_NAME octree) |
| 8 | 2 | |
| 9 | −find_package(Qt5Widgets REQUIRED) | |
| 3 | +# use setup_hifi_library macro to setup our project and link appropriate Qt modules | |
| 4 | +setup_hifi_library() | |
| 10 | 5 | |
| 11 | −include(${MACRO_DIR}/SetupHifiLibrary.cmake) | |
| 12 | −setup_hifi_library(${TARGET_NAME}) | |
| 6 | +include_glm() | |
| 13 | 7 | |
| 14 | −include(${MACRO_DIR}/IncludeGLM.cmake) | |
| 15 | −include_glm(${TARGET_NAME} "${ROOT_DIR}") | |
| 8 | +link_hifi_libraries(shared networking) | |
| 16 | 9 | |
| 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) | |
| 23 | 12 | |
| 24 | 13 | include_directories(SYSTEM "${ZLIB_INCLUDE_DIRS}") |
| 25 | −target_link_libraries(${TARGET_NAME} "${ZLIB_LIBRARIES}" Qt5::Widgets) | |
| 26 | 14 | |
| 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() | |
| @@ -20,7 +20,7 @@ EditPacketBuffer::EditPacketBuffer() : · 19 unmodified lines | ||
| 20 | 20 | |
| 21 | 21 | } |
| 22 | 22 | |
| 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) : | |
| 24 | 24 | _nodeUUID(nodeUUID), |
| 25 | 25 | _currentType(type), |
| 26 | 26 | _currentSize(length), |
| @@ -21,13 +21,13 @@ · 20 unmodified lines | ||
| 21 | 21 | class EditPacketBuffer { |
| 22 | 22 | public: |
| 23 | 23 | EditPacketBuffer(); |
| 24 | − EditPacketBuffer(PacketType type, unsigned char* codeColorBuffer, ssize_t length, | |
| 24 | + EditPacketBuffer(PacketType type, unsigned char* codeColorBuffer, size_t length, | |
| 25 | 25 | qint64 satoshiCost = 0, const QUuid nodeUUID = QUuid()); |
| 26 | 26 | |
| 27 | 27 | QUuid _nodeUUID; |
| 28 | 28 | PacketType _currentType; |
| 29 | 29 | unsigned char _currentBuffer[MAX_PACKET_SIZE]; |
| 30 | − ssize_t _currentSize; | |
| 30 | + size_t _currentSize; | |
| 31 | 31 | qint64 _satoshiCost; |
| 32 | 32 | }; |
| 33 | 33 | |
| @@ -35,7 +35,7 @@ void JurisdictionListener::nodeKilled(SharedNodePointer node) { · 34 unmodified lines | ||
| 35 | 35 | bool JurisdictionListener::queueJurisdictionRequest() { |
| 36 | 36 | static unsigned char buffer[MAX_PACKET_SIZE]; |
| 37 | 37 | unsigned char* bufferOut = &buffer[0]; |
| 38 | − ssize_t sizeOut = populatePacketHeader(reinterpret_cast<char*>(bufferOut), PacketTypeJurisdictionRequest); | |
| 38 | + int sizeOut = populatePacketHeader(reinterpret_cast<char*>(bufferOut), PacketTypeJurisdictionRequest); | |
| 39 | 39 | int nodeCount = 0; |
| 40 | 40 | |
| 41 | 41 | NodeList* nodeList = NodeList::getInstance(); |
| @@ -47,10 +47,6 @@ public slots: · 46 unmodified lines | ||
| 47 | 47 | protected: |
| 48 | 48 | /// Callback for processing of received packets. Will process any queued PacketType_JURISDICTION and update the |
| 49 | 49 | /// 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 | |
| 54 | 50 | virtual void processPacket(const SharedNodePointer& sendingNode, const QByteArray& packet); |
| 55 | 51 | |
| 56 | 52 | private: |
| @@ -47,7 +47,7 @@ bool JurisdictionSender::process() { · 46 unmodified lines | ||
| 47 | 47 | // add our packet to our own queue, then let the PacketSender class do the rest of the work. |
| 48 | 48 | static unsigned char buffer[MAX_PACKET_SIZE]; |
| 49 | 49 | unsigned char* bufferOut = &buffer[0]; |
| 50 | − ssize_t sizeOut = 0; | |
| 50 | + int sizeOut = 0; | |
| 51 | 51 | |
| 52 | 52 | if (_jurisdictionMap) { |
| 53 | 53 | sizeOut = _jurisdictionMap->packIntoMessage(bufferOut, MAX_PACKET_SIZE); |
| @@ -83,7 +83,7 @@ bool OctreeEditPacketSender::serversExist() const { · 82 unmodified lines | ||
| 83 | 83 | // This method is called when the edit packet layer has determined that it has a fully formed packet destined for |
| 84 | 84 | // a known nodeID. |
| 85 | 85 | void OctreeEditPacketSender::queuePacketToNode(const QUuid& nodeUUID, unsigned char* buffer, |
| 86 | − ssize_t length, qint64 satoshiCost) { | |
| 86 | + size_t length, qint64 satoshiCost) { | |
| 87 | 87 | |
| 88 | 88 | bool wantDebug = false; |
| 89 | 89 | if (wantDebug) { |
| @@ -171,7 +171,7 @@ void OctreeEditPacketSender::processPreServerExistsPackets() { · 81 unmodified lines | ||
| 171 | 171 | } |
| 172 | 172 | |
| 173 | 173 | void OctreeEditPacketSender::queuePendingPacketToNodes(PacketType type, unsigned char* buffer, |
| 174 | − ssize_t length, qint64 satoshiCost) { | |
| 174 | + size_t length, qint64 satoshiCost) { | |
| 175 | 175 | // If we're asked to save messages while waiting for voxel servers to arrive, then do so... |
| 176 | 176 | |
| 177 | 177 | if (_maxPendingMessages > 0) { |
| @@ -189,7 +189,7 @@ void OctreeEditPacketSender::queuePendingPacketToNodes(PacketType type, unsigned · 11 unmodified lines | ||
| 189 | 189 | } |
| 190 | 190 | } |
| 191 | 191 | |
| 192 | −void OctreeEditPacketSender::queuePacketToNodes(unsigned char* buffer, ssize_t length, qint64 satoshiCost) { | |
| 192 | +void OctreeEditPacketSender::queuePacketToNodes(unsigned char* buffer, size_t length, qint64 satoshiCost) { | |
| 193 | 193 | if (!_shouldSend) { |
| 194 | 194 | return; // bail early |
| 195 | 195 | } |
| @@ -30,7 +30,7 @@ public: · 29 unmodified lines | ||
| 30 | 30 | /// Queues a single edit message. Will potentially send a pending multi-command packet. Determines which server |
| 31 | 31 | /// node or nodes the packet should be sent to. Can be called even before servers are known, in which case up to |
| 32 | 32 | /// 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); | |
| 34 | 34 | |
| 35 | 35 | /// Releases all queued messages even if those messages haven't filled an MTU packet. This will move the packed message |
| 36 | 36 | /// 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 | ||
| 82 | 82 | // you must override these... |
| 83 | 83 | virtual char getMyNodeType() const = 0; |
| 84 | 84 | virtual void adjustEditPacketForClockSkew(PacketType type, |
| 85 | − unsigned char* editPacketBuffer, ssize_t length, int clockSkew) { }; | |
| 85 | + unsigned char* editPacketBuffer, size_t length, int clockSkew) { }; | |
| 86 | 86 | |
| 87 | 87 | bool hasDestinationWalletUUID() const { return !_destinationWalletUUID.isNull(); } |
| 88 | 88 | void setDestinationWalletUUID(const QUuid& destinationWalletUUID) { _destinationWalletUUID = destinationWalletUUID; } |
| @@ -98,9 +98,9 @@ signals: · 9 unmodified lines | ||
| 98 | 98 | |
| 99 | 99 | protected: |
| 100 | 100 | 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); | |
| 104 | 104 | void initializePacket(EditPacketBuffer& packetBuffer, PacketType type); |
| 105 | 105 | void releaseQueuedPacket(EditPacketBuffer& packetBuffer); // releases specific queued packet |
| 106 | 106 | |
| @@ -9,7 +9,9 @@ · 8 unmodified lines | ||
| 9 | 9 | // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html |
| 10 | 10 | // |
| 11 | 11 | |
| 12 | +#include <GLMHelpers.h> | |
| 12 | 13 | #include <PerfStat.h> |
| 14 | + | |
| 13 | 15 | #include "OctreePacketData.h" |
| 14 | 16 | |
| 15 | 17 | bool OctreePacketData::_debug = false; |
| @@ -9,8 +9,9 @@ · 8 unmodified lines | ||
| 9 | 9 | // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html |
| 10 | 10 | // |
| 11 | 11 | |
| 12 | −#include <PacketHeaders.h> | |
| 13 | −#include <SharedUtil.h> | |
| 12 | +//#include <PacketHeaders.h> | |
| 13 | +//#include <SharedUtil.h> | |
| 14 | +#include <GLMHelpers.h> | |
| 14 | 15 | |
| 15 | 16 | #include "OctreeConstants.h" |
| 16 | 17 | #include "OctreeQuery.h" |
| @@ -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 | − | |
| 7 | 1 | set(TARGET_NAME particles) |
| 8 | 2 | |
| 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) | |
| 23 | 5 | |
| 24 | −# link ZLIB | |
| 25 | −find_package(ZLIB) | |
| 6 | +include_glm() | |
| 26 | 7 | |
| 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) | |
| 29 | 10 | |
| 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() | |
| @@ -13,7 +13,7 @@ · 12 unmodified lines | ||
| 13 | 13 | |
| 14 | 14 | #include <Octree.h> |
| 15 | 15 | #include <RegisteredMetaTypes.h> |
| 16 | −#include <SharedUtil.h> // usecTimestampNow() | |
| 16 | +#include <GLMHelpers.h> | |
| 17 | 17 | #include <VoxelsScriptingInterface.h> |
| 18 | 18 | #include <VoxelDetail.h> |
| 19 | 19 | |
| @@ -22,7 +22,7 @@ · 2 unmodified lines | ||
| 22 | 22 | // I'm open to other potential solutions. Could we change cmake to allow libraries to reference each others |
| 23 | 23 | // headers, but not link to each other, this is essentially what this construct is doing, but would be |
| 24 | 24 | // better to add includes to the include path, but not link |
| 25 | −#include "../../script-engine/src/ScriptEngine.h" | |
| 25 | +#include <ScriptEngine.h> | |
| 26 | 26 | |
| 27 | 27 | #include "ParticlesScriptingInterface.h" |
| 28 | 28 | #include "Particle.h" |
| @@ -856,7 +856,7 @@ bool Particle::encodeParticleEditMessageDetails(PacketType command, ParticleID i · 827 unmodified lines | ||
| 856 | 856 | } |
| 857 | 857 | |
| 858 | 858 | // 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) { | |
| 860 | 860 | unsigned char* dataAt = codeColorBuffer; |
| 861 | 861 | int octets = numberOfThreeBitSectionsInCode(dataAt); |
| 862 | 862 | int lengthOfOctcode = bytesRequiredForCodeLength(octets); |
| @@ -292,7 +292,7 @@ public: · 291 unmodified lines | ||
| 292 | 292 | static bool encodeParticleEditMessageDetails(PacketType command, ParticleID id, const ParticleProperties& details, |
| 293 | 293 | unsigned char* bufferOut, int sizeIn, int& sizeOut); |
| 294 | 294 | |
| 295 | − static void adjustEditPacketForClockSkew(unsigned char* codeColorBuffer, ssize_t length, int clockSkew); | |
| 295 | + static void adjustEditPacketForClockSkew(unsigned char* codeColorBuffer, size_t length, int clockSkew); | |
| 296 | 296 | |
| 297 | 297 | void applyHardCollision(const CollisionInfo& collisionInfo); |
| 298 | 298 | |
| @@ -38,8 +38,8 @@ void ParticleEditPacketSender::sendEditParticleMessage(PacketType type, Particle · 37 unmodified lines | ||
| 38 | 38 | } |
| 39 | 39 | } |
| 40 | 40 | |
| 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) { | |
| 43 | 43 | Particle::adjustEditPacketForClockSkew(codeColorBuffer, length, clockSkew); |
| 44 | 44 | } |
| 45 | 45 | |
| @@ -31,6 +31,6 @@ public: · 30 unmodified lines | ||
| 31 | 31 | |
| 32 | 32 | // My server type is the particle server |
| 33 | 33 | 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); | |
| 35 | 35 | }; |
| 36 | 36 | #endif // hifi_ParticleEditPacketSender_h |
| @@ -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 | − | |
| 7 | 1 | set(TARGET_NAME script-engine) |
| 8 | 2 | |
| 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) | |
| 25 | 5 | |
| 26 | −# link ZLIB | |
| 27 | −find_package(ZLIB) | |
| 6 | +include_glm() | |
| 28 | 7 | |
| 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) | |
| 31 | 9 | |
| 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() | |
| @@ -14,7 +14,7 @@ · 13 unmodified lines | ||
| 14 | 14 | #include <QDebug> |
| 15 | 15 | |
| 16 | 16 | #include <OctreeConstants.h> |
| 17 | −#include <SharedUtil.h> | |
| 17 | +#include <GLMHelpers.h> | |
| 18 | 18 | #include "Quat.h" |
| 19 | 19 | |
| 20 | 20 | |
| @@ -1,33 +1,7 @@ | ||
| 1 | −set(ROOT_DIR ../..) | |
| 2 | −set(MACRO_DIR "${ROOT_DIR}/cmake/macros") | |
| 3 | − | |
| 4 | 1 | 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) | |
| 28 | 2 | |
| 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) | |
| 32 | 5 | |
| 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() | |
| @@ -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 */ | |
| @@ -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 | − | |
| @@ -11,8 +11,9 @@ · 10 unmodified lines | ||
| 11 | 11 | |
| 12 | 12 | #include <glm/gtx/norm.hpp> |
| 13 | 13 | |
| 14 | +#include "GLMHelpers.h" | |
| 15 | + | |
| 14 | 16 | #include "AngularConstraint.h" |
| 15 | −#include "SharedUtil.h" | |
| 16 | 17 | |
| 17 | 18 | // helper function |
| 18 | 19 | /// \param angle radian angle to be clamped within angleMin and angleMax |
| @@ -14,7 +14,6 @@ · 13 unmodified lines | ||
| 14 | 14 | |
| 15 | 15 | #include <glm/glm.hpp> |
| 16 | 16 | |
| 17 | − | |
| 18 | 17 | class AngularConstraint { |
| 19 | 18 | public: |
| 20 | 19 | /// \param minAngles minumum euler angles for the constraint |
| @@ -13,56 +13,128 @@ · 12 unmodified lines | ||
| 13 | 13 | #include "Shape.h" |
| 14 | 14 | #include "SharedUtil.h" |
| 15 | 15 | |
| 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) { | |
| 18 | 25 | } |
| 19 | 26 | |
| 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), | |
| 22 | 31 | _numPointsA(0), _numPoints(0), _normal(0.0f) { |
| 23 | 32 | |
| 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 | + | |
| 27 | 36 | float pLength = glm::length(collision._penetration); |
| 28 | 37 | if (pLength > EPSILON) { |
| 29 | 38 | _normal = collision._penetration / pLength; |
| 30 | 39 | } |
| 31 | − | |
| 32 | 40 | if (_shapeA->getID() > _shapeB->getID()) { |
| 33 | 41 | // swap so that _shapeA always has lower ID |
| 34 | 42 | _shapeA = collision.getShapeB(); |
| 35 | 43 | _shapeB = collision.getShapeA(); |
| 36 | − | |
| 37 | − glm::vec3 temp = _offsetA; | |
| 38 | − _offsetA = _offsetB; | |
| 39 | − _offsetB = temp; | |
| 40 | 44 | _normal = - _normal; |
| 45 | + pointA = pointB; | |
| 46 | + pointB = collision._contactPoint; | |
| 41 | 47 | } |
| 42 | 48 | |
| 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 | + | |
| 43 | 56 | _shapeA->getVerletPoints(_points); |
| 44 | 57 | _numPointsA = _points.size(); |
| 45 | 58 | _shapeB->getVerletPoints(_points); |
| 46 | 59 | _numPoints = _points.size(); |
| 47 | 60 | |
| 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 | + | |
| 48 | 77 | // compute offsets for shapeA |
| 49 | 78 | for (int i = 0; i < _numPointsA; ++i) { |
| 50 | − glm::vec3 offset = _points[i]->_position - collision._contactPoint; | |
| 79 | + glm::vec3 offset = _points[i]->_position - pointA; | |
| 51 | 80 | _offsets.push_back(offset); |
| 52 | 81 | _distances.push_back(glm::length(offset)); |
| 53 | 82 | } |
| 54 | 83 | // compute offsets for shapeB |
| 55 | 84 | 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; | |
| 57 | 86 | _offsets.push_back(offset); |
| 58 | 87 | _distances.push_back(glm::length(offset)); |
| 59 | 88 | } |
| 60 | 89 | } |
| 61 | 90 | |
| 62 | −// virtual | |
| 63 | 91 | 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) { | |
| 66 | 138 | glm::vec3& position = _points[i]->_position; |
| 67 | 139 | // TODO: use a fast distance approximation |
| 68 | 140 | float newDistance = glm::distance(_contactPoint, position); |
| @@ -75,60 +147,17 @@ float ContactPoint::enforce() { · 6 unmodified lines | ||
| 75 | 147 | position = center - (0.5f * constrainedDistance) * direction; |
| 76 | 148 | } |
| 77 | 149 | } |
| 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 | + */ | |
| 125 | 151 | } |
| 126 | 152 | |
| 127 | 153 | void ContactPoint::updateContact(const CollisionInfo& collision, quint32 frame) { |
| 128 | 154 | _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) | |
| 132 | 161 | float pLength = glm::length(collision._penetration); |
| 133 | 162 | if (pLength > EPSILON) { |
| 134 | 163 | _normal = collision._penetration / pLength; |
| @@ -138,19 +167,26 @@ void ContactPoint::updateContact(const CollisionInfo& collision, quint32 frame) · 3 unmodified lines | ||
| 138 | 167 | |
| 139 | 168 | if (collision._shapeA->getID() > collision._shapeB->getID()) { |
| 140 | 169 | // our _shapeA always has lower ID |
| 141 | − glm::vec3 temp = _offsetA; | |
| 142 | − _offsetA = _offsetB; | |
| 143 | − _offsetB = temp; | |
| 144 | 170 | _normal = - _normal; |
| 171 | + pointA = pointB; | |
| 172 | + pointB = collision._contactPoint; | |
| 145 | 173 | } |
| 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(); | |
| 146 | 182 | |
| 147 | 183 | // compute offsets for shapeA |
| 148 | 184 | assert(_offsets.size() == _numPoints); |
| 149 | 185 | for (int i = 0; i < _numPointsA; ++i) { |
| 150 | − _offsets[i] = (_points[i]->_position - collision._contactPoint); | |
| 186 | + _offsets[i] = _points[i]->_position - pointA; | |
| 151 | 187 | } |
| 152 | 188 | // compute offsets for shapeB |
| 153 | 189 | for (int i = _numPointsA; i < _numPoints; ++i) { |
| 154 | − _offsets[i] = (_points[i]->_position - collision._contactPoint + collision._penetration); | |
| 190 | + _offsets[i] = _points[i]->_position - pointB; | |
| 155 | 191 | } |
| 156 | 192 | } |
| @@ -26,8 +26,8 @@ public: · 25 unmodified lines | ||
| 26 | 26 | ContactPoint(const CollisionInfo& collision, quint32 frame); |
| 27 | 27 | |
| 28 | 28 | virtual float enforce(); |
| 29 | − | |
| 30 | − void buildConstraints(); | |
| 29 | + | |
| 30 | + void applyFriction(); | |
| 31 | 31 | void updateContact(const CollisionInfo& collision, quint32 frame); |
| 32 | 32 | quint32 getLastFrame() const { return _lastFrame; } |
| 33 | 33 | |
| @@ -41,6 +41,8 @@ protected: · 7 unmodified lines | ||
| 41 | 41 | glm::vec3 _offsetA; // contact point relative to A's center |
| 42 | 42 | glm::vec3 _offsetB; // contact point relative to B's center |
| 43 | 43 | glm::vec3 _contactPoint; // a "virtual" point that is added to the simulation |
| 44 | + float _relativeMassA; // massA / totalMass | |
| 45 | + float _relativeMassB; // massB / totalMass | |
| 44 | 46 | int _numPointsA; // number of VerletPoints that belong to _shapeA |
| 45 | 47 | int _numPoints; // total number of VerletPoints |
| 46 | 48 | QVector<VerletPoint*> _points; // points that belong to colliding shapes |
| @@ -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 | +} | |
| @@ -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 | |
| @@ -12,6 +12,7 @@ · 11 unmodified lines | ||
| 12 | 12 | #ifndef hifi_MovingMinMaxAvg_h |
| 13 | 13 | #define hifi_MovingMinMaxAvg_h |
| 14 | 14 | |
| 15 | +#include <cassert> | |
| 15 | 16 | #include <limits> |
| 16 | 17 | |
| 17 | 18 | #include "RingBufferHistory.h" |
| @@ -47,12 +47,12 @@ PhysicsSimulation::~PhysicsSimulation() { · 46 unmodified lines | ||
| 47 | 47 | void PhysicsSimulation::setRagdoll(Ragdoll* ragdoll) { |
| 48 | 48 | if (_ragdoll != ragdoll) { |
| 49 | 49 | if (_ragdoll) { |
| 50 | − _ragdoll->_ragdollSimulation = NULL; | |
| 50 | + _ragdoll->_simulation = NULL; | |
| 51 | 51 | } |
| 52 | 52 | _ragdoll = ragdoll; |
| 53 | 53 | if (_ragdoll) { |
| 54 | − assert(!(_ragdoll->_ragdollSimulation)); | |
| 55 | − _ragdoll->_ragdollSimulation = this; | |
| 54 | + assert(!(_ragdoll->_simulation)); | |
| 55 | + _ragdoll->_simulation = this; | |
| 56 | 56 | } |
| 57 | 57 | } |
| 58 | 58 | } |
| @@ -144,7 +144,7 @@ bool PhysicsSimulation::addRagdoll(Ragdoll* doll) { · 85 unmodified lines | ||
| 144 | 144 | // list is full |
| 145 | 145 | return false; |
| 146 | 146 | } |
| 147 | − if (doll->_ragdollSimulation == this) { | |
| 147 | + if (doll->_simulation == this) { | |
| 148 | 148 | for (int i = 0; i < numDolls; ++i) { |
| 149 | 149 | if (doll == _otherRagdolls[i]) { |
| 150 | 150 | // already in list |
| @@ -153,8 +153,8 @@ bool PhysicsSimulation::addRagdoll(Ragdoll* doll) { · 2 unmodified lines | ||
| 153 | 153 | } |
| 154 | 154 | } |
| 155 | 155 | // add to list |
| 156 | − assert(!(doll->_ragdollSimulation)); | |
| 157 | − doll->_ragdollSimulation = this; | |
| 156 | + assert(!(doll->_simulation)); | |
| 157 | + doll->_simulation = this; | |
| 158 | 158 | _otherRagdolls.push_back(doll); |
| 159 | 159 | |
| 160 | 160 | // set the massScale of otherRagdolls artificially high |
| @@ -164,7 +164,7 @@ bool PhysicsSimulation::addRagdoll(Ragdoll* doll) { · 3 unmodified lines | ||
| 164 | 164 | |
| 165 | 165 | void PhysicsSimulation::removeRagdoll(Ragdoll* doll) { |
| 166 | 166 | int numDolls = _otherRagdolls.size(); |
| 167 | − if (doll->_ragdollSimulation != this) { | |
| 167 | + if (doll->_simulation != this) { | |
| 168 | 168 | return; |
| 169 | 169 | } |
| 170 | 170 | for (int i = 0; i < numDolls; ++i) { |
| @@ -178,7 +178,7 @@ void PhysicsSimulation::removeRagdoll(Ragdoll* doll) { · 7 unmodified lines | ||
| 178 | 178 | _otherRagdolls.pop_back(); |
| 179 | 179 | _otherRagdolls[i] = lastDoll; |
| 180 | 180 | } |
| 181 | − doll->_ragdollSimulation = NULL; | |
| 181 | + doll->_simulation = NULL; | |
| 182 | 182 | doll->setMassScale(1.0f); |
| 183 | 183 | break; |
| 184 | 184 | } |
| @@ -195,13 +195,13 @@ void PhysicsSimulation::stepForward(float deltaTime, float minError, int maxIter · 10 unmodified lines | ||
| 195 | 195 | quint64 expiry = startTime + maxUsec; |
| 196 | 196 | |
| 197 | 197 | moveRagdolls(deltaTime); |
| 198 | − buildContactConstraints(); | |
| 198 | + enforceContacts(); | |
| 199 | 199 | int numDolls = _otherRagdolls.size(); |
| 200 | 200 | { |
| 201 | 201 | PerformanceTimer perfTimer("enforce"); |
| 202 | − _ragdoll->enforceRagdollConstraints(); | |
| 202 | + _ragdoll->enforceConstraints(); | |
| 203 | 203 | for (int i = 0; i < numDolls; ++i) { |
| 204 | − _otherRagdolls[i]->enforceRagdollConstraints(); | |
| 204 | + _otherRagdolls[i]->enforceConstraints(); | |
| 205 | 205 | } |
| 206 | 206 | } |
| 207 | 207 | |
| @@ -214,12 +214,12 @@ void PhysicsSimulation::stepForward(float deltaTime, float minError, int maxIter · 6 unmodified lines | ||
| 214 | 214 | |
| 215 | 215 | { // enforce constraints |
| 216 | 216 | PerformanceTimer perfTimer("enforce"); |
| 217 | − error = _ragdoll->enforceRagdollConstraints(); | |
| 217 | + error = _ragdoll->enforceConstraints(); | |
| 218 | 218 | for (int i = 0; i < numDolls; ++i) { |
| 219 | − error = glm::max(error, _otherRagdolls[i]->enforceRagdollConstraints()); | |
| 219 | + error = glm::max(error, _otherRagdolls[i]->enforceConstraints()); | |
| 220 | 220 | } |
| 221 | 221 | } |
| 222 | − enforceContactConstraints(); | |
| 222 | + applyContactFriction(); | |
| 223 | 223 | ++iterations; |
| 224 | 224 | |
| 225 | 225 | now = usecTimestampNow(); |
| @@ -230,10 +230,10 @@ void PhysicsSimulation::stepForward(float deltaTime, float minError, int maxIter · 4 unmodified lines | ||
| 230 | 230 | |
| 231 | 231 | void PhysicsSimulation::moveRagdolls(float deltaTime) { |
| 232 | 232 | PerformanceTimer perfTimer("integrate"); |
| 233 | − _ragdoll->stepRagdollForward(deltaTime); | |
| 233 | + _ragdoll->stepForward(deltaTime); | |
| 234 | 234 | int numDolls = _otherRagdolls.size(); |
| 235 | 235 | for (int i = 0; i < numDolls; ++i) { |
| 236 | − _otherRagdolls[i]->stepRagdollForward(deltaTime); | |
| 236 | + _otherRagdolls[i]->stepForward(deltaTime); | |
| 237 | 237 | } |
| 238 | 238 | } |
| 239 | 239 | |
| @@ -288,20 +288,20 @@ void PhysicsSimulation::resolveCollisions() { · 48 unmodified lines | ||
| 288 | 288 | } |
| 289 | 289 | } |
| 290 | 290 | |
| 291 | −void PhysicsSimulation::buildContactConstraints() { | |
| 291 | +void PhysicsSimulation::enforceContacts() { | |
| 292 | 292 | PerformanceTimer perfTimer("contacts"); |
| 293 | 293 | QMap<quint64, ContactPoint>::iterator itr = _contacts.begin(); |
| 294 | 294 | while (itr != _contacts.end()) { |
| 295 | − itr.value().buildConstraints(); | |
| 295 | + itr.value().enforce(); | |
| 296 | 296 | ++itr; |
| 297 | 297 | } |
| 298 | 298 | } |
| 299 | 299 | |
| 300 | −void PhysicsSimulation::enforceContactConstraints() { | |
| 300 | +void PhysicsSimulation::applyContactFriction() { | |
| 301 | 301 | PerformanceTimer perfTimer("contacts"); |
| 302 | 302 | QMap<quint64, ContactPoint>::iterator itr = _contacts.begin(); |
| 303 | 303 | while (itr != _contacts.end()) { |
| 304 | − itr.value().enforce(); | |
| 304 | + itr.value().applyFriction(); | |
| 305 | 305 | ++itr; |
| 306 | 306 | } |
| 307 | 307 | } |
| @@ -56,8 +56,8 @@ protected: · 55 unmodified lines | ||
| 56 | 56 | void computeCollisions(); |
| 57 | 57 | void resolveCollisions(); |
| 58 | 58 | |
| 59 | − void buildContactConstraints(); | |
| 60 | − void enforceContactConstraints(); | |
| 59 | + void enforceContacts(); | |
| 60 | + void applyContactFriction(); | |
| 61 | 61 | void updateContacts(); |
| 62 | 62 | void pruneContacts(); |
| 63 | 63 | |
| @@ -19,24 +19,27 @@ · 18 unmodified lines | ||
| 19 | 19 | #include "PhysicsSimulation.h" |
| 20 | 20 | #include "SharedUtil.h" // for EPSILON |
| 21 | 21 | |
| 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) { | |
| 23 | 23 | } |
| 24 | 24 | |
| 25 | 25 | Ragdoll::~Ragdoll() { |
| 26 | − clearRagdollConstraintsAndPoints(); | |
| 26 | + clearConstraintsAndPoints(); | |
| 27 | + if (_simulation) { | |
| 28 | + _simulation->removeRagdoll(this); | |
| 29 | + } | |
| 27 | 30 | } |
| 28 | 31 | |
| 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); | |
| 32 | 35 | } |
| 33 | − int numPoints = _ragdollPoints.size(); | |
| 36 | + int numPoints = _points.size(); | |
| 34 | 37 | for (int i = 0; i < numPoints; ++i) { |
| 35 | − _ragdollPoints[i].integrateForward(); | |
| 38 | + _points[i].integrateForward(); | |
| 36 | 39 | } |
| 37 | 40 | } |
| 38 | 41 | |
| 39 | −void Ragdoll::clearRagdollConstraintsAndPoints() { | |
| 42 | +void Ragdoll::clearConstraintsAndPoints() { | |
| 40 | 43 | int numConstraints = _boneConstraints.size(); |
| 41 | 44 | for (int i = 0; i < numConstraints; ++i) { |
| 42 | 45 | delete _boneConstraints[i]; |
| @@ -47,10 +50,10 @@ void Ragdoll::clearRagdollConstraintsAndPoints() { · 4 unmodified lines | ||
| 47 | 50 | delete _fixedConstraints[i]; |
| 48 | 51 | } |
| 49 | 52 | _fixedConstraints.clear(); |
| 50 | − _ragdollPoints.clear(); | |
| 53 | + _points.clear(); | |
| 51 | 54 | } |
| 52 | 55 | |
| 53 | −float Ragdoll::enforceRagdollConstraints() { | |
| 56 | +float Ragdoll::enforceConstraints() { | |
| 54 | 57 | float maxDistance = 0.0f; |
| 55 | 58 | // enforce the bone constraints first |
| 56 | 59 | int numConstraints = _boneConstraints.size(); |
| @@ -65,16 +68,16 @@ float Ragdoll::enforceRagdollConstraints() { · 8 unmodified lines | ||
| 65 | 68 | return maxDistance; |
| 66 | 69 | } |
| 67 | 70 | |
| 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(); | |
| 71 | 74 | _translationInSimulationFrame = glm::vec3(0.0f); |
| 72 | 75 | _rotationInSimulationFrame = glm::quat(); |
| 73 | 76 | } |
| 74 | 77 | |
| 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; | |
| 78 | 81 | } |
| 79 | 82 | |
| 80 | 83 | 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 | ||
| 90 | 93 | glm::quat deltaRotation = rotation * glm::inverse(_rotationInSimulationFrame); |
| 91 | 94 | |
| 92 | 95 | // apply the deltas to all ragdollPoints |
| 93 | − int numPoints = _ragdollPoints.size(); | |
| 96 | + int numPoints = _points.size(); | |
| 94 | 97 | for (int i = 0; i < numPoints; ++i) { |
| 95 | − _ragdollPoints[i].move(deltaPosition, deltaRotation, _translationInSimulationFrame); | |
| 98 | + _points[i].move(deltaPosition, deltaRotation, _translationInSimulationFrame); | |
| 96 | 99 | } |
| 97 | 100 | |
| 98 | 101 | // remember the current transform |
| @@ -106,9 +109,9 @@ void Ragdoll::setMassScale(float scale) { · 7 unmodified lines | ||
| 106 | 109 | scale = glm::clamp(glm::abs(scale), MIN_SCALE, MAX_SCALE); |
| 107 | 110 | if (scale != _massScale) { |
| 108 | 111 | float rescale = scale / _massScale; |
| 109 | − int numPoints = _ragdollPoints.size(); | |
| 112 | + int numPoints = _points.size(); | |
| 110 | 113 | for (int i = 0; i < numPoints; ++i) { |
| 111 | − _ragdollPoints[i].setMass(rescale * _ragdollPoints[i].getMass()); | |
| 114 | + _points[i].setMass(rescale * _points[i].getMass()); | |
| 112 | 115 | } |
| 113 | 116 | _massScale = scale; |
| 114 | 117 | } |
| @@ -33,44 +33,44 @@ public: · 32 unmodified lines | ||
| 33 | 33 | Ragdoll(); |
| 34 | 34 | virtual ~Ragdoll(); |
| 35 | 35 | |
| 36 | − virtual void stepRagdollForward(float deltaTime); | |
| 36 | + virtual void stepForward(float deltaTime); | |
| 37 | 37 | |
| 38 | 38 | /// \return max distance of point movement |
| 39 | − float enforceRagdollConstraints(); | |
| 39 | + float enforceConstraints(); | |
| 40 | 40 | |
| 41 | 41 | // 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; } | |
| 44 | 44 | |
| 45 | − void initRagdollTransform(); | |
| 45 | + void initTransform(); | |
| 46 | 46 | |
| 47 | 47 | /// 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); | |
| 49 | 49 | |
| 50 | 50 | const glm::vec3& getTranslationInSimulationFrame() const { return _translationInSimulationFrame; } |
| 51 | 51 | |
| 52 | 52 | void setMassScale(float scale); |
| 53 | 53 | float getMassScale() const { return _massScale; } |
| 54 | 54 | |
| 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; | |
| 59 | 58 | |
| 59 | +protected: | |
| 60 | 60 | 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 | |
| 63 | 63 | glm::vec3 _translationInSimulationFrame; |
| 64 | 64 | glm::quat _rotationInSimulationFrame; |
| 65 | 65 | |
| 66 | − QVector<VerletPoint> _ragdollPoints; | |
| 66 | + QVector<VerletPoint> _points; | |
| 67 | 67 | QVector<DistanceConstraint*> _boneConstraints; |
| 68 | 68 | QVector<FixedConstraint*> _fixedConstraints; |
| 69 | 69 | private: |
| 70 | 70 | void updateSimulationTransforms(const glm::vec3& translation, const glm::quat& rotation); |
| 71 | 71 | |
| 72 | 72 | friend class PhysicsSimulation; |
| 73 | − PhysicsSimulation* _ragdollSimulation; | |
| 73 | + PhysicsSimulation* _simulation; | |
| 74 | 74 | }; |
| 75 | 75 | |
| 76 | 76 | #endif // hifi_Ragdoll_h |
| @@ -9,6 +9,7 @@ · 8 unmodified lines | ||
| 9 | 9 | // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html |
| 10 | 10 | // |
| 11 | 11 | |
| 12 | +#include <cassert> | |
| 12 | 13 | #include <cstdlib> |
| 13 | 14 | #include <cstdio> |
| 14 | 15 | #include <cstring> |
| @@ -79,40 +80,6 @@ bool shouldDo(float desiredInterval, float deltaTime) { · 64 unmodified lines | ||
| 79 | 80 | return randFloat() < deltaTime / desiredInterval; |
| 80 | 81 | } |
| 81 | 82 | |
| 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 | − | |
| 116 | 83 | void outputBufferBits(const unsigned char* buffer, int length, QDebug* continuedDebug) { |
| 117 | 84 | for (int i = 0; i < length; i++) { |
| 118 | 85 | outputBits(buffer[i], continuedDebug); |
| @@ -489,73 +456,6 @@ int removeFromSortedArrays(void* value, void** valueArray, float* keyArray, int* · 370 unmodified lines | ||
| 489 | 456 | return -1; // error case |
| 490 | 457 | } |
| 491 | 458 | |
| 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 | − | |
| 559 | 459 | float SMALL_LIMIT = 10.f; |
| 560 | 460 | float LARGE_LIMIT = 1000.f; |
| 561 | 461 | |
| @@ -651,199 +551,10 @@ void debug::checkDeadBeef(void* memoryVoid, int size) { · 89 unmodified lines | ||
| 651 | 551 | assert(memcmp((unsigned char*)memoryVoid, DEADBEEF, std::min(size, DEADBEEF_SIZE)) != 0); |
| 652 | 552 | } |
| 653 | 553 | |
| 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 | − | |
| 824 | 554 | bool isNaN(float value) { |
| 825 | 555 | return value != value; |
| 826 | 556 | } |
| 827 | 557 | |
| 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 | − | |
| 847 | 558 | QString formatUsecTime(float usecs, int prec) { |
| 848 | 559 | static const quint64 SECONDS_PER_MINUTE = 60; |
| 849 | 560 | static const quint64 USECS_PER_MINUTE = USECS_PER_SECOND * SECONDS_PER_MINUTE; |
| @@ -19,9 +19,6 @@ · 18 unmodified lines | ||
| 19 | 19 | #include <unistd.h> // not on windows, not needed for mac or windows |
| 20 | 20 | #endif |
| 21 | 21 | |
| 22 | −#include <glm/glm.hpp> | |
| 23 | −#include <glm/gtc/quaternion.hpp> | |
| 24 | − | |
| 25 | 22 | #include <QtCore/QDebug> |
| 26 | 23 | |
| 27 | 24 | const int BYTES_PER_COLOR = 3; |
| @@ -71,8 +68,6 @@ float randomSign(); /// \return -1.0 or 1.0 · 43 unmodified lines | ||
| 71 | 68 | unsigned char randomColorValue(int minimum = 0); |
| 72 | 69 | bool randomBoolean(); |
| 73 | 70 | |
| 74 | −glm::quat safeMix(const glm::quat& q1, const glm::quat& q2, float alpha); | |
| 75 | − | |
| 76 | 71 | bool shouldDo(float desiredInterval, float deltaTime); |
| 77 | 72 | |
| 78 | 73 | 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 | ||
| 108 | 103 | int removeFromSortedArrays(void* value, void** valueArray, float* keyArray, int* originalIndexArray, |
| 109 | 104 | int currentCount, int maxCount); |
| 110 | 105 | |
| 111 | − | |
| 112 | − | |
| 113 | 106 | // Helper Class for debugging |
| 114 | 107 | class debug { |
| 115 | 108 | public: |
| @@ -124,71 +117,9 @@ private: · 8 unmodified lines | ||
| 124 | 117 | bool isBetween(int64_t value, int64_t max, int64_t min); |
| 125 | 118 | |
| 126 | 119 | |
| 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 | − | |
| 187 | 120 | /// \return bool is the float NaN |
| 188 | 121 | bool isNaN(float value); |
| 189 | 122 | |
| 190 | −QByteArray createByteArray(const glm::vec3& vector); | |
| 191 | − | |
| 192 | 123 | QString formatUsecTime(float usecs, int prec = 3); |
| 193 | 124 | |
| 194 | 125 | |
| @@ -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 | − | |
| 7 | 1 | set(TARGET_NAME voxels) |
| 8 | 2 | |
| 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) | |
| 13 | 5 | |
| 14 | −include(${MACRO_DIR}/IncludeGLM.cmake) | |
| 15 | −include_glm(${TARGET_NAME} "${ROOT_DIR}") | |
| 6 | +include_glm() | |
| 16 | 7 | |
| 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) | |
| 24 | 9 | |
| 10 | +# find ZLIB | |
| 11 | +find_package(ZLIB REQUIRED) | |
| 25 | 12 | include_directories(SYSTEM "${ZLIB_INCLUDE_DIRS}") |
| 26 | −target_link_libraries(${TARGET_NAME} "${ZLIB_LIBRARIES}" Qt5::Widgets Qt5::Script) | |
| 27 | 13 | |
| 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() | |
| @@ -28,7 +28,7 @@ public: · 27 unmodified lines | ||
| 28 | 28 | /// Queues a single voxel edit message. Will potentially send a pending multi-command packet. Determines which voxel-server |
| 29 | 29 | /// node or nodes the packet should be sent to. Can be called even before voxel servers are known, in which case up to |
| 30 | 30 | /// 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) { | |
| 32 | 32 | queueOctreeEditMessage(type, codeColorBuffer, length); |
| 33 | 33 | } |
| 34 | 34 | |
| @@ -1,34 +1,10 @@ | ||
| 1 | 1 | set(TARGET_NAME audio-tests) |
| 2 | 2 | |
| 3 | −set(ROOT_DIR ../..) | |
| 4 | −set(MACRO_DIR ${ROOT_DIR}/cmake/macros) | |
| 3 | +setup_hifi_project() | |
| 5 | 4 | |
| 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() | |
| 24 | 6 | |
| 25 | 7 | # 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) | |
| 34 | 9 | |
| 10 | +link_shared_dependencies() | |
| @@ -1,33 +1,8 @@ | ||
| 1 | 1 | set(TARGET_NAME jitter-tests) |
| 2 | 2 | |
| 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() | |
| 24 | 4 | |
| 25 | 5 | # 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) | |
| 33 | 7 | |
| 8 | +link_shared_dependencies() | |
| @@ -13,12 +13,17 @@ · 12 unmodified lines | ||
| 13 | 13 | #include <sys/socket.h> |
| 14 | 14 | #include <arpa/inet.h> |
| 15 | 15 | #endif |
| 16 | +#include <cerrno> | |
| 16 | 17 | #include <stdio.h> |
| 17 | 18 | |
| 18 | −#include <MovingMinMaxAvg.h> // for MovingMinMaxAvg | |
| 19 | +#include <MovingMinMaxAvg.h> | |
| 20 | +#include <SequenceNumberStats.h> | |
| 19 | 21 | #include <SharedUtil.h> // for usecTimestampNow |
| 22 | +#include <SimpleMovingAverage.h> | |
| 23 | +#include <StdDev.h> | |
| 20 | 24 | |
| 21 | 25 | 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 | |
| 22 | 27 | |
| 23 | 28 | void runSend(const char* addressOption, int port, int gap, int size, int report); |
| 24 | 29 | 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 | ||
| 56 | 61 | void runSend(const char* addressOption, int port, int gap, int size, int report) { |
| 57 | 62 | std::cout << "runSend...\n"; |
| 58 | 63 | |
| 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 | + | |
| 59 | 72 | int sockfd; |
| 60 | 73 | 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 | + | |
| 67 | 77 | memset(&servaddr, 0, sizeof(servaddr)); |
| 68 | 78 | 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); | |
| 73 | 81 | |
| 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; | |
| 74 | 87 | 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; | |
| 75 | 113 | |
| 76 | − MovingMinMaxAvg<int> timeGaps(1, SAMPLES_FOR_30_SECONDS); // stats | |
| 77 | − | |
| 78 | 114 | quint64 last = usecTimestampNow(); |
| 79 | 115 | quint64 lastReport = 0; |
| 80 | − | |
| 116 | + | |
| 81 | 117 | while (true) { |
| 82 | 118 | |
| 83 | 119 | quint64 now = usecTimestampNow(); |
| 84 | 120 | int actualGap = now - last; |
| 85 | − | |
| 86 | − | |
| 121 | + | |
| 122 | + | |
| 87 | 123 | 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 | + | |
| 90 | 140 | int gapDifferece = actualGap - gap; |
| 141 | + | |
| 91 | 142 | timeGaps.update(gapDifferece); |
| 143 | + timeGapsPerReport.update(gapDifferece); | |
| 144 | + stDev.addValue(gapDifferece); | |
| 145 | + stDev30s.addValue(gapDifferece); | |
| 146 | + stDevReportInterval.addValue(gapDifferece); | |
| 92 | 147 | 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 | + } | |
| 93 | 159 | |
| 94 | 160 | 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 | + | |
| 103 | 189 | lastReport = now; |
| 104 | 190 | } |
| 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 | + | |
| 105 | 199 | } |
| 106 | 200 | } |
| 201 | + delete[] outputBuffer; | |
| 202 | + | |
| 203 | +#ifdef _WIN32 | |
| 204 | + WSACleanup(); | |
| 205 | +#endif | |
| 107 | 206 | } |
| 108 | 207 | |
| 109 | 208 | void runReceive(const char* addressOption, int port, int gap, int size, int report) { |
| 110 | 209 | std::cout << "runReceive...\n"; |
| 111 | 210 | |
| 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 | |
| 112 | 218 | |
| 113 | − int sockfd,n; | |
| 219 | + int sockfd, n; | |
| 114 | 220 | 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 | + | |
| 121 | 224 | memset(&myaddr, 0, sizeof(myaddr)); |
| 122 | 225 | 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); | |
| 125 | 228 | |
| 126 | − const int SAMPLES_FOR_30_SECONDS = 30 * 1000000 / gap; | |
| 127 | 229 | |
| 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; | |
| 128 | 235 | 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; | |
| 129 | 261 | |
| 130 | − MovingMinMaxAvg<int> timeGaps(1, SAMPLES_FOR_30_SECONDS); // stats | |
| 131 | − | |
| 132 | 262 | if (bind(sockfd, (struct sockaddr *)&myaddr, sizeof(myaddr)) < 0) { |
| 133 | 263 | std::cout << "bind failed\n"; |
| 134 | 264 | return; |
| 135 | − } | |
| 136 | − | |
| 265 | + } | |
| 266 | + | |
| 137 | 267 | quint64 last = 0; // first case |
| 138 | 268 | quint64 lastReport = 0; |
| 139 | 269 | |
| 140 | 270 | while (true) { |
| 271 | + | |
| 272 | + quint64 networkStart = usecTimestampNow(); | |
| 141 | 273 | 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 | + | |
| 143 | 285 | if (last == 0) { |
| 144 | 286 | last = usecTimestampNow(); |
| 145 | 287 | std::cout << "first packet received\n"; |
| 146 | 288 | } else { |
| 289 | + | |
| 290 | + quint64 statsCalcultionStart = usecTimestampNow(); | |
| 147 | 291 | quint64 now = usecTimestampNow(); |
| 148 | 292 | int actualGap = now - last; |
| 293 | + | |
| 149 | 294 | int gapDifferece = actualGap - gap; |
| 150 | 295 | timeGaps.update(gapDifferece); |
| 296 | + timeGapsPerReport.update(gapDifferece); | |
| 297 | + stDev.addValue(gapDifferece); | |
| 298 | + stDev30s.addValue(gapDifferece); | |
| 299 | + stDevReportInterval.addValue(gapDifferece); | |
| 151 | 300 | last = now; |
| 152 | 301 | |
| 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 | + | |
| 153 | 313 | 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 | + | |
| 162 | 358 | lastReport = now; |
| 163 | 359 | } |
| 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; | |
| 164 | 368 | } |
| 165 | 369 | } |
| 166 | −} | |
| 370 | + delete[] inputBuffer; | |
| 167 | 371 | |
| 372 | +#ifdef _WIN32 | |
| 373 | + WSACleanup(); | |
| 374 | +#endif | |
| 375 | +} | |
| @@ -1,32 +1,12 @@ | ||
| 1 | 1 | set(TARGET_NAME metavoxel-tests) |
| 2 | 2 | |
| 3 | −set(ROOT_DIR ../..) | |
| 4 | −set(MACRO_DIR "${ROOT_DIR}/cmake/macros") | |
| 3 | +auto_mtc() | |
| 5 | 4 | |
| 6 | −# setup for find modules | |
| 7 | −set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/../../cmake/modules/") | |
| 5 | +include_glm() | |
| 8 | 6 | |
| 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) | |
| 20 | 8 | |
| 21 | 9 | # 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) | |
| 32 | 11 | |
| 12 | +link_shared_dependencies() | |
| @@ -1,33 +1,8 @@ | ||
| 1 | 1 | set(TARGET_NAME networking-tests) |
| 2 | 2 | |
| 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() | |
| 24 | 4 | |
| 25 | 5 | # 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) | |
| 33 | 7 | |
| 8 | +link_shared_dependencies() | |
| @@ -9,11 +9,12 @@ · 8 unmodified lines | ||
| 9 | 9 | // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html |
| 10 | 10 | // |
| 11 | 11 | |
| 12 | −#include "SequenceNumberStatsTests.h" | |
| 13 | − | |
| 14 | −#include "SharedUtil.h" | |
| 12 | +#include <cassert> | |
| 15 | 13 | #include <limits> |
| 16 | 14 | |
| 15 | +#include <SharedUtil.h> | |
| 16 | + | |
| 17 | +#include "SequenceNumberStatsTests.h" | |
| 17 | 18 | |
| 18 | 19 | void SequenceNumberStatsTests::runAllTests() { |
| 19 | 20 | rolloverTest(); |
| @@ -1,44 +1,19 @@ | ||
| 1 | 1 | set(TARGET_NAME octree-tests) |
| 2 | 2 | |
| 3 | −set(ROOT_DIR ../..) | |
| 4 | −set(MACRO_DIR ${ROOT_DIR}/cmake/macros) | |
| 3 | +setup_hifi_project(Script Network) | |
| 5 | 4 | |
| 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() | |
| 24 | 6 | |
| 25 | 7 | # 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() | |
| @@ -1,32 +1,10 @@ | ||
| 1 | 1 | set(TARGET_NAME physics-tests) |
| 2 | 2 | |
| 3 | −set(ROOT_DIR ../..) | |
| 4 | −set(MACRO_DIR ${ROOT_DIR}/cmake/macros) | |
| 3 | +setup_hifi_project() | |
| 5 | 4 | |
| 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() | |
| 24 | 6 | |
| 25 | 7 | # 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) | |
| 32 | 9 | |
| 10 | +link_shared_dependencies() | |
| @@ -1,32 +1,10 @@ | ||
| 1 | 1 | set(TARGET_NAME shared-tests) |
| 2 | 2 | |
| 3 | −set(ROOT_DIR ../..) | |
| 4 | −set(MACRO_DIR ${ROOT_DIR}/cmake/macros) | |
| 3 | +setup_hifi_project() | |
| 5 | 4 | |
| 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() | |
| 24 | 6 | |
| 25 | 7 | # 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) | |
| 32 | 9 | |
| 10 | +link_shared_dependencies() | |
| @@ -1,4 +1,3 @@ | ||
| 1 | − | |
| 2 | 1 | # add the tool directories |
| 3 | 2 | add_subdirectory(bitstream2json) |
| 4 | 3 | add_subdirectory(json2bitstream) |
| @@ -1,25 +1,8 @@ | ||
| 1 | 1 | set(TARGET_NAME bitstream2json) |
| 2 | +setup_hifi_project(Widgets Script) | |
| 2 | 3 | |
| 3 | −set(ROOT_DIR ../..) | |
| 4 | −set(MACRO_DIR "${ROOT_DIR}/cmake/macros") | |
| 4 | +include_glm() | |
| 5 | 5 | |
| 6 | −# setup for find modules | |
| 7 | −set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/../../cmake/modules/") | |
| 6 | +link_hifi_libraries(metavoxels) | |
| 8 | 7 | |
| 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() | |
| @@ -1,25 +1,8 @@ | ||
| 1 | 1 | set(TARGET_NAME json2bitstream) |
| 2 | +setup_hifi_project(Widgets Script) | |
| 2 | 3 | |
| 3 | −set(ROOT_DIR ../..) | |
| 4 | −set(MACRO_DIR "${ROOT_DIR}/cmake/macros") | |
| 4 | +include_glm() | |
| 5 | 5 | |
| 6 | −# setup for find modules | |
| 7 | −set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/../../cmake/modules/") | |
| 6 | +link_hifi_libraries(metavoxels) | |
| 8 | 7 | |
| 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() | |
| @@ -1,7 +1,4 @@ | ||
| 1 | 1 | set(TARGET_NAME mtc) |
| 2 | +setup_hifi_project() | |
| 2 | 3 | |
| 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() | |
| @@ -1,40 +1,9 @@ | ||
| 1 | 1 | set(TARGET_NAME voxel-edit) |
| 2 | 2 | |
| 3 | −set(ROOT_DIR ..) | |
| 4 | −set(MACRO_DIR "${ROOT_DIR}/cmake/macros") | |
| 3 | +setup_hifi_project() | |
| 5 | 4 | |
| 6 | −# setup for find modules | |
| 7 | −set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/../cmake/modules/") | |
| 5 | +include_glm() | |
| 8 | 6 | |
| 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) | |
| 12 | 8 | |
| 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() | |