mirror of
https://github.com/JulianGro/overte.git
synced 2025-04-08 17:03:11 +02:00
Merge branch 'master' of http://github.com/highfidelity/hifi into ao
This commit is contained in:
commit
ed42c94772
33 changed files with 802 additions and 217 deletions
|
@ -3411,20 +3411,11 @@ void DomainServer::maybeHandleReplacementEntityFile() {
|
|||
}
|
||||
|
||||
void DomainServer::handleOctreeFileReplacement(QByteArray octreeFile) {
|
||||
//Assume we have compressed data
|
||||
auto compressedOctree = octreeFile;
|
||||
QByteArray jsonOctree;
|
||||
|
||||
bool wasCompressed = gunzip(compressedOctree, jsonOctree);
|
||||
if (!wasCompressed) {
|
||||
// the source was not compressed, assume we were sent regular JSON data
|
||||
jsonOctree = compressedOctree;
|
||||
}
|
||||
|
||||
OctreeUtils::RawEntityData data;
|
||||
if (data.readOctreeDataInfoFromData(jsonOctree)) {
|
||||
if (data.readOctreeDataInfoFromData(octreeFile)) {
|
||||
data.resetIdAndVersion();
|
||||
|
||||
QByteArray compressedOctree;
|
||||
gzip(data.toByteArray(), compressedOctree);
|
||||
|
||||
// write the compressed octree data to a special file
|
||||
|
|
|
@ -26,6 +26,8 @@ Item {
|
|||
|
||||
property bool interactive: false
|
||||
|
||||
property bool blurOnCtrlShift: true
|
||||
|
||||
StylesUIt.HifiConstants {
|
||||
id: hifi
|
||||
}
|
||||
|
@ -180,8 +182,8 @@ Item {
|
|||
}
|
||||
|
||||
Keys.onPressed: {
|
||||
if ((event.modifiers & Qt.ShiftModifier) && (event.modifiers & Qt.ControlModifier)) {
|
||||
webViewCore.focus = false;
|
||||
if (blurOnCtrlShift && (event.modifiers & Qt.ShiftModifier) && (event.modifiers & Qt.ControlModifier)) {
|
||||
webViewCore.focus = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,6 +22,7 @@ Item {
|
|||
property bool punctuationMode: false
|
||||
property bool passwordField: false
|
||||
property alias flickable: webroot.interactive
|
||||
property alias blurOnCtrlShift: webroot.blurOnCtrlShift
|
||||
|
||||
function stop() {
|
||||
webroot.stop();
|
||||
|
|
|
@ -70,8 +70,8 @@ OriginalDesktop.Desktop {
|
|||
anchors.horizontalCenter: settings.constrainToolbarToCenterX ? desktop.horizontalCenter : undefined;
|
||||
// Literal 50 is overwritten by settings from previous session, and sysToolbar.x comes from settings when not constrained.
|
||||
x: sysToolbar.x
|
||||
buttonModel: tablet ? tablet.buttons : null;
|
||||
shown: tablet ? tablet.toolbarMode : false;
|
||||
buttonModel: tablet.buttons;
|
||||
shown: tablet.toolbarMode;
|
||||
}
|
||||
|
||||
Settings {
|
||||
|
|
|
@ -12,4 +12,5 @@ WebView {
|
|||
id: entityListToolWebView
|
||||
url: Paths.defaultScripts + "/system/html/entityList.html"
|
||||
enabled: true
|
||||
blurOnCtrlShift: false
|
||||
}
|
||||
|
|
|
@ -245,6 +245,7 @@ TabBar {
|
|||
id: entityListToolWebView
|
||||
url: Paths.defaultScripts + "/system/html/entityList.html"
|
||||
enabled: true
|
||||
blurOnCtrlShift: false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -260,6 +261,7 @@ TabBar {
|
|||
id: entityPropertiesWebView
|
||||
url: Paths.defaultScripts + "/system/html/entityProperties.html"
|
||||
enabled: true
|
||||
blurOnCtrlShift: false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -275,6 +277,7 @@ TabBar {
|
|||
id: gridControlsWebView
|
||||
url: Paths.defaultScripts + "/system/html/gridControls.html"
|
||||
enabled: true
|
||||
blurOnCtrlShift: false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -251,6 +251,7 @@ TabBar {
|
|||
id: entityPropertiesWebView
|
||||
url: Paths.defaultScripts + "/system/html/entityProperties.html"
|
||||
enabled: true
|
||||
blurOnCtrlShift: false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -266,6 +267,7 @@ TabBar {
|
|||
id: gridControlsWebView
|
||||
url: Paths.defaultScripts + "/system/html/gridControls.html"
|
||||
enabled: true
|
||||
blurOnCtrlShift: false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,4 +2,5 @@ WebView {
|
|||
id: entityListToolWebView
|
||||
url: Paths.defaultScripts + "/system/html/entityList.html"
|
||||
enabled: true
|
||||
blurOnCtrlShift: false
|
||||
}
|
||||
|
|
|
@ -115,9 +115,9 @@ Item {
|
|||
property int previousIndex: -1
|
||||
Repeater {
|
||||
id: pageRepeater
|
||||
model: tabletProxy != null ? Math.ceil(tabletProxy.buttons.rowCount() / TabletEnums.ButtonsOnPage) : 0
|
||||
model: Math.ceil(tabletProxy.buttons.rowCount() / TabletEnums.ButtonsOnPage)
|
||||
onItemAdded: {
|
||||
item.proxyModel.sourceModel = tabletProxy != null ? tabletProxy.buttons : null;
|
||||
item.proxyModel.sourceModel = tabletProxy.buttons;
|
||||
item.proxyModel.pageIndex = index;
|
||||
}
|
||||
|
||||
|
|
|
@ -4039,21 +4039,23 @@ void Application::keyPressEvent(QKeyEvent* event) {
|
|||
break;
|
||||
|
||||
case Qt::Key_P: {
|
||||
AudioInjectorOptions options;
|
||||
options.localOnly = true;
|
||||
options.stereo = true;
|
||||
Setting::Handle<bool> notificationSounds{ MenuOption::NotificationSounds, true};
|
||||
Setting::Handle<bool> notificationSoundSnapshot{ MenuOption::NotificationSoundsSnapshot, true};
|
||||
if (notificationSounds.get() && notificationSoundSnapshot.get()) {
|
||||
if (_snapshotSoundInjector) {
|
||||
_snapshotSoundInjector->setOptions(options);
|
||||
_snapshotSoundInjector->restart();
|
||||
} else {
|
||||
QByteArray samples = _snapshotSound->getByteArray();
|
||||
_snapshotSoundInjector = AudioInjector::playSound(samples, options);
|
||||
if (!isShifted && !isMeta && !isOption && !event->isAutoRepeat()) {
|
||||
AudioInjectorOptions options;
|
||||
options.localOnly = true;
|
||||
options.stereo = true;
|
||||
Setting::Handle<bool> notificationSounds{ MenuOption::NotificationSounds, true };
|
||||
Setting::Handle<bool> notificationSoundSnapshot{ MenuOption::NotificationSoundsSnapshot, true };
|
||||
if (notificationSounds.get() && notificationSoundSnapshot.get()) {
|
||||
if (_snapshotSoundInjector) {
|
||||
_snapshotSoundInjector->setOptions(options);
|
||||
_snapshotSoundInjector->restart();
|
||||
} else {
|
||||
QByteArray samples = _snapshotSound->getByteArray();
|
||||
_snapshotSoundInjector = AudioInjector::playSound(samples, options);
|
||||
}
|
||||
}
|
||||
takeSnapshot(true);
|
||||
}
|
||||
takeSnapshot(true);
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
|
@ -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"
|
||||
|
@ -2785,6 +2786,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;
|
||||
|
|
|
@ -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();
|
||||
|
|
50
libraries/entities/src/RecurseOctreeToJSONOperator.cpp
Normal file
50
libraries/entities/src/RecurseOctreeToJSONOperator.cpp
Normal file
|
@ -0,0 +1,50 @@
|
|||
//
|
||||
// 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&, QScriptEngine* engine,
|
||||
QString jsonPrefix, bool skipDefaults, bool skipThoseWithBadParents):
|
||||
_engine(engine),
|
||||
_json(jsonPrefix),
|
||||
_skipDefaults(skipDefaults),
|
||||
_skipThoseWithBadParents(skipThoseWithBadParents)
|
||||
{
|
||||
_toStringMethod = _engine->evaluate("(function() { return JSON.stringify(this, null, ' ') })");
|
||||
}
|
||||
|
||||
bool RecurseOctreeToJSONOperator::postRecursion(const OctreeElementPointer& element) {
|
||||
EntityTreeElementPointer entityTreeElement = std::static_pointer_cast<EntityTreeElement>(element);
|
||||
|
||||
entityTreeElement->forEachEntity([&](const EntityItemPointer& entity) { processEntity(entity); } );
|
||||
return true;
|
||||
}
|
||||
|
||||
void RecurseOctreeToJSONOperator::processEntity(const EntityItemPointer& entity) {
|
||||
if (_skipThoseWithBadParents && !entity->isParentIDValid()) {
|
||||
return; // we weren't able to resolve a parent from _parentID, so don't save this entity.
|
||||
}
|
||||
|
||||
QScriptValue qScriptValues = _skipDefaults
|
||||
? EntityItemNonDefaultPropertiesToScriptValue(_engine, entity->getProperties())
|
||||
: EntityItemPropertiesToScriptValue(_engine, entity->getProperties());
|
||||
|
||||
if (_comma) {
|
||||
_json += ',';
|
||||
};
|
||||
_comma = true;
|
||||
_json += "\n ";
|
||||
|
||||
// Override default toString():
|
||||
qScriptValues.setProperty("toString", _toStringMethod);
|
||||
_json += qScriptValues.toString();
|
||||
}
|
33
libraries/entities/src/RecurseOctreeToJSONOperator.h
Normal file
33
libraries/entities/src/RecurseOctreeToJSONOperator.h
Normal file
|
@ -0,0 +1,33 @@
|
|||
//
|
||||
// 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&, QScriptEngine* engine, QString jsonPrefix = QString(), bool skipDefaults = true,
|
||||
bool skipThoseWithBadParents = false);
|
||||
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);
|
||||
|
||||
QScriptEngine* _engine;
|
||||
QScriptValue _toStringMethod;
|
||||
|
||||
QString _json;
|
||||
const bool _skipDefaults;
|
||||
bool _skipThoseWithBadParents;
|
||||
bool _comma { false };
|
||||
};
|
|
@ -50,7 +50,7 @@
|
|||
#include "OctreeLogging.h"
|
||||
#include "OctreeQueryNode.h"
|
||||
#include "OctreeUtils.h"
|
||||
|
||||
#include "OctreeEntitiesFileParser.h"
|
||||
|
||||
QVector<QString> PERSIST_EXTENSIONS = {"json", "json.gz"};
|
||||
|
||||
|
@ -792,28 +792,26 @@ bool Octree::readFromStream(
|
|||
}
|
||||
|
||||
|
||||
namespace {
|
||||
// hack to get the marketplace id into the entities. We will create a way to get this from a hash of
|
||||
// the entity later, but this helps us move things along for now
|
||||
QJsonDocument addMarketplaceIDToDocumentEntities(QJsonDocument& doc, const QString& marketplaceID) {
|
||||
QVariantMap addMarketplaceIDToDocumentEntities(QVariantMap& doc, const QString& marketplaceID) {
|
||||
if (!marketplaceID.isEmpty()) {
|
||||
QJsonDocument newDoc;
|
||||
QJsonObject rootObj = doc.object();
|
||||
QJsonArray newEntitiesArray;
|
||||
QVariantList newEntitiesArray;
|
||||
|
||||
// build a new entities array
|
||||
auto entitiesArray = rootObj["Entities"].toArray();
|
||||
for(auto it = entitiesArray.begin(); it != entitiesArray.end(); it++) {
|
||||
auto entity = (*it).toObject();
|
||||
auto entitiesArray = doc["Entities"].toList();
|
||||
for (auto it = entitiesArray.begin(); it != entitiesArray.end(); it++) {
|
||||
auto entity = (*it).toMap();
|
||||
entity["marketplaceID"] = marketplaceID;
|
||||
newEntitiesArray.append(entity);
|
||||
}
|
||||
rootObj["Entities"] = newEntitiesArray;
|
||||
newDoc.setObject(rootObj);
|
||||
return newDoc;
|
||||
doc["Entities"] = newEntitiesArray;
|
||||
}
|
||||
return doc;
|
||||
}
|
||||
|
||||
} // Unnamed namepsace
|
||||
const int READ_JSON_BUFFER_SIZE = 2048;
|
||||
|
||||
bool Octree::readJSONFromStream(
|
||||
|
@ -839,12 +837,18 @@ bool Octree::readJSONFromStream(
|
|||
jsonBuffer += QByteArray(rawData, got);
|
||||
}
|
||||
|
||||
QJsonDocument asDocument = QJsonDocument::fromJson(jsonBuffer);
|
||||
if (!marketplaceID.isEmpty()) {
|
||||
asDocument = addMarketplaceIDToDocumentEntities(asDocument, marketplaceID);
|
||||
OctreeEntitiesFileParser octreeParser;
|
||||
octreeParser.setEntitiesString(jsonBuffer);
|
||||
QVariantMap asMap;
|
||||
if (!octreeParser.parseEntities(asMap)) {
|
||||
qCritical() << "Couldn't parse Entities JSON:" << octreeParser.getErrorString().c_str();
|
||||
return false;
|
||||
}
|
||||
QVariant asVariant = asDocument.toVariant();
|
||||
QVariantMap asMap = asVariant.toMap();
|
||||
|
||||
if (!marketplaceID.isEmpty()) {
|
||||
addMarketplaceIDToDocumentEntities(asMap, marketplaceID);
|
||||
}
|
||||
|
||||
bool success = readFromMap(asMap);
|
||||
delete[] rawData;
|
||||
return success;
|
||||
|
@ -889,26 +893,52 @@ bool Octree::toJSONDocument(QJsonDocument* doc, const OctreeElementPointer& elem
|
|||
return false;
|
||||
}
|
||||
|
||||
*doc = QJsonDocument::fromVariant(entityDescription);
|
||||
|
||||
bool noEntities = entityDescription["Entities"].toList().empty();
|
||||
QJsonDocument jsonDocTree = QJsonDocument::fromVariant(entityDescription);
|
||||
QJsonValue entitiesJson = jsonDocTree["Entities"];
|
||||
if (entitiesJson.isNull() || (entitiesJson.toArray().empty() && !noEntities)) {
|
||||
// Json version of entities too large.
|
||||
return false;
|
||||
} else {
|
||||
*doc = jsonDocTree;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Octree::toJSONString(QString& jsonString, const OctreeElementPointer& element) {
|
||||
OctreeElementPointer top;
|
||||
if (element) {
|
||||
top = element;
|
||||
} else {
|
||||
top = _rootElement;
|
||||
}
|
||||
|
||||
jsonString += QString("{\n \"DataVersion\": %1,\n \"Entities\": [").arg(_persistDataVersion);
|
||||
|
||||
writeToJSON(jsonString, top);
|
||||
|
||||
// include the "bitstream" version
|
||||
PacketType expectedType = expectedDataPacketType();
|
||||
PacketVersion expectedVersion = versionForPacketType(expectedType);
|
||||
|
||||
jsonString += QString("\n ],\n \"Id\": \"%1\",\n \"Version\": %2\n}\n").arg(_persistID.toString()).arg((int)expectedVersion);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Octree::toJSON(QByteArray* data, const OctreeElementPointer& element, bool doGzip) {
|
||||
QJsonDocument doc;
|
||||
if (!toJSONDocument(&doc, element)) {
|
||||
qCritical("Failed to convert Entities to QVariantMap while converting to json.");
|
||||
return false;
|
||||
}
|
||||
QString jsonString;
|
||||
toJSONString(jsonString);
|
||||
|
||||
if (doGzip) {
|
||||
QByteArray jsonData = doc.toJson();
|
||||
|
||||
if (!gzip(jsonData, *data, -1)) {
|
||||
if (!gzip(jsonString.toUtf8(), *data, -1)) {
|
||||
qCritical("Unable to gzip data while saving to json.");
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
*data = doc.toJson();
|
||||
*data = jsonString.toUtf8();
|
||||
}
|
||||
|
||||
return true;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
|
||||
#include "OctreeDataUtils.h"
|
||||
#include "OctreeEntitiesFileParser.h"
|
||||
|
||||
#include <Gzip.h>
|
||||
#include <udt/PacketHeaders.h>
|
||||
|
@ -18,33 +19,13 @@
|
|||
#include <QJsonDocument>
|
||||
#include <QFile>
|
||||
|
||||
// Reads octree file and parses it into a QJsonDocument. Handles both gzipped and non-gzipped files.
|
||||
// Returns true if the file was successfully opened and parsed, otherwise false.
|
||||
// Example failures: file does not exist, gzipped file cannot be unzipped, invalid JSON.
|
||||
bool readOctreeFile(QString path, QJsonDocument* doc) {
|
||||
QFile file(path);
|
||||
if (!file.open(QIODevice::ReadOnly)) {
|
||||
return false;
|
||||
bool OctreeUtils::RawOctreeData::readOctreeDataInfoFromMap(const QVariantMap& map) {
|
||||
if (map.contains("Id") && map.contains("DataVersion") && map.contains("Version")) {
|
||||
id = map["Id"].toUuid();
|
||||
dataVersion = map["DataVersion"].toInt();
|
||||
version = map["Version"].toInt();
|
||||
}
|
||||
|
||||
QByteArray data = file.readAll();
|
||||
QByteArray jsonData;
|
||||
|
||||
if (!gunzip(data, jsonData)) {
|
||||
jsonData = data;
|
||||
}
|
||||
|
||||
*doc = QJsonDocument::fromJson(jsonData);
|
||||
return !doc->isNull();
|
||||
}
|
||||
|
||||
bool OctreeUtils::RawOctreeData::readOctreeDataInfoFromJSON(QJsonObject root) {
|
||||
if (root.contains("Id") && root.contains("DataVersion") && root.contains("Version")) {
|
||||
id = root["Id"].toVariant().toUuid();
|
||||
dataVersion = root["DataVersion"].toInt();
|
||||
version = root["Version"].toInt();
|
||||
}
|
||||
readSubclassData(root);
|
||||
readSubclassData(map);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -54,40 +35,41 @@ bool OctreeUtils::RawOctreeData::readOctreeDataInfoFromData(QByteArray data) {
|
|||
data = jsonData;
|
||||
}
|
||||
|
||||
auto doc = QJsonDocument::fromJson(data);
|
||||
if (doc.isNull()) {
|
||||
OctreeEntitiesFileParser jsonParser;
|
||||
jsonParser.setEntitiesString(data);
|
||||
QVariantMap entitiesMap;
|
||||
if (!jsonParser.parseEntities(entitiesMap)) {
|
||||
qCritical() << "Can't parse Entities JSON: " << jsonParser.getErrorString().c_str();
|
||||
return false;
|
||||
}
|
||||
|
||||
auto root = doc.object();
|
||||
return readOctreeDataInfoFromJSON(root);
|
||||
return readOctreeDataInfoFromMap(entitiesMap);
|
||||
}
|
||||
|
||||
// Reads octree file and parses it into a RawOctreeData object.
|
||||
// Returns false if readOctreeFile fails.
|
||||
bool OctreeUtils::RawOctreeData::readOctreeDataInfoFromFile(QString path) {
|
||||
QJsonDocument doc;
|
||||
if (!readOctreeFile(path, &doc)) {
|
||||
QFile file(path);
|
||||
if (!file.open(QIODevice::ReadOnly)) {
|
||||
qCritical() << "Cannot open json file for reading: " << path;
|
||||
return false;
|
||||
}
|
||||
|
||||
auto root = doc.object();
|
||||
return readOctreeDataInfoFromJSON(root);
|
||||
QByteArray data = file.readAll();
|
||||
|
||||
return readOctreeDataInfoFromData(data);
|
||||
}
|
||||
|
||||
QByteArray OctreeUtils::RawOctreeData::toByteArray() {
|
||||
QJsonObject obj {
|
||||
{ "DataVersion", QJsonValue((qint64)dataVersion) },
|
||||
{ "Id", QJsonValue(id.toString()) },
|
||||
{ "Version", QJsonValue((qint64)version) },
|
||||
};
|
||||
QByteArray jsonString;
|
||||
|
||||
writeSubclassData(obj);
|
||||
jsonString += QString("{\n \"DataVersion\": %1,\n").arg(dataVersion);
|
||||
|
||||
QJsonDocument doc;
|
||||
doc.setObject(obj);
|
||||
writeSubclassData(jsonString);
|
||||
|
||||
return doc.toJson();
|
||||
jsonString += QString(",\n \"Id\": \"%1\",\n \"Version\": %2\n}").arg(id.toString()).arg(version);
|
||||
|
||||
return jsonString;
|
||||
}
|
||||
|
||||
QByteArray OctreeUtils::RawOctreeData::toGzippedByteArray() {
|
||||
|
@ -114,14 +96,21 @@ void OctreeUtils::RawOctreeData::resetIdAndVersion() {
|
|||
qDebug() << "Reset octree data to: " << id << dataVersion;
|
||||
}
|
||||
|
||||
void OctreeUtils::RawEntityData::readSubclassData(const QJsonObject& root) {
|
||||
if (root.contains("Entities")) {
|
||||
entityData = root["Entities"].toArray();
|
||||
void OctreeUtils::RawEntityData::readSubclassData(const QVariantMap& root) {
|
||||
variantEntityData = root["Entities"].toList();
|
||||
}
|
||||
|
||||
void OctreeUtils::RawEntityData::writeSubclassData(QByteArray& root) const {
|
||||
root += " \"Entities\": [";
|
||||
for (auto entityIter = variantEntityData.begin(); entityIter != variantEntityData.end(); ++entityIter) {
|
||||
if (entityIter != variantEntityData.begin()) {
|
||||
root += ",";
|
||||
}
|
||||
root += "\n ";
|
||||
// Convert to string and remove trailing LF.
|
||||
root += QJsonDocument(entityIter->toJsonObject()).toJson().chopped(1);
|
||||
}
|
||||
root += "]";
|
||||
}
|
||||
|
||||
void OctreeUtils::RawEntityData::writeSubclassData(QJsonObject& root) const {
|
||||
root["Entities"] = entityData;
|
||||
}
|
||||
|
||||
PacketType OctreeUtils::RawEntityData::dataPacketType() const { return PacketType::EntityData; }
|
||||
PacketType OctreeUtils::RawEntityData::dataPacketType() const { return PacketType::EntityData; }
|
||||
|
|
|
@ -33,8 +33,8 @@ public:
|
|||
|
||||
virtual PacketType dataPacketType() const;
|
||||
|
||||
virtual void readSubclassData(const QJsonObject& root) { }
|
||||
virtual void writeSubclassData(QJsonObject& root) const { }
|
||||
virtual void readSubclassData(const QVariantMap& root) { }
|
||||
virtual void writeSubclassData(QByteArray& root) const { }
|
||||
|
||||
void resetIdAndVersion();
|
||||
QByteArray toByteArray();
|
||||
|
@ -42,15 +42,16 @@ public:
|
|||
|
||||
bool readOctreeDataInfoFromData(QByteArray data);
|
||||
bool readOctreeDataInfoFromFile(QString path);
|
||||
bool readOctreeDataInfoFromJSON(QJsonObject root);
|
||||
bool readOctreeDataInfoFromMap(const QVariantMap& map);
|
||||
};
|
||||
|
||||
class RawEntityData : public RawOctreeData {
|
||||
public:
|
||||
PacketType dataPacketType() const override;
|
||||
void readSubclassData(const QJsonObject& root) override;
|
||||
void writeSubclassData(QJsonObject& root) const override;
|
||||
void readSubclassData(const QVariantMap& root) override;
|
||||
void writeSubclassData(QByteArray& root) const override;
|
||||
|
||||
QJsonArray entityData;
|
||||
QVariantList variantEntityData;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
273
libraries/octree/src/OctreeEntitiesFileParser.cpp
Normal file
273
libraries/octree/src/OctreeEntitiesFileParser.cpp
Normal file
|
@ -0,0 +1,273 @@
|
|||
//
|
||||
// OctreeEntititesFileParser.cpp
|
||||
// libraries/octree/src
|
||||
//
|
||||
// Created by Simon Walton on Oct 15, 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 <sstream>
|
||||
#include <cctype>
|
||||
#include <QUuid>
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonObject>
|
||||
|
||||
#include "OctreeEntitiesFileParser.h"
|
||||
|
||||
using std::string;
|
||||
|
||||
std::string OctreeEntitiesFileParser::getErrorString() const {
|
||||
std::ostringstream err;
|
||||
if (_errorString.size() != 0) {
|
||||
err << "Error: Line " << _line << ", byte position " << _position << ": " << _errorString;
|
||||
};
|
||||
|
||||
return err.str();
|
||||
}
|
||||
|
||||
void OctreeEntitiesFileParser::setEntitiesString(const QByteArray& entitiesContents) {
|
||||
_entitiesContents = entitiesContents;
|
||||
_entitiesLength = _entitiesContents.length();
|
||||
_position = 0;
|
||||
_line = 1;
|
||||
}
|
||||
|
||||
bool OctreeEntitiesFileParser::parseEntities(QVariantMap& parsedEntities) {
|
||||
if (nextToken() != '{') {
|
||||
_errorString = "Text before start of object";
|
||||
return false;
|
||||
}
|
||||
|
||||
bool gotDataVersion = false;
|
||||
bool gotEntities = false;
|
||||
bool gotId = false;
|
||||
bool gotVersion = false;
|
||||
|
||||
while (!(gotDataVersion && gotEntities && gotId && gotVersion)) {
|
||||
if (nextToken() != '"') {
|
||||
_errorString = "Incorrect key string";
|
||||
return false;
|
||||
}
|
||||
|
||||
string key = readString();
|
||||
if (key.size() == 0) {
|
||||
_errorString = "Missing object key";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (nextToken() != ':') {
|
||||
_errorString = "Ill-formed id/value entry";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (key == "DataVersion") {
|
||||
if (gotDataVersion) {
|
||||
_errorString = "Duplicate DataVersion entries";
|
||||
return false;
|
||||
}
|
||||
|
||||
int dataVersionValue = readInteger();
|
||||
parsedEntities["DataVersion"] = dataVersionValue;
|
||||
gotDataVersion = true;
|
||||
} else if (key == "Entities") {
|
||||
if (gotEntities) {
|
||||
_errorString = "Duplicate Entities entries";
|
||||
return false;
|
||||
}
|
||||
|
||||
QVariantList entitiesValue;
|
||||
if (!readEntitiesArray(entitiesValue)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
parsedEntities["Entities"] = std::move(entitiesValue);
|
||||
gotEntities = true;
|
||||
} else if (key == "Id") {
|
||||
if (gotId) {
|
||||
_errorString = "Duplicate Id entries";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (nextToken() != '"') {
|
||||
_errorString = "Invalid Id value";
|
||||
return false;
|
||||
};
|
||||
string idString = readString();
|
||||
if (idString.size() == 0) {
|
||||
_errorString = "Invalid Id string";
|
||||
return false;
|
||||
}
|
||||
QUuid idValue = QUuid::fromString(QLatin1String(idString.c_str()) );
|
||||
if (idValue.isNull()) {
|
||||
_errorString = "Id value invalid UUID string: " + idString;
|
||||
return false;
|
||||
}
|
||||
|
||||
parsedEntities["Id"] = idValue;
|
||||
gotId = true;
|
||||
} else if (key == "Version") {
|
||||
if (gotVersion) {
|
||||
_errorString = "Duplicate Version entries";
|
||||
return false;
|
||||
}
|
||||
|
||||
int versionValue = readInteger();
|
||||
parsedEntities["Version"] = versionValue;
|
||||
gotVersion = true;
|
||||
} else if (key == "Paths") {
|
||||
// Serverless JSON has optional Paths entry.
|
||||
if (nextToken() != '{') {
|
||||
_errorString = "Paths item is not an object";
|
||||
return false;
|
||||
}
|
||||
|
||||
int matchingBrace = findMatchingBrace();
|
||||
if (matchingBrace < 0) {
|
||||
_errorString = "Unterminated entity object";
|
||||
return false;
|
||||
}
|
||||
|
||||
QByteArray jsonObject = _entitiesContents.mid(_position - 1, matchingBrace - _position + 1);
|
||||
QJsonDocument pathsObject = QJsonDocument::fromJson(jsonObject);
|
||||
if (pathsObject.isNull()) {
|
||||
_errorString = "Ill-formed paths entry";
|
||||
return false;
|
||||
}
|
||||
|
||||
parsedEntities["Paths"] = pathsObject.object();
|
||||
_position = matchingBrace;
|
||||
} else {
|
||||
_errorString = "Unrecognized key name: " + key;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (gotDataVersion && gotEntities && gotId && gotVersion) {
|
||||
break;
|
||||
} else if (nextToken() != ',') {
|
||||
_errorString = "Id/value incorrectly terminated";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (nextToken() != '}' || nextToken() != -1) {
|
||||
_errorString = "Ill-formed end of object";
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int OctreeEntitiesFileParser::nextToken() {
|
||||
while (_position < _entitiesLength) {
|
||||
char c = _entitiesContents[_position++];
|
||||
if (c != ' ' && c != '\t' && c != '\n' && c != '\r') {
|
||||
return c;
|
||||
}
|
||||
if (c == '\n') {
|
||||
++_line;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
string OctreeEntitiesFileParser::readString() {
|
||||
string returnString;
|
||||
while (_position < _entitiesLength) {
|
||||
char c = _entitiesContents[_position++];
|
||||
if (c == '"') {
|
||||
break;
|
||||
} else {
|
||||
returnString.push_back(c);
|
||||
}
|
||||
}
|
||||
|
||||
return returnString;
|
||||
}
|
||||
|
||||
int OctreeEntitiesFileParser::readInteger() {
|
||||
const char* currentPosition = _entitiesContents.constData() + _position;
|
||||
int i = std::atoi(currentPosition);
|
||||
|
||||
int token;
|
||||
do {
|
||||
token = nextToken();
|
||||
} while (token == '-' || token == '+' || std::isdigit(token));
|
||||
|
||||
--_position;
|
||||
return i;
|
||||
}
|
||||
|
||||
bool OctreeEntitiesFileParser::readEntitiesArray(QVariantList& entitiesArray) {
|
||||
if (nextToken() != '[') {
|
||||
_errorString = "Entities entry is not an array";
|
||||
return false;
|
||||
}
|
||||
|
||||
while (true) {
|
||||
if (nextToken() != '{') {
|
||||
_errorString = "Entity array item is not an object";
|
||||
return false;
|
||||
}
|
||||
int matchingBrace = findMatchingBrace();
|
||||
if (matchingBrace < 0) {
|
||||
_errorString = "Unterminated entity object";
|
||||
return false;
|
||||
}
|
||||
|
||||
QByteArray jsonEntity = _entitiesContents.mid(_position - 1, matchingBrace - _position + 1);
|
||||
QJsonDocument entity = QJsonDocument::fromJson(jsonEntity);
|
||||
if (entity.isNull()) {
|
||||
_errorString = "Ill-formed entity";
|
||||
return false;
|
||||
}
|
||||
|
||||
entitiesArray.append(entity.object());
|
||||
_position = matchingBrace;
|
||||
char c = nextToken();
|
||||
if (c == ']') {
|
||||
return true;
|
||||
} else if (c != ',') {
|
||||
_errorString = "Entity array item incorrectly terminated";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
int OctreeEntitiesFileParser::findMatchingBrace() const {
|
||||
int index = _position;
|
||||
int nestCount = 1;
|
||||
while (index < _entitiesLength && nestCount != 0) {
|
||||
switch (_entitiesContents[index++]) {
|
||||
case '{':
|
||||
++nestCount;
|
||||
break;
|
||||
|
||||
case '}':
|
||||
--nestCount;
|
||||
break;
|
||||
|
||||
case '"':
|
||||
// Skip string
|
||||
while (index < _entitiesLength) {
|
||||
if (_entitiesContents[index] == '"') {
|
||||
++index;
|
||||
break;
|
||||
} else if (_entitiesContents[index] == '\\' && _entitiesContents[++index] == 'u') {
|
||||
index += 4;
|
||||
}
|
||||
++index;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return nestCount == 0 ? index : -1;
|
||||
}
|
40
libraries/octree/src/OctreeEntitiesFileParser.h
Normal file
40
libraries/octree/src/OctreeEntitiesFileParser.h
Normal file
|
@ -0,0 +1,40 @@
|
|||
//
|
||||
// OctreeEntititesFileParser.h
|
||||
// libraries/octree/src
|
||||
//
|
||||
// Created by Simon Walton on Oct 15, 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
|
||||
//
|
||||
|
||||
// Parse the top-level of the Models object ourselves - use QJsonDocument for each Entity object.
|
||||
|
||||
#ifndef hifi_OctreeEntitiesFileParser_h
|
||||
#define hifi_OctreeEntitiesFileParser_h
|
||||
|
||||
#include <QByteArray>
|
||||
#include <QVariant>
|
||||
|
||||
class OctreeEntitiesFileParser {
|
||||
public:
|
||||
void setEntitiesString(const QByteArray& entitiesContents);
|
||||
bool parseEntities(QVariantMap& parsedEntities);
|
||||
std::string getErrorString() const;
|
||||
|
||||
private:
|
||||
int nextToken();
|
||||
std::string readString();
|
||||
int readInteger();
|
||||
bool readEntitiesArray(QVariantList& entitiesArray);
|
||||
int findMatchingBrace() const;
|
||||
|
||||
QByteArray _entitiesContents;
|
||||
int _position { 0 };
|
||||
int _line { 1 };
|
||||
int _entitiesLength { 0 };
|
||||
std::string _errorString;
|
||||
};
|
||||
|
||||
#endif // hifi_OctreeEntitiesFileParser_h
|
|
@ -31,6 +31,7 @@
|
|||
#include <NumericalConstants.h>
|
||||
#include <PerfStat.h>
|
||||
#include <PathUtils.h>
|
||||
#include <Gzip.h>
|
||||
|
||||
#include "OctreeLogging.h"
|
||||
#include "OctreeUtils.h"
|
||||
|
@ -72,14 +73,27 @@ void OctreePersistThread::start() {
|
|||
|
||||
OctreeUtils::RawOctreeData data;
|
||||
qCDebug(octree) << "Reading octree data from" << _filename;
|
||||
if (data.readOctreeDataInfoFromFile(_filename)) {
|
||||
qCDebug(octree) << "Current octree data: ID(" << data.id << ") DataVersion(" << data.version << ")";
|
||||
packet->writePrimitive(true);
|
||||
auto id = data.id.toRfc4122();
|
||||
packet->write(id);
|
||||
packet->writePrimitive(data.version);
|
||||
QFile file(_filename);
|
||||
if (file.open(QIODevice::ReadOnly)) {
|
||||
QByteArray jsonData(file.readAll());
|
||||
file.close();
|
||||
if (!gunzip(jsonData, _cachedJSONData)) {
|
||||
_cachedJSONData = jsonData;
|
||||
}
|
||||
|
||||
if (data.readOctreeDataInfoFromData(_cachedJSONData)) {
|
||||
qCDebug(octree) << "Current octree data: ID(" << data.id << ") DataVersion(" << data.version << ")";
|
||||
packet->writePrimitive(true);
|
||||
auto id = data.id.toRfc4122();
|
||||
packet->write(id);
|
||||
packet->writePrimitive(data.version);
|
||||
} else {
|
||||
_cachedJSONData.clear();
|
||||
qCWarning(octree) << "No octree data found";
|
||||
packet->writePrimitive(false);
|
||||
}
|
||||
} else {
|
||||
qCWarning(octree) << "No octree data found";
|
||||
qCWarning(octree) << "Couldn't access file" << _filename << file.errorString();
|
||||
packet->writePrimitive(false);
|
||||
}
|
||||
|
||||
|
@ -99,6 +113,7 @@ void OctreePersistThread::handleOctreeDataFileReply(QSharedPointer<ReceivedMessa
|
|||
OctreeUtils::RawOctreeData data;
|
||||
bool hasValidOctreeData { false };
|
||||
if (includesNewData) {
|
||||
_cachedJSONData.clear();
|
||||
replacementData = message->readAll();
|
||||
replaceData(replacementData);
|
||||
hasValidOctreeData = data.readOctreeDataInfoFromFile(_filename);
|
||||
|
@ -108,7 +123,7 @@ void OctreePersistThread::handleOctreeDataFileReply(QSharedPointer<ReceivedMessa
|
|||
|
||||
OctreeUtils::RawEntityData data;
|
||||
qCDebug(octree) << "Reading octree data from" << _filename;
|
||||
if (data.readOctreeDataInfoFromFile(_filename)) {
|
||||
if (data.readOctreeDataInfoFromData(_cachedJSONData)) {
|
||||
hasValidOctreeData = true;
|
||||
if (data.id.isNull()) {
|
||||
qCDebug(octree) << "Current octree data has a null id, updating";
|
||||
|
@ -138,10 +153,16 @@ void OctreePersistThread::handleOctreeDataFileReply(QSharedPointer<ReceivedMessa
|
|||
_tree->withWriteLock([&] {
|
||||
PerformanceWarning warn(true, "Loading Octree File", true);
|
||||
|
||||
persistentFileRead = _tree->readFromFile(_filename.toLocal8Bit().constData());
|
||||
if (_cachedJSONData.isEmpty()) {
|
||||
persistentFileRead = _tree->readFromFile(_filename.toLocal8Bit().constData());
|
||||
} else {
|
||||
QDataStream jsonStream(_cachedJSONData);
|
||||
persistentFileRead = _tree->readFromStream(-1, jsonStream);
|
||||
}
|
||||
_tree->pruneTree();
|
||||
});
|
||||
|
||||
_cachedJSONData.clear();
|
||||
quint64 loadDone = usecTimestampNow();
|
||||
_loadTimeUSecs = loadDone - loadStarted;
|
||||
|
||||
|
|
|
@ -78,6 +78,7 @@ private:
|
|||
quint64 _lastTimeDebug;
|
||||
|
||||
QString _persistAsFileType;
|
||||
QByteArray _cachedJSONData;
|
||||
};
|
||||
|
||||
#endif // hifi_OctreePersistThread_h
|
||||
|
|
|
@ -32,8 +32,10 @@ LAYOUT_STD140(binding=0) uniform standardInputsBuffer {
|
|||
vec4 date;
|
||||
// Offset 16, acts as vec4 for alignment purposes
|
||||
vec3 worldPosition;
|
||||
// Offset 32, acts as vec4 for alignment purposes
|
||||
// Offset 32, acts as vec4 for alignment purposes (but not packing purposes)
|
||||
vec3 worldScale;
|
||||
// We need this float here to keep globalTime from getting pulled to offset 44
|
||||
float _spare0;
|
||||
// Offset 48
|
||||
float globalTime;
|
||||
// Offset 52
|
||||
|
|
|
@ -387,12 +387,8 @@ void OffscreenSurface::finishQmlLoad(QQmlComponent* qmlComponent,
|
|||
if (!parent) {
|
||||
parent = getRootItem();
|
||||
}
|
||||
// manually control children items lifetime
|
||||
QQmlEngine::setObjectOwnership(newObject, QQmlEngine::CppOwnership);
|
||||
|
||||
// add object to the manual deletion list
|
||||
_sharedObject->addToDeletionList(newObject);
|
||||
|
||||
// Allow child windows to be destroyed from JS
|
||||
QQmlEngine::setObjectOwnership(newObject, QQmlEngine::JavaScriptOwnership);
|
||||
newObject->setParent(parent);
|
||||
newItem->setParentItem(parent);
|
||||
} else {
|
||||
|
|
|
@ -15,7 +15,6 @@
|
|||
#include <QtQml/QQmlEngine>
|
||||
|
||||
#include <QtGui/QOpenGLContext>
|
||||
#include <QPointer>
|
||||
|
||||
#include <NumericalConstants.h>
|
||||
#include <shared/NsightHelpers.h>
|
||||
|
@ -82,6 +81,7 @@ SharedObject::SharedObject() {
|
|||
SharedObject::~SharedObject() {
|
||||
// After destroy returns, the rendering thread should be gone
|
||||
destroy();
|
||||
|
||||
// _renderTimer is created with `this` as the parent, so need no explicit destruction
|
||||
#ifndef DISABLE_QML
|
||||
// Destroy the event hand
|
||||
|
@ -96,11 +96,6 @@ SharedObject::~SharedObject() {
|
|||
}
|
||||
#endif
|
||||
|
||||
// already deleted objects will be reset to null by QPointer so it should be safe just iterate here
|
||||
for (auto qmlObject : _deletionList) {
|
||||
delete qmlObject;
|
||||
}
|
||||
|
||||
if (_rootItem) {
|
||||
delete _rootItem;
|
||||
_rootItem = nullptr;
|
||||
|
@ -417,11 +412,6 @@ bool SharedObject::fetchTexture(TextureAndFence& textureAndFence) {
|
|||
return true;
|
||||
}
|
||||
|
||||
void hifi::qml::impl::SharedObject::addToDeletionList(QObject * object)
|
||||
{
|
||||
_deletionList.append(QPointer<QObject>(object));
|
||||
}
|
||||
|
||||
void SharedObject::setProxyWindow(QWindow* window) {
|
||||
#ifndef DISABLE_QML
|
||||
_proxyWindow = window;
|
||||
|
|
|
@ -66,7 +66,7 @@ public:
|
|||
void resume();
|
||||
bool isPaused() const;
|
||||
bool fetchTexture(TextureAndFence& textureAndFence);
|
||||
void addToDeletionList(QObject* object);
|
||||
|
||||
|
||||
private:
|
||||
bool event(QEvent* e) override;
|
||||
|
@ -91,8 +91,6 @@ private:
|
|||
void onAboutToQuit();
|
||||
void updateTextureAndFence(const TextureAndFence& newTextureAndFence);
|
||||
|
||||
QList<QPointer<QObject>> _deletionList;
|
||||
|
||||
// Texture management
|
||||
TextureAndFence _latestTextureAndFence{ 0, 0 };
|
||||
QQuickItem* _item{ nullptr };
|
||||
|
|
|
@ -12,7 +12,8 @@
|
|||
|
||||
/* global Script, SelectionDisplay, LightOverlayManager, CameraManager, Grid, GridTool, EntityListTool, Vec3, SelectionManager,
|
||||
Overlays, OverlayWebWindow, UserActivityLogger, Settings, Entities, Tablet, Toolbars, Messages, Menu, Camera,
|
||||
progressDialog, tooltip, MyAvatar, Quat, Controller, Clipboard, HMD, UndoStack, OverlaySystemWindow */
|
||||
progressDialog, tooltip, MyAvatar, Quat, Controller, Clipboard, HMD, UndoStack, OverlaySystemWindow,
|
||||
keyUpEventFromUIWindow:true */
|
||||
|
||||
(function() { // BEGIN LOCAL_SCOPE
|
||||
|
||||
|
@ -113,7 +114,6 @@ selectionManager.addEventListener(function () {
|
|||
entityIconOverlayManager.updatePositions();
|
||||
});
|
||||
|
||||
var KEY_P = 80; //Key code for letter p used for Parenting hotkey.
|
||||
var DEGREES_TO_RADIANS = Math.PI / 180.0;
|
||||
var RADIANS_TO_DEGREES = 180.0 / Math.PI;
|
||||
|
||||
|
@ -1964,14 +1964,6 @@ var keyReleaseEvent = function (event) {
|
|||
if (isActive) {
|
||||
cameraManager.keyReleaseEvent(event);
|
||||
}
|
||||
// since sometimes our menu shortcut keys don't work, trap our menu items here also and fire the appropriate menu items
|
||||
if (event.key === KEY_P && event.isControl && !event.isAutoRepeat) {
|
||||
if (event.isShifted) {
|
||||
unparentSelectedEntities();
|
||||
} else {
|
||||
parentSelectedEntities();
|
||||
}
|
||||
}
|
||||
};
|
||||
Controller.keyReleaseEvent.connect(keyReleaseEvent);
|
||||
Controller.keyPressEvent.connect(keyPressEvent);
|
||||
|
@ -2365,10 +2357,6 @@ var PropertiesTool = function (opts) {
|
|||
}
|
||||
pushCommandForSelections();
|
||||
selectionManager._update(false, this);
|
||||
} else if (data.type === 'parent') {
|
||||
parentSelectedEntities();
|
||||
} else if (data.type === 'unparent') {
|
||||
unparentSelectedEntities();
|
||||
} else if (data.type === 'saveUserData' || data.type === 'saveMaterialData') {
|
||||
//the event bridge and json parsing handle our avatar id string differently.
|
||||
var actualID = data.id.split('"')[1];
|
||||
|
@ -2681,9 +2669,14 @@ function whenReleased(fn) {
|
|||
};
|
||||
}
|
||||
|
||||
var isOnMacPlatform = Controller.getValue(Controller.Hardware.Application.PlatformMac);
|
||||
|
||||
var mapping = Controller.newMapping(CONTROLLER_MAPPING_NAME);
|
||||
mapping.from([Controller.Hardware.Keyboard.Delete]).when([!Controller.Hardware.Application.PlatformMac]).to(deleteKey);
|
||||
mapping.from([Controller.Hardware.Keyboard.Backspace]).when([Controller.Hardware.Application.PlatformMac]).to(deleteKey);
|
||||
if (isOnMacPlatform) {
|
||||
mapping.from([Controller.Hardware.Keyboard.Backspace]).to(deleteKey);
|
||||
} else {
|
||||
mapping.from([Controller.Hardware.Keyboard.Delete]).to(deleteKey);
|
||||
}
|
||||
mapping.from([Controller.Hardware.Keyboard.T]).to(toggleKey);
|
||||
mapping.from([Controller.Hardware.Keyboard.F]).to(focusKey);
|
||||
mapping.from([Controller.Hardware.Keyboard.G]).to(gridKey);
|
||||
|
@ -2706,6 +2699,53 @@ mapping.from([Controller.Hardware.Keyboard.Z])
|
|||
.to(whenPressed(function() { undoHistory.redo() }));
|
||||
|
||||
|
||||
mapping.from([Controller.Hardware.Keyboard.P])
|
||||
.when([Controller.Hardware.Keyboard.Control, Controller.Hardware.Keyboard.Shift])
|
||||
.to(whenReleased(function() { unparentSelectedEntities(); }));
|
||||
|
||||
mapping.from([Controller.Hardware.Keyboard.P])
|
||||
.when([Controller.Hardware.Keyboard.Control, !Controller.Hardware.Keyboard.Shift])
|
||||
.to(whenReleased(function() { parentSelectedEntities(); }));
|
||||
|
||||
keyUpEventFromUIWindow = function(keyUpEvent) {
|
||||
var WANT_DEBUG_MISSING_SHORTCUTS = false;
|
||||
|
||||
var pressedValue = 0.0;
|
||||
|
||||
if ((!isOnMacPlatform && keyUpEvent.keyCodeString === "Delete")
|
||||
|| (isOnMacPlatform && keyUpEvent.keyCodeString === "Backspace")) {
|
||||
|
||||
deleteKey(pressedValue);
|
||||
} else if (keyUpEvent.keyCodeString === "T") {
|
||||
toggleKey(pressedValue);
|
||||
} else if (keyUpEvent.keyCodeString === "F") {
|
||||
focusKey(pressedValue);
|
||||
} else if (keyUpEvent.keyCodeString === "G") {
|
||||
gridKey(pressedValue);
|
||||
} else if (keyUpEvent.controlKey && keyUpEvent.keyCodeString === "X") {
|
||||
selectionManager.cutSelectedEntities();
|
||||
} else if (keyUpEvent.controlKey && keyUpEvent.keyCodeString === "C") {
|
||||
selectionManager.copySelectedEntities();
|
||||
} else if (keyUpEvent.controlKey && keyUpEvent.keyCodeString === "V") {
|
||||
selectionManager.pasteEntities();
|
||||
} else if (keyUpEvent.controlKey && keyUpEvent.keyCodeString === "D") {
|
||||
selectionManager.duplicateSelection();
|
||||
} else if (keyUpEvent.controlKey && !keyUpEvent.shiftKey && keyUpEvent.keyCodeString === "Z") {
|
||||
undoHistory.undo();
|
||||
} else if (keyUpEvent.controlKey && !keyUpEvent.shiftKey && keyUpEvent.keyCodeString === "P") {
|
||||
parentSelectedEntities();
|
||||
} else if (keyUpEvent.controlKey && keyUpEvent.shiftKey && keyUpEvent.keyCodeString === "P") {
|
||||
unparentSelectedEntities();
|
||||
} else if (
|
||||
(keyUpEvent.controlKey && keyUpEvent.shiftKey && keyUpEvent.keyCodeString === "Z") ||
|
||||
(keyUpEvent.controlKey && keyUpEvent.keyCodeString === "Y")) {
|
||||
|
||||
undoHistory.redo();
|
||||
} else if (WANT_DEBUG_MISSING_SHORTCUTS) {
|
||||
console.warn("unhandled key event: " + JSON.stringify(keyUpEvent))
|
||||
}
|
||||
};
|
||||
|
||||
var propertyMenu = new PopupMenu();
|
||||
|
||||
propertyMenu.onSelectMenuItem = function (name) {
|
||||
|
@ -2719,22 +2759,6 @@ var showMenuItem = propertyMenu.addMenuItem("Show in Marketplace");
|
|||
|
||||
var propertiesTool = new PropertiesTool();
|
||||
|
||||
entityListTool.webView.webEventReceived.connect(function(data) {
|
||||
try {
|
||||
data = JSON.parse(data);
|
||||
} catch(e) {
|
||||
print("edit.js: Error parsing JSON");
|
||||
return;
|
||||
}
|
||||
|
||||
if (data.type === 'parent') {
|
||||
parentSelectedEntities();
|
||||
} else if (data.type === 'unparent') {
|
||||
unparentSelectedEntities();
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
selectionDisplay.onSpaceModeChange = function(spaceMode) {
|
||||
entityListTool.setSpaceMode(spaceMode);
|
||||
propertiesTool.setSpaceMode(spaceMode);
|
||||
|
|
|
@ -21,8 +21,6 @@ const MAX_LENGTH_RADIUS = 9;
|
|||
const MINIMUM_COLUMN_WIDTH = 24;
|
||||
const SCROLLBAR_WIDTH = 20;
|
||||
const RESIZER_WIDTH = 10;
|
||||
const DELETE = 46; // Key code for the delete key.
|
||||
const KEY_P = 80; // Key code for letter p used for Parenting hotkey.
|
||||
|
||||
const COLUMNS = {
|
||||
type: {
|
||||
|
@ -136,6 +134,8 @@ const FILTER_TYPES = [
|
|||
"Web",
|
||||
"Material",
|
||||
"ParticleEffect",
|
||||
"PolyLine",
|
||||
"PolyVox",
|
||||
"Text",
|
||||
];
|
||||
|
||||
|
@ -148,6 +148,8 @@ const ICON_FOR_TYPE = {
|
|||
Web: "q",
|
||||
Material: "",
|
||||
ParticleEffect: "",
|
||||
PolyLine: "",
|
||||
PolyVox: "",
|
||||
Text: "l",
|
||||
};
|
||||
|
||||
|
@ -1114,22 +1116,70 @@ function loaded() {
|
|||
elToggleSpaceMode.innerText = "World";
|
||||
}
|
||||
}
|
||||
|
||||
const KEY_CODES = {
|
||||
BACKSPACE: 8,
|
||||
DELETE: 46
|
||||
};
|
||||
|
||||
document.addEventListener("keydown", function (keyDownEvent) {
|
||||
if (keyDownEvent.target.nodeName === "INPUT") {
|
||||
document.addEventListener("keyup", function (keyUpEvent) {
|
||||
if (keyUpEvent.target.nodeName === "INPUT") {
|
||||
return;
|
||||
}
|
||||
let keyCode = keyDownEvent.keyCode;
|
||||
if (keyCode === DELETE) {
|
||||
EventBridge.emitWebEvent(JSON.stringify({ type: 'delete' }));
|
||||
|
||||
let {code, key, keyCode, altKey, ctrlKey, metaKey, shiftKey} = keyUpEvent;
|
||||
|
||||
let controlKey = window.navigator.platform.startsWith("Mac") ? metaKey : ctrlKey;
|
||||
|
||||
let keyCodeString;
|
||||
switch (keyCode) {
|
||||
case KEY_CODES.DELETE:
|
||||
keyCodeString = "Delete";
|
||||
break;
|
||||
case KEY_CODES.BACKSPACE:
|
||||
keyCodeString = "Backspace";
|
||||
break;
|
||||
default:
|
||||
keyCodeString = String.fromCharCode(keyUpEvent.keyCode);
|
||||
break;
|
||||
}
|
||||
if (keyDownEvent.keyCode === KEY_P && keyDownEvent.ctrlKey) {
|
||||
if (keyDownEvent.shiftKey) {
|
||||
EventBridge.emitWebEvent(JSON.stringify({ type: 'unparent' }));
|
||||
} else {
|
||||
EventBridge.emitWebEvent(JSON.stringify({ type: 'parent' }));
|
||||
|
||||
if (controlKey && keyCodeString === "A") {
|
||||
let visibleEntityIDs = visibleEntities.map(visibleEntity => visibleEntity.id);
|
||||
let selectionIncludesAllVisibleEntityIDs = visibleEntityIDs.every(visibleEntityID => {
|
||||
return selectedEntities.includes(visibleEntityID);
|
||||
});
|
||||
|
||||
let selection = [];
|
||||
|
||||
if (!selectionIncludesAllVisibleEntityIDs) {
|
||||
selection = visibleEntityIDs;
|
||||
}
|
||||
|
||||
updateSelectedEntities(selection);
|
||||
|
||||
EventBridge.emitWebEvent(JSON.stringify({
|
||||
type: "selectionUpdate",
|
||||
focus: false,
|
||||
entityIds: selection,
|
||||
}));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
EventBridge.emitWebEvent(JSON.stringify({
|
||||
type: 'keyUpEvent',
|
||||
keyUpEvent: {
|
||||
code,
|
||||
key,
|
||||
keyCode,
|
||||
keyCodeString,
|
||||
altKey,
|
||||
controlKey,
|
||||
shiftKey,
|
||||
}
|
||||
}));
|
||||
}, false);
|
||||
|
||||
if (window.EventBridge !== undefined) {
|
||||
|
|
|
@ -1342,6 +1342,8 @@ const GROUPS_PER_TYPE = {
|
|||
Material: [ 'base', 'material', 'spatial', 'behavior' ],
|
||||
ParticleEffect: [ 'base', 'particles', 'particles_emit', 'particles_size', 'particles_color', 'particles_alpha',
|
||||
'particles_acceleration', 'particles_spin', 'particles_constraints', 'spatial', 'behavior', 'physics' ],
|
||||
PolyLine: [ 'base', 'spatial', 'behavior', 'collision', 'physics' ],
|
||||
PolyVox: [ 'base', 'spatial', 'behavior', 'collision', 'physics' ],
|
||||
Multiple: [ 'base', 'spatial', 'behavior', 'collision', 'physics' ],
|
||||
};
|
||||
|
||||
|
@ -1352,8 +1354,6 @@ const COLOR_MIN = 0;
|
|||
const COLOR_MAX = 255;
|
||||
const COLOR_STEP = 1;
|
||||
|
||||
const KEY_P = 80; // Key code for letter p used for Parenting hotkey.
|
||||
|
||||
const MATERIAL_PREFIX_STRING = "mat::";
|
||||
|
||||
const PENDING_SCRIPT_STATUS = "[ Fetching status ]";
|
||||
|
@ -3494,16 +3494,46 @@ function loaded() {
|
|||
el.parentNode.removeChild(el);
|
||||
elDropdowns = document.getElementsByTagName("select");
|
||||
}
|
||||
|
||||
document.addEventListener("keydown", function (keyDown) {
|
||||
if (keyDown.keyCode === KEY_P && keyDown.ctrlKey) {
|
||||
if (keyDown.shiftKey) {
|
||||
EventBridge.emitWebEvent(JSON.stringify({ type: 'unparent' }));
|
||||
} else {
|
||||
EventBridge.emitWebEvent(JSON.stringify({ type: 'parent' }));
|
||||
}
|
||||
|
||||
const KEY_CODES = {
|
||||
BACKSPACE: 8,
|
||||
DELETE: 46
|
||||
};
|
||||
|
||||
document.addEventListener("keyup", function (keyUpEvent) {
|
||||
if (keyUpEvent.target.nodeName === "INPUT") {
|
||||
return;
|
||||
}
|
||||
});
|
||||
let {code, key, keyCode, altKey, ctrlKey, metaKey, shiftKey} = keyUpEvent;
|
||||
|
||||
let controlKey = window.navigator.platform.startsWith("Mac") ? metaKey : ctrlKey;
|
||||
|
||||
let keyCodeString;
|
||||
switch (keyCode) {
|
||||
case KEY_CODES.DELETE:
|
||||
keyCodeString = "Delete";
|
||||
break;
|
||||
case KEY_CODES.BACKSPACE:
|
||||
keyCodeString = "Backspace";
|
||||
break;
|
||||
default:
|
||||
keyCodeString = String.fromCharCode(keyUpEvent.keyCode);
|
||||
break;
|
||||
}
|
||||
|
||||
EventBridge.emitWebEvent(JSON.stringify({
|
||||
type: 'keyUpEvent',
|
||||
keyUpEvent: {
|
||||
code,
|
||||
key,
|
||||
keyCode,
|
||||
keyCodeString,
|
||||
altKey,
|
||||
controlKey,
|
||||
shiftKey,
|
||||
}
|
||||
}));
|
||||
}, false);
|
||||
|
||||
window.onblur = function() {
|
||||
// Fake a change event
|
||||
|
|
|
@ -6,8 +6,6 @@
|
|||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
|
||||
const KEY_P = 80; //Key code for letter p used for Parenting hotkey.
|
||||
|
||||
function loaded() {
|
||||
openEventBridge(function() {
|
||||
elPosY = document.getElementById("horiz-y");
|
||||
|
@ -105,7 +103,7 @@ function loaded() {
|
|||
elColorBlue.value = blue;
|
||||
gridColor = { red: red, green: green, blue: blue };
|
||||
emitUpdate();
|
||||
}
|
||||
};
|
||||
|
||||
elColorRed.addEventListener('change', colorChangeFunction);
|
||||
elColorGreen.addEventListener('change', colorChangeFunction);
|
||||
|
@ -131,15 +129,47 @@ function loaded() {
|
|||
|
||||
EventBridge.emitWebEvent(JSON.stringify({ type: 'init' }));
|
||||
});
|
||||
document.addEventListener("keydown", function (keyDown) {
|
||||
if (keyDown.keyCode === KEY_P && keyDown.ctrlKey) {
|
||||
if (keyDown.shiftKey) {
|
||||
EventBridge.emitWebEvent(JSON.stringify({ type: 'unparent' }));
|
||||
} else {
|
||||
EventBridge.emitWebEvent(JSON.stringify({ type: 'parent' }));
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
const KEY_CODES = {
|
||||
BACKSPACE: 8,
|
||||
DELETE: 46
|
||||
};
|
||||
|
||||
document.addEventListener("keyup", function (keyUpEvent) {
|
||||
if (keyUpEvent.target.nodeName === "INPUT") {
|
||||
return;
|
||||
}
|
||||
let {code, key, keyCode, altKey, ctrlKey, metaKey, shiftKey} = keyUpEvent;
|
||||
|
||||
let controlKey = window.navigator.platform.startsWith("Mac") ? metaKey : ctrlKey;
|
||||
|
||||
let keyCodeString;
|
||||
switch (keyCode) {
|
||||
case KEY_CODES.DELETE:
|
||||
keyCodeString = "Delete";
|
||||
break;
|
||||
case KEY_CODES.BACKSPACE:
|
||||
keyCodeString = "Backspace";
|
||||
break;
|
||||
default:
|
||||
keyCodeString = String.fromCharCode(keyUpEvent.keyCode);
|
||||
break;
|
||||
}
|
||||
|
||||
EventBridge.emitWebEvent(JSON.stringify({
|
||||
type: 'keyUpEvent',
|
||||
keyUpEvent: {
|
||||
code,
|
||||
key,
|
||||
keyCode,
|
||||
keyCodeString,
|
||||
altKey,
|
||||
controlKey,
|
||||
shiftKey,
|
||||
}
|
||||
}));
|
||||
}, false);
|
||||
|
||||
// Disable right-click context menu which is not visible in the HMD and makes it seem like the app has locked
|
||||
document.addEventListener("contextmenu", function (event) {
|
||||
event.preventDefault();
|
||||
|
|
|
@ -8,4 +8,5 @@ HifiControls.WebView {
|
|||
id: entityListToolWebView
|
||||
url: Qt.resolvedUrl("../html/entityList.html")
|
||||
enabled: true
|
||||
blurOnCtrlShift: false
|
||||
}
|
||||
|
|
|
@ -9,7 +9,8 @@
|
|||
//
|
||||
|
||||
/* global EntityListTool, Tablet, selectionManager, Entities, Camera, MyAvatar, Vec3, Menu, Messages,
|
||||
cameraManager, MENU_EASE_ON_FOCUS, deleteSelectedEntities, toggleSelectedEntitiesLocked, toggleSelectedEntitiesVisible */
|
||||
cameraManager, MENU_EASE_ON_FOCUS, deleteSelectedEntities, toggleSelectedEntitiesLocked, toggleSelectedEntitiesVisible,
|
||||
keyUpEventFromUIWindow */
|
||||
|
||||
var PROFILING_ENABLED = false;
|
||||
var profileIndent = '';
|
||||
|
@ -298,6 +299,8 @@ EntityListTool = function(shouldUseEditTabletApp) {
|
|||
SelectionManager._update();
|
||||
} else if (data.type === "toggleSpaceMode") {
|
||||
SelectionDisplay.toggleSpaceMode();
|
||||
} else if (data.type === 'keyUpEvent') {
|
||||
keyUpEventFromUIWindow(data.keyUpEvent);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
/* global keyUpEventFromUIWindow */
|
||||
|
||||
var GRID_CONTROLS_HTML_URL = Script.resolvePath('../html/gridControls.html');
|
||||
|
||||
Grid = function() {
|
||||
|
@ -270,24 +272,26 @@ GridTool = function(opts) {
|
|||
return;
|
||||
}
|
||||
|
||||
if (data.type == "init") {
|
||||
if (data.type === "init") {
|
||||
horizontalGrid.emitUpdate();
|
||||
} else if (data.type == "update") {
|
||||
} else if (data.type === "update") {
|
||||
horizontalGrid.update(data);
|
||||
for (var i = 0; i < listeners.length; i++) {
|
||||
listeners[i] && listeners[i](data);
|
||||
}
|
||||
} else if (data.type == "action") {
|
||||
} else if (data.type === "action") {
|
||||
var action = data.action;
|
||||
if (action == "moveToAvatar") {
|
||||
if (action === "moveToAvatar") {
|
||||
var position = MyAvatar.getJointPosition("LeftFoot");
|
||||
if (position.x == 0 && position.y == 0 && position.z == 0) {
|
||||
if (position.x === 0 && position.y === 0 && position.z === 0) {
|
||||
position = MyAvatar.position;
|
||||
}
|
||||
horizontalGrid.setPosition(position);
|
||||
} else if (action == "moveToSelection") {
|
||||
} else if (action === "moveToSelection") {
|
||||
horizontalGrid.moveToSelection();
|
||||
}
|
||||
} else if (data.type === 'keyUpEvent') {
|
||||
keyUpEventFromUIWindow(data.keyUpEvent);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
Loading…
Reference in a new issue