diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index a7c88ddc7d..aa2167029d 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -35,6 +35,7 @@ #include "QVariantGLM.h" #include "EntitiesLogging.h" #include "RecurseOctreeToMapOperator.h" +#include "RecurseOctreeToJsonOperator.h" #include "LogHandler.h" #include "EntityEditFilters.h" #include "EntityDynamicFactoryInterface.h" @@ -2658,6 +2659,17 @@ bool EntityTree::readFromMap(QVariantMap& map) { return success; } +bool EntityTree::writeToJSON(QString & jsonString, const OctreeElementPointer & element) { + QScriptEngine scriptEngine; + RecurseOctreeToJSONOperator theOperator(element, &scriptEngine, jsonString); + withReadLock([&] { + recurseTreeWithOperator(&theOperator); + }); + + jsonString = theOperator.getJson(); + return true; +} + void EntityTree::resetClientEditStats() { _treeResetTime = usecTimestampNow(); _maxEditDelta = 0; diff --git a/libraries/entities/src/EntityTree.h b/libraries/entities/src/EntityTree.h index 2f971b8566..b852a695d8 100644 --- a/libraries/entities/src/EntityTree.h +++ b/libraries/entities/src/EntityTree.h @@ -224,6 +224,8 @@ public: virtual bool writeToMap(QVariantMap& entityDescription, OctreeElementPointer element, bool skipDefaultValues, bool skipThoseWithBadParents) override; virtual bool readFromMap(QVariantMap& entityDescription) override; + virtual bool writeToJSON(QString& jsonString, const OctreeElementPointer& element) override; + glm::vec3 getContentsDimensions(); float getContentsLargestDimension(); diff --git a/libraries/entities/src/RecurseOctreeToJSONOperator.cpp b/libraries/entities/src/RecurseOctreeToJSONOperator.cpp new file mode 100644 index 0000000000..2db1a2b228 --- /dev/null +++ b/libraries/entities/src/RecurseOctreeToJSONOperator.cpp @@ -0,0 +1,45 @@ +// +// RecurseOctreeToJSONOperator.cpp +// libraries/entities/src +// +// Created by Simon Walton on Oct 11, 2018. +// Copyright 2018 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 "RecurseOctreeToJSONOperator.h" + +#include "EntityItemProperties.h" + +RecurseOctreeToJSONOperator::RecurseOctreeToJSONOperator(const OctreeElementPointer& top, QScriptEngine* engine, + QString jsonPrefix /* = QString() */) + : _top(top) + , _engine(engine) + , _json(jsonPrefix) +{ + _toStringMethod = _engine->evaluate("(function() { return JSON.stringify(this, null, ' ') })"); +} + +bool RecurseOctreeToJSONOperator::postRecursion(const OctreeElementPointer& element) { + EntityTreeElementPointer entityTreeElement = std::static_pointer_cast(element); + + entityTreeElement->forEachEntity([&](const EntityItemPointer& entity) { processEntity(entity); } ); + return true; +} + +void RecurseOctreeToJSONOperator::processEntity(const EntityItemPointer& entity) { + QScriptValue qScriptValues = EntityItemNonDefaultPropertiesToScriptValue(_engine, entity->getProperties()); + if (comma) { + _json += ','; + }; + comma = true; + _json += "\n "; + + // Override default toString(): + qScriptValues.setProperty("toString", _toStringMethod); + QString jsonResult = qScriptValues.toString(); + //auto exceptionString2 = _engine->uncaughtException().toString(); + _json += jsonResult; +} diff --git a/libraries/entities/src/RecurseOctreeToJSONOperator.h b/libraries/entities/src/RecurseOctreeToJSONOperator.h new file mode 100644 index 0000000000..92ce6bfdca --- /dev/null +++ b/libraries/entities/src/RecurseOctreeToJSONOperator.h @@ -0,0 +1,31 @@ +// +// RecurseOctreeToJSONOperator.h +// libraries/entities/src +// +// Created by Simon Walton on Oct 11, 2018. +// Copyright 2018 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 RecurseOctreeToJSONOperator : public RecurseOctreeOperator { +public: + RecurseOctreeToJSONOperator(const OctreeElementPointer& top, QScriptEngine* engine, QString jsonPrefix = QString()); + virtual bool preRecursion(const OctreeElementPointer& element) override { return true; }; + virtual bool postRecursion(const OctreeElementPointer& element) override; + + QString getJson() const { return _json; } + +private: + void processEntity(const EntityItemPointer& entity); + + const OctreeElementPointer& _top; + QScriptEngine* _engine; + QScriptValue _toStringMethod; + + QString _json; + bool comma { false }; +}; diff --git a/libraries/octree/src/Octree.cpp b/libraries/octree/src/Octree.cpp index 37dd4e410f..40d2449a38 100644 --- a/libraries/octree/src/Octree.cpp +++ b/libraries/octree/src/Octree.cpp @@ -51,7 +51,6 @@ #include "OctreeQueryNode.h" #include "OctreeUtils.h" - QVector PERSIST_EXTENSIONS = {"json", "json.gz"}; Octree::Octree(bool shouldReaverage) : @@ -892,13 +891,61 @@ bool Octree::toJSONDocument(QJsonDocument* doc, const OctreeElementPointer& elem return true; } +bool Octree::toJSONString(QString& jsonString, const OctreeElementPointer& element) { + OctreeElementPointer top; + if (element) { + top = element; + } else { + top = _rootElement; + } + + jsonString += QString(R"({ + "DataVersion": %1, + "Entities": [)").arg(_persistDataVersion); + + writeToJSON(jsonString, top); + + // include the "bitstream" version + PacketType expectedType = expectedDataPacketType(); + PacketVersion expectedVersion = versionForPacketType(expectedType); + + jsonString += QString(R"( + ], + "Id": "%1", + "Version": %2 +} +)").arg(_persistID.toString()).arg((int)expectedVersion); + + return true; +} + bool Octree::toJSON(QByteArray* data, const OctreeElementPointer& element, bool doGzip) { +#define HIFI_USE_DIRECT_TO_JSON +#ifdef HIFI_USE_DIRECT_TO_JSON + + QString jsonString; + toJSONString(jsonString); + + if (doGzip) { + if (!gzip(jsonString.toUtf8(), *data, -1)) { + qCritical("Unable to gzip data while saving to json."); + return false; + } + } else { + *data = jsonString.toUtf8(); + } + +#else + QJsonDocument doc; if (!toJSONDocument(&doc, element)) { qCritical("Failed to convert Entities to JSON document."); return false; } + QString jsonString; + toJSONString(jsonString); + if (doGzip) { QByteArray jsonData = doc.toJson(); @@ -909,6 +956,7 @@ bool Octree::toJSON(QByteArray* data, const OctreeElementPointer& element, bool } else { *data = doc.toJson(); } +#endif // HIFI_USE_DIRECT_TO_JSON return true; } diff --git a/libraries/octree/src/Octree.h b/libraries/octree/src/Octree.h index a2b2f227cb..678d8aa5de 100644 --- a/libraries/octree/src/Octree.h +++ b/libraries/octree/src/Octree.h @@ -202,11 +202,13 @@ public: // Octree exporters bool toJSONDocument(QJsonDocument* doc, const OctreeElementPointer& element = nullptr); + bool toJSONString(QString& jsonString, const OctreeElementPointer& element = nullptr); bool toJSON(QByteArray* data, const OctreeElementPointer& element = nullptr, bool doGzip = false); bool writeToFile(const char* filename, const OctreeElementPointer& element = nullptr, QString persistAsFileType = "json.gz"); bool writeToJSONFile(const char* filename, const OctreeElementPointer& element = nullptr, bool doGzip = false); virtual bool writeToMap(QVariantMap& entityDescription, OctreeElementPointer element, bool skipDefaultValues, bool skipThoseWithBadParents) = 0; + virtual bool writeToJSON(QString& jsonString, const OctreeElementPointer& element) = 0; // Octree importers bool readFromFile(const char* filename);