diff --git a/assignment-client/src/octree/OctreeServer.cpp b/assignment-client/src/octree/OctreeServer.cpp index 85d4749b27..7cca9d3ef5 100644 --- a/assignment-client/src/octree/OctreeServer.cpp +++ b/assignment-client/src/octree/OctreeServer.cpp @@ -1036,6 +1036,13 @@ void OctreeServer::readConfiguration() { strcpy(_persistFilename, qPrintable(persistFilename)); qDebug("persistFilename=%s", _persistFilename); + QString persistAsFileType; + if (!readOptionString(QString("persistAsFileType"), settingsSectionObject, persistAsFileType)) { + persistAsFileType = "svo"; + } + _persistAsFileType = persistAsFileType; + qDebug() << "persistAsFileType=" << _persistAsFileType; + _persistInterval = OctreePersistThread::DEFAULT_PERSIST_INTERVAL; readOptionInt(QString("persistInterval"), settingsSectionObject, _persistInterval); qDebug() << "persistInterval=" << _persistInterval; @@ -1131,7 +1138,7 @@ void OctreeServer::run() { // now set up PersistThread _persistThread = new OctreePersistThread(_tree, _persistFilename, _persistInterval, - _wantBackup, _settings, _debugTimestampNow); + _wantBackup, _settings, _debugTimestampNow, _persistAsFileType); if (_persistThread) { _persistThread->initialize(true); } diff --git a/assignment-client/src/octree/OctreeServer.h b/assignment-client/src/octree/OctreeServer.h index 0f90c2941e..41cd3259cf 100644 --- a/assignment-client/src/octree/OctreeServer.h +++ b/assignment-client/src/octree/OctreeServer.h @@ -157,6 +157,7 @@ protected: QString _statusHost; char _persistFilename[MAX_FILENAME_LENGTH]; + QString _persistAsFileType; int _packetsPerClientPerInterval; int _packetsTotalPerInterval; Octree* _tree; // this IS a reaveraging tree diff --git a/domain-server/resources/describe-settings.json b/domain-server/resources/describe-settings.json index fdaede8c44..9af44f6a1b 100644 --- a/domain-server/resources/describe-settings.json +++ b/domain-server/resources/describe-settings.json @@ -325,6 +325,24 @@ "default": "resources/models.svo", "advanced": true }, + { + "name": "persistAsFileType", + "label": "File format for entity server's persistent data", + "help": "This defines how the entity server will save entities to disk.", + "default": "svo", + "type": "select", + "options": [ + { + "value": "svo", + "label": "Entity server persists data as SVO" + }, + { + "value": "json", + "label": "Entity server persists data as JSON" + } + ], + "advanced": true + }, { "name": "persistInterval", "label": "Save Check Interval", diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 571cf493bd..81ffe021b2 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1798,7 +1798,7 @@ bool Application::importEntities(const QString& urlOrFilename) { url = QUrl::fromLocalFile(urlOrFilename); } - bool success = _entityClipboard.readFromSVOURL(url.toString()); + bool success = _entityClipboard.readFromURL(url.toString()); if (success) { _entityClipboard.reaverageOctreeElements(); } @@ -2795,7 +2795,27 @@ QImage Application::renderAvatarBillboard() { return image; } +// FIXME, preprocessor guard this check to occur only in DEBUG builds +static QThread * activeRenderingThread = nullptr; + +ViewFrustum* Application::getViewFrustum() { + if (QThread::currentThread() == activeRenderingThread) { + // FIXME, should this be an assert? + qWarning() << "Calling Application::getViewFrustum() from the active rendering thread, did you mean Application::getDisplayViewFrustum()?"; + } + return &_viewFrustum; +} + +ViewFrustum* Application::getDisplayViewFrustum() { + if (QThread::currentThread() != activeRenderingThread) { + // FIXME, should this be an assert? + qWarning() << "Calling Application::getDisplayViewFrustum() from outside the active rendering thread or outside rendering, did you mean Application::getViewFrustum()?"; + } + return &_displayViewFrustum; +} + void Application::displaySide(Camera& theCamera, bool selfAvatarOnly, RenderArgs::RenderSide renderSide) { + activeRenderingThread = QThread::currentThread(); PROFILE_RANGE(__FUNCTION__); PerformanceTimer perfTimer("display"); PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), "Application::displaySide()"); @@ -3020,6 +3040,7 @@ void Application::displaySide(Camera& theCamera, bool selfAvatarOnly, RenderArgs glClear(GL_DEPTH_BUFFER_BIT); _overlays.renderWorld(true); } + activeRenderingThread = nullptr; } void Application::updateUntranslatedViewMatrix(const glm::vec3& viewMatrixTranslation) { diff --git a/interface/src/Application.h b/interface/src/Application.h index b013692393..ae68374fed 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -174,8 +174,12 @@ public: bool isThrottleRendering() const { return _glWidget->isThrottleRendering(); } Camera* getCamera() { return &_myCamera; } - ViewFrustum* getViewFrustum() { return &_viewFrustum; } - ViewFrustum* getDisplayViewFrustum() { return &_displayViewFrustum; } + // Represents the current view frustum of the avatar. + ViewFrustum* getViewFrustum(); + // Represents the view frustum of the current rendering pass, + // which might be different from the viewFrustum, i.e. shadowmap + // passes, mirror window passes, etc + ViewFrustum* getDisplayViewFrustum(); ViewFrustum* getShadowViewFrustum() { return &_shadowViewFrustum; } const OctreePacketProcessor& getOctreePacketProcessor() const { return _octreeProcessor; } EntityTreeRenderer* getEntities() { return &_entities; } diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index 75d77b780a..431b6e0d37 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -338,7 +338,7 @@ void Avatar::render(const glm::vec3& cameraPosition, RenderMode renderMode, bool // simple frustum check float boundingRadius = getBillboardSize(); ViewFrustum* frustum = (renderMode == Avatar::SHADOW_RENDER_MODE) ? - Application::getInstance()->getShadowViewFrustum() : Application::getInstance()->getViewFrustum(); + Application::getInstance()->getShadowViewFrustum() : Application::getInstance()->getDisplayViewFrustum(); if (frustum->sphereInFrustum(getPosition(), boundingRadius) == ViewFrustum::OUTSIDE) { return; } @@ -1013,16 +1013,18 @@ float Avatar::getSkeletonHeight() const { } float Avatar::getHeadHeight() const { - Extents extents = getHead()->getFaceModel().getBindExtents(); + Extents extents = getHead()->getFaceModel().getMeshExtents(); if (!extents.isEmpty()) { return extents.maximum.y - extents.minimum.y; } + + extents = _skeletonModel.getMeshExtents(); glm::vec3 neckPosition; - glm::vec3 headPosition; - if (_skeletonModel.getNeckPosition(neckPosition) && _skeletonModel.getHeadPosition(headPosition)) { - return glm::distance(neckPosition, headPosition); + if (!extents.isEmpty() && _skeletonModel.getNeckPosition(neckPosition)) { + return extents.maximum.y / 2.0f - neckPosition.y + _position.y; } - const float DEFAULT_HEAD_HEIGHT = 0.1f; + + const float DEFAULT_HEAD_HEIGHT = 0.25f; return DEFAULT_HEAD_HEIGHT; } diff --git a/interface/src/ui/overlays/Overlays.cpp b/interface/src/ui/overlays/Overlays.cpp index c7b350f100..158deb00ff 100644 --- a/interface/src/ui/overlays/Overlays.cpp +++ b/interface/src/ui/overlays/Overlays.cpp @@ -122,7 +122,7 @@ void Overlays::renderWorld(bool drawFront, RenderArgs::RenderMode renderMode, Re float myAvatarScale = 1.0f; auto lodManager = DependencyManager::get(); - RenderArgs args = { NULL, Application::getInstance()->getViewFrustum(), + RenderArgs args = { NULL, Application::getInstance()->getDisplayViewFrustum(), lodManager->getOctreeSizeScale(), lodManager->getBoundaryLevelAdjust(), renderMode, renderSide, diff --git a/libraries/entities/src/EntityItemProperties.cpp b/libraries/entities/src/EntityItemProperties.cpp index 6571f0d472..dbb3cfa338 100644 --- a/libraries/entities/src/EntityItemProperties.cpp +++ b/libraries/entities/src/EntityItemProperties.cpp @@ -198,7 +198,9 @@ void buildStringToShapeTypeLookup() { } QString EntityItemProperties::getShapeTypeAsString() const { - return QString(shapeTypeNames[_shapeType]); + if (_shapeType < sizeof(shapeTypeNames) / sizeof(char *)) + return QString(shapeTypeNames[_shapeType]); + return QString("none"); } void EntityItemProperties::setShapeTypeFromString(const QString& shapeName) { diff --git a/libraries/entities/src/EntityScriptingInterface.cpp b/libraries/entities/src/EntityScriptingInterface.cpp index 2585b5d33e..5624e0765b 100644 --- a/libraries/entities/src/EntityScriptingInterface.cpp +++ b/libraries/entities/src/EntityScriptingInterface.cpp @@ -9,6 +9,8 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +#include + #include "EntityScriptingInterface.h" #include "EntityTree.h" #include "LightEntityItem.h" diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index 9be004828b..3ccff46a04 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -10,13 +10,18 @@ // #include +#include +#include #include "EntityTree.h" #include "EntitySimulation.h" +#include "VariantMapToScriptValue.h" #include "AddEntityOperator.h" #include "MovingEntitiesOperator.h" #include "UpdateEntityOperator.h" +#include "QVariantGLM.h" +#include "RecurseOctreeToMapOperator.h" EntityTree::EntityTree(bool shouldReaverage) : Octree(shouldReaverage), @@ -466,7 +471,7 @@ bool EntityTree::findNearPointOperation(OctreeElement* element, void* extraData) return true; // keep searching in case children have closer entities } - // if this element doesn't contain the point, then none of it's children can contain the point, so stop searching + // if this element doesn't contain the point, then none of its children can contain the point, so stop searching return false; } @@ -1080,3 +1085,41 @@ bool EntityTree::sendEntitiesOperation(OctreeElement* element, void* extraData) return true; } +bool EntityTree::writeToMap(QVariantMap& entityDescription, OctreeElement* element) { + entityDescription["Entities"] = QVariantList(); + QScriptEngine scriptEngine; + RecurseOctreeToMapOperator theOperator(entityDescription, element, &scriptEngine); + recurseTreeWithOperator(&theOperator); + return true; +} + +bool EntityTree::readFromMap(QVariantMap& map) { + // map will have a top-level list keyed as "Entities". This will be extracted + // and iterated over. Each member of this list is converted to a QVariantMap, then + // to a QScriptValue, and then to EntityItemProperties. These properties are used + // to add the new entity to the EnitytTree. + QVariantList entitiesQList = map["Entities"].toList(); + QScriptEngine scriptEngine; + + foreach (QVariant entityVariant, entitiesQList) { + // QVariantMap --> QScriptValue --> EntityItemProperties --> Entity + QVariantMap entityMap = entityVariant.toMap(); + QScriptValue entityScriptValue = variantMapToScriptValue(entityMap, scriptEngine); + EntityItemProperties properties; + EntityItemPropertiesFromScriptValue(entityScriptValue, properties); + + EntityItemID entityItemID; + if (entityMap.contains("id")) { + entityItemID = EntityItemID(QUuid(entityMap["id"].toString())); + } else { + entityItemID = EntityItemID(QUuid::createUuid()); + } + + EntityItem* entity = addEntity(entityItemID, properties); + if (!entity) { + qDebug() << "adding Entity failed:" << entityItemID << entity->getType(); + } + } + + return true; +} diff --git a/libraries/entities/src/EntityTree.h b/libraries/entities/src/EntityTree.h index 75a5daab4e..e1521ebd50 100644 --- a/libraries/entities/src/EntityTree.h +++ b/libraries/entities/src/EntityTree.h @@ -164,6 +164,9 @@ public: bool wantEditLogging() const { return _wantEditLogging; } void setWantEditLogging(bool value) { _wantEditLogging = value; } + bool writeToMap(QVariantMap& entityDescription, OctreeElement* element); + bool readFromMap(QVariantMap& entityDescription); + signals: void deletingEntity(const EntityItemID& entityID); void addingEntity(const EntityItemID& entityID); diff --git a/libraries/entities/src/RecurseOctreeToMapOperator.cpp b/libraries/entities/src/RecurseOctreeToMapOperator.cpp new file mode 100644 index 0000000000..afe28e17e0 --- /dev/null +++ b/libraries/entities/src/RecurseOctreeToMapOperator.cpp @@ -0,0 +1,54 @@ +// +// RecurseOctreeToMapOperator.cpp +// libraries/entities/src +// +// Created by Seth Alves on 3/16/15. +// Copyright 2013 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 "RecurseOctreeToMapOperator.h" + + +RecurseOctreeToMapOperator::RecurseOctreeToMapOperator(QVariantMap& map, OctreeElement *top, QScriptEngine *engine) : + RecurseOctreeOperator(), + _map(map), + _top(top), + _engine(engine) +{ + // if some element "top" was given, only save information for that element and it's children. + if (_top) { + _withinTop = false; + } else { + // top was NULL, export entire tree. + _withinTop = true; + } +}; + +bool RecurseOctreeToMapOperator::preRecursion(OctreeElement* element) { + if (element == _top) { + _withinTop = true; + } + return true; +} + +bool RecurseOctreeToMapOperator::postRecursion(OctreeElement* element) { + + EntityTreeElement* entityTreeElement = static_cast(element); + const QList& entities = entityTreeElement->getEntities(); + + QVariantList entitiesQList = qvariant_cast(_map["Entities"]); + + foreach (EntityItem* entityItem, entities) { + EntityItemProperties properties = entityItem->getProperties(); + QScriptValue qScriptValues = EntityItemPropertiesToScriptValue(_engine, properties); + entitiesQList << qScriptValues.toVariant(); + } + _map["Entities"] = entitiesQList; + if (element == _top) { + _withinTop = false; + } + return true; +} diff --git a/libraries/entities/src/RecurseOctreeToMapOperator.h b/libraries/entities/src/RecurseOctreeToMapOperator.h new file mode 100644 index 0000000000..6bd44f3cbf --- /dev/null +++ b/libraries/entities/src/RecurseOctreeToMapOperator.h @@ -0,0 +1,24 @@ +// +// RecurseOctreeToMapOperator.h +// libraries/entities/src +// +// Created by Seth Alves on 3/16/15. +// Copyright 2013 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 "EntityTree.h" + +class RecurseOctreeToMapOperator : public RecurseOctreeOperator { +public: + RecurseOctreeToMapOperator(QVariantMap& map, OctreeElement *top, QScriptEngine *engine); + bool preRecursion(OctreeElement* element); + bool postRecursion(OctreeElement* element); + private: + QVariantMap& _map; + OctreeElement *_top; + QScriptEngine *_engine; + bool _withinTop; +}; diff --git a/libraries/octree/src/Octree.cpp b/libraries/octree/src/Octree.cpp index 2b29529f4e..85ea09b5ef 100644 --- a/libraries/octree/src/Octree.cpp +++ b/libraries/octree/src/Octree.cpp @@ -27,6 +27,10 @@ #include #include #include +#include +#include +#include +#include #include #include @@ -35,6 +39,7 @@ #include #include #include +#include #include "CoverageMap.h" #include "OctreeConstants.h" @@ -42,6 +47,9 @@ #include "Octree.h" #include "ViewFrustum.h" + +QVector PERSIST_EXTENSIONS = {"svo", "json"}; + float boundaryDistanceForRenderLevel(unsigned int renderLevel, float voxelSizeScale) { return voxelSizeScale / powf(2, renderLevel); } @@ -1841,21 +1849,22 @@ int Octree::encodeTreeBitstreamRecursion(OctreeElement* element, return bytesAtThisLevel; } -bool Octree::readFromSVOFile(const char* fileName) { +bool Octree::readFromFile(const char* fileName) { bool fileOk = false; - QFile file(fileName); + QString qFileName = findMostRecentFileExtension(fileName, PERSIST_EXTENSIONS); + QFile file(qFileName); fileOk = file.open(QIODevice::ReadOnly); if(fileOk) { QDataStream fileInputStream(&file); - QFileInfo fileInfo(fileName); + QFileInfo fileInfo(qFileName); unsigned long fileLength = fileInfo.size(); emit importSize(1.0f, 1.0f, 1.0f); emit importProgress(0); - qDebug("Loading file %s...", fileName); + qDebug() << "Loading file" << qFileName << "..."; fileOk = readFromStream(fileLength, fileInputStream); @@ -1866,14 +1875,14 @@ bool Octree::readFromSVOFile(const char* fileName) { return fileOk; } -bool Octree::readFromSVOURL(const QString& urlString) { +bool Octree::readFromURL(const QString& urlString) { bool readOk = false; // determine if this is a local file or a network resource QUrl url(urlString); if (url.isLocalFile()) { - readOk = readFromSVOFile(qPrintable(url.toLocalFile())); + readOk = readFromFile(qPrintable(url.toLocalFile())); } else { QNetworkRequest request; request.setHeader(QNetworkRequest::UserAgentHeader, HIGH_FIDELITY_USER_AGENT); @@ -1899,6 +1908,23 @@ bool Octree::readFromSVOURL(const QString& urlString) { bool Octree::readFromStream(unsigned long streamLength, QDataStream& inputStream) { + + // decide if this is SVO or JSON + QIODevice *device = inputStream.device(); + char firstChar; + device->getChar(&firstChar); + device->ungetChar(firstChar); + + if (firstChar == (char) PacketTypeEntityData) { + return readSVOFromStream(streamLength, inputStream); + } else { + return readJSONFromStream(streamLength, inputStream); + } +} + + +bool Octree::readSVOFromStream(unsigned long streamLength, QDataStream& inputStream) { + bool fileOk = false; PacketVersion gotVersion = 0; @@ -2026,6 +2052,53 @@ bool Octree::readFromStream(unsigned long streamLength, QDataStream& inputStream return fileOk; } +bool Octree::readJSONFromStream(unsigned long streamLength, QDataStream& inputStream) { + char *rawData = new char[streamLength]; + inputStream.readRawData(rawData, streamLength); + QJsonDocument d = QJsonDocument::fromJson(rawData); + QVariant v = d.toVariant(); + QVariantMap m = v.toMap(); + readFromMap(m); + delete rawData; + return true; +} + +void Octree::writeToFile(const char* fileName, OctreeElement* element, QString persistAsFileType) { + // make the sure file extension makes sense + QString qFileName = fileNameWithoutExtension(QString(fileName), PERSIST_EXTENSIONS) + "." + persistAsFileType; + QByteArray byteArray = qFileName.toUtf8(); + const char* cFileName = byteArray.constData(); + + if (persistAsFileType == "svo") { + writeToSVOFile(fileName, element); + } else if (persistAsFileType == "json") { + writeToJSONFile(cFileName, element); + } else { + qDebug() << "unable to write octree to file of type" << persistAsFileType; + } +} + +void Octree::writeToJSONFile(const char* fileName, OctreeElement* element) { + QFile persistFile(fileName); + QVariantMap entityDescription; + + qDebug("Saving to file %s...", fileName); + + OctreeElement* top; + if (element) { + top = element; + } else { + top = _rootElement; + } + + bool entityDescriptionSuccess = writeToMap(entityDescription, top); + if (entityDescriptionSuccess && persistFile.open(QIODevice::WriteOnly)) { + persistFile.write(QJsonDocument::fromVariant(entityDescription).toJson()); + } else { + qCritical("Could not write to JSON description of entities."); + } +} + void Octree::writeToSVOFile(const char* fileName, OctreeElement* element) { std::ofstream file(fileName, std::ios::out|std::ios::binary); diff --git a/libraries/octree/src/Octree.h b/libraries/octree/src/Octree.h index 672a3b63d5..21c3efc01d 100644 --- a/libraries/octree/src/Octree.h +++ b/libraries/octree/src/Octree.h @@ -35,6 +35,9 @@ class Shape; #include #include + +extern QVector PERSIST_EXTENSIONS; + /// derive from this class to use the Octree::recurseTreeWithOperator() method class RecurseOctreeOperator { public: @@ -324,13 +327,19 @@ public: // Note: this assumes the fileFormat is the HIO individual voxels code files void loadOctreeFile(const char* fileName, bool wantColorRandomizer); - // these will read/write files that match the wireformat, excluding the 'V' leading + // Octree exporters + void writeToFile(const char* filename, OctreeElement* element = NULL, QString persistAsFileType = "svo"); + void writeToJSONFile(const char* filename, OctreeElement* element = NULL); void writeToSVOFile(const char* filename, OctreeElement* element = NULL); + virtual bool writeToMap(QVariantMap& entityDescription, OctreeElement* element) = 0; - bool readFromSVOFile(const char* filename); - bool readFromSVOURL(const QString& url); // will support file urls as well... + // Octree importers + bool readFromFile(const char* filename); + bool readFromURL(const QString& url); // will support file urls as well... bool readFromStream(unsigned long streamLength, QDataStream& inputStream); - + bool readSVOFromStream(unsigned long streamLength, QDataStream& inputStream); + bool readJSONFromStream(unsigned long streamLength, QDataStream& inputStream); + virtual bool readFromMap(QVariantMap& entityDescription) = 0; unsigned long getOctreeElementsCount(); diff --git a/libraries/octree/src/OctreePersistThread.cpp b/libraries/octree/src/OctreePersistThread.cpp index 2a563dc50a..02f46ee64b 100644 --- a/libraries/octree/src/OctreePersistThread.cpp +++ b/libraries/octree/src/OctreePersistThread.cpp @@ -20,16 +20,19 @@ #include #include #include +#include #include #include +#include #include "OctreePersistThread.h" const int OctreePersistThread::DEFAULT_PERSIST_INTERVAL = 1000 * 30; // every 30 seconds OctreePersistThread::OctreePersistThread(Octree* tree, const QString& filename, int persistInterval, - bool wantBackup, const QJsonObject& settings, bool debugTimestampNow) : + bool wantBackup, const QJsonObject& settings, bool debugTimestampNow, + QString persistAsFileType) : _tree(tree), _filename(filename), _persistInterval(persistInterval), @@ -38,9 +41,14 @@ OctreePersistThread::OctreePersistThread(Octree* tree, const QString& filename, _lastCheck(0), _wantBackup(wantBackup), _debugTimestampNow(debugTimestampNow), - _lastTimeDebug(0) + _lastTimeDebug(0), + _persistAsFileType(persistAsFileType) { parseSettings(settings); + + // in case the persist filename has an extension that doesn't match the file type + QString sansExt = fileNameWithoutExtension(_filename, PERSIST_EXTENSIONS); + _filename = sansExt + "." + _persistAsFileType; } void OctreePersistThread::parseSettings(const QJsonObject& settings) { @@ -140,7 +148,7 @@ bool OctreePersistThread::process() { qDebug() << "Loading Octree... lock file removed:" << lockFileName; } - persistantFileRead = _tree->readFromSVOFile(_filename.toLocal8Bit().constData()); + persistantFileRead = _tree->readFromFile(qPrintable(_filename.toLocal8Bit())); _tree->pruneTree(); } _tree->unlock(); @@ -242,9 +250,7 @@ void OctreePersistThread::persist() { if(lockFile.is_open()) { qDebug() << "saving Octree lock file created at:" << lockFileName; - qDebug() << "saving Octree to file " << _filename << "..."; - - _tree->writeToSVOFile(qPrintable(_filename)); + _tree->writeToFile(qPrintable(_filename), NULL, _persistAsFileType); time(&_lastPersistTime); _tree->clearDirtyBit(); // tree is clean after saving qDebug() << "DONE saving Octree to file..."; @@ -346,8 +352,8 @@ void OctreePersistThread::rollOldBackupVersions(const BackupRule& rule) { QString backupExtensionNplusOne = rule.extensionFormat; backupExtensionN.replace(QString("%N"), QString::number(n)); backupExtensionNplusOne.replace(QString("%N"), QString::number(n+1)); - - QString backupFilenameN = _filename + backupExtensionN; + + QString backupFilenameN = findMostRecentFileExtension(_filename, PERSIST_EXTENSIONS) + backupExtensionN; QString backupFilenameNplusOne = _filename + backupExtensionNplusOne; QFile backupFileN(backupFilenameN); diff --git a/libraries/octree/src/OctreePersistThread.h b/libraries/octree/src/OctreePersistThread.h index 374de79f0a..e756c13f59 100644 --- a/libraries/octree/src/OctreePersistThread.h +++ b/libraries/octree/src/OctreePersistThread.h @@ -34,8 +34,8 @@ public: static const int DEFAULT_PERSIST_INTERVAL; OctreePersistThread(Octree* tree, const QString& filename, int persistInterval = DEFAULT_PERSIST_INTERVAL, - bool wantBackup = false, const QJsonObject& settings = QJsonObject(), - bool debugTimestampNow = false); + bool wantBackup = false, const QJsonObject& settings = QJsonObject(), + bool debugTimestampNow = false, QString persistAsFileType="svo"); bool isInitialLoadComplete() const { return _initialLoadComplete; } quint64 getLoadElapsedTime() const { return _loadTimeUSecs; } @@ -72,6 +72,8 @@ private: bool _debugTimestampNow; quint64 _lastTimeDebug; + + QString _persistAsFileType; }; #endif // hifi_OctreePersistThread_h diff --git a/libraries/shared/src/PathUtils.cpp b/libraries/shared/src/PathUtils.cpp index 0b99f22228..545183e8f5 100644 --- a/libraries/shared/src/PathUtils.cpp +++ b/libraries/shared/src/PathUtils.cpp @@ -11,6 +11,9 @@ #include #include +#include +#include +#include #include "PathUtils.h" @@ -23,3 +26,30 @@ QString& PathUtils::resourcesPath() { #endif return staticResourcePath; } + + +QString fileNameWithoutExtension(const QString& fileName, const QVector possibleExtensions) { + foreach (const QString possibleExtension, possibleExtensions) { + if (fileName.endsWith(possibleExtension) || + fileName.endsWith(possibleExtension.toUpper()) || + fileName.endsWith(possibleExtension.toLower())) { + return fileName.left(fileName.count() - possibleExtension.count() - 1); + } + } + return fileName; +} + +QString findMostRecentFileExtension(const QString& originalFileName, QVector possibleExtensions) { + QString sansExt = fileNameWithoutExtension(originalFileName, possibleExtensions); + QString newestFileName = originalFileName; + QDateTime newestTime = QDateTime::fromMSecsSinceEpoch(0); + foreach (QString possibleExtension, possibleExtensions) { + QString fileName = sansExt + "." + possibleExtension; + QFileInfo fileInfo(fileName); + if (fileInfo.exists() && fileInfo.lastModified() > newestTime) { + newestFileName = fileName; + newestTime = fileInfo.lastModified(); + } + } + return newestFileName; +} diff --git a/libraries/shared/src/PathUtils.h b/libraries/shared/src/PathUtils.h index 71cabc727d..6b6893574b 100644 --- a/libraries/shared/src/PathUtils.h +++ b/libraries/shared/src/PathUtils.h @@ -19,4 +19,7 @@ namespace PathUtils { QString& resourcesPath(); } -#endif // hifi_PathUtils_h \ No newline at end of file +QString fileNameWithoutExtension(const QString& fileName, const QVector possibleExtensions); +QString findMostRecentFileExtension(const QString& originalFileName, QVector possibleExtensions); + +#endif // hifi_PathUtils_h diff --git a/libraries/shared/src/QVariantGLM.cpp b/libraries/shared/src/QVariantGLM.cpp new file mode 100644 index 0000000000..aa8fa40593 --- /dev/null +++ b/libraries/shared/src/QVariantGLM.cpp @@ -0,0 +1,48 @@ +// +// QVariantGLM.cpp +// libraries/shared/src +// +// Created by Seth Alves on 3/9/15. +// Copyright 2013 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 "QVariantGLM.h" +#include "OctalCode.h" + +QVariantList glmToQList(const glm::vec3& g) { + return QVariantList() << g[0] << g[1] << g[2]; +} + +QVariantList glmToQList(const glm::quat& g) { + return QVariantList() << g.x << g.y << g.z << g.w; +} + +QVariantList rgbColorToQList(rgbColor& v) { + return QVariantList() << (int)(v[0]) << (int)(v[1]) << (int)(v[2]); +} + + + +glm::vec3 qListToGlmVec3(const QVariant q) { + QVariantList qList = q.toList(); + return glm::vec3(qList[RED_INDEX].toFloat(), qList[GREEN_INDEX].toFloat(), qList[BLUE_INDEX].toFloat()); +} + +glm::quat qListToGlmQuat(const QVariant q) { + QVariantList qList = q.toList(); + float x = qList[0].toFloat(); + float y = qList[1].toFloat(); + float z = qList[2].toFloat(); + float w = qList[3].toFloat(); + return glm::quat(w, x, y, z); +} + +void qListtoRgbColor(const QVariant q, rgbColor returnValue) { + QVariantList qList = q.toList(); + returnValue[RED_INDEX] = qList[RED_INDEX].toInt(); + returnValue[GREEN_INDEX] = qList[GREEN_INDEX].toInt(); + returnValue[BLUE_INDEX] = qList[BLUE_INDEX].toInt(); +} diff --git a/libraries/shared/src/QVariantGLM.h b/libraries/shared/src/QVariantGLM.h new file mode 100644 index 0000000000..4cc1d038a1 --- /dev/null +++ b/libraries/shared/src/QVariantGLM.h @@ -0,0 +1,26 @@ +// +// QVariantGLM.h +// libraries/shared/src +// +// Created by Seth Alves on 3/9/15. +// Copyright 2013 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 "SharedUtil.h" + +QVariantList glmToQList(const glm::vec3& g); +QVariantList glmToQList(const glm::quat& g); +QVariantList rgbColorToQList(rgbColor& v); + +glm::vec3 qListToGlmVec3(const QVariant q); +glm::quat qListToGlmQuat(const QVariant q); +void qListtoRgbColor(const QVariant q, rgbColor returnValue); diff --git a/libraries/shared/src/VariantMapToScriptValue.cpp b/libraries/shared/src/VariantMapToScriptValue.cpp new file mode 100644 index 0000000000..6fa3fd04e6 --- /dev/null +++ b/libraries/shared/src/VariantMapToScriptValue.cpp @@ -0,0 +1,47 @@ +// +// VariantMapToScriptValue.cpp +// libraries/shared/src/ +// +// Created by Brad Hefta-Gaub on 12/6/13. +// Copyright 2013 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 "VariantMapToScriptValue.h" + +QScriptValue variantMapToScriptValue(QVariantMap& variantMap, QScriptEngine& scriptEngine) { + QScriptValue scriptValue = scriptEngine.newObject(); + + for (QVariantMap::const_iterator iter = variantMap.begin(); iter != variantMap.end(); ++iter) { + QString key = iter.key(); + QVariant qValue = iter.value(); + + switch(qValue.type()) { + case QVariant::Bool: + scriptValue.setProperty(key, qValue.toBool()); + break; + case QVariant::Int: + scriptValue.setProperty(key, qValue.toInt()); + break; + case QVariant::Double: + scriptValue.setProperty(key, qValue.toDouble()); + break; + case QVariant::String: { + scriptValue.setProperty(key, scriptEngine.newVariant(qValue)); + break; + } + case QVariant::Map: { + QVariantMap childMap = qValue.toMap(); + scriptValue.setProperty(key, variantMapToScriptValue(childMap, scriptEngine)); + break; + } + default: + qDebug() << "unhandled QScript type" << qValue.type(); + } + } + + return scriptValue; +} diff --git a/libraries/shared/src/VariantMapToScriptValue.h b/libraries/shared/src/VariantMapToScriptValue.h new file mode 100644 index 0000000000..503f8c6490 --- /dev/null +++ b/libraries/shared/src/VariantMapToScriptValue.h @@ -0,0 +1,16 @@ +// +// VariantMapToScriptValue.h +// libraries/shared/src/ +// +// Created by Brad Hefta-Gaub on 12/6/13. +// Copyright 2013 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 + +QScriptValue variantMapToScriptValue(QVariantMap& variantMap, QScriptEngine& scriptEngine);