From 8538c700b5dfc16a0582aa5c0d33a5585887a722 Mon Sep 17 00:00:00 2001 From: Dante Ruiz Date: Thu, 25 May 2017 21:41:41 +0100 Subject: [PATCH 01/13] saving head work --- interface/resources/controllers/vive.json | 22 ++++---- plugins/openvr/src/ViveControllerManager.cpp | 53 ++++++++++++++++++-- plugins/openvr/src/ViveControllerManager.h | 26 +++++----- 3 files changed, 70 insertions(+), 31 deletions(-) diff --git a/interface/resources/controllers/vive.json b/interface/resources/controllers/vive.json index 591b6f515e..984f9c65fb 100644 --- a/interface/resources/controllers/vive.json +++ b/interface/resources/controllers/vive.json @@ -34,36 +34,32 @@ { "from": "Vive.RSCenter", "to": "Standard.RightPrimaryThumb" }, { "from": "Vive.RightApplicationMenu", "to": "Standard.RightSecondaryThumb" }, - { "from": "Vive.LeftHand", "to": "Standard.LeftHand", "when": [ "Application.InHMD" ] }, - { "from": "Vive.RightHand", "to": "Standard.RightHand", "when": [ "Application.InHMD" ] }, + { "from": "Vive.LeftHand", "to": "Standard.LeftHand" }, + { "from": "Vive.RightHand", "to": "Standard.RightHand" }, { "from": "Vive.LeftFoot", "to" : "Standard.LeftFoot", - "filters" : [{"type" : "lowVelocity", "rotation" : 1.0, "translation": 1.0}], - "when": [ "Application.InHMD"] + "filters" : [{"type" : "lowVelocity", "rotation" : 1.0, "translation": 1.0}] }, { "from": "Vive.RightFoot", "to" : "Standard.RightFoot", - "filters" : [{"type" : "lowVelocity", "rotation" : 1.0, "translation": 1.0}], - "when": [ "Application.InHMD"] + "filters" : [{"type" : "lowVelocity", "rotation" : 1.0, "translation": 1.0}] }, { "from": "Vive.Hips", "to" : "Standard.Hips", - "filters" : [{"type" : "lowVelocity", "rotation" : 0.01, "translation": 0.01}], - "when": [ "Application.InHMD"] + "filters" : [{"type" : "lowVelocity", "rotation" : 0.01, "translation": 0.01}] }, { "from": "Vive.Spine2", "to" : "Standard.Spine2", - "filters" : [{"type" : "lowVelocity", "rotation" : 0.01, "translation": 0.01}], - "when": [ "Application.InHMD"] + "filters" : [{"type" : "lowVelocity", "rotation" : 0.01, "translation": 0.01}] }, - { "from": "Vive.Head", "to" : "Standard.Head", "when" : [ "Application.InHMD"] }, + { "from": "Vive.Head", "to" : "Standard.Head"}, - { "from": "Vive.RightArm", "to" : "Standard.RightArm", "when" : [ "Application.InHMD"] }, - { "from": "Vive.LeftArm", "to" : "Standard.LeftArm", "when" : [ "Application.InHMD"] } + { "from": "Vive.RightArm", "to" : "Standard.RightArm"}, + { "from": "Vive.LeftArm", "to" : "Standard.LeftArm"} ] } diff --git a/plugins/openvr/src/ViveControllerManager.cpp b/plugins/openvr/src/ViveControllerManager.cpp index 016b2a1c55..a9bc066dc2 100644 --- a/plugins/openvr/src/ViveControllerManager.cpp +++ b/plugins/openvr/src/ViveControllerManager.cpp @@ -51,8 +51,7 @@ static const int MIN_PUCK_COUNT = 2; static const int MIN_FEET_AND_HIPS = 3; static const int MIN_FEET_HIPS_CHEST = 4; static const int MIN_FEET_HIPS_SHOULDERS = 5; -static const int MIN_FEET_HIPS_CHEST_AND_HANDS = 6; -static const int MIN_FEET_HIPS_SHOULDERS_AND_HANDS = 7; +static const int MIN_FEET_HIPS_CHEST_HEAD = 5; static const int FIRST_FOOT = 0; static const int SECOND_FOOT = 1; static const int HIP = 2; @@ -177,6 +176,7 @@ ViveControllerManager::InputDevice::InputDevice(vr::IVRSystem*& system) : contro _configStringMap[Config::FeetAndHips] = QString("FeetAndHips"); _configStringMap[Config::FeetHipsAndChest] = QString("FeetHipsAndChest"); _configStringMap[Config::FeetHipsAndShoulders] = QString("FeetHipsAndShoulders"); + _configStringMap[Config::FeetHipsChestAndHead] = QString("FeetHipsChestAndHead"); if (openVrSupported()) { createPreferences(); @@ -351,12 +351,18 @@ void ViveControllerManager::InputDevice::calibrate(const controller::InputCalibr calibrateFeet(defaultToReferenceMat, inputCalibration); calibrateHips(defaultToReferenceMat, inputCalibration); calibrateChest(defaultToReferenceMat, inputCalibration); - } else if (_config == Config::FeetHipsAndShoulders && puckCount >= MIN_FEET_HIPS_SHOULDERS){ + } else if (_config == Config::FeetHipsAndShoulders && puckCount >= MIN_FEET_HIPS_SHOULDERS) { calibrateFeet(defaultToReferenceMat, inputCalibration); calibrateHips(defaultToReferenceMat, inputCalibration); int firstShoulderIndex = 3; int secondShoulderIndex = 4; calibrateShoulders(defaultToReferenceMat, inputCalibration, firstShoulderIndex, secondShoulderIndex); + } else if (_config == Config::FeetHipsChestAndHead && puckCount == MIN_FEET_HIPS_CHEST_HEAD) { + glm::mat4 headPuckDefaultToReferenceMat = recalculateDefaultToReferenceForHeadPuck(inputCalibration); + calibrateFeet(headPuckDefaultToReferenceMat, inputCalibration); + calibrateHips(headPuckDefaultToReferenceMat, inputCalibration); + calibrateChest(headPuckDefaultToReferenceMat, inputCalibration); + calibrateHead(headPuckDefaultToReferenceMat, inputCalibration); } else { qDebug() << "Puck Calibration: " << configToString(_config) << " Config Failed: Could not meet the minimal # of pucks"; uncalibrate(); @@ -371,6 +377,7 @@ void ViveControllerManager::InputDevice::uncalibrate() { _pucksOffset.clear(); _jointToPuckMap.clear(); _calibrated = false; + _overrideHead = false; } void ViveControllerManager::InputDevice::updateCalibratedLimbs() { @@ -380,6 +387,10 @@ void ViveControllerManager::InputDevice::updateCalibratedLimbs() { _poseStateMap[controller::SPINE2] = addOffsetToPuckPose(controller::SPINE2); _poseStateMap[controller::RIGHT_ARM] = addOffsetToPuckPose(controller::RIGHT_ARM); _poseStateMap[controller::LEFT_ARM] = addOffsetToPuckPose(controller::LEFT_ARM); + + if (_overrideHead) { + _poseStateMap[controller::HEAD] = addOffsetToPuckPose(controller::HEAD); + } } controller::Pose ViveControllerManager::InputDevice::addOffsetToPuckPose(int joint) const { @@ -445,6 +456,27 @@ void ViveControllerManager::InputDevice::handleHandController(float deltaTime, u } } +glm::mat4 ViveControllerManager::InputDevice::recalculateDefaultToReferenceForHeadPuck(const controller::InputCalibrationData& inputCalibration) { + size_t headPuckIndex = _validTrackedObjects.size() - 1; + controller::Pose headPuckPose = _validTrackedObjects[headPuckIndex].second; + + // make the head puck rotation to match the default head rotation + glm::quat headPuckRotation = cancelOutRollAndPitch(headPuckPose.getRotation()); + glm::quat defaultHeadRotation = glmExtractRotation(inputCalibration.defaultHeadMat); + glm::quat defaultRotationOffset = glm::inverse(headPuckRotation) * defaultHeadRotation; + glm::quat finalHeadPuckRotation = defaultRotationOffset * headPuckRotation; + glm::vec3 headPuckTranslation = headPuckPose.getTranslation(); + glm::mat4 headPuckMat = createMatFromQuatAndPos(finalHeadPuckRotation, headPuckTranslation); + + // calculate the offset from the centerOfEye to defaultHeadMat + glm::mat4 defaultHeadOffset = glm::inverse(inputCalibration.defaultCenterEyeMat) * inputCalibration.defaultHeadMat; + + glm::mat4 currentHead = headPuckMat * defaultHeadOffset; + + // calculate the defaultToRefrenceXform + return currentHead * glm::inverse(inputCalibration.defaultHeadMat); +} + void ViveControllerManager::InputDevice::partitionTouchpad(int sButton, int xAxis, int yAxis, int centerPseudoButton, int xPseudoButton, int yPseudoButton) { // Populate the L/RS_CENTER/OUTER pseudo buttons, corresponding to a partition of the L/RS space based on the X/Y values. const float CENTER_DEADBAND = 0.6f; @@ -664,7 +696,16 @@ void ViveControllerManager::InputDevice::calibrateShoulders(glm::mat4& defaultTo } void ViveControllerManager::InputDevice::calibrateHead(glm::mat4& defaultToReferenceMat, const controller::InputCalibrationData& inputCalibration) { - int headIndex = _validTrackedObjects.size() - 1; + size_t headIndex = _validTrackedObjects.size() - 1; + const PuckPosePair& head = _validTrackedObjects[headIndex]; + + // assume the person is wearing the head puck on his/her forehead + glm::mat4 defaultHeadOffset = glm::inverse(inputCalibration.defaultCenterEyeMat) * inputCalibration.defaultHeadMat; + controller::Pose newHead = head.second.postTransform(defaultHeadOffset); + + _jointToPuckMap[controller::HEAD] = head.first; + _pucksOffset[head.first] = computeOffset(defaultToReferenceMat, inputCalibration.defaultHeadMat, newHead); + _overrideHead = true; } @@ -701,6 +742,8 @@ void ViveControllerManager::InputDevice::setConfigFromString(const QString& valu _preferedConfig = Config::FeetHipsAndChest; } else if (value == "FeetHipsAndShoulders") { _preferedConfig = Config::FeetHipsAndShoulders; + } else if (value == "FeetHipsChestAndHead") { + _preferedConfig = Config::FeetHipsChestAndHead; } } @@ -713,7 +756,7 @@ void ViveControllerManager::InputDevice::createPreferences() { auto getter = [this]()->QString { return _configStringMap[_preferedConfig]; }; auto setter = [this](const QString& value) { setConfigFromString(value); saveSettings(); }; auto preference = new ComboBoxPreference(VIVE_PUCKS_CONFIG, "Configuration", getter, setter); - QStringList list = (QStringList() << "Auto" << "Feet" << "FeetAndHips" << "FeetHipsAndChest" << "FeetHipsAndShoulders"); + QStringList list = (QStringList() << "Feet" << "FeetAndHips" << "FeetHipsAndChest" << "FeetHipsAndShoulders" << "FeetHipsChestAndHead"); preference->setItems(list); preferences->addPreference(preference); diff --git a/plugins/openvr/src/ViveControllerManager.h b/plugins/openvr/src/ViveControllerManager.h index bc9558beaa..f3b87ea3a0 100644 --- a/plugins/openvr/src/ViveControllerManager.h +++ b/plugins/openvr/src/ViveControllerManager.h @@ -67,6 +67,7 @@ private: void calibrate(const controller::InputCalibrationData& inputCalibration); void uncalibrate(); controller::Pose addOffsetToPuckPose(int joint) const; + glm::mat4 recalculateDefaultToReferenceForHeadPuck(const controller::InputCalibrationData& inputCalibration); void updateCalibratedLimbs(); bool checkForCalibrationEvent(); void handleHandController(float deltaTime, uint32_t deviceIndex, const controller::InputCalibrationData& inputCalibrationData, bool isLeftHand); @@ -80,6 +81,17 @@ private: const vec3& angularVelocity); void partitionTouchpad(int sButton, int xAxis, int yAxis, int centerPsuedoButton, int xPseudoButton, int yPseudoButton); void printDeviceTrackingResultChange(uint32_t deviceIndex); + void setConfigFromString(const QString& value); + void loadSettings(); + void saveSettings() const; + void calibrateFeet(glm::mat4& defaultToReferenceMat, const controller::InputCalibrationData& inputCalibration); + void calibrateHips(glm::mat4& defaultToReferenceMat, const controller::InputCalibrationData& inputCalibration); + void calibrateChest(glm::mat4& defaultToReferenceMat, const controller::InputCalibrationData& inputCalibration); + + void calibrateShoulders(glm::mat4& defaultToReferenceMat, const controller::InputCalibrationData& inputCalibration, + int firstShoulderIndex, int secondShoulderIndex); + + void calibrateHead(glm::mat4& defaultToReferenceMat, const controller::InputCalibrationData& inputCalibration); class FilteredStick { public: @@ -106,9 +118,7 @@ private: }; enum class Config { Auto, - Head, Feet, - Shoulders, FeetAndHips, FeetHipsAndChest, FeetHipsAndShoulders, @@ -139,21 +149,11 @@ private: bool _triggersPressedHandled { false }; bool _calibrated { false }; bool _timeTilCalibrationSet { false }; - bool overrideHands { false }; + bool _overrideHead { false }; mutable std::recursive_mutex _lock; QString configToString(Config config); - void setConfigFromString(const QString& value); - void loadSettings(); - void saveSettings() const; - void calibrateFeet(glm::mat4& defaultToReferenceMat, const controller::InputCalibrationData& inputCalibration); - void calibrateHips(glm::mat4& defaultToReferenceMat, const controller::InputCalibrationData& inputCalibration); - void calibrateChest(glm::mat4& defaultToReferenceMat, const controller::InputCalibrationData& inputCalibration); - void calibrateShoulders(glm::mat4& defaultToReferenceMat, const controller::InputCalibrationData& inputCalibration, - int firstShoulderIndex, int secondShoulderIndex); - - void calibrateHead(glm::mat4& defaultToReferenceMat, const controller::InputCalibrationData& inputCalibration); friend class ViveControllerManager; }; From 765bd3890eee93e7af0a5b516f96c22922443065 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Tue, 30 May 2017 10:00:30 -0700 Subject: [PATCH 02/13] Improving test code for KTX library --- libraries/ktx/src/khronos/KHR.h | 2 + tests/ktx/CMakeLists.txt | 20 +-- tests/ktx/src/KtxTests.cpp | 195 ++++++++++++++++++++++++++++ tests/ktx/src/KtxTests.h | 21 +++ tests/ktx/src/main.cpp | 223 -------------------------------- 5 files changed, 225 insertions(+), 236 deletions(-) create mode 100644 tests/ktx/src/KtxTests.cpp create mode 100644 tests/ktx/src/KtxTests.h delete mode 100644 tests/ktx/src/main.cpp diff --git a/libraries/ktx/src/khronos/KHR.h b/libraries/ktx/src/khronos/KHR.h index a98f2cc0d4..98cc1a4736 100644 --- a/libraries/ktx/src/khronos/KHR.h +++ b/libraries/ktx/src/khronos/KHR.h @@ -211,6 +211,8 @@ namespace khronos { template inline uint32_t evalAlignedCompressedBlockCount(uint32_t value) { + enum { val = ALIGNMENT && !(ALIGNMENT & (ALIGNMENT - 1)) }; + static_assert(val, "template parameter ALIGNMENT must be a power of 2"); // FIXME add static assert that ALIGNMENT is a power of 2 static uint32_t ALIGNMENT_REMAINDER = ALIGNMENT - 1; return (value + ALIGNMENT_REMAINDER) / ALIGNMENT; diff --git a/tests/ktx/CMakeLists.txt b/tests/ktx/CMakeLists.txt index 7133c30898..599435a90b 100644 --- a/tests/ktx/CMakeLists.txt +++ b/tests/ktx/CMakeLists.txt @@ -1,15 +1,9 @@ +# Declare dependencies +macro (SETUP_TESTCASE_DEPENDENCIES) + # link in the shared libraries + link_hifi_libraries(shared ktx gpu image) -set(TARGET_NAME ktx-test) - -if (WIN32) - SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /ignore:4049 /ignore:4217") -endif() + package_libraries_for_deployment() +endmacro () -# This is not a testcase -- just set it up as a regular hifi project -setup_hifi_project(Quick Gui OpenGL) -set_target_properties(${TARGET_NAME} PROPERTIES FOLDER "Tests/manual-tests/") - -# link in the shared libraries -link_hifi_libraries(shared octree ktx gl gpu gpu-gl render model model-networking networking render-utils fbx entities entities-renderer animation audio avatars script-engine physics image) - -package_libraries_for_deployment() +setup_hifi_testcase() diff --git a/tests/ktx/src/KtxTests.cpp b/tests/ktx/src/KtxTests.cpp new file mode 100644 index 0000000000..94e5d7e8e7 --- /dev/null +++ b/tests/ktx/src/KtxTests.cpp @@ -0,0 +1,195 @@ +// +// Created by Bradley Austin Davis on 2016/07/01 +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include "KtxTests.h" + +#include + +#include + +#include +#include +#include + + +QTEST_GUILESS_MAIN(KtxTests) + +QString getRootPath() { + static std::once_flag once; + static QString result; + std::call_once(once, [&] { + QFileInfo file(__FILE__); + QDir parent = file.absolutePath(); + result = QDir::cleanPath(parent.currentPath() + "/../../.."); + }); + return result; +} + +void KtxTests::initTestCase() { +} + +void KtxTests::cleanupTestCase() { +} + +void KtxTests::testKhronosCompressionFunctions() { + using namespace khronos::gl::texture; + QCOMPARE(evalAlignedCompressedBlockCount<4>(0), (uint32_t)0x0); + QCOMPARE(evalAlignedCompressedBlockCount<4>(1), (uint32_t)0x1); + QCOMPARE(evalAlignedCompressedBlockCount<4>(4), (uint32_t)0x1); + QCOMPARE(evalAlignedCompressedBlockCount<4>(5), (uint32_t)0x2); + QCOMPARE(evalCompressedBlockCount(InternalFormat::COMPRESSED_SRGB_S3TC_DXT1_EXT, 0x00), (uint32_t)0x00); + QCOMPARE(evalCompressedBlockCount(InternalFormat::COMPRESSED_SRGB_S3TC_DXT1_EXT, 0x01), (uint32_t)0x01); + QCOMPARE(evalCompressedBlockCount(InternalFormat::COMPRESSED_SRGB_S3TC_DXT1_EXT, 0x04), (uint32_t)0x01); + QCOMPARE(evalCompressedBlockCount(InternalFormat::COMPRESSED_SRGB_S3TC_DXT1_EXT, 0x05), (uint32_t)0x02); + QCOMPARE(evalCompressedBlockCount(InternalFormat::COMPRESSED_SRGB_S3TC_DXT1_EXT, 0x1000), (uint32_t)0x400); + + QVERIFY_EXCEPTION_THROWN(evalCompressedBlockCount(InternalFormat::RGBA8, 0x00), std::runtime_error); +} + +void KtxTests::testKtxEvalFunctions() { + QCOMPARE(sizeof(ktx::Header), (size_t)64); + QCOMPARE(ktx::evalPadding(0x0), (uint8_t)0); + QCOMPARE(ktx::evalPadding(0x1), (uint8_t)3); + QCOMPARE(ktx::evalPadding(0x2), (uint8_t)2); + QCOMPARE(ktx::evalPadding(0x3), (uint8_t)1); + QCOMPARE(ktx::evalPadding(0x4), (uint8_t)0); + QCOMPARE(ktx::evalPadding(0x400), (uint8_t)0); + QCOMPARE(ktx::evalPadding(0x401), (uint8_t)3); + QCOMPARE(ktx::evalPaddedSize(0x0), 0x0); + QCOMPARE(ktx::evalPaddedSize(0x1), 0x4); + QCOMPARE(ktx::evalPaddedSize(0x2), 0x4); + QCOMPARE(ktx::evalPaddedSize(0x3), 0x4); + QCOMPARE(ktx::evalPaddedSize(0x4), 0x4); + QCOMPARE(ktx::evalPaddedSize(0x400), 0x400); + QCOMPARE(ktx::evalPaddedSize(0x401), 0x404); + QCOMPARE(ktx::evalAlignedCount((uint32_t)0x0), (uint32_t)0x0); + QCOMPARE(ktx::evalAlignedCount((uint32_t)0x1), (uint32_t)0x1); + QCOMPARE(ktx::evalAlignedCount((uint32_t)0x4), (uint32_t)0x1); + QCOMPARE(ktx::evalAlignedCount((uint32_t)0x5), (uint32_t)0x2); +} + +void KtxTests::testKtxSerialization() { + const QString TEST_IMAGE = getRootPath() + "/scripts/developer/tests/cube_texture.png"; + QImage image(TEST_IMAGE); + gpu::TexturePointer testTexture = image::TextureUsage::process2DTextureColorFromImage(image, TEST_IMAGE.toStdString(), true); + auto ktxMemory = gpu::Texture::serialize(*testTexture); + QVERIFY(ktxMemory.get()); + + // Serialize the image to a file + QTemporaryFile TEST_IMAGE_KTX; + { + const auto& ktxStorage = ktxMemory->getStorage(); + QVERIFY(ktx::KTX::validate(ktxStorage)); + QVERIFY(ktxMemory->isValid()); + + auto& outFile = TEST_IMAGE_KTX; + if (!outFile.open()) { + QFAIL("Unable to open file"); + } + auto ktxSize = ktxStorage->size(); + outFile.resize(ktxSize); + auto dest = outFile.map(0, ktxSize); + memcpy(dest, ktxStorage->data(), ktxSize); + outFile.unmap(dest); + outFile.close(); + } + + + { + auto ktxStorage = std::make_shared(TEST_IMAGE_KTX.fileName()); + QVERIFY(ktx::KTX::validate(ktxStorage)); + auto ktxFile = ktx::KTX::create(ktxStorage); + QVERIFY(ktxFile.get()); + QVERIFY(ktxFile->isValid()); + { + const auto& memStorage = ktxMemory->getStorage(); + const auto& fileStorage = ktxFile->getStorage(); + QVERIFY(memStorage->size() == fileStorage->size()); + QVERIFY(memStorage->data() != fileStorage->data()); + QVERIFY(0 == memcmp(memStorage->data(), fileStorage->data(), memStorage->size())); + QVERIFY(ktxFile->_images.size() == ktxMemory->_images.size()); + auto imageCount = ktxFile->_images.size(); + auto startMemory = ktxMemory->_storage->data(); + auto startFile = ktxFile->_storage->data(); + for (size_t i = 0; i < imageCount; ++i) { + auto memImages = ktxMemory->_images[i]; + auto fileImages = ktxFile->_images[i]; + QVERIFY(memImages._padding == fileImages._padding); + QVERIFY(memImages._numFaces == fileImages._numFaces); + QVERIFY(memImages._imageSize == fileImages._imageSize); + QVERIFY(memImages._faceSize == fileImages._faceSize); + QVERIFY(memImages._faceBytes.size() == memImages._numFaces); + QVERIFY(fileImages._faceBytes.size() == fileImages._numFaces); + auto faceCount = fileImages._numFaces; + for (uint32_t face = 0; face < faceCount; ++face) { + auto memFace = memImages._faceBytes[face]; + auto memOffset = memFace - startMemory; + auto fileFace = fileImages._faceBytes[face]; + auto fileOffset = fileFace - startFile; + QVERIFY(memOffset % 4 == 0); + QVERIFY(memOffset == fileOffset); + } + } + } + } + testTexture->setKtxBacking(TEST_IMAGE_KTX.fileName().toStdString()); +} + +#if 0 + +static const QString TEST_FOLDER { "H:/ktx_cacheold" }; +//static const QString TEST_FOLDER { "C:/Users/bdavis/Git/KTX/testimages" }; + +//static const QString EXTENSIONS { "4bbdf8f786470e4ab3e672d44b8e8df2.ktx" }; +static const QString EXTENSIONS { "*.ktx" }; + +int mainTemp(int, char**) { + qInstallMessageHandler(messageHandler); + auto fileInfoList = QDir { TEST_FOLDER }.entryInfoList(QStringList { EXTENSIONS }); + for (auto fileInfo : fileInfoList) { + qDebug() << fileInfo.filePath(); + std::shared_ptr storage { new storage::FileStorage { fileInfo.filePath() } }; + + if (!ktx::KTX::validate(storage)) { + qDebug() << "KTX invalid"; + } + + auto ktxFile = ktx::KTX::create(storage); + ktx::KTXDescriptor ktxDescriptor = ktxFile->toDescriptor(); + + qDebug() << "Contains " << ktxDescriptor.keyValues.size() << " key value pairs"; + for (const auto& kv : ktxDescriptor.keyValues) { + qDebug() << "\t" << kv._key.c_str(); + } + + auto offsetToMinMipKV = ktxDescriptor.getValueOffsetForKey(ktx::HIFI_MIN_POPULATED_MIP_KEY); + if (offsetToMinMipKV) { + auto data = storage->data() + ktx::KTX_HEADER_SIZE + offsetToMinMipKV; + auto minMipLevelAvailable = *data; + qDebug() << "\tMin mip available " << minMipLevelAvailable; + assert(minMipLevelAvailable < ktxDescriptor.header.numberOfMipmapLevels); + } + auto storageSize = storage->size(); + for (const auto& faceImageDesc : ktxDescriptor.images) { + //assert(0 == (faceImageDesc._faceSize % 4)); + for (const auto& faceOffset : faceImageDesc._faceOffsets) { + assert(0 == (faceOffset % 4)); + auto faceEndOffset = faceOffset + faceImageDesc._faceSize; + assert(faceEndOffset <= storageSize); + } + } + + for (const auto& faceImage : ktxFile->_images) { + for (const ktx::Byte* faceBytes : faceImage._faceBytes) { + assert(0 == (reinterpret_cast(faceBytes) % 4)); + } + } + } + return 0; +} +#endif diff --git a/tests/ktx/src/KtxTests.h b/tests/ktx/src/KtxTests.h new file mode 100644 index 0000000000..5627dc313d --- /dev/null +++ b/tests/ktx/src/KtxTests.h @@ -0,0 +1,21 @@ +// +// Created by Bradley Austin Davis on 2016/07/01 +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include + +class KtxTests : public QObject { + Q_OBJECT +private slots: + void initTestCase(); + void cleanupTestCase(); + void testKtxEvalFunctions(); + void testKhronosCompressionFunctions(); + void testKtxSerialization(); +}; + + diff --git a/tests/ktx/src/main.cpp b/tests/ktx/src/main.cpp deleted file mode 100644 index 3b62b89948..0000000000 --- a/tests/ktx/src/main.cpp +++ /dev/null @@ -1,223 +0,0 @@ -// -// Created by Bradley Austin Davis on 2016/07/01 -// Copyright 2014 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#include -#include -#include -#include - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -QSharedPointer logger; - -gpu::Texture* cacheTexture(const std::string& name, gpu::Texture* srcTexture, bool write = true, bool read = true); - - -void messageHandler(QtMsgType type, const QMessageLogContext& context, const QString& message) { - QString logMessage = LogHandler::getInstance().printMessage((LogMsgType)type, context, message); - - if (!logMessage.isEmpty()) { -#ifdef Q_OS_WIN - OutputDebugStringA(logMessage.toLocal8Bit().constData()); - OutputDebugStringA("\n"); -#endif - if (logger) { - logger->addMessage(qPrintable(logMessage + "\n")); - } - } -} - -const char * LOG_FILTER_RULES = R"V0G0N( -hifi.gpu=true -)V0G0N"; - -QString getRootPath() { - static std::once_flag once; - static QString result; - std::call_once(once, [&] { - QFileInfo file(__FILE__); - QDir parent = file.absolutePath(); - result = QDir::cleanPath(parent.currentPath() + "/../../.."); - }); - return result; -} - -const QString TEST_IMAGE = getRootPath() + "/scripts/developer/tests/cube_texture.png"; -const QString TEST_IMAGE_KTX = getRootPath() + "/scripts/developer/tests/cube_texture.ktx"; - -int main(int argc, char** argv) { - QApplication app(argc, argv); - QCoreApplication::setApplicationName("KTX"); - QCoreApplication::setOrganizationName("High Fidelity"); - QCoreApplication::setOrganizationDomain("highfidelity.com"); - logger.reset(new FileLogger()); - - Q_ASSERT(ktx::evalPadding(0) == 0); - Q_ASSERT(ktx::evalPadding(1) == 3); - Q_ASSERT(ktx::evalPadding(2) == 2); - Q_ASSERT(ktx::evalPadding(3) == 1); - Q_ASSERT(ktx::evalPadding(4) == 0); - Q_ASSERT(ktx::evalPadding(1024) == 0); - Q_ASSERT(ktx::evalPadding(1025) == 3); - Q_ASSERT(ktx::evalPaddedSize(0) == 0); - Q_ASSERT(ktx::evalPaddedSize(1) == 4); - Q_ASSERT(ktx::evalPaddedSize(2) == 4); - Q_ASSERT(ktx::evalPaddedSize(3) == 4); - Q_ASSERT(ktx::evalPaddedSize(4) == 4); - Q_ASSERT(ktx::evalPaddedSize(1024) == 1024); - Q_ASSERT(ktx::evalPaddedSize(1025) == 1028); - Q_ASSERT(sizeof(ktx::Header) == 12 + (sizeof(uint32_t) * 13)); - - DependencyManager::set(); - qInstallMessageHandler(messageHandler); - QLoggingCategory::setFilterRules(LOG_FILTER_RULES); - - QImage image(TEST_IMAGE); - gpu::TexturePointer testTexture = image::TextureUsage::process2DTextureColorFromImage(image, TEST_IMAGE.toStdString(), true); - - auto ktxMemory = gpu::Texture::serialize(*testTexture); - { - const auto& ktxStorage = ktxMemory->getStorage(); - Q_ASSERT_X(ktx::KTX::validate(ktxStorage), __FUNCTION__, "KTX storage validation failed"); - Q_ASSERT_X(ktxMemory->isValid(), __FUNCTION__, "KTX self-validation failed"); - QSaveFile outFile(TEST_IMAGE_KTX); - if (!outFile.open(QFile::WriteOnly)) { - throw std::runtime_error("Unable to open file"); - } - auto ktxSize = ktxStorage->size(); - outFile.resize(ktxSize); - auto dest = outFile.map(0, ktxSize); - memcpy(dest, ktxStorage->data(), ktxSize); - outFile.unmap(dest); - outFile.commit(); - } - - { - auto ktxFile = ktx::KTX::create(std::shared_ptr(new storage::FileStorage(TEST_IMAGE_KTX))); - { - const auto& memStorage = ktxMemory->getStorage(); - const auto& fileStorage = ktxFile->getStorage(); - Q_ASSERT(memStorage->size() == fileStorage->size()); - Q_ASSERT(memStorage->data() != fileStorage->data()); - Q_ASSERT(0 == memcmp(memStorage->data(), fileStorage->data(), memStorage->size())); - Q_ASSERT(ktxFile->_images.size() == ktxMemory->_images.size()); - auto imageCount = ktxFile->_images.size(); - auto startMemory = ktxMemory->_storage->data(); - auto startFile = ktxFile->_storage->data(); - for (size_t i = 0; i < imageCount; ++i) { - auto memImages = ktxMemory->_images[i]; - auto fileImages = ktxFile->_images[i]; - Q_ASSERT(memImages._padding == fileImages._padding); - Q_ASSERT(memImages._numFaces == fileImages._numFaces); - Q_ASSERT(memImages._imageSize == fileImages._imageSize); - Q_ASSERT(memImages._faceSize == fileImages._faceSize); - Q_ASSERT(memImages._faceBytes.size() == memImages._numFaces); - Q_ASSERT(fileImages._faceBytes.size() == fileImages._numFaces); - auto faceCount = fileImages._numFaces; - for (uint32_t face = 0; face < faceCount; ++face) { - auto memFace = memImages._faceBytes[face]; - auto memOffset = memFace - startMemory; - auto fileFace = fileImages._faceBytes[face]; - auto fileOffset = fileFace - startFile; - Q_ASSERT(memOffset % 4 == 0); - Q_ASSERT(memOffset == fileOffset); - } - } - } - } - testTexture->setKtxBacking(TEST_IMAGE_KTX.toStdString()); - return 0; -} - -#if 0 -static const QString TEST_FOLDER { "H:/ktx_cacheold" }; -//static const QString TEST_FOLDER { "C:/Users/bdavis/Git/KTX/testimages" }; - -//static const QString EXTENSIONS { "4bbdf8f786470e4ab3e672d44b8e8df2.ktx" }; -static const QString EXTENSIONS { "*.ktx" }; - -int mainTemp(int, char**) { - qInstallMessageHandler(messageHandler); - auto fileInfoList = QDir { TEST_FOLDER }.entryInfoList(QStringList { EXTENSIONS }); - for (auto fileInfo : fileInfoList) { - qDebug() << fileInfo.filePath(); - std::shared_ptr storage { new storage::FileStorage { fileInfo.filePath() } }; - - if (!ktx::KTX::validate(storage)) { - qDebug() << "KTX invalid"; - } - - auto ktxFile = ktx::KTX::create(storage); - ktx::KTXDescriptor ktxDescriptor = ktxFile->toDescriptor(); - - qDebug() << "Contains " << ktxDescriptor.keyValues.size() << " key value pairs"; - for (const auto& kv : ktxDescriptor.keyValues) { - qDebug() << "\t" << kv._key.c_str(); - } - - auto offsetToMinMipKV = ktxDescriptor.getValueOffsetForKey(ktx::HIFI_MIN_POPULATED_MIP_KEY); - if (offsetToMinMipKV) { - auto data = storage->data() + ktx::KTX_HEADER_SIZE + offsetToMinMipKV; - auto minMipLevelAvailable = *data; - qDebug() << "\tMin mip available " << minMipLevelAvailable; - assert(minMipLevelAvailable < ktxDescriptor.header.numberOfMipmapLevels); - } - auto storageSize = storage->size(); - for (const auto& faceImageDesc : ktxDescriptor.images) { - //assert(0 == (faceImageDesc._faceSize % 4)); - for (const auto& faceOffset : faceImageDesc._faceOffsets) { - assert(0 == (faceOffset % 4)); - auto faceEndOffset = faceOffset + faceImageDesc._faceSize; - assert(faceEndOffset <= storageSize); - } - } - - for (const auto& faceImage : ktxFile->_images) { - for (const ktx::Byte* faceBytes : faceImage._faceBytes) { - assert(0 == (reinterpret_cast(faceBytes) % 4)); - } - } - } - return 0; -} -#endif - -#include "main.moc" - From 4c4811dca82ea881bdf19d07dd581d179b807194 Mon Sep 17 00:00:00 2001 From: Dante Ruiz Date: Tue, 30 May 2017 22:52:39 +0100 Subject: [PATCH 03/13] finished the head puck --- libraries/shared/src/GLMHelpers.h | 3 +- plugins/openvr/src/ViveControllerManager.cpp | 78 +++++++++++++++++--- plugins/openvr/src/ViveControllerManager.h | 1 + 3 files changed, 70 insertions(+), 12 deletions(-) diff --git a/libraries/shared/src/GLMHelpers.h b/libraries/shared/src/GLMHelpers.h index 5574007b9b..ef92552d1f 100644 --- a/libraries/shared/src/GLMHelpers.h +++ b/libraries/shared/src/GLMHelpers.h @@ -147,8 +147,7 @@ bool isPointBehindTrianglesPlane(glm::vec3 point, glm::vec3 p0, glm::vec3 p1, gl glm::vec3 extractTranslation(const glm::mat4& matrix); -void setTranslation(glm::mat4& matrix, cons - t glm::vec3& translation); +void setTranslation(glm::mat4& matrix, const glm::vec3& translation); glm::quat extractRotation(const glm::mat4& matrix, bool assumeOrthogonal = false); glm::quat glmExtractRotation(const glm::mat4& matrix); diff --git a/plugins/openvr/src/ViveControllerManager.cpp b/plugins/openvr/src/ViveControllerManager.cpp index 0f0cc5860e..82457928a2 100644 --- a/plugins/openvr/src/ViveControllerManager.cpp +++ b/plugins/openvr/src/ViveControllerManager.cpp @@ -82,6 +82,28 @@ static bool sortPucksXPosition(PuckPosePair firstPuck, PuckPosePair secondPuck) return (firstPuck.second.translation.x < secondPuck.second.translation.x); } +static bool comparePosePositions(const controller::Pose& poseA, const controller::Pose& poseB, glm::vec3 axis, glm::vec3 axisOrigin) { + glm::vec3 poseAPosition = poseA.getTranslation(); + glm::vec3 poseBPosition = poseB.getTranslation(); + + glm::vec3 poseAFinalPosition = poseAPosition - axisOrigin; + glm::vec3 poseBFinalPosition = poseBPosition - axisOrigin; + + float poseADistance = glm::dot(poseAFinalPosition, axis); + float poseBDistance = glm::dot(poseBFinalPosition, axis); + return (poseADistance > poseBDistance); +} + +static glm::vec3 getHeadXAxis(glm::mat4 defaultToReferenceMat, glm::mat4 defaultHead) { + glm::mat4 finalHead = defaultToReferenceMat * defaultHead; + return glmExtractRotation(finalHead) * Vectors::UNIT_X; +} + +static glm::vec3 getHeadPosition(glm::mat4 defaultToReferenceMat, glm::mat4 defaultHead) { + glm::mat4 finalHead = defaultToReferenceMat * defaultHead; + return extractTranslation(finalHead); +} + static QString deviceTrackingResultToString(vr::ETrackingResult trackingResult) { QString result; auto iterator = TRACKING_RESULT_TO_STRING.find(trackingResult); @@ -361,12 +383,16 @@ void ViveControllerManager::InputDevice::calibrate(const controller::InputCalibr calibrateShoulders(defaultToReferenceMat, inputCalibration, firstShoulderIndex, secondShoulderIndex); } else if (_config == Config::FeetHipsAndHead && puckCount == MIN_FEET_HIPS_HEAD) { glm::mat4 headPuckDefaultToReferenceMat = recalculateDefaultToReferenceForHeadPuck(inputCalibration); - calibrateFeet(headPuckDefaultToReferenceMat, inputCalibration); + glm::vec3 headXAxis = getHeadXAxis(headPuckDefaultToReferenceMat, inputCalibration.defaultHeadMat); + glm::vec3 headPosition = getHeadPosition(headPuckDefaultToReferenceMat, inputCalibration.defaultHeadMat); + calibrateFeet(headPuckDefaultToReferenceMat, inputCalibration, headXAxis, headPosition); calibrateHips(headPuckDefaultToReferenceMat, inputCalibration); calibrateHead(headPuckDefaultToReferenceMat, inputCalibration); } else if (_config == Config::FeetHipsChestAndHead && puckCount == MIN_FEET_HIPS_CHEST_HEAD) { glm::mat4 headPuckDefaultToReferenceMat = recalculateDefaultToReferenceForHeadPuck(inputCalibration); - calibrateFeet(headPuckDefaultToReferenceMat, inputCalibration); + glm::vec3 headXAxis = getHeadXAxis(headPuckDefaultToReferenceMat, inputCalibration.defaultHeadMat); + glm::vec3 headPosition = getHeadPosition(headPuckDefaultToReferenceMat, inputCalibration.defaultHeadMat); + calibrateFeet(headPuckDefaultToReferenceMat, inputCalibration, headXAxis, headPosition); calibrateHips(headPuckDefaultToReferenceMat, inputCalibration); calibrateChest(headPuckDefaultToReferenceMat, inputCalibration); calibrateHead(headPuckDefaultToReferenceMat, inputCalibration); @@ -468,17 +494,29 @@ glm::mat4 ViveControllerManager::InputDevice::recalculateDefaultToReferenceForHe glm::mat4 sensorToAvatarMat = glm::inverse(inputCalibration.avatarMat) * inputCalibration.sensorToWorldMat; size_t headPuckIndex = _validTrackedObjects.size() - 1; controller::Pose headPuckPose = _validTrackedObjects[headPuckIndex].second; - glm::mat4 headPuckSensorMat = avatarToSensorMat * createMatFromQuatAndPos(headPuckPose.getRotation(), headPuckPose.getTranslation()); - glm::vec3 headPuckTranslation = extractTranslation(headPuckSensorMat); - glm::vec3 headPuckYRotation = glmExtractRotation(headPuckSensorMat) * glm::vec3(0.0f, 1.0f, 0.0f); + glm::mat4 headPuckAvatarMat = createMatFromQuatAndPos(headPuckPose.getRotation(), headPuckPose.getTranslation()) * Matrices::Y_180; + glm::vec3 headPuckTranslation = extractTranslation(headPuckAvatarMat); + glm::vec3 headPuckZAxis = cancelOutRollAndPitch(glmExtractRotation(headPuckAvatarMat)) * glm::vec3(0.0f, 0.0f, 1.0f); glm::vec3 worldUp = glm::vec3(0.0f, 1.0f, 0.0f); - glm::vec3 desiredY = glm::vec3(0.0f, 1.0f, 0.0f); - glm::vec3 xPrime = glm::normalize(glm::cross(worldUp, headPuckYRotation)); - glm::vec3 zPrime = glm::normalize(glm::cross(xPrime, desiredY)); + glm::vec3 yPrime = glm::vec3(0.0f, 1.0f, 0.0f); + glm::vec3 xPrime = glm::normalize(glm::cross(worldUp, headPuckZAxis)); + glm::vec3 zPrime = glm::normalize(glm::cross(xPrime, yPrime)); + glm::mat4 newHeadPuck = glm::mat4(glm::vec4(xPrime, 0.0f), glm::vec4(yPrime, 0.0f), + glm::vec4(zPrime, 0.0f), glm::vec4(headPuckTranslation, 1.0f)); - glm::mat4 - return glm::mat4(); + glm::mat4 headPuckOffset = glm::mat4(glm::vec4(1.0f, 0.0f, 0.0f, 0.0f), glm::vec4(0.0f, 1.0f, 0.0f, 0.0f), + glm::vec4(0.0f, 0.0f, 1.0f, 0.0f), glm::vec4(0.0f, -0.005f, 0.0f, 1.0f)); + + glm::mat4 finalHeadPuck = newHeadPuck * headPuckOffset; + + glm::mat4 defaultHeadOffset = glm::inverse(inputCalibration.defaultCenterEyeMat) * inputCalibration.defaultHeadMat; + + glm::mat4 currentHead = finalHeadPuck * defaultHeadOffset; + + // calculate the defaultToRefrenceXform + glm::mat4 defaultToReferenceMat = currentHead * glm::inverse(inputCalibration.defaultHeadMat); + return defaultToReferenceMat; } void ViveControllerManager::InputDevice::partitionTouchpad(int sButton, int xAxis, int yAxis, int centerPseudoButton, int xPseudoButton, int yPseudoButton) { @@ -668,6 +706,26 @@ void ViveControllerManager::InputDevice::calibrateFeet(glm::mat4& defaultToRefer } } + +void ViveControllerManager::InputDevice::calibrateFeet(glm::mat4& defaultToReferenceMat, const controller::InputCalibrationData& inputCalibration, glm::vec3 headXAxis, glm::vec3 headPosition) { + auto& firstFoot = _validTrackedObjects[FIRST_FOOT]; + auto& secondFoot = _validTrackedObjects[SECOND_FOOT]; + controller::Pose& firstFootPose = firstFoot.second; + controller::Pose& secondFootPose = secondFoot.second; + + if (comparePosePositions(firstFootPose, secondFootPose, headXAxis, headPosition)) { + _jointToPuckMap[controller::LEFT_FOOT] = firstFoot.first; + _pucksOffset[firstFoot.first] = computeOffset(defaultToReferenceMat, inputCalibration.defaultLeftFoot, firstFootPose); + _jointToPuckMap[controller::RIGHT_FOOT] = secondFoot.first; + _pucksOffset[secondFoot.first] = computeOffset(defaultToReferenceMat, inputCalibration.defaultRightFoot, secondFootPose); + } else { + _jointToPuckMap[controller::LEFT_FOOT] = secondFoot.first; + _pucksOffset[secondFoot.first] = computeOffset(defaultToReferenceMat, inputCalibration.defaultLeftFoot, secondFootPose); + _jointToPuckMap[controller::RIGHT_FOOT] = firstFoot.first; + _pucksOffset[firstFoot.first] = computeOffset(defaultToReferenceMat, inputCalibration.defaultRightFoot, firstFootPose); + } +} + void ViveControllerManager::InputDevice::calibrateHips(glm::mat4& defaultToReferenceMat, const controller::InputCalibrationData& inputCalibration) { _jointToPuckMap[controller::HIPS] = _validTrackedObjects[HIP].first; _pucksOffset[_validTrackedObjects[HIP].first] = computeOffset(defaultToReferenceMat, inputCalibration.defaultHips, _validTrackedObjects[HIP].second); diff --git a/plugins/openvr/src/ViveControllerManager.h b/plugins/openvr/src/ViveControllerManager.h index 545af1b00b..c32579b0d8 100644 --- a/plugins/openvr/src/ViveControllerManager.h +++ b/plugins/openvr/src/ViveControllerManager.h @@ -85,6 +85,7 @@ private: void loadSettings(); void saveSettings() const; void calibrateFeet(glm::mat4& defaultToReferenceMat, const controller::InputCalibrationData& inputCalibration); + void calibrateFeet(glm::mat4& defaultToReferenceMat, const controller::InputCalibrationData& inputCalibration, glm::vec3 headXAxis, glm::vec3 headPosition); void calibrateHips(glm::mat4& defaultToReferenceMat, const controller::InputCalibrationData& inputCalibration); void calibrateChest(glm::mat4& defaultToReferenceMat, const controller::InputCalibrationData& inputCalibration); From 9c787eb784326adc4f650dc113fe03bf400a445b Mon Sep 17 00:00:00 2001 From: Dante Ruiz Date: Tue, 30 May 2017 22:59:12 +0100 Subject: [PATCH 04/13] removed commented code --- scripts/defaultScripts.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/defaultScripts.js b/scripts/defaultScripts.js index db0b705f8e..81ce72d901 100644 --- a/scripts/defaultScripts.js +++ b/scripts/defaultScripts.js @@ -13,7 +13,7 @@ var DEFAULT_SCRIPTS_COMBINED = [ "system/progress.js", - //"system/away.js", + "system/away.js", "system/audio.js", "system/hmd.js", "system/menu.js", From b3401d97629a6544358b2c908ff43349268f091e Mon Sep 17 00:00:00 2001 From: Dante Ruiz Date: Tue, 30 May 2017 23:55:45 +0100 Subject: [PATCH 05/13] making some final changes --- interface/resources/controllers/vive.json | 22 ++++++++++++-------- plugins/openvr/src/ViveControllerManager.cpp | 2 +- 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/interface/resources/controllers/vive.json b/interface/resources/controllers/vive.json index 984f9c65fb..a0e9bd30d4 100644 --- a/interface/resources/controllers/vive.json +++ b/interface/resources/controllers/vive.json @@ -34,32 +34,36 @@ { "from": "Vive.RSCenter", "to": "Standard.RightPrimaryThumb" }, { "from": "Vive.RightApplicationMenu", "to": "Standard.RightSecondaryThumb" }, - { "from": "Vive.LeftHand", "to": "Standard.LeftHand" }, - { "from": "Vive.RightHand", "to": "Standard.RightHand" }, + { "from": "Vive.LeftHand", "to": "Standard.LeftHand", "when": [ "Application.InHMD" ] }, + { "from": "Vive.RightHand", "to": "Standard.RightHand", "when": [ "Application.InHMD" ] }, { "from": "Vive.LeftFoot", "to" : "Standard.LeftFoot", - "filters" : [{"type" : "lowVelocity", "rotation" : 1.0, "translation": 1.0}] + "filters" : [{"type" : "lowVelocity", "rotation" : 1.0, "translation": 1.0}], + "when": [ "Application.InHMD" ] }, { "from": "Vive.RightFoot", "to" : "Standard.RightFoot", - "filters" : [{"type" : "lowVelocity", "rotation" : 1.0, "translation": 1.0}] + "filters" : [{"type" : "lowVelocity", "rotation" : 1.0, "translation": 1.0}], + "when": [ "Application.InHMD" ] }, { "from": "Vive.Hips", "to" : "Standard.Hips", - "filters" : [{"type" : "lowVelocity", "rotation" : 0.01, "translation": 0.01}] + "filters" : [{"type" : "lowVelocity", "rotation" : 0.01, "translation": 0.01}], + "when": [ "Application.InHMD" ] }, { "from": "Vive.Spine2", "to" : "Standard.Spine2", - "filters" : [{"type" : "lowVelocity", "rotation" : 0.01, "translation": 0.01}] + "filters" : [{"type" : "lowVelocity", "rotation" : 0.01, "translation": 0.01}], + "when": [ "Application.InHMD" ] }, - { "from": "Vive.Head", "to" : "Standard.Head"}, + { "from": "Vive.Head", "to" : "Standard.Head", "when": [ "Application.InHMD" ] }, - { "from": "Vive.RightArm", "to" : "Standard.RightArm"}, - { "from": "Vive.LeftArm", "to" : "Standard.LeftArm"} + { "from": "Vive.RightArm", "to" : "Standard.RightArm", "when": [ "Application.InHMD" ] }, + { "from": "Vive.LeftArm", "to" : "Standard.LeftArm", "when": [ "Application.InHMD" ] } ] } diff --git a/plugins/openvr/src/ViveControllerManager.cpp b/plugins/openvr/src/ViveControllerManager.cpp index 82457928a2..6edcbcb3fa 100644 --- a/plugins/openvr/src/ViveControllerManager.cpp +++ b/plugins/openvr/src/ViveControllerManager.cpp @@ -506,7 +506,7 @@ glm::mat4 ViveControllerManager::InputDevice::recalculateDefaultToReferenceForHe glm::vec4(zPrime, 0.0f), glm::vec4(headPuckTranslation, 1.0f)); glm::mat4 headPuckOffset = glm::mat4(glm::vec4(1.0f, 0.0f, 0.0f, 0.0f), glm::vec4(0.0f, 1.0f, 0.0f, 0.0f), - glm::vec4(0.0f, 0.0f, 1.0f, 0.0f), glm::vec4(0.0f, -0.005f, 0.0f, 1.0f)); + glm::vec4(0.0f, 0.0f, 1.0f, 0.0f), glm::vec4(0.0f, -0.0254f, -0.152f, 1.0f)); glm::mat4 finalHeadPuck = newHeadPuck * headPuckOffset; From 41869430f7dab1dc1eefb6a5788926c19f811305 Mon Sep 17 00:00:00 2001 From: Dante Ruiz Date: Wed, 31 May 2017 01:29:32 +0100 Subject: [PATCH 06/13] made some changes that were requested --- plugins/openvr/src/ViveControllerManager.cpp | 33 +++++++++++--------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/plugins/openvr/src/ViveControllerManager.cpp b/plugins/openvr/src/ViveControllerManager.cpp index 6edcbcb3fa..e08929cf08 100644 --- a/plugins/openvr/src/ViveControllerManager.cpp +++ b/plugins/openvr/src/ViveControllerManager.cpp @@ -57,6 +57,8 @@ static const int FIRST_FOOT = 0; static const int SECOND_FOOT = 1; static const int HIP = 2; static const int CHEST = 3; +static const float HEAD_PUCK_Y_OFFSET = -0.0254f; +static const float HEAD_PUCK_Z_OFFSET = -0.152f; const char* ViveControllerManager::NAME { "OpenVR" }; @@ -82,24 +84,24 @@ static bool sortPucksXPosition(PuckPosePair firstPuck, PuckPosePair secondPuck) return (firstPuck.second.translation.x < secondPuck.second.translation.x); } -static bool comparePosePositions(const controller::Pose& poseA, const controller::Pose& poseB, glm::vec3 axis, glm::vec3 axisOrigin) { +static bool determineFeetOrdering(const controller::Pose& poseA, const controller::Pose& poseB, glm::vec3 axis, glm::vec3 axisOrigin) { glm::vec3 poseAPosition = poseA.getTranslation(); glm::vec3 poseBPosition = poseB.getTranslation(); - glm::vec3 poseAFinalPosition = poseAPosition - axisOrigin; - glm::vec3 poseBFinalPosition = poseBPosition - axisOrigin; + glm::vec3 poseAVector = poseAPosition - axisOrigin; + glm::vec3 poseBVector = poseBPosition - axisOrigin; - float poseADistance = glm::dot(poseAFinalPosition, axis); - float poseBDistance = glm::dot(poseBFinalPosition, axis); - return (poseADistance > poseBDistance); + float poseAProjection = glm::dot(poseAVector, axis); + float poseBProjection = glm::dot(poseBVector, axis); + return (poseAProjection > poseBProjection); } -static glm::vec3 getHeadXAxis(glm::mat4 defaultToReferenceMat, glm::mat4 defaultHead) { +static glm::vec3 getReferenceHeadXAxis(glm::mat4 defaultToReferenceMat, glm::mat4 defaultHead) { glm::mat4 finalHead = defaultToReferenceMat * defaultHead; return glmExtractRotation(finalHead) * Vectors::UNIT_X; } -static glm::vec3 getHeadPosition(glm::mat4 defaultToReferenceMat, glm::mat4 defaultHead) { +static glm::vec3 getReferenceHeadPosition(glm::mat4 defaultToReferenceMat, glm::mat4 defaultHead) { glm::mat4 finalHead = defaultToReferenceMat * defaultHead; return extractTranslation(finalHead); } @@ -383,19 +385,21 @@ void ViveControllerManager::InputDevice::calibrate(const controller::InputCalibr calibrateShoulders(defaultToReferenceMat, inputCalibration, firstShoulderIndex, secondShoulderIndex); } else if (_config == Config::FeetHipsAndHead && puckCount == MIN_FEET_HIPS_HEAD) { glm::mat4 headPuckDefaultToReferenceMat = recalculateDefaultToReferenceForHeadPuck(inputCalibration); - glm::vec3 headXAxis = getHeadXAxis(headPuckDefaultToReferenceMat, inputCalibration.defaultHeadMat); - glm::vec3 headPosition = getHeadPosition(headPuckDefaultToReferenceMat, inputCalibration.defaultHeadMat); + glm::vec3 headXAxis = getReferenceHeadXAxis(headPuckDefaultToReferenceMat, inputCalibration.defaultHeadMat); + glm::vec3 headPosition = getReferenceHeadPosition(headPuckDefaultToReferenceMat, inputCalibration.defaultHeadMat); calibrateFeet(headPuckDefaultToReferenceMat, inputCalibration, headXAxis, headPosition); calibrateHips(headPuckDefaultToReferenceMat, inputCalibration); calibrateHead(headPuckDefaultToReferenceMat, inputCalibration); + _overrideHead = true; } else if (_config == Config::FeetHipsChestAndHead && puckCount == MIN_FEET_HIPS_CHEST_HEAD) { glm::mat4 headPuckDefaultToReferenceMat = recalculateDefaultToReferenceForHeadPuck(inputCalibration); - glm::vec3 headXAxis = getHeadXAxis(headPuckDefaultToReferenceMat, inputCalibration.defaultHeadMat); - glm::vec3 headPosition = getHeadPosition(headPuckDefaultToReferenceMat, inputCalibration.defaultHeadMat); + glm::vec3 headXAxis = getReferenceHeadXAxis(headPuckDefaultToReferenceMat, inputCalibration.defaultHeadMat); + glm::vec3 headPosition = getReferenceHeadPosition(headPuckDefaultToReferenceMat, inputCalibration.defaultHeadMat); calibrateFeet(headPuckDefaultToReferenceMat, inputCalibration, headXAxis, headPosition); calibrateHips(headPuckDefaultToReferenceMat, inputCalibration); calibrateChest(headPuckDefaultToReferenceMat, inputCalibration); calibrateHead(headPuckDefaultToReferenceMat, inputCalibration); + _overrideHead = true; } else { qDebug() << "Puck Calibration: " << configToString(_config) << " Config Failed: Could not meet the minimal # of pucks"; uncalibrate(); @@ -506,7 +510,7 @@ glm::mat4 ViveControllerManager::InputDevice::recalculateDefaultToReferenceForHe glm::vec4(zPrime, 0.0f), glm::vec4(headPuckTranslation, 1.0f)); glm::mat4 headPuckOffset = glm::mat4(glm::vec4(1.0f, 0.0f, 0.0f, 0.0f), glm::vec4(0.0f, 1.0f, 0.0f, 0.0f), - glm::vec4(0.0f, 0.0f, 1.0f, 0.0f), glm::vec4(0.0f, -0.0254f, -0.152f, 1.0f)); + glm::vec4(0.0f, 0.0f, 1.0f, 0.0f), glm::vec4(0.0f, HEAD_PUCK_Y_OFFSET, HEAD_PUCK_Z_OFFSET, 1.0f)); glm::mat4 finalHeadPuck = newHeadPuck * headPuckOffset; @@ -713,7 +717,7 @@ void ViveControllerManager::InputDevice::calibrateFeet(glm::mat4& defaultToRefer controller::Pose& firstFootPose = firstFoot.second; controller::Pose& secondFootPose = secondFoot.second; - if (comparePosePositions(firstFootPose, secondFootPose, headXAxis, headPosition)) { + if (determineFeetOrdering(firstFootPose, secondFootPose, headXAxis, headPosition)) { _jointToPuckMap[controller::LEFT_FOOT] = firstFoot.first; _pucksOffset[firstFoot.first] = computeOffset(defaultToReferenceMat, inputCalibration.defaultLeftFoot, firstFootPose); _jointToPuckMap[controller::RIGHT_FOOT] = secondFoot.first; @@ -766,7 +770,6 @@ void ViveControllerManager::InputDevice::calibrateHead(glm::mat4& defaultToRefer _jointToPuckMap[controller::HEAD] = head.first; _pucksOffset[head.first] = computeOffset(defaultToReferenceMat, inputCalibration.defaultHeadMat, newHead); - _overrideHead = true; } From f4328af66f6d2535707a5bef5f86b438defe2761 Mon Sep 17 00:00:00 2001 From: Dante Ruiz Date: Wed, 31 May 2017 17:12:27 +0100 Subject: [PATCH 07/13] made final requested changes --- plugins/openvr/src/ViveControllerManager.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/plugins/openvr/src/ViveControllerManager.cpp b/plugins/openvr/src/ViveControllerManager.cpp index e08929cf08..8ddf458210 100644 --- a/plugins/openvr/src/ViveControllerManager.cpp +++ b/plugins/openvr/src/ViveControllerManager.cpp @@ -503,6 +503,13 @@ glm::mat4 ViveControllerManager::InputDevice::recalculateDefaultToReferenceForHe glm::vec3 headPuckZAxis = cancelOutRollAndPitch(glmExtractRotation(headPuckAvatarMat)) * glm::vec3(0.0f, 0.0f, 1.0f); glm::vec3 worldUp = glm::vec3(0.0f, 1.0f, 0.0f); + // check that the head puck z axis is not parrallel to the world up + const float EPSILON = 1.0e-4f; + glm::vec3 zAxis = glmExtractRotation(headPuckAvatarMat) * glm::vec3(0.0f, 0.0f, 1.0f); + if (fabsf(fabsf(glm::dot(glm::normalize(worldUp), glm::normalize(zAxis))) - 1.0f) < EPSILON) { + headPuckZAxis = glm::vec3(1.0f, 0.0f, 0.0f); + } + glm::vec3 yPrime = glm::vec3(0.0f, 1.0f, 0.0f); glm::vec3 xPrime = glm::normalize(glm::cross(worldUp, headPuckZAxis)); glm::vec3 zPrime = glm::normalize(glm::cross(xPrime, yPrime)); From 7fcdc6124450e988df0a8900a9843edeb33816a0 Mon Sep 17 00:00:00 2001 From: Dante Ruiz Date: Wed, 31 May 2017 18:20:35 +0100 Subject: [PATCH 08/13] add head puck offset preference --- plugins/openvr/src/ViveControllerManager.cpp | 36 ++++++++++++++++++-- 1 file changed, 34 insertions(+), 2 deletions(-) diff --git a/plugins/openvr/src/ViveControllerManager.cpp b/plugins/openvr/src/ViveControllerManager.cpp index 8ddf458210..5583ec6351 100644 --- a/plugins/openvr/src/ViveControllerManager.cpp +++ b/plugins/openvr/src/ViveControllerManager.cpp @@ -57,8 +57,8 @@ static const int FIRST_FOOT = 0; static const int SECOND_FOOT = 1; static const int HIP = 2; static const int CHEST = 3; -static const float HEAD_PUCK_Y_OFFSET = -0.0254f; -static const float HEAD_PUCK_Z_OFFSET = -0.152f; +static float HEAD_PUCK_Y_OFFSET = -0.0254f; +static float HEAD_PUCK_Z_OFFSET = -0.152f; const char* ViveControllerManager::NAME { "OpenVR" }; @@ -825,6 +825,38 @@ void ViveControllerManager::InputDevice::createPreferences() { auto preferences = DependencyManager::get(); static const QString VIVE_PUCKS_CONFIG = "Vive Pucks Configuration"; + { + static const float MIN_VALUE = -3.0f; + static const float MAX_VALUE = 3.0f; + static const float STEP = 0.01f; + + auto getter = [this]()->float { return HEAD_PUCK_Y_OFFSET; }; + auto setter = [this](const float& value) { HEAD_PUCK_Y_OFFSET = value; }; + + auto preference = new SpinnerPreference(VIVE_PUCKS_CONFIG, "HeadPuckYOffset", getter, setter); + preference->setMin(MIN_VALUE); + preference->setMax(MAX_VALUE); + preference->setDecimals(3); + preference->setStep(STEP); + preferences->addPreference(preference); + } + + { + static const float MIN_VALUE = -3.0f; + static const float MAX_VALUE = 3.0f; + static const float STEP = 0.01f; + + auto getter = [this]()->float { return HEAD_PUCK_Z_OFFSET; }; + auto setter = [this](const float& value) { HEAD_PUCK_Z_OFFSET = value; }; + + auto preference = new SpinnerPreference(VIVE_PUCKS_CONFIG, "HeadPuckXOffset", getter, setter); + preference->setMin(MIN_VALUE); + preference->setMax(MAX_VALUE); + preference->setStep(STEP); + preference->setDecimals(3); + preferences->addPreference(preference); + } + { auto getter = [this]()->QString { return _configStringMap[_preferedConfig]; }; auto setter = [this](const QString& value) { setConfigFromString(value); saveSettings(); }; From a77491ccb1128679fbe92f47d8caa9eb75b08b80 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Thu, 25 May 2017 10:33:34 -0700 Subject: [PATCH 09/13] FileCache refactoring and tests --- libraries/networking/src/FileCache.cpp | 173 +++++++++++++++------- libraries/networking/src/FileCache.h | 52 ++++--- libraries/shared/src/NumericalConstants.h | 3 + tests/networking/src/FileCacheTests.cpp | 170 +++++++++++++++++++++ tests/networking/src/FileCacheTests.h | 30 ++++ 5 files changed, 353 insertions(+), 75 deletions(-) create mode 100644 tests/networking/src/FileCacheTests.cpp create mode 100644 tests/networking/src/FileCacheTests.h diff --git a/libraries/networking/src/FileCache.cpp b/libraries/networking/src/FileCache.cpp index 8f3509d8f3..6cb8cd8f7c 100644 --- a/libraries/networking/src/FileCache.cpp +++ b/libraries/networking/src/FileCache.cpp @@ -11,52 +11,81 @@ #include "FileCache.h" -#include -#include -#include -#include -#include -#include +#include +#include +#include + +#include +#include +#include +#include #include +#include +#ifdef Q_OS_WIN +#include +#else +#include +#endif + +#ifdef NDEBUG Q_LOGGING_CATEGORY(file_cache, "hifi.file_cache", QtWarningMsg) - +#else +Q_LOGGING_CATEGORY(file_cache, "hifi.file_cache") +#endif using namespace cache; -static const std::string MANIFEST_NAME = "manifest"; +static const char DIR_SEP = '/'; +static const char EXT_SEP = '.'; -static const size_t BYTES_PER_MEGABYTES = 1024 * 1024; -static const size_t BYTES_PER_GIGABYTES = 1024 * BYTES_PER_MEGABYTES; -const size_t FileCache::DEFAULT_UNUSED_MAX_SIZE = 5 * BYTES_PER_GIGABYTES; // 5GB -const size_t FileCache::MAX_UNUSED_MAX_SIZE = 100 * BYTES_PER_GIGABYTES; // 100GB -const size_t FileCache::DEFAULT_OFFLINE_MAX_SIZE = 2 * BYTES_PER_GIGABYTES; // 2GB +const size_t FileCache::DEFAULT_MAX_SIZE { GB_TO_BYTES(5) }; +const size_t FileCache::MAX_MAX_SIZE { GB_TO_BYTES(100) }; +const size_t FileCache::DEFAULT_MIN_FREE_STORAGE_SPACE { GB_TO_BYTES(1) }; -void FileCache::setUnusedFileCacheSize(size_t unusedFilesMaxSize) { - _unusedFilesMaxSize = std::min(unusedFilesMaxSize, MAX_UNUSED_MAX_SIZE); - reserve(0); + +std::string getCacheName(const std::string& dirname_str) { + QString dirname { dirname_str.c_str() }; + QDir dir(dirname); + if (!dir.isAbsolute()) { + return dirname_str; + } + return dir.dirName().toStdString(); +} + +std::string getCachePath(const std::string& dirname_str) { + QString dirname { dirname_str.c_str() }; + QDir dir(dirname); + if (dir.isAbsolute()) { + return dirname_str; + } + return PathUtils::getAppLocalDataFilePath(dirname).toStdString(); +} + +void FileCache::setMinFreeSize(size_t size) { + _minFreeSpaceSize = size; + clean(); emit dirty(); } -void FileCache::setOfflineFileCacheSize(size_t offlineFilesMaxSize) { - _offlineFilesMaxSize = std::min(offlineFilesMaxSize, MAX_UNUSED_MAX_SIZE); +void FileCache::setMaxSize(size_t maxSize) { + _maxSize = std::min(maxSize, MAX_MAX_SIZE); + clean(); + emit dirty(); } FileCache::FileCache(const std::string& dirname, const std::string& ext, QObject* parent) : QObject(parent), _ext(ext), - _dirname(dirname), - _dirpath(PathUtils::getAppLocalDataFilePath(dirname.c_str()).toStdString()) {} + _dirname(getCacheName(dirname)), + _dirpath(getCachePath(dirname)) { +} FileCache::~FileCache() { clear(); } -void fileDeleter(File* file) { - file->deleter(); -} - void FileCache::initialize() { QDir dir(_dirpath.c_str()); @@ -84,7 +113,7 @@ void FileCache::initialize() { } FilePointer FileCache::addFile(Metadata&& metadata, const std::string& filepath) { - FilePointer file(createFile(std::move(metadata), filepath).release(), &fileDeleter); + FilePointer file(createFile(std::move(metadata), filepath).release(), std::mem_fn(&File::deleter)); if (file) { _numTotalFiles += 1; _totalFilesSize += file->getLength(); @@ -141,6 +170,7 @@ FilePointer FileCache::getFile(const Key& key) { if (it != _files.cend()) { file = it->second.lock(); if (file) { + file->touch(); // if it exists, it is active - remove it from the cache removeUnusedFile(file); qCDebug(file_cache, "[%s] Found %s", _dirname.c_str(), key.c_str()); @@ -155,44 +185,74 @@ FilePointer FileCache::getFile(const Key& key) { } std::string FileCache::getFilepath(const Key& key) { - return _dirpath + '/' + key + '.' + _ext; + return _dirpath + DIR_SEP + key + EXT_SEP + _ext; } -void FileCache::addUnusedFile(const FilePointer file) { +void FileCache::addUnusedFile(const FilePointer& file) { { Lock lock(_filesMutex); _files[file->getKey()] = file; } - reserve(file->getLength()); - file->_LRUKey = ++_lastLRUKey; - { Lock lock(_unusedFilesMutex); - _unusedFiles.insert({ file->_LRUKey, file }); + _unusedFiles.insert(file); _numUnusedFiles += 1; _unusedFilesSize += file->getLength(); } + clean(); emit dirty(); } -void FileCache::removeUnusedFile(const FilePointer file) { +void FileCache::removeUnusedFile(const FilePointer& file) { Lock lock(_unusedFilesMutex); - const auto it = _unusedFiles.find(file->_LRUKey); - if (it != _unusedFiles.cend()) { - _unusedFiles.erase(it); + if (_unusedFiles.erase(file)) { _numUnusedFiles -= 1; _unusedFilesSize -= file->getLength(); } } -void FileCache::reserve(size_t length) { +size_t FileCache::getOverbudgetAmount() const { + size_t result = 0; + + size_t currentFreeSpace = QStorageInfo(_dirpath.c_str()).bytesFree(); + if (_minFreeSpaceSize > currentFreeSpace) { + result = _minFreeSpaceSize - currentFreeSpace; + } + + if (_totalFilesSize > _maxSize) { + result = std::max(_totalFilesSize - _maxSize, result); + } + + return result; +} + +namespace cache { + struct FilePointerComparator { + bool operator()(const FilePointer& a, const FilePointer& b) { + return a->_modified > b->_modified; + } + }; +} + +void FileCache::clean() { + size_t overbudgetAmount = getOverbudgetAmount(); + + // Avoid sorting the unused files by LRU if we're not over budget / under free space + if (0 == overbudgetAmount) { + return; + } + Lock unusedLock(_unusedFilesMutex); - while (!_unusedFiles.empty() && - _unusedFilesSize + length > _unusedFilesMaxSize) { - auto it = _unusedFiles.begin(); - auto file = it->second; + using Queue = std::priority_queue, FilePointerComparator>; + Queue queue; + for (const auto& file : _unusedFiles) { + queue.push(file); + } + while (!queue.empty() && overbudgetAmount > 0) { + auto file = queue.top(); + queue.pop(); auto length = file->getLength(); unusedLock.unlock(); @@ -203,34 +263,32 @@ void FileCache::reserve(size_t length) { } unusedLock.lock(); - _unusedFiles.erase(it); + _unusedFiles.erase(file); _numTotalFiles -= 1; _numUnusedFiles -= 1; _totalFilesSize -= length; _unusedFilesSize -= length; + overbudgetAmount -= std::min(length, overbudgetAmount); } } void FileCache::clear() { - Lock unusedFilesLock(_unusedFilesMutex); - for (const auto& pair : _unusedFiles) { - auto& file = pair.second; - file->_cache = nullptr; + // Eliminate any overbudget files + clean(); - if (_totalFilesSize > _offlineFilesMaxSize) { - _totalFilesSize -= file->getLength(); - } else { - file->_shouldPersist = true; - qCDebug(file_cache, "[%s] Persisting %s", _dirname.c_str(), file->getKey().c_str()); - } + // Mark everything remaining as persisted + Lock unusedFilesLock(_unusedFilesMutex); + for (auto& file : _unusedFiles) { + file->_shouldPersist = true; + file->_cache = nullptr; + qCDebug(file_cache, "[%s] Persisting %s", _dirname.c_str(), file->getKey().c_str()); } _unusedFiles.clear(); } void File::deleter() { if (_cache) { - FilePointer self(this, &fileDeleter); - _cache->addUnusedFile(self); + _cache->addUnusedFile(FilePointer(this, std::mem_fn(&File::deleter))); } else { deleteLater(); } @@ -239,7 +297,9 @@ void File::deleter() { File::File(Metadata&& metadata, const std::string& filepath) : _key(std::move(metadata.key)), _length(metadata.length), - _filepath(filepath) {} + _filepath(filepath), + _modified(QFileInfo(_filepath.c_str()).lastRead().toMSecsSinceEpoch()) { +} File::~File() { QFile file(getFilepath().c_str()); @@ -248,3 +308,8 @@ File::~File() { file.remove(); } } + +void File::touch() { + utime(_filepath.c_str(), nullptr); + _modified = std::max(QFileInfo(_filepath.c_str()).lastRead().toMSecsSinceEpoch(), _modified); +} \ No newline at end of file diff --git a/libraries/networking/src/FileCache.h b/libraries/networking/src/FileCache.h index 908ddcd285..040e1ab592 100644 --- a/libraries/networking/src/FileCache.h +++ b/libraries/networking/src/FileCache.h @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -35,24 +36,28 @@ class FileCache : public QObject { Q_PROPERTY(size_t sizeTotal READ getSizeTotalFiles NOTIFY dirty) Q_PROPERTY(size_t sizeCached READ getSizeCachedFiles NOTIFY dirty) - static const size_t DEFAULT_UNUSED_MAX_SIZE; - static const size_t MAX_UNUSED_MAX_SIZE; - static const size_t DEFAULT_OFFLINE_MAX_SIZE; + static const size_t DEFAULT_MAX_SIZE; + static const size_t MAX_MAX_SIZE; + static const size_t DEFAULT_MIN_FREE_STORAGE_SPACE; public: + // You can initialize the FileCache with a directory name (ex.: "temp_jpgs") that will be relative to the application local data, OR with a full path + // The file cache will ignore any file that doesn't match the extension provided + FileCache(const std::string& dirname, const std::string& ext, QObject* parent = nullptr); + virtual ~FileCache(); + size_t getNumTotalFiles() const { return _numTotalFiles; } size_t getNumCachedFiles() const { return _numUnusedFiles; } size_t getSizeTotalFiles() const { return _totalFilesSize; } size_t getSizeCachedFiles() const { return _unusedFilesSize; } - void setUnusedFileCacheSize(size_t unusedFilesMaxSize); - size_t getUnusedFileCacheSize() const { return _unusedFilesSize; } + // Set the maximum amount of disk space to use on disk + void setMaxSize(size_t maxCacheSize); - void setOfflineFileCacheSize(size_t offlineFilesMaxSize); - - // initialize FileCache with a directory name (not a path, ex.: "temp_jpgs") and an ext (ex.: "jpg") - FileCache(const std::string& dirname, const std::string& ext, QObject* parent = nullptr); - virtual ~FileCache(); + // Set the minumum amount of free disk space to retain. This supercedes the max size, + // so if the cache is consuming all but 500 MB of the drive, unused entries will be ejected + // to free up more space, regardless of the cache max size + void setMinFreeSize(size_t size); using Key = std::string; struct Metadata { @@ -76,10 +81,11 @@ public: signals: void dirty(); -protected: +public: /// must be called after construction to create the cache on the fs and restore persisted files void initialize(); + // Add file to the cache and return the cache entry. FilePointer writeFile(const char* data, Metadata&& metadata, bool overwrite = false); FilePointer getFile(const Key& key); @@ -95,11 +101,17 @@ private: std::string getFilepath(const Key& key); FilePointer addFile(Metadata&& metadata, const std::string& filepath); - void addUnusedFile(const FilePointer file); - void removeUnusedFile(const FilePointer file); - void reserve(size_t length); + void addUnusedFile(const FilePointer& file); + void removeUnusedFile(const FilePointer& file); + void clean(); void clear(); + size_t getOverbudgetAmount() const; + + // FIXME it might be desirable to have the min free space variable be static so it can be + // shared among multiple instances of FileCache + std::atomic _minFreeSpaceSize { DEFAULT_MIN_FREE_STORAGE_SPACE }; + std::atomic _maxSize { DEFAULT_MAX_SIZE }; std::atomic _numTotalFiles { 0 }; std::atomic _numUnusedFiles { 0 }; std::atomic _totalFilesSize { 0 }; @@ -113,12 +125,8 @@ private: std::unordered_map> _files; Mutex _filesMutex; - std::map _unusedFiles; + std::unordered_set _unusedFiles; Mutex _unusedFilesMutex; - size_t _unusedFilesMaxSize { DEFAULT_UNUSED_MAX_SIZE }; - int _lastLRUKey { 0 }; - - size_t _offlineFilesMaxSize { DEFAULT_OFFLINE_MAX_SIZE }; }; class File : public QObject { @@ -142,13 +150,15 @@ protected: private: friend class FileCache; + friend struct FilePointerComparator; const Key _key; const size_t _length; const std::string _filepath; - FileCache* _cache; - int _LRUKey { 0 }; + void touch(); + FileCache* _cache { nullptr }; + int64_t _modified { 0 }; bool _shouldPersist { false }; }; diff --git a/libraries/shared/src/NumericalConstants.h b/libraries/shared/src/NumericalConstants.h index d37e1e31c5..e2d2409d56 100644 --- a/libraries/shared/src/NumericalConstants.h +++ b/libraries/shared/src/NumericalConstants.h @@ -50,10 +50,13 @@ const int KILO_PER_MEGA = 1000; #define KB_TO_BYTES_SHIFT 10 #define MB_TO_BYTES_SHIFT 20 +#define GB_TO_BYTES_SHIFT 30 +#define GB_TO_BYTES(X) ((size_t)(X) << GB_TO_BYTES_SHIFT) #define MB_TO_BYTES(X) ((size_t)(X) << MB_TO_BYTES_SHIFT) #define KB_TO_BYTES(X) ((size_t)(X) << KB_TO_BYTES_SHIFT) +#define BYTES_TO_GB(X) (X >> GB_TO_BYTES_SHIFT) #define BYTES_TO_MB(X) (X >> MB_TO_BYTES_SHIFT) #define BYTES_TO_KB(X) (X >> KB_TO_BYTES_SHIFT) diff --git a/tests/networking/src/FileCacheTests.cpp b/tests/networking/src/FileCacheTests.cpp new file mode 100644 index 0000000000..0813d05a54 --- /dev/null +++ b/tests/networking/src/FileCacheTests.cpp @@ -0,0 +1,170 @@ +// +// ResoruceTests.cpp +// +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + + +#include "FileCacheTests.h" + +#include + +QTEST_GUILESS_MAIN(FileCacheTests) + +using namespace cache; + +// Limit the file size to 10 MB +static const size_t MAX_UNUSED_SIZE { 1024 * 1024 * 10 }; +static const QByteArray TEST_DATA { 1024 * 1024, '0' }; + +static std::string getFileKey(int i) { + return QString(QByteArray { 1, (char)i }.toHex()).toStdString(); +} + +class TestFile : public File { + using Parent = File; +public: + TestFile(Metadata&& metadata, const std::string& filepath) + : Parent(std::move(metadata), filepath) { + } +}; + +class TestFileCache : public FileCache { + using Parent = FileCache; + +public: + TestFileCache(const std::string& dirname, const std::string& ext, QObject* parent = nullptr) : Parent(dirname, ext, nullptr) { + initialize(); + } + + std::unique_ptr createFile(Metadata&& metadata, const std::string& filepath) override { + qCInfo(file_cache) << "Wrote KTX" << metadata.key.c_str(); + return std::unique_ptr(new TestFile(std::move(metadata), filepath)); + } +}; + +using CachePointer = std::shared_ptr; + +// The FileCache relies on deleteLater to clear unused files, but QTest classes don't run a conventional event loop +// so we need to call this function to force any pending deletes to occur in the File destructor +static void forceDeletes() { + while (QCoreApplication::hasPendingEvents()) { + QCoreApplication::sendPostedEvents(); + QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); + QCoreApplication::processEvents(); + } +} + +size_t FileCacheTests::getCacheDirectorySize() const { + size_t result = 0; + QDir dir(_testDir.path()); + for (const auto& file : dir.entryList({ "*.tmp" })) { + result += QFileInfo(dir.absoluteFilePath(file)).size(); + } + return result; +} + +CachePointer makeFileCache(QString& location) { + auto result = std::make_shared(location.toStdString(), "tmp"); + result->setMaxSize(MAX_UNUSED_SIZE); + return result; +} + +void FileCacheTests::initTestCase() { +} + +void FileCacheTests::testUnusedFiles() { + auto cache = makeFileCache(_testDir.path()); + std::list inUseFiles; + + { + for (int i = 0; i < 100; ++i) { + std::string key = getFileKey(i); + auto file = cache->writeFile(TEST_DATA.data(), TestFileCache::Metadata(key, TEST_DATA.size())); + inUseFiles.push_back(file); + forceDeletes(); + QThread::msleep(10); + } + QCOMPARE(cache->getNumCachedFiles(), (size_t)0); + QCOMPARE(cache->getNumTotalFiles(), (size_t)100); + // Release the in-use files + inUseFiles.clear(); + // Cache state is updated, but the directory state is unchanged, + // because the file deletes are triggered by an event loop + QCOMPARE(cache->getNumCachedFiles(), (size_t)10); + QCOMPARE(cache->getNumTotalFiles(), (size_t)10); + QVERIFY(getCacheDirectorySize() > MAX_UNUSED_SIZE); + forceDeletes(); + QCOMPARE(cache->getNumCachedFiles(), (size_t)10); + QCOMPARE(cache->getNumTotalFiles(), (size_t)10); + QVERIFY(getCacheDirectorySize() <= MAX_UNUSED_SIZE); + } + + // Reset the cache + cache = makeFileCache(_testDir.path()); + { + // Test files 0 to 89 are missing, because the LRU algorithm deleted them when we released the files + for (int i = 0; i < 90; ++i) { + std::string key = getFileKey(i); + auto file = cache->getFile(key); + QVERIFY(!file.get()); + } + + QThread::msleep(1000); + // Test files 90 to 99 are present + for (int i = 90; i < 100; ++i) { + std::string key = getFileKey(i); + auto file = cache->getFile(key); + QVERIFY(file.get()); + inUseFiles.push_back(file); + // Each access touches the file, so we need to sleep here to ensure that the files are + // spaced out in numeric order, otherwise later tests can't reliably determine the order + // for cache ejection + QThread::msleep(1000); + } + QCOMPARE(cache->getNumCachedFiles(), (size_t)0); + QCOMPARE(cache->getNumTotalFiles(), (size_t)10); + inUseFiles.clear(); + QCOMPARE(cache->getNumCachedFiles(), (size_t)10); + QCOMPARE(cache->getNumTotalFiles(), (size_t)10); + } +} + +size_t FileCacheTests::getFreeSpace() const { + return QStorageInfo(_testDir.path()).bytesFree(); +} + +// FIXME if something else is changing the amount of free space on the target drive concurrently with this test +// running, then it may fail +void FileCacheTests::testFreeSpacePreservation() { + QCOMPARE(getCacheDirectorySize(), MAX_UNUSED_SIZE); + // Set the target free space to slightly above whatever the current free space is... + size_t targetFreeSpace = getFreeSpace() + MAX_UNUSED_SIZE / 2; + + // Reset the cache + auto cache = makeFileCache(_testDir.path()); + // Setting the min free space causes it to eject the oldest files that cause the cache to exceed the minimum space + cache->setMinFreeSize(targetFreeSpace); + QVERIFY(getFreeSpace() < targetFreeSpace); + forceDeletes(); + QCOMPARE(cache->getNumCachedFiles(), (size_t)5); + QCOMPARE(cache->getNumTotalFiles(), (size_t)5); + QVERIFY(getFreeSpace() >= targetFreeSpace); + for (int i = 0; i < 95; ++i) { + std::string key = getFileKey(i); + auto file = cache->getFile(key); + QVERIFY(!file.get()); + } + for (int i = 95; i < 100; ++i) { + std::string key = getFileKey(i); + auto file = cache->getFile(key); + QVERIFY(file.get()); + } +} + +void FileCacheTests::cleanupTestCase() { +} + diff --git a/tests/networking/src/FileCacheTests.h b/tests/networking/src/FileCacheTests.h new file mode 100644 index 0000000000..838c15afb8 --- /dev/null +++ b/tests/networking/src/FileCacheTests.h @@ -0,0 +1,30 @@ +// +// ResourceTests.h +// +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_ResourceTests_h +#define hifi_ResourceTests_h + +#include +#include + +class FileCacheTests : public QObject { + Q_OBJECT +private slots: + void initTestCase(); + void testUnusedFiles(); + void testFreeSpacePreservation(); + void cleanupTestCase(); + +private: + size_t getFreeSpace() const; + size_t getCacheDirectorySize() const; + QTemporaryDir _testDir; +}; + +#endif // hifi_ResourceTests_h From cca0fa66004464713641aef86bd73a75ba4963bd Mon Sep 17 00:00:00 2001 From: Dante Ruiz Date: Thu, 1 Jun 2017 00:23:37 +0100 Subject: [PATCH 10/13] fixed some input recorder design issue --- .../controllers/src/controllers/Actions.h | 4 +- .../src/controllers/InputRecorder.cpp | 120 +++++++++++++----- .../src/controllers/InputRecorder.h | 20 +-- .../impl/endpoints/ActionEndpoint.cpp | 14 +- 4 files changed, 111 insertions(+), 47 deletions(-) diff --git a/libraries/controllers/src/controllers/Actions.h b/libraries/controllers/src/controllers/Actions.h index 5727d4906e..2cb500c42a 100644 --- a/libraries/controllers/src/controllers/Actions.h +++ b/libraries/controllers/src/controllers/Actions.h @@ -42,8 +42,6 @@ enum class Action { LEFT_HAND = NUM_COMBINED_AXES, RIGHT_HAND, - LEFT_ARM, - RIGHT_ARM, LEFT_FOOT, RIGHT_FOOT, HIPS, @@ -103,6 +101,8 @@ enum class Action { // Bisected aliases for TRANSLATE_CAMERA_Z BOOM_IN, BOOM_OUT, + LEFT_ARM, + RIGHT_ARM, NUM_ACTIONS, diff --git a/libraries/controllers/src/controllers/InputRecorder.cpp b/libraries/controllers/src/controllers/InputRecorder.cpp index 60ff592144..a5bd58196d 100644 --- a/libraries/controllers/src/controllers/InputRecorder.cpp +++ b/libraries/controllers/src/controllers/InputRecorder.cpp @@ -9,7 +9,6 @@ #include "InputRecorder.h" #include -#include #include #include #include @@ -19,9 +18,12 @@ #include #include #include +#include #include #include +#include +#include "UserInputMapper.h" QString SAVE_DIRECTORY = QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation) + "/" + BuildInfo::MODIFIED_ORGANIZATION + "/" + BuildInfo::INTERFACE_NAME + "/hifi-input-recordings/"; QString FILE_PREFIX_NAME = "input-recording-"; @@ -108,6 +110,7 @@ namespace controller { QJsonDocument saveData(object); QByteArray compressedData = qCompress(saveData.toJson(QJsonDocument::Compact)); saveFile.write(compressedData); + saveFile.close(); } QJsonObject openFile(const QString& file, bool& status) { @@ -122,6 +125,7 @@ namespace controller { QJsonDocument jsonDoc = QJsonDocument::fromJson(compressedData); object = jsonDoc.object(); status = true; + openFile.close(); return object; } @@ -146,31 +150,43 @@ namespace controller { _actionStateList.clear(); } - void InputRecorder::saveRecording() { + QJsonObject InputRecorder::recordDataToJson() { QJsonObject data; data["frameCount"] = _framesRecorded; - + data["version"] = "1.0"; + QJsonArray actionArrayList; QJsonArray poseArrayList; for(const ActionStates actionState: _actionStateList) { QJsonArray actionArray; - for (const float value: actionState) { - actionArray.append(value); + for (const auto action: actionState) { + QJsonObject actionJson; + actionJson["name"] = action.first; + actionJson["value"] = action.second; + actionArray.append(actionJson); } actionArrayList.append(actionArray); } for (const PoseStates poseState: _poseStateList) { QJsonArray poseArray; - for (const Pose pose: poseState) { - poseArray.append(poseToJsonObject(pose)); + for (const auto pose: poseState) { + QJsonObject poseJson; + poseJson["name"] = pose.first; + poseJson["pose"] = poseToJsonObject(pose.second); + poseArray.append(poseJson); } poseArrayList.append(poseArray); } data["actionList"] = actionArrayList; data["poseList"] = poseArrayList; - exportToFile(data); + + return data; + } + + void InputRecorder::saveRecording() { + exportToFile(recordDataToJson()); } void InputRecorder::loadRecording(const QString& path) { @@ -181,8 +197,8 @@ namespace controller { resetFrame(); _poseStateList.clear(); _actionStateList.clear(); - QString filePath = path; - filePath.remove(0,8); + QUrl urlPath(path); + QString filePath = urlPath.toLocalFile(); QFileInfo info(filePath); QString extension = info.suffix(); if (extension != "gz") { @@ -190,8 +206,9 @@ namespace controller { return; } bool success = false; - QJsonObject data = openFile(info.absoluteFilePath(), success); - if (success) { + QJsonObject data = openFile(filePath, success); + auto keyValue = data.find("version"); + if (success && keyValue != data.end()) { _framesRecorded = data["frameCount"].toInt(); QJsonArray actionArrayList = data["actionList"].toArray(); QJsonArray poseArrayList = data["poseList"].toArray(); @@ -199,25 +216,71 @@ namespace controller { for (int actionIndex = 0; actionIndex < actionArrayList.size(); actionIndex++) { QJsonArray actionState = actionArrayList[actionIndex].toArray(); for (int index = 0; index < actionState.size(); index++) { - _currentFrameActions[index] = actionState[index].toDouble(); + QJsonObject actionObject = actionState[index].toObject();; + _currentFrameActions[actionObject["name"].toString()] = actionObject["value"].toDouble(); } _actionStateList.push_back(_currentFrameActions); - _currentFrameActions = ActionStates(toInt(Action::NUM_ACTIONS)); + _currentFrameActions.clear(); } for (int poseIndex = 0; poseIndex < poseArrayList.size(); poseIndex++) { QJsonArray poseState = poseArrayList[poseIndex].toArray(); for (int index = 0; index < poseState.size(); index++) { - _currentFramePoses[index] = jsonObjectToPose(poseState[index].toObject()); + QJsonObject poseObject = poseState[index].toObject(); + _currentFramePoses[poseObject["name"].toString()] = jsonObjectToPose(poseObject["pose"].toObject()); } _poseStateList.push_back(_currentFramePoses); - _currentFramePoses = PoseStates(toInt(Action::NUM_ACTIONS)); + _currentFramePoses.clear(); } + } else if (success) { + //convert recording to new reacording standard and rewrite file + auto userInputMapper = DependencyManager::get(); + _framesRecorded = data["frameCount"].toInt(); + QJsonArray actionArrayList = data["actionList"].toArray(); + QJsonArray poseArrayList = data["poseList"].toArray(); + + for (int actionIndex = 0; actionIndex < actionArrayList.size(); actionIndex++) { + QJsonArray actionState = actionArrayList[actionIndex].toArray(); + for (int index = 0; index < actionState.size(); index++) { + QString actionName = userInputMapper->getActionName(Action(index)); + _currentFrameActions[actionName] = actionState[index].toDouble(); + } + _actionStateList.push_back(_currentFrameActions); + _currentFrameActions.clear(); + } + + for (int poseIndex = 0; poseIndex < poseArrayList.size(); poseIndex++) { + QJsonArray poseState = poseArrayList[poseIndex].toArray(); + for (int index = 0; index < poseState.size(); index++) { + QString actionName = userInputMapper->getActionName(Action(index)); + _currentFramePoses[actionName] = jsonObjectToPose(poseState[index].toObject()); + } + _poseStateList.push_back(_currentFramePoses); + _currentFramePoses.clear(); + } + + convertFile(filePath); } _loading = false; } + void InputRecorder::convertFile(const QString& path) { + if (!QDir(SAVE_DIRECTORY).exists()) { + QDir().mkdir(SAVE_DIRECTORY); + } + + QJsonObject data = recordDataToJson(); + QFile saveFile (path); + if (!saveFile.open(QIODevice::WriteOnly | QIODevice::Truncate)) { + qWarning() << "could not open file: " << path; + return; + } + QJsonDocument saveData(data); + QByteArray compressedData = qCompress(saveData.toJson(QJsonDocument::Compact)); + saveFile.write(compressedData); + } + void InputRecorder::stopRecording() { _recording = false; _framesRecorded = (int)_actionStateList.size(); @@ -234,41 +297,36 @@ namespace controller { _playCount = 0; } - void InputRecorder::setActionState(controller::Action action, float value) { + void InputRecorder::setActionState(const QString& action, float value) { if (_recording) { - _currentFrameActions[toInt(action)] += value; + _currentFrameActions[action] += value; } } - void InputRecorder::setActionState(controller::Action action, const controller::Pose pose) { + void InputRecorder::setActionState(const QString& action, const controller::Pose& pose) { if (_recording) { - _currentFramePoses[toInt(action)] = pose; + _currentFramePoses[action] = pose; } } void InputRecorder::resetFrame() { if (_recording) { - for(auto& channel : _currentFramePoses) { - channel = Pose(); - } - - for(auto& channel : _currentFrameActions) { - channel = 0.0f; - } + _currentFramePoses.clear(); + _currentFrameActions.clear(); } } - float InputRecorder::getActionState(controller::Action action) { + float InputRecorder::getActionState(const QString& action) { if (_actionStateList.size() > 0 ) { - return _actionStateList[_playCount][toInt(action)]; + return _actionStateList[_playCount][action]; } return 0.0f; } - controller::Pose InputRecorder::getPoseState(controller::Action action) { + controller::Pose InputRecorder::getPoseState(const QString& action) { if (_poseStateList.size() > 0) { - return _poseStateList[_playCount][toInt(action)]; + return _poseStateList[_playCount][action]; } return Pose(); diff --git a/libraries/controllers/src/controllers/InputRecorder.h b/libraries/controllers/src/controllers/InputRecorder.h index d1cc9a32eb..a8dd12724a 100644 --- a/libraries/controllers/src/controllers/InputRecorder.h +++ b/libraries/controllers/src/controllers/InputRecorder.h @@ -12,8 +12,10 @@ #include #include #include +#include #include +#include #include "Pose.h" #include "Actions.h" @@ -21,8 +23,8 @@ namespace controller { class InputRecorder { public: - using PoseStates = std::vector; - using ActionStates = std::vector; + using PoseStates = std::map; + using ActionStates = std::map; InputRecorder(); ~InputRecorder(); @@ -31,6 +33,7 @@ namespace controller { void saveRecording(); void loadRecording(const QString& path); + void convertFile(const QString& path); void startRecording(); void startPlayback(); void stopPlayback(); @@ -40,20 +43,21 @@ namespace controller { void resetFrame(); bool isRecording() { return _recording; } bool isPlayingback() { return (_playback && !_loading); } - void setActionState(controller::Action action, float value); - void setActionState(controller::Action action, const controller::Pose pose); - float getActionState(controller::Action action); - controller::Pose getPoseState(controller::Action action); + void setActionState(const QString& action, float value); + void setActionState(const QString& action, const controller::Pose& pose); + float getActionState(const QString& action); + controller::Pose getPoseState(const QString& action); QString getSaveDirectory(); void frameTick(); private: + QJsonObject recordDataToJson(); bool _recording { false }; bool _playback { false }; bool _loading { false }; std::vector _poseStateList = std::vector(); std::vector _actionStateList = std::vector(); - PoseStates _currentFramePoses = PoseStates(toInt(Action::NUM_ACTIONS)); - ActionStates _currentFrameActions = ActionStates(toInt(Action::NUM_ACTIONS)); + PoseStates _currentFramePoses; + ActionStates _currentFrameActions; int _framesRecorded { 0 }; int _playCount { 0 }; diff --git a/libraries/controllers/src/controllers/impl/endpoints/ActionEndpoint.cpp b/libraries/controllers/src/controllers/impl/endpoints/ActionEndpoint.cpp index 6c14533f02..eb834a0bf2 100644 --- a/libraries/controllers/src/controllers/impl/endpoints/ActionEndpoint.cpp +++ b/libraries/controllers/src/controllers/impl/endpoints/ActionEndpoint.cpp @@ -17,31 +17,33 @@ using namespace controller; void ActionEndpoint::apply(float newValue, const Pointer& source) { InputRecorder* inputRecorder = InputRecorder::getInstance(); + auto userInputMapper = DependencyManager::get(); + QString actionName = userInputMapper->getActionName(Action(_input.getChannel())); if(inputRecorder->isPlayingback()) { - newValue = inputRecorder->getActionState(Action(_input.getChannel())); + newValue = inputRecorder->getActionState(actionName); } _currentValue += newValue; if (_input != Input::INVALID_INPUT) { - auto userInputMapper = DependencyManager::get(); userInputMapper->deltaActionState(Action(_input.getChannel()), newValue); } - inputRecorder->setActionState(Action(_input.getChannel()), newValue); + inputRecorder->setActionState(actionName, newValue); } void ActionEndpoint::apply(const Pose& value, const Pointer& source) { _currentPose = value; InputRecorder* inputRecorder = InputRecorder::getInstance(); - inputRecorder->setActionState(Action(_input.getChannel()), _currentPose); + auto userInputMapper = DependencyManager::get(); + QString actionName = userInputMapper->getActionName(Action(_input.getChannel())); + inputRecorder->setActionState(actionName, _currentPose); if (inputRecorder->isPlayingback()) { - _currentPose = inputRecorder->getPoseState(Action(_input.getChannel())); + _currentPose = inputRecorder->getPoseState(actionName); } if (!_currentPose.isValid()) { return; } if (_input != Input::INVALID_INPUT) { - auto userInputMapper = DependencyManager::get(); userInputMapper->setActionState(Action(_input.getChannel()), _currentPose); } } From c686acc071002c14fe0410c8a0ca778cd8276824 Mon Sep 17 00:00:00 2001 From: Dante Ruiz Date: Thu, 1 Jun 2017 18:20:56 +0100 Subject: [PATCH 11/13] fixing build issue --- libraries/controllers/src/controllers/InputRecorder.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/libraries/controllers/src/controllers/InputRecorder.cpp b/libraries/controllers/src/controllers/InputRecorder.cpp index a5bd58196d..c0a3adb4b1 100644 --- a/libraries/controllers/src/controllers/InputRecorder.cpp +++ b/libraries/controllers/src/controllers/InputRecorder.cpp @@ -93,7 +93,7 @@ namespace controller { } - void exportToFile(QJsonObject& object) { + void exportToFile(const QJsonObject& object) { if (!QDir(SAVE_DIRECTORY).exists()) { QDir().mkdir(SAVE_DIRECTORY); } @@ -186,7 +186,8 @@ namespace controller { } void InputRecorder::saveRecording() { - exportToFile(recordDataToJson()); + QJsonObject jsonData = recordDataToJson(); + exportToFile(jsonData); } void InputRecorder::loadRecording(const QString& path) { From 0e82b33d6d4cf9cadccde4abd9b33a127d0003b3 Mon Sep 17 00:00:00 2001 From: Dante Ruiz Date: Thu, 1 Jun 2017 19:00:31 +0100 Subject: [PATCH 12/13] final changes --- .../src/controllers/InputRecorder.cpp | 20 +------------------ .../src/controllers/InputRecorder.h | 1 - 2 files changed, 1 insertion(+), 20 deletions(-) diff --git a/libraries/controllers/src/controllers/InputRecorder.cpp b/libraries/controllers/src/controllers/InputRecorder.cpp index c0a3adb4b1..e8bcd3a006 100644 --- a/libraries/controllers/src/controllers/InputRecorder.cpp +++ b/libraries/controllers/src/controllers/InputRecorder.cpp @@ -259,34 +259,16 @@ namespace controller { _poseStateList.push_back(_currentFramePoses); _currentFramePoses.clear(); } - - convertFile(filePath); } _loading = false; } - - void InputRecorder::convertFile(const QString& path) { - if (!QDir(SAVE_DIRECTORY).exists()) { - QDir().mkdir(SAVE_DIRECTORY); - } - - QJsonObject data = recordDataToJson(); - QFile saveFile (path); - if (!saveFile.open(QIODevice::WriteOnly | QIODevice::Truncate)) { - qWarning() << "could not open file: " << path; - return; - } - QJsonDocument saveData(data); - QByteArray compressedData = qCompress(saveData.toJson(QJsonDocument::Compact)); - saveFile.write(compressedData); - } void InputRecorder::stopRecording() { _recording = false; _framesRecorded = (int)_actionStateList.size(); } - + void InputRecorder::startPlayback() { _playback = true; _recording = false; diff --git a/libraries/controllers/src/controllers/InputRecorder.h b/libraries/controllers/src/controllers/InputRecorder.h index a8dd12724a..9adb8e386f 100644 --- a/libraries/controllers/src/controllers/InputRecorder.h +++ b/libraries/controllers/src/controllers/InputRecorder.h @@ -33,7 +33,6 @@ namespace controller { void saveRecording(); void loadRecording(const QString& path); - void convertFile(const QString& path); void startRecording(); void startPlayback(); void stopPlayback(); From 725388043f2cd1d7ebbf82f977a8fdfd82e2c331 Mon Sep 17 00:00:00 2001 From: Dante Ruiz Date: Thu, 1 Jun 2017 22:30:50 +0100 Subject: [PATCH 13/13] removed double semi-colons --- libraries/controllers/src/controllers/InputRecorder.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/controllers/src/controllers/InputRecorder.cpp b/libraries/controllers/src/controllers/InputRecorder.cpp index e8bcd3a006..dbc2e4df52 100644 --- a/libraries/controllers/src/controllers/InputRecorder.cpp +++ b/libraries/controllers/src/controllers/InputRecorder.cpp @@ -217,7 +217,7 @@ namespace controller { for (int actionIndex = 0; actionIndex < actionArrayList.size(); actionIndex++) { QJsonArray actionState = actionArrayList[actionIndex].toArray(); for (int index = 0; index < actionState.size(); index++) { - QJsonObject actionObject = actionState[index].toObject();; + QJsonObject actionObject = actionState[index].toObject(); _currentFrameActions[actionObject["name"].toString()] = actionObject["value"].toDouble(); } _actionStateList.push_back(_currentFrameActions);