From 541692c5cafc389c96d1f0b25a4d4b39d6e1011f Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Sat, 28 Jan 2017 15:38:18 -0800 Subject: [PATCH 001/302] start on scripting interface to allow saving polyvox meshes to obj files --- interface/src/Application.cpp | 1 + interface/src/Application.h | 2 + .../entities/src/EntityScriptingInterface.cpp | 4 ++ .../entities/src/EntityScriptingInterface.h | 2 + libraries/fbx/src/OBJWriter.cpp | 71 +++++++++++++++++++ libraries/fbx/src/OBJWriter.h | 6 ++ .../src/ModelScriptingInterface.cpp | 31 ++++++++ .../src/ModelScriptingInterface.h | 52 ++++++++++++++ libraries/script-engine/src/ScriptEngine.cpp | 7 +- 9 files changed, 175 insertions(+), 1 deletion(-) create mode 100644 libraries/fbx/src/OBJWriter.cpp create mode 100644 libraries/fbx/src/OBJWriter.h create mode 100644 libraries/script-engine/src/ModelScriptingInterface.cpp create mode 100644 libraries/script-engine/src/ModelScriptingInterface.h diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index c96a63ce1c..93a67fe639 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -175,6 +175,7 @@ #include #include #include +#include // On Windows PC, NVidia Optimus laptop, we want to enable NVIDIA GPU // FIXME seems to be broken. diff --git a/interface/src/Application.h b/interface/src/Application.h index 3b89aa52f3..cac11e3ed9 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -71,6 +71,8 @@ #include #include +#include + class OffscreenGLCanvas; class GLCanvas; diff --git a/libraries/entities/src/EntityScriptingInterface.cpp b/libraries/entities/src/EntityScriptingInterface.cpp index 46bc46adab..c6a17a56bd 100644 --- a/libraries/entities/src/EntityScriptingInterface.cpp +++ b/libraries/entities/src/EntityScriptingInterface.cpp @@ -911,6 +911,10 @@ bool EntityScriptingInterface::setVoxelsInCuboid(QUuid entityID, const glm::vec3 }); } +MeshProxy* EntityScriptingInterface::voxelsToMesh(QUuid entityID) { + return nullptr; +} + bool EntityScriptingInterface::setAllPoints(QUuid entityID, const QVector& points) { EntityItemPointer entity = static_cast(_entityTree->findEntityByEntityItemID(entityID)); if (!entity) { diff --git a/libraries/entities/src/EntityScriptingInterface.h b/libraries/entities/src/EntityScriptingInterface.h index 0353fa08a8..c0260bd199 100644 --- a/libraries/entities/src/EntityScriptingInterface.h +++ b/libraries/entities/src/EntityScriptingInterface.h @@ -35,6 +35,7 @@ #include "EntityItemProperties.h" class EntityTree; +class MeshProxy; class RayToEntityIntersectionResult { public: @@ -227,6 +228,7 @@ public slots: Q_INVOKABLE bool setAllVoxels(QUuid entityID, int value); Q_INVOKABLE bool setVoxelsInCuboid(QUuid entityID, const glm::vec3& lowPosition, const glm::vec3& cuboidSize, int value); + Q_INVOKABLE MeshProxy* voxelsToMesh(QUuid entityID); Q_INVOKABLE bool setAllPoints(QUuid entityID, const QVector& points); Q_INVOKABLE bool appendPoint(QUuid entityID, const glm::vec3& point); diff --git a/libraries/fbx/src/OBJWriter.cpp b/libraries/fbx/src/OBJWriter.cpp new file mode 100644 index 0000000000..ab84e7b0fd --- /dev/null +++ b/libraries/fbx/src/OBJWriter.cpp @@ -0,0 +1,71 @@ +// +// FBXReader.h +// libraries/fbx/src/ +// +// Created by Seth Alves on 2017-1-27. +// Copyright 2017 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 "OBJWriter.h" + + +bool writeOBJ(QString outFileName, QVector) { +//bool writeOBJ(QString outFileName, FBXGeometry& geometry, bool outputCentimeters, int whichMeshPart) { + // QFile file(outFileName); + // if (!file.open(QIODevice::WriteOnly)) { + // qWarning() << "unable to write to" << outFileName; + // return false; + // } + + // QTextStream out(&file); + // if (outputCentimeters) { + // out << "# This file uses centimeters as units\n\n"; + // } + + // unsigned int nth = 0; + + // // vertex indexes in obj files span the entire file + // // vertex indexes in a mesh span just that mesh + + // int vertexIndexOffset = 0; + + // foreach (const FBXMesh& mesh, geometry.meshes) { + // bool verticesHaveBeenOutput = false; + // foreach (const FBXMeshPart &meshPart, mesh.parts) { + // if (whichMeshPart >= 0 && nth != (unsigned int) whichMeshPart) { + // nth++; + // continue; + // } + + // if (!verticesHaveBeenOutput) { + // for (int i = 0; i < mesh.vertices.size(); i++) { + // glm::vec4 v = mesh.modelTransform * glm::vec4(mesh.vertices[i], 1.0f); + // out << "v "; + // out << formatFloat(v[0]) << " "; + // out << formatFloat(v[1]) << " "; + // out << formatFloat(v[2]) << "\n"; + // } + // verticesHaveBeenOutput = true; + // } + + // out << "g hull-" << nth++ << "\n"; + // int triangleCount = meshPart.triangleIndices.size() / 3; + // for (int i = 0; i < triangleCount; i++) { + // out << "f "; + // out << vertexIndexOffset + meshPart.triangleIndices[i*3] + 1 << " "; + // out << vertexIndexOffset + meshPart.triangleIndices[i*3+1] + 1 << " "; + // out << vertexIndexOffset + meshPart.triangleIndices[i*3+2] + 1 << "\n"; + // } + // out << "\n"; + // } + + // if (verticesHaveBeenOutput) { + // vertexIndexOffset += mesh.vertices.size(); + // } + // } + + return true; +} diff --git a/libraries/fbx/src/OBJWriter.h b/libraries/fbx/src/OBJWriter.h new file mode 100644 index 0000000000..c0400b3d8e --- /dev/null +++ b/libraries/fbx/src/OBJWriter.h @@ -0,0 +1,6 @@ + +#include +#include +#include + +bool writeOBJ(QString outFileName, QVector); diff --git a/libraries/script-engine/src/ModelScriptingInterface.cpp b/libraries/script-engine/src/ModelScriptingInterface.cpp new file mode 100644 index 0000000000..fd550749f7 --- /dev/null +++ b/libraries/script-engine/src/ModelScriptingInterface.cpp @@ -0,0 +1,31 @@ +// +// ModelScriptingInterface.cpp +// libraries/script-engine/src +// +// Created by Seth Alves on 2017-1-27. +// Copyright 2017 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 "ModelScriptingInterface.h" + +ModelScriptingInterface::ModelScriptingInterface(QObject* parent) : QObject(parent) { +} + + +MeshProxy::MeshProxy(MeshPointer mesh) : _mesh(mesh) { +} + +MeshProxy::~MeshProxy() { +} + + +QScriptValue meshToScriptValue(QScriptEngine* engine, MeshProxy* const &in) { + QScriptValue obj("something"); + return obj; +} + +void meshFromScriptValue(const QScriptValue& value, MeshProxy* &out) { +} diff --git a/libraries/script-engine/src/ModelScriptingInterface.h b/libraries/script-engine/src/ModelScriptingInterface.h new file mode 100644 index 0000000000..3d3e6b94a2 --- /dev/null +++ b/libraries/script-engine/src/ModelScriptingInterface.h @@ -0,0 +1,52 @@ +// +// ModelScriptingInterface.h +// libraries/script-engine/src +// +// Created by Seth Alves on 2017-1-27. +// Copyright 2017 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + + +#ifndef hifi_ModelScriptingInterface_h +#define hifi_ModelScriptingInterface_h + +#include +#include +#include +#include + +using MeshPointer = std::shared_ptr; + +class ModelScriptingInterface : public QObject { + Q_OBJECT + +public: + ModelScriptingInterface(QObject* parent); +}; + +class MeshProxy : public QObject { + Q_OBJECT + +public: + MeshProxy(MeshPointer mesh); + // MeshProxy(const MeshProxy& meshProxy) { _mesh = meshProxy.getMeshPointer(); } + ~MeshProxy(); + + MeshPointer getMeshPointer() const { return _mesh; } + +protected: + MeshPointer _mesh; +}; + + +Q_DECLARE_METATYPE(MeshProxy*); + + +QScriptValue meshToScriptValue(QScriptEngine* engine, MeshProxy* const &in); +// QScriptValue meshToScriptValue(QScriptEngine* engine, const MeshPointer& in); +void meshFromScriptValue(const QScriptValue& value, MeshProxy* &out); + +#endif // hifi_ModelScriptingInterface_h diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index 2191d45d45..b687da7e23 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -63,6 +63,8 @@ #include "RecordingScriptingInterface.h" #include "ScriptEngines.h" #include "TabletScriptingInterface.h" +#include "ModelScriptingInterface.h" + #include "MIDIEvent.h" @@ -570,7 +572,7 @@ void ScriptEngine::init() { registerGlobalObject("Messages", DependencyManager::get().data()); registerGlobalObject("File", new FileScriptingInterface(this)); - + qScriptRegisterMetaType(this, animVarMapToScriptValue, animVarMapFromScriptValue); qScriptRegisterMetaType(this, resultHandlerToScriptValue, resultHandlerFromScriptValue); @@ -586,6 +588,9 @@ void ScriptEngine::init() { registerGlobalObject("Tablet", DependencyManager::get().data()); registerGlobalObject("Assets", &_assetScriptingInterface); registerGlobalObject("Resources", DependencyManager::get().data()); + + registerGlobalObject("Model", new ModelScriptingInterface(this)); + qScriptRegisterMetaType(this, meshToScriptValue, meshFromScriptValue); } void ScriptEngine::registerValue(const QString& valueName, QScriptValue value) { From d158f6afd16cf8641f577a14294e98498bd05cd0 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Sat, 28 Jan 2017 20:42:14 -0800 Subject: [PATCH 002/302] hook up a way to get mesh out of polyvox --- .../src/RenderablePolyVoxEntityItem.cpp | 27 ++++++++++---- .../src/RenderablePolyVoxEntityItem.h | 5 ++- .../entities/src/EntityScriptingInterface.cpp | 37 +++++++++++-------- .../entities/src/EntityScriptingInterface.h | 4 +- libraries/entities/src/PolyVoxEntityItem.cpp | 4 ++ libraries/entities/src/PolyVoxEntityItem.h | 4 +- .../src/ModelScriptingInterface.cpp | 7 +++- .../src/ModelScriptingInterface.h | 2 + 8 files changed, 60 insertions(+), 30 deletions(-) diff --git a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp index 7646f0a454..0179814641 100644 --- a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp @@ -14,6 +14,7 @@ #include #include #include +#include "ModelScriptingInterface.h" #if defined(__GNUC__) && !defined(__clang__) #pragma GCC diagnostic push @@ -73,7 +74,7 @@ const float MARCHING_CUBE_COLLISION_HULL_OFFSET = 0.5; _meshDirty In RenderablePolyVoxEntityItem::render, these flags are checked and changes are propagated along the chain. - decompressVolumeData() is called to decompress _voxelData into _volData. getMesh() is called to invoke the + decompressVolumeData() is called to decompress _voxelData into _volData. recomputeMesh() is called to invoke the polyVox surface extractor to create _mesh (as well as set Simulation _dirtyFlags). Because Simulation::DIRTY_SHAPE is set, isReadyToComputeShape() gets called and _shape is created either from _volData or _shape, depending on the surface style. @@ -81,7 +82,7 @@ const float MARCHING_CUBE_COLLISION_HULL_OFFSET = 0.5; When a script changes _volData, compressVolumeDataAndSendEditPacket is called to update _voxelData and to send a packet to the entity-server. - decompressVolumeData, getMesh, computeShapeInfoWorker, and compressVolumeDataAndSendEditPacket are too expensive + decompressVolumeData, recomputeMesh, computeShapeInfoWorker, and compressVolumeDataAndSendEditPacket are too expensive to run on a thread that has other things to do. These use QtConcurrent::run to spawn a thread. As each thread finishes, it adjusts the dirty flags so that the next call to render() will kick off the next step. @@ -570,7 +571,7 @@ void RenderablePolyVoxEntityItem::render(RenderArgs* args) { if (voxelDataDirty) { decompressVolumeData(); } else if (volDataDirty) { - getMesh(); + recomputeMesh(); } model::MeshPointer mesh; @@ -1096,7 +1097,7 @@ void RenderablePolyVoxEntityItem::copyUpperEdgesFromNeighbors() { } } -void RenderablePolyVoxEntityItem::getMesh() { +void RenderablePolyVoxEntityItem::recomputeMesh() { // use _volData to make a renderable mesh PolyVoxSurfaceStyle voxelSurfaceStyle; withReadLock([&] { @@ -1171,7 +1172,7 @@ void RenderablePolyVoxEntityItem::getMesh() { } void RenderablePolyVoxEntityItem::setMesh(model::MeshPointer mesh) { - // this catches the payload from getMesh + // this catches the payload from recomputeMesh bool neighborsNeedUpdate; withWriteLock([&] { _dirtyFlags |= Simulation::DIRTY_SHAPE | Simulation::DIRTY_MASS; @@ -1426,7 +1427,6 @@ std::shared_ptr RenderablePolyVoxEntityItem::getZPN return std::dynamic_pointer_cast(_zPNeighbor.lock()); } - void RenderablePolyVoxEntityItem::bonkNeighbors() { // flag neighbors to the negative of this entity as needing to rebake their meshes. cacheNeighbors(); @@ -1446,7 +1446,6 @@ void RenderablePolyVoxEntityItem::bonkNeighbors() { } } - void RenderablePolyVoxEntityItem::locationChanged(bool tellPhysics) { EntityItem::locationChanged(tellPhysics); if (!_pipeline || !render::Item::isValidID(_myItem)) { @@ -1458,3 +1457,17 @@ void RenderablePolyVoxEntityItem::locationChanged(bool tellPhysics) { scene->enqueuePendingChanges(pendingChanges); } + +bool RenderablePolyVoxEntityItem::getMeshAsScriptValue(QScriptEngine *engine, QScriptValue& result) const { + bool success = false; + MeshProxy* meshProxy = nullptr; + model::MeshPointer mesh = nullptr; + withReadLock([&] { + if (_meshInitialized) { + success = true; + meshProxy = new MeshProxy(_mesh); + } + }); + result = meshToScriptValue(engine, meshProxy); + return success; +} diff --git a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.h b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.h index ee4c3b318f..e86356deb3 100644 --- a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.h +++ b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.h @@ -130,6 +130,7 @@ public: std::function thunk); void setMesh(model::MeshPointer mesh); + bool getMeshAsScriptValue(QScriptEngine *engine, QScriptValue& result) const override; void setCollisionPoints(ShapeInfo::PointCollection points, AABox box); PolyVox::SimpleVolume* getVolData() { return _volData; } @@ -164,7 +165,7 @@ private: ShapeInfo _shapeInfo; PolyVox::SimpleVolume* _volData = nullptr; - bool _volDataDirty = false; // does getMesh need to be called? + bool _volDataDirty = false; // does recomputeMesh need to be called? int _onCount; // how many non-zero voxels are in _volData bool _neighborsNeedUpdate { false }; @@ -175,7 +176,7 @@ private: // these are run off the main thread void decompressVolumeData(); void compressVolumeDataAndSendEditPacket(); - virtual void getMesh() override; // recompute mesh + virtual void recomputeMesh() override; // recompute mesh void computeShapeInfoWorker(); // these are cached lookups of _xNNeighborID, _yNNeighborID, _zNNeighborID, _xPNeighborID, _yPNeighborID, _zPNeighborID diff --git a/libraries/entities/src/EntityScriptingInterface.cpp b/libraries/entities/src/EntityScriptingInterface.cpp index c6a17a56bd..b83816cc19 100644 --- a/libraries/entities/src/EntityScriptingInterface.cpp +++ b/libraries/entities/src/EntityScriptingInterface.cpp @@ -822,8 +822,7 @@ void RayToEntityIntersectionResultFromScriptValue(const QScriptValue& object, Ra } } -bool EntityScriptingInterface::setVoxels(QUuid entityID, - std::function actor) { +bool EntityScriptingInterface::polyVoxWorker(QUuid entityID, std::function actor) { if (!_entityTree) { return false; } @@ -887,32 +886,38 @@ bool EntityScriptingInterface::setPoints(QUuid entityID, std::function& points) { diff --git a/libraries/entities/src/EntityScriptingInterface.h b/libraries/entities/src/EntityScriptingInterface.h index c0260bd199..5822d93df2 100644 --- a/libraries/entities/src/EntityScriptingInterface.h +++ b/libraries/entities/src/EntityScriptingInterface.h @@ -228,7 +228,7 @@ public slots: Q_INVOKABLE bool setAllVoxels(QUuid entityID, int value); Q_INVOKABLE bool setVoxelsInCuboid(QUuid entityID, const glm::vec3& lowPosition, const glm::vec3& cuboidSize, int value); - Q_INVOKABLE MeshProxy* voxelsToMesh(QUuid entityID); + Q_INVOKABLE void voxelsToMesh(QUuid entityID, QScriptValue callback); Q_INVOKABLE bool setAllPoints(QUuid entityID, const QVector& points); Q_INVOKABLE bool appendPoint(QUuid entityID, const glm::vec3& point); @@ -321,7 +321,7 @@ signals: private: bool actionWorker(const QUuid& entityID, std::function actor); - bool setVoxels(QUuid entityID, std::function actor); + bool polyVoxWorker(QUuid entityID, std::function actor); bool setPoints(QUuid entityID, std::function actor); void queueEntityMessage(PacketType packetType, EntityItemID entityID, const EntityItemProperties& properties); diff --git a/libraries/entities/src/PolyVoxEntityItem.cpp b/libraries/entities/src/PolyVoxEntityItem.cpp index 2a374c1d17..a827a9903d 100644 --- a/libraries/entities/src/PolyVoxEntityItem.cpp +++ b/libraries/entities/src/PolyVoxEntityItem.cpp @@ -242,3 +242,7 @@ const QByteArray PolyVoxEntityItem::getVoxelData() const { }); return voxelDataCopy; } + +bool PolyVoxEntityItem::getMeshAsScriptValue(QScriptEngine *engine, QScriptValue& result) const { + return false; +} diff --git a/libraries/entities/src/PolyVoxEntityItem.h b/libraries/entities/src/PolyVoxEntityItem.h index 4f478c8bf7..bc8ace2827 100644 --- a/libraries/entities/src/PolyVoxEntityItem.h +++ b/libraries/entities/src/PolyVoxEntityItem.h @@ -129,7 +129,9 @@ class PolyVoxEntityItem : public EntityItem { virtual void rebakeMesh() {}; void setVoxelDataDirty(bool value) { withWriteLock([&] { _voxelDataDirty = value; }); } - virtual void getMesh() {}; // recompute mesh + virtual void recomputeMesh() {}; + + virtual bool getMeshAsScriptValue(QScriptEngine *engine, QScriptValue& result) const; protected: glm::vec3 _voxelVolumeSize; // this is always 3 bytes diff --git a/libraries/script-engine/src/ModelScriptingInterface.cpp b/libraries/script-engine/src/ModelScriptingInterface.cpp index fd550749f7..30ef9a713e 100644 --- a/libraries/script-engine/src/ModelScriptingInterface.cpp +++ b/libraries/script-engine/src/ModelScriptingInterface.cpp @@ -9,6 +9,8 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +#include +#include #include "ModelScriptingInterface.h" ModelScriptingInterface::ModelScriptingInterface(QObject* parent) : QObject(parent) { @@ -23,9 +25,10 @@ MeshProxy::~MeshProxy() { QScriptValue meshToScriptValue(QScriptEngine* engine, MeshProxy* const &in) { - QScriptValue obj("something"); - return obj; + return engine->newQObject(in, QScriptEngine::QtOwnership, + QScriptEngine::ExcludeDeleteLater | QScriptEngine::ExcludeChildObjects); } void meshFromScriptValue(const QScriptValue& value, MeshProxy* &out) { + out = qobject_cast(value.toQObject()); } diff --git a/libraries/script-engine/src/ModelScriptingInterface.h b/libraries/script-engine/src/ModelScriptingInterface.h index 3d3e6b94a2..4cc493b257 100644 --- a/libraries/script-engine/src/ModelScriptingInterface.h +++ b/libraries/script-engine/src/ModelScriptingInterface.h @@ -37,6 +37,8 @@ public: MeshPointer getMeshPointer() const { return _mesh; } + Q_INVOKABLE int getNumVertices() const { return _mesh->getNumVertices(); } + protected: MeshPointer _mesh; }; From bb3155ee6100fd9ba73c60667737fd8286e620f2 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Tue, 31 Jan 2017 14:29:42 -0800 Subject: [PATCH 003/302] obj file is now produced in /tmp --- .../src/RenderablePolyVoxEntityItem.cpp | 8 + libraries/fbx/src/OBJWriter.cpp | 157 ++++++++++++------ libraries/fbx/src/OBJWriter.h | 22 ++- libraries/script-engine/src/MeshProxy.h | 39 +++++ .../src/ModelScriptingInterface.cpp | 20 ++- .../src/ModelScriptingInterface.h | 25 +-- 6 files changed, 194 insertions(+), 77 deletions(-) create mode 100644 libraries/script-engine/src/MeshProxy.h diff --git a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp index 0179814641..78a9e6a84a 100644 --- a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp @@ -1167,6 +1167,14 @@ void RenderablePolyVoxEntityItem::recomputeMesh() { vertexBufferPtr->getSize() , sizeof(PolyVox::PositionMaterialNormal), gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::RAW))); + + std::vector parts; + parts.emplace_back(model::Mesh::Part(0, // startIndex + vecIndices.size(), // numIndices + 0, // baseVertex + model::Mesh::TRIANGLES)); // topology + mesh->setPartBuffer(gpu::BufferView(new gpu::Buffer(parts.size() * sizeof(model::Mesh::Part), + (gpu::Byte*) parts.data()), gpu::Element::PART_DRAWCALL)); entity->setMesh(mesh); }); } diff --git a/libraries/fbx/src/OBJWriter.cpp b/libraries/fbx/src/OBJWriter.cpp index ab84e7b0fd..d33d767e5d 100644 --- a/libraries/fbx/src/OBJWriter.cpp +++ b/libraries/fbx/src/OBJWriter.cpp @@ -1,5 +1,5 @@ // -// FBXReader.h +// OBJWriter.cpp // libraries/fbx/src/ // // Created by Seth Alves on 2017-1-27. @@ -9,63 +9,126 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +#include +#include +#include "model/Geometry.h" #include "OBJWriter.h" -bool writeOBJ(QString outFileName, QVector) { -//bool writeOBJ(QString outFileName, FBXGeometry& geometry, bool outputCentimeters, int whichMeshPart) { - // QFile file(outFileName); - // if (!file.open(QIODevice::WriteOnly)) { - // qWarning() << "unable to write to" << outFileName; - // return false; - // } - // QTextStream out(&file); - // if (outputCentimeters) { - // out << "# This file uses centimeters as units\n\n"; - // } +static QString formatFloat(double n) { + // limit precision to 6, but don't output trailing zeros. + QString s = QString::number(n, 'f', 6); + while (s.endsWith("0")) { + s.remove(s.size() - 1, 1); + } + if (s.endsWith(".")) { + s.remove(s.size() - 1, 1); + } + return s; +} - // unsigned int nth = 0; - // // vertex indexes in obj files span the entire file - // // vertex indexes in a mesh span just that mesh - // int vertexIndexOffset = 0; +bool writeOBJToTextStream(QTextStream& out, std::vector meshes) { + qDebug() << "writeOBJToTextStream mesh count is" << meshes.size(); - // foreach (const FBXMesh& mesh, geometry.meshes) { - // bool verticesHaveBeenOutput = false; - // foreach (const FBXMeshPart &meshPart, mesh.parts) { - // if (whichMeshPart >= 0 && nth != (unsigned int) whichMeshPart) { - // nth++; - // continue; - // } + // each mesh's vertices are numbered from zero. We're combining all their vertices into one list here, + // so keep track of the start index for each mesh. + QList meshVertexStartOffset; + int currentVertexStartOffset = 0; - // if (!verticesHaveBeenOutput) { - // for (int i = 0; i < mesh.vertices.size(); i++) { - // glm::vec4 v = mesh.modelTransform * glm::vec4(mesh.vertices[i], 1.0f); - // out << "v "; - // out << formatFloat(v[0]) << " "; - // out << formatFloat(v[1]) << " "; - // out << formatFloat(v[2]) << "\n"; - // } - // verticesHaveBeenOutput = true; - // } + // write out all vertices + foreach (const MeshPointer& mesh, meshes) { + meshVertexStartOffset.append(currentVertexStartOffset); + const gpu::BufferView& vertexBuffer = mesh->getVertexBuffer(); + int vertexCount = 0; + gpu::BufferView::Iterator vertexItr = vertexBuffer.cbegin(); + while (vertexItr != vertexBuffer.cend()) { + glm::vec3 v = *vertexItr; + out << "v "; + out << formatFloat(v[0]) << " "; + out << formatFloat(v[1]) << " "; + out << formatFloat(v[2]) << "\n"; + vertexItr++; + vertexCount++; + } + currentVertexStartOffset += vertexCount; + } + out << "\n"; - // out << "g hull-" << nth++ << "\n"; - // int triangleCount = meshPart.triangleIndices.size() / 3; - // for (int i = 0; i < triangleCount; i++) { - // out << "f "; - // out << vertexIndexOffset + meshPart.triangleIndices[i*3] + 1 << " "; - // out << vertexIndexOffset + meshPart.triangleIndices[i*3+1] + 1 << " "; - // out << vertexIndexOffset + meshPart.triangleIndices[i*3+2] + 1 << "\n"; - // } - // out << "\n"; - // } + // write out faces + int nth = 0; + foreach (const MeshPointer& mesh, meshes) { + currentVertexStartOffset = meshVertexStartOffset.takeFirst(); - // if (verticesHaveBeenOutput) { - // vertexIndexOffset += mesh.vertices.size(); - // } - // } + const gpu::BufferView& partBuffer = mesh->getPartBuffer(); + const gpu::BufferView& indexBuffer = mesh->getIndexBuffer(); + // const gpu::BufferView& vertexBuffer = mesh->getVertexBuffer(); + + int partCount = mesh->getNumParts(); + qDebug() << "writeOBJToTextStream part count is" << partCount; + for (int partIndex = 0; partIndex < partCount; partIndex++) { + const model::Mesh::Part& part = partBuffer.get(partIndex); + + out << "g part-" << nth++ << "\n"; + + // model::Mesh::TRIANGLES + // XXX handle other formats + gpu::BufferView::Iterator indexItr = indexBuffer.cbegin(); + indexItr += part._startIndex; + + int indexCount = 0; + while (indexItr != indexBuffer.cend() && indexItr != part._numIndices) { + uint32_t index0 = *indexItr; + indexItr++; + indexCount++; + if (indexItr == indexBuffer.cend() || indexItr == part._numIndices) { + qDebug() << "OBJWriter -- index buffer length isn't multiple of 3"; + break; + } + uint32_t index1 = *indexItr; + indexItr++; + indexCount++; + if (indexItr == indexBuffer.cend() || indexItr == part._numIndices) { + qDebug() << "OBJWriter -- index buffer length isn't multiple of 3"; + break; + } + uint32_t index2 = *indexItr; + indexItr++; + indexCount++; + + out << "f "; + out << currentVertexStartOffset + index0 + 1 << " "; + out << currentVertexStartOffset + index1 + 1 << " "; + out << currentVertexStartOffset + index2 + 1 << "\n"; + } + out << "\n"; + } + } return true; } + + +bool writeOBJToFile(QString path, MeshPointer mesh) { + if (QFileInfo(path).exists() && !QFile::remove(path)) { + qDebug() << "OBJ writer failed, file exists:" << path; // XXX qCDebug + return false; + } + + QFile file(path); + if (!file.open(QIODevice::WriteOnly)) { + qDebug() << "OBJ writer failed to open output file:" << path; // XXX qCDebug + return false; + } + + QTextStream outStream(&file); + + bool success; + std::vector meshes { mesh }; + success = writeOBJToTextStream(outStream, meshes); + + file.close(); + return success; +} diff --git a/libraries/fbx/src/OBJWriter.h b/libraries/fbx/src/OBJWriter.h index c0400b3d8e..58dc93b84a 100644 --- a/libraries/fbx/src/OBJWriter.h +++ b/libraries/fbx/src/OBJWriter.h @@ -1,6 +1,26 @@ +// +// OBJWriter.h +// libraries/fbx/src/ +// +// Created by Seth Alves on 2017-1-27. +// Copyright 2017 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_objwriter_h +#define hifi_objwriter_h + #include #include #include -bool writeOBJ(QString outFileName, QVector); +using MeshPointer = std::shared_ptr; + +bool writeOBJToTextStream(QTextStream& out, std::vector meshes); +bool writeOBJToFile(QString path, MeshPointer mesh); + + +#endif // hifi_objwriter_h diff --git a/libraries/script-engine/src/MeshProxy.h b/libraries/script-engine/src/MeshProxy.h new file mode 100644 index 0000000000..3226e0f467 --- /dev/null +++ b/libraries/script-engine/src/MeshProxy.h @@ -0,0 +1,39 @@ +// +// MeshProxy.h +// libraries/script-engine/src +// +// Created by Seth Alves on 2017-1-27. +// Copyright 2017 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_MeshProxy_h +#define hifi_MeshProxy_h + +#include + +using MeshPointer = std::shared_ptr; + +class MeshProxy : public QObject { + Q_OBJECT + +public: + MeshProxy(MeshPointer mesh) : _mesh(mesh) {} + ~MeshProxy() {} + + MeshPointer getMeshPointer() const { return _mesh; } + + Q_INVOKABLE int getNumVertices() const { return _mesh->getNumVertices(); } + Q_INVOKABLE glm::vec3 getPos3(int index) const { return _mesh->getPos3(index); } + + +protected: + MeshPointer _mesh; +}; + + +Q_DECLARE_METATYPE(MeshProxy*); + +#endif // hifi_MeshProxy_h diff --git a/libraries/script-engine/src/ModelScriptingInterface.cpp b/libraries/script-engine/src/ModelScriptingInterface.cpp index 30ef9a713e..1fb8e8f193 100644 --- a/libraries/script-engine/src/ModelScriptingInterface.cpp +++ b/libraries/script-engine/src/ModelScriptingInterface.cpp @@ -12,18 +12,12 @@ #include #include #include "ModelScriptingInterface.h" +#include "OBJWriter.h" + ModelScriptingInterface::ModelScriptingInterface(QObject* parent) : QObject(parent) { } - -MeshProxy::MeshProxy(MeshPointer mesh) : _mesh(mesh) { -} - -MeshProxy::~MeshProxy() { -} - - QScriptValue meshToScriptValue(QScriptEngine* engine, MeshProxy* const &in) { return engine->newQObject(in, QScriptEngine::QtOwnership, QScriptEngine::ExcludeDeleteLater | QScriptEngine::ExcludeChildObjects); @@ -32,3 +26,13 @@ QScriptValue meshToScriptValue(QScriptEngine* engine, MeshProxy* const &in) { void meshFromScriptValue(const QScriptValue& value, MeshProxy* &out) { out = qobject_cast(value.toQObject()); } + +QString ModelScriptingInterface::meshToOBJ(MeshProxy* const &in) { + bool success; + QString filename = "/tmp/okokok.obj"; + success = writeOBJToFile(filename, in->getMeshPointer()); + if (!success) { + return ""; + } + return filename; +} diff --git a/libraries/script-engine/src/ModelScriptingInterface.h b/libraries/script-engine/src/ModelScriptingInterface.h index 4cc493b257..4d8f7ad999 100644 --- a/libraries/script-engine/src/ModelScriptingInterface.h +++ b/libraries/script-engine/src/ModelScriptingInterface.h @@ -17,6 +17,7 @@ #include #include #include +#include "MeshProxy.h" using MeshPointer = std::shared_ptr; @@ -25,30 +26,12 @@ class ModelScriptingInterface : public QObject { public: ModelScriptingInterface(QObject* parent); + + Q_INVOKABLE QString meshToOBJ(MeshProxy* const &in); }; -class MeshProxy : public QObject { - Q_OBJECT - -public: - MeshProxy(MeshPointer mesh); - // MeshProxy(const MeshProxy& meshProxy) { _mesh = meshProxy.getMeshPointer(); } - ~MeshProxy(); - - MeshPointer getMeshPointer() const { return _mesh; } - - Q_INVOKABLE int getNumVertices() const { return _mesh->getNumVertices(); } - -protected: - MeshPointer _mesh; -}; - - -Q_DECLARE_METATYPE(MeshProxy*); - - QScriptValue meshToScriptValue(QScriptEngine* engine, MeshProxy* const &in); -// QScriptValue meshToScriptValue(QScriptEngine* engine, const MeshPointer& in); void meshFromScriptValue(const QScriptValue& value, MeshProxy* &out); + #endif // hifi_ModelScriptingInterface_h From 91e542a7a7128b71e5300b0b331574774f7d66f0 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Tue, 31 Jan 2017 21:26:49 -0800 Subject: [PATCH 004/302] ModelScriptingInterface::meshToOBJ takes a list of MeshProxys instead of just one --- libraries/fbx/src/OBJWriter.cpp | 10 ++----- libraries/fbx/src/OBJWriter.h | 6 ++--- libraries/script-engine/src/MeshProxy.h | 4 ++- .../src/ModelScriptingInterface.cpp | 26 +++++++++++++++++-- .../src/ModelScriptingInterface.h | 4 ++- libraries/script-engine/src/ScriptEngine.cpp | 1 + 6 files changed, 36 insertions(+), 15 deletions(-) diff --git a/libraries/fbx/src/OBJWriter.cpp b/libraries/fbx/src/OBJWriter.cpp index d33d767e5d..b2d391fea3 100644 --- a/libraries/fbx/src/OBJWriter.cpp +++ b/libraries/fbx/src/OBJWriter.cpp @@ -14,8 +14,6 @@ #include "model/Geometry.h" #include "OBJWriter.h" - - static QString formatFloat(double n) { // limit precision to 6, but don't output trailing zeros. QString s = QString::number(n, 'f', 6); @@ -28,9 +26,7 @@ static QString formatFloat(double n) { return s; } - - -bool writeOBJToTextStream(QTextStream& out, std::vector meshes) { +bool writeOBJToTextStream(QTextStream& out, QList meshes) { qDebug() << "writeOBJToTextStream mesh count is" << meshes.size(); // each mesh's vertices are numbered from zero. We're combining all their vertices into one list here, @@ -110,8 +106,7 @@ bool writeOBJToTextStream(QTextStream& out, std::vector meshes) { return true; } - -bool writeOBJToFile(QString path, MeshPointer mesh) { +bool writeOBJToFile(QString path, QList meshes) { if (QFileInfo(path).exists() && !QFile::remove(path)) { qDebug() << "OBJ writer failed, file exists:" << path; // XXX qCDebug return false; @@ -126,7 +121,6 @@ bool writeOBJToFile(QString path, MeshPointer mesh) { QTextStream outStream(&file); bool success; - std::vector meshes { mesh }; success = writeOBJToTextStream(outStream, meshes); file.close(); diff --git a/libraries/fbx/src/OBJWriter.h b/libraries/fbx/src/OBJWriter.h index 58dc93b84a..dcf5a3ee39 100644 --- a/libraries/fbx/src/OBJWriter.h +++ b/libraries/fbx/src/OBJWriter.h @@ -14,13 +14,13 @@ #include -#include +#include #include using MeshPointer = std::shared_ptr; -bool writeOBJToTextStream(QTextStream& out, std::vector meshes); -bool writeOBJToFile(QString path, MeshPointer mesh); +bool writeOBJToTextStream(QTextStream& out, QList meshes); +bool writeOBJToFile(QString path, QList meshes); #endif // hifi_objwriter_h diff --git a/libraries/script-engine/src/MeshProxy.h b/libraries/script-engine/src/MeshProxy.h index 3226e0f467..93c5ce5918 100644 --- a/libraries/script-engine/src/MeshProxy.h +++ b/libraries/script-engine/src/MeshProxy.h @@ -33,7 +33,9 @@ protected: MeshPointer _mesh; }; - Q_DECLARE_METATYPE(MeshProxy*); +class MeshProxyList : public QList {}; // typedef and using fight with the Qt macros/templates, do this instead +Q_DECLARE_METATYPE(MeshProxyList); + #endif // hifi_MeshProxy_h diff --git a/libraries/script-engine/src/ModelScriptingInterface.cpp b/libraries/script-engine/src/ModelScriptingInterface.cpp index 1fb8e8f193..922dee2e8f 100644 --- a/libraries/script-engine/src/ModelScriptingInterface.cpp +++ b/libraries/script-engine/src/ModelScriptingInterface.cpp @@ -10,6 +10,7 @@ // #include +#include #include #include "ModelScriptingInterface.h" #include "OBJWriter.h" @@ -27,10 +28,31 @@ void meshFromScriptValue(const QScriptValue& value, MeshProxy* &out) { out = qobject_cast(value.toQObject()); } -QString ModelScriptingInterface::meshToOBJ(MeshProxy* const &in) { +QScriptValue meshesToScriptValue(QScriptEngine* engine, const MeshProxyList &in) { + return engine->toScriptValue(in); +} + +void meshesFromScriptValue(const QScriptValue& value, MeshProxyList &out) { + QScriptValueIterator itr(value); + while(itr.hasNext()) { + itr.next(); + MeshProxy* meshProxy = qscriptvalue_cast(itr.value()); + if (meshProxy) { + out.append(meshProxy); + } + } +} + +QString ModelScriptingInterface::meshToOBJ(MeshProxyList in) { bool success; QString filename = "/tmp/okokok.obj"; - success = writeOBJToFile(filename, in->getMeshPointer()); + + QList meshes; + foreach (const MeshProxy* meshProxy, in) { + meshes.append(meshProxy->getMeshPointer()); + } + + success = writeOBJToFile(filename, meshes); if (!success) { return ""; } diff --git a/libraries/script-engine/src/ModelScriptingInterface.h b/libraries/script-engine/src/ModelScriptingInterface.h index 4d8f7ad999..94b7338ae2 100644 --- a/libraries/script-engine/src/ModelScriptingInterface.h +++ b/libraries/script-engine/src/ModelScriptingInterface.h @@ -27,11 +27,13 @@ class ModelScriptingInterface : public QObject { public: ModelScriptingInterface(QObject* parent); - Q_INVOKABLE QString meshToOBJ(MeshProxy* const &in); + Q_INVOKABLE QString meshToOBJ(MeshProxyList in); }; QScriptValue meshToScriptValue(QScriptEngine* engine, MeshProxy* const &in); void meshFromScriptValue(const QScriptValue& value, MeshProxy* &out); +QScriptValue meshesToScriptValue(QScriptEngine* engine, const MeshProxyList &in); +void meshesFromScriptValue(const QScriptValue& value, MeshProxyList &out); #endif // hifi_ModelScriptingInterface_h diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index b687da7e23..31dc30d2c5 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -591,6 +591,7 @@ void ScriptEngine::init() { registerGlobalObject("Model", new ModelScriptingInterface(this)); qScriptRegisterMetaType(this, meshToScriptValue, meshFromScriptValue); + qScriptRegisterMetaType(this, meshesToScriptValue, meshesFromScriptValue); } void ScriptEngine::registerValue(const QString& valueName, QScriptValue value) { From 7d5d6823cdefa085cab105bde85ca7ec7c1df5df Mon Sep 17 00:00:00 2001 From: sam Date: Wed, 15 Feb 2017 00:54:16 -0800 Subject: [PATCH 005/302] Adding the ktx library --- libraries/gpu/CMakeLists.txt | 2 +- libraries/ktx/CMakeLists.txt | 3 + libraries/ktx/src/ktx/KTX.cpp | 92 +++++++ libraries/ktx/src/ktx/KTX.h | 397 +++++++++++++++++++++++++++++++ libraries/ktx/src/ktx/Reader.cpp | 159 +++++++++++++ libraries/ktx/src/ktx/Writer.cpp | 21 ++ 6 files changed, 673 insertions(+), 1 deletion(-) create mode 100644 libraries/ktx/CMakeLists.txt create mode 100644 libraries/ktx/src/ktx/KTX.cpp create mode 100644 libraries/ktx/src/ktx/KTX.h create mode 100644 libraries/ktx/src/ktx/Reader.cpp create mode 100644 libraries/ktx/src/ktx/Writer.cpp diff --git a/libraries/gpu/CMakeLists.txt b/libraries/gpu/CMakeLists.txt index 384c5709ee..207431d8c7 100644 --- a/libraries/gpu/CMakeLists.txt +++ b/libraries/gpu/CMakeLists.txt @@ -1,6 +1,6 @@ set(TARGET_NAME gpu) autoscribe_shader_lib(gpu) setup_hifi_library() -link_hifi_libraries(shared) +link_hifi_libraries(shared ktx) target_nsight() diff --git a/libraries/ktx/CMakeLists.txt b/libraries/ktx/CMakeLists.txt new file mode 100644 index 0000000000..404660b247 --- /dev/null +++ b/libraries/ktx/CMakeLists.txt @@ -0,0 +1,3 @@ +set(TARGET_NAME ktx) +setup_hifi_library() +link_hifi_libraries() \ No newline at end of file diff --git a/libraries/ktx/src/ktx/KTX.cpp b/libraries/ktx/src/ktx/KTX.cpp new file mode 100644 index 0000000000..2d5e0bc812 --- /dev/null +++ b/libraries/ktx/src/ktx/KTX.cpp @@ -0,0 +1,92 @@ +// +// KTX.cpp +// ktx/src/ktx +// +// Created by Zach Pomerantz on 2/08/2017. +// Copyright 2017 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 "KTX.h" + +#include //min max and more + +using namespace ktx; + +const Header::Identifier ktx::Header::IDENTIFIER {{ + 0xAB, 0x4B, 0x54, 0x58, 0x20, 0x31, 0x31, 0xBB, 0x0D, 0x0A, 0x1A, 0x0A +}}; + +Header::Header() { + memcpy(identifier, IDENTIFIER.data(), IDENTIFIER_LENGTH); +} + +uint32_t Header::evalMaxDimension() const { + return std::max(pixelWidth, std::max(pixelHeight, pixelDepth)); +} + +uint32_t Header::evalMaxLevel() const { + return 1 + log2(evalMaxDimension()); +} + +uint32_t Header::evalPixelWidth(uint32_t level) const { + return std::max(pixelWidth >> level, 1U); +} +uint32_t Header::evalPixelHeight(uint32_t level) const { + return std::max(pixelHeight >> level, 1U); +} +uint32_t Header::evalPixelDepth(uint32_t level) const { + return std::max(pixelDepth >> level, 1U); +} + +size_t Header::evalPixelSize() const { + return glTypeSize; // Really we should generate the size from the FOrmat etc +} + +size_t Header::evalRowSize(uint32_t level) const { + auto pixelWidth = evalPixelWidth(level); + auto pixSize = evalPixelSize(); + auto netSize = pixelWidth * pixSize; + auto packing = netSize % 4; + return netSize + (packing ? 4 - packing : 0); +} +size_t Header::evalFaceSize(uint32_t level) const { + auto pixelHeight = evalPixelHeight(level); + auto pixelDepth = evalPixelDepth(level); + auto rowSize = evalRowSize(level); + return pixelDepth * pixelHeight * rowSize; +} +size_t Header::evalImageSize(uint32_t level) const { + auto faceSize = evalFaceSize(level); + if (numberOfFaces == 6 && numberOfArrayElements == 0) { + return faceSize; + } else { + return (numberOfArrayElements * numberOfFaces * faceSize); + } +} + + +KTX::KTX() { +} + +void KTX::resetStorage(Storage* storage) { + _storage.reset(storage); +} + +const Header* KTX::getHeader() const { + if (_storage) { + return reinterpret_cast (_storage->_bytes); + } else { + return nullptr; + } +} + +const Byte* KTX::getKeyValueData() const { + if (_storage) { + return (_storage->_bytes + sizeof(Header)); + } else { + return nullptr; + } +} diff --git a/libraries/ktx/src/ktx/KTX.h b/libraries/ktx/src/ktx/KTX.h new file mode 100644 index 0000000000..003d3f0d1b --- /dev/null +++ b/libraries/ktx/src/ktx/KTX.h @@ -0,0 +1,397 @@ +// +// KTX.h +// ktx/src/ktx +// +// Created by Zach Pomerantz on 2/08/2017. +// Copyright 2017 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 +// +#pragma once +#ifndef hifi_ktx_KTX_h +#define hifi_ktx_KTX_h + +#include +#include +#include +#include +#include + +/* KTX Spec: + +Byte[12] identifier +UInt32 endianness +UInt32 glType +UInt32 glTypeSize +UInt32 glFormat +Uint32 glInternalFormat +Uint32 glBaseInternalFormat +UInt32 pixelWidth +UInt32 pixelHeight +UInt32 pixelDepth +UInt32 numberOfArrayElements +UInt32 numberOfFaces +UInt32 numberOfMipmapLevels +UInt32 bytesOfKeyValueData + +for each keyValuePair that fits in bytesOfKeyValueData + UInt32 keyAndValueByteSize + Byte keyAndValue[keyAndValueByteSize] + Byte valuePadding[3 - ((keyAndValueByteSize + 3) % 4)] +end + +for each mipmap_level in numberOfMipmapLevels* + UInt32 imageSize; + for each array_element in numberOfArrayElements* + for each face in numberOfFaces + for each z_slice in pixelDepth* + for each row or row_of_blocks in pixelHeight* + for each pixel or block_of_pixels in pixelWidth + Byte data[format-specific-number-of-bytes]** + end + end + end + Byte cubePadding[0-3] + end + end + Byte mipPadding[3 - ((imageSize + 3) % 4)] +end + +* Replace with 1 if this field is 0. + +** Uncompressed texture data matches a GL_UNPACK_ALIGNMENT of 4. +*/ + + + +namespace ktx { + + enum GLType : uint32_t { + COMPRESSED_TYPE = 0, + + // GL 4.4 Table 8.2 + UNSIGNED_BYTE = 0x1401, + BYTE = 0x1400, + UNSIGNED_SHORT = 0x1403, + SHORT = 0x1402, + UNSIGNED_INT = 0x1405, + INT = 0x1404, + HALF_FLOAT = 0x140B, + FLOAT = 0x1406, + UNSIGNED_BYTE_3_3_2 = 0x8032, + UNSIGNED_BYTE_2_3_3_REV = 0x8362, + UNSIGNED_SHORT_5_6_5 = 0x8363, + UNSIGNED_SHORT_5_6_5_REV = 0x8364, + UNSIGNED_SHORT_4_4_4_4 = 0x8033, + UNSIGNED_SHORT_4_4_4_4_REV = 0x8365, + UNSIGNED_SHORT_5_5_5_1 = 0x8034, + UNSIGNED_SHORT_1_5_5_5_REV = 0x8366, + UNSIGNED_INT_8_8_8_8 = 0x8035, + UNSIGNED_INT_8_8_8_8_REV = 0x8367, + UNSIGNED_INT_10_10_10_2 = 0x8036, + UNSIGNED_INT_2_10_10_10_REV = 0x8368, + UNSIGNED_INT_24_8 = 0x84FA, + UNSIGNED_INT_10F_11F_11F_REV = 0x8C3B, + UNSIGNED_INT_5_9_9_9_REV = 0x8C3E, + FLOAT_32_UNSIGNED_INT_24_8_REV = 0x8DAD, + + NUM_GLTYPES = 25, + }; + + enum GLFormat : uint32_t { + COMPRESSED_FORMAT = 0, + + // GL 4.4 Table 8.3 + STENCIL_INDEX = 0x1901, + DEPTH_COMPONENT = 0x1902, + DEPTH_STENCIL = 0x84F9, + + RED = 0x1903, + GREEN = 0x1904, + BLUE = 0x1905, + RG = 0x8227, + RGB = 0x1907, + RGBA = 0x1908, + BGR = 0x80E0, + BGRA = 0x80E1, + + RG_INTEGER = 0x8228, + RED_INTEGER = 0x8D94, + GREEN_INTEGER = 0x8D95, + BLUE_INTEGER = 0x8D96, + RGB_INTEGER = 0x8D98, + RGBA_INTEGER = 0x8D99, + BGR_INTEGER = 0x8D9A, + BGRA_INTEGER = 0x8D9B, + + NUM_GLFORMATS = 20, + }; + + enum GLInternalFormat_Uncompressed : uint32_t { + // GL 4.4 Table 8.12 + R8 = 0x8229, + R8_SNORM = 0x8F94, + + R16 = 0x822A, + R16_SNORM = 0x8F98, + + RG8 = 0x822B, + RG8_SNORM = 0x8F95, + + RG16 = 0x822C, + RG16_SNORM = 0x8F99, + + R3_G3_B2 = 0x2A10, + RGB4 = 0x804F, + RGB5 = 0x8050, + RGB565 = 0x8D62, + + RGB8 = 0x8051, + RGB8_SNORM = 0x8F96, + RGB10 = 0x8052, + RGB12 = 0x8053, + + RGB16 = 0x8054, + RGB16_SNORM = 0x8F9A, + + RGBA2 = 0x8055, + RGBA4 = 0x8056, + RGB5_A1 = 0x8057, + RGBA8 = 0x8058, + RGBA8_SNORM = 0x8F97, + + RGB10_A2 = 0x8059, + RGB10_A2UI = 0x906F, + + RGBA12 = 0x805A, + RGBA16 = 0x805B, + RGBA16_SNORM = 0x8F9B, + + SRGB8 = 0x8C41, + SRGB8_ALPHA8 = 0x8C43, + + R16F = 0x822D, + RG16F = 0x822F, + RGB16F = 0x881B, + RGBA16F = 0x881A, + + R32F = 0x822E, + RG32F = 0x8230, + RGB32F = 0x8815, + RGBA32F = 0x8814, + + R11F_G11F_B10F = 0x8C3A, + RGB9_E5 = 0x8C3D, + + + R8I = 0x8231, + R8UI = 0x8232, + R16I = 0x8233, + R16UI = 0x8234, + R32I = 0x8235, + R32UI = 0x8236, + RG8I = 0x8237, + RG8UI = 0x8238, + RG16I = 0x8239, + RG16UI = 0x823A, + RG32I = 0x823B, + RG32UI = 0x823C, + + RGB8I = 0x8D8F, + RGB8UI = 0x8D7D, + RGB16I = 0x8D89, + RGB16UI = 0x8D77, + + RGB32I = 0x8D83, + RGB32UI = 0x8D71, + RGBA8I = 0x8D8E, + RGBA8UI = 0x8D7C, + RGBA16I = 0x8D88, + RGBA16UI = 0x8D76, + RGBA32I = 0x8D82, + + RGBA32UI = 0x8D70, + + // GL 4.4 Table 8.13 + DEPTH_COMPONENT16 = 0x81A5, + DEPTH_COMPONENT24 = 0x81A6, + DEPTH_COMPONENT32 = 0x81A7, + + DEPTH_COMPONENT32F = 0x8CAC, + DEPTH24_STENCIL8 = 0x88F0, + DEPTH32F_STENCIL8 = 0x8CAD, + + STENCIL_INDEX1 = 0x8D46, + STENCIL_INDEX4 = 0x8D47, + STENCIL_INDEX8 = 0x8D48, + STENCIL_INDEX16 = 0x8D49, + + NUM_UNCOMPRESSED_GLINTERNALFORMATS = 74, + }; + + enum GLInternalFormat_Compressed : uint32_t { + // GL 4.4 Table 8.14 + COMPRESSED_RED = 0x8225, + COMPRESSED_RG = 0x8226, + COMPRESSED_RGB = 0x84ED, + COMPRESSED_RGBA = 0x84EE, + + COMPRESSED_SRGB = 0x8C48, + COMPRESSED_SRGB_ALPHA = 0x8C49, + + COMPRESSED_RED_RGTC1 = 0x8DBB, + COMPRESSED_SIGNED_RED_RGTC1 = 0x8DBC, + COMPRESSED_RG_RGTC2 = 0x8DBD, + COMPRESSED_SIGNED_RG_RGTC2 = 0x8DBE, + + COMPRESSED_RGBA_BPTC_UNORM = 0x8E8C, + COMPRESSED_SRGB_ALPHA_BPTC_UNORM = 0x8E8D, + COMPRESSED_RGB_BPTC_SIGNED_FLOAT = 0x8E8E, + COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT = 0x8E8F, + + COMPRESSED_RGB8_ETC2 = 0x9274, + COMPRESSED_SRGB8_ETC2 = 0x9275, + COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2 = 0x9276, + COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2 = 0x9277, + COMPRESSED_RGBA8_ETC2_EAC = 0x9278, + COMPRESSED_SRGB8_ALPHA8_ETC2_EAC = 0x9279, + + COMPRESSED_R11_EAC = 0x9270, + COMPRESSED_SIGNED_R11_EAC = 0x9271, + COMPRESSED_RG11_EAC = 0x9272, + COMPRESSED_SIGNED_RG11_EAC = 0x9273, + + NUM_COMPRESSED_GLINTERNALFORMATS = 24, + }; + + enum GLBaseInternalFormat : uint32_t { + // GL 4.4 Table 8.11 + BIF_DEPTH_COMPONENT = 0x1902, + BIF_DEPTH_STENCIL = 0x84F9, + BIF_RED = 0x1903, + BIF_RG = 0x8227, + BIF_RGB = 0x1907, + BIF_RGBA = 0x1908, + BIF_STENCIL_INDEX = 0x1901, + + NUM_GLBASEINTERNALFORMATS = 7, + }; + + enum CubeMapFace { + POS_X = 0, + NEG_X = 1, + POS_Y = 2, + NEG_Y = 3, + POS_Z = 4, + NEG_Z = 5, + NUM_CUBEMAPFACES = 6, + }; + + using Byte = uint8_t; + + // Chunk of data + struct Storage { + size_t _size {0}; + Byte* _bytes {nullptr}; + + Byte* data() { + return _bytes; + } + const Byte* data() const { + return _bytes; + } + size_t size() const { return _size; } + + ~Storage() { if (_bytes) { delete _bytes; } } + + Storage() {} + Storage(size_t size) : + _size(size) + { + if (_size) { _bytes = new Byte[_size]; } + } + + Storage(size_t size, Byte* bytes) : + _size(size) + { + if (_size && _bytes) { _bytes = bytes; } + } + }; + + // Header + struct Header { + static const size_t IDENTIFIER_LENGTH = 12; + using Identifier = std::array; + static const Identifier IDENTIFIER; + + static const uint32_t ENDIAN_TEST = 0x04030201; + static const uint32_t REVERSE_ENDIAN_TEST = 0x01020304; + + Header(); + + Byte identifier[IDENTIFIER_LENGTH]; + uint32_t endianness { ENDIAN_TEST }; + uint32_t glType; + uint32_t glTypeSize; + uint32_t glFormat; + uint32_t glInternalFormat; + uint32_t glBaseInternalFormat; + uint32_t pixelWidth; + uint32_t pixelHeight; + uint32_t pixelDepth; + uint32_t numberOfArrayElements; + uint32_t numberOfFaces; + uint32_t numberOfMipmapLevels; + uint32_t bytesOfKeyValueData; + + uint32_t evalMaxDimension() const; + uint32_t evalMaxLevel() const; + uint32_t evalPixelWidth(uint32_t level) const; + uint32_t evalPixelHeight(uint32_t level) const; + uint32_t evalPixelDepth(uint32_t level) const; + + size_t evalPixelSize() const; + size_t evalRowSize(uint32_t level) const; + size_t evalFaceSize(uint32_t level) const; + size_t evalImageSize(uint32_t level) const; + + }; + + // Key Values + using KeyValue = std::pair; + using KeyValues = std::list; + + + struct Mip { + uint32_t imageSize; + const Byte* _bytes; + }; + using Mips = std::vector; + + class KTX { + void resetStorage(Storage* src); + + public: + + KTX(); + + bool read(const Storage* src); + bool read(Storage* src); + + std::unique_ptr _storage; + + const Header* getHeader() const; + const Byte* getKeyValueData() const; + + KeyValues _keyValues; + + Mips _mips; + + static bool checkStorageHeader(const Storage& storage); + static KTX* create(const Storage* src); + }; + +} + +#endif // hifi_ktx_KTX_h diff --git a/libraries/ktx/src/ktx/Reader.cpp b/libraries/ktx/src/ktx/Reader.cpp new file mode 100644 index 0000000000..8ec4de7686 --- /dev/null +++ b/libraries/ktx/src/ktx/Reader.cpp @@ -0,0 +1,159 @@ +// +// Reader.cpp +// ktx/src/ktx +// +// Created by Zach Pomerantz on 2/08/2017. +// Copyright 2017 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 "KTX.h" + +#include +#include + +namespace ktx { + class Exception: public std::exception { + public: + Exception(std::string explanation) : _explanation(explanation) {} + const char* what() const override { + return ("KTX deserialization error: " + _explanation).c_str(); + } + private: + std::string _explanation; + }; + + bool checkEndianness(uint32_t endianness, bool& matching) { + switch (endianness) { + case Header::ENDIAN_TEST: { + matching = true; + return true; + } + break; + case Header::REVERSE_ENDIAN_TEST: + { + matching = false; + return true; + } + break; + default: + return false; + } + } + + bool checkIdentifier(const Byte* identifier) { + return memcmp(identifier, Header::IDENTIFIER.data(), Header::IDENTIFIER_LENGTH); + } + + KeyValues getKeyValues(size_t length, const Byte* src) { + KeyValues keyValues; + size_t offset = 0; + + while (offset < length) { + // determine byte size + uint32_t keyValueByteSize; + memcpy(&keyValueByteSize, src, sizeof(uint32_t)); + if (keyValueByteSize > length - offset) { + throw Exception("invalid key-value size"); + } + + // find the first null character \0 + int keyLength = 0; + while (reinterpret_cast(src[++keyLength]) != '\0') { + if (keyLength == keyValueByteSize) { + // key must be null-terminated, and there must be space for the value + throw Exception("invalid key-value " + std::string(reinterpret_cast(src), keyLength)); + } + } + + // populate the key-value + keyValues.emplace_back( + std::move(std::string(reinterpret_cast(src), keyLength)), + std::move(std::string(reinterpret_cast(src + keyLength), keyValueByteSize - keyLength))); + + // advance offset/src + uint32_t keyValuePadding = 3 - ((keyValueByteSize + 3) % 4); + offset += keyValueByteSize + keyValuePadding; + src += keyValueByteSize + keyValuePadding; + } + + return keyValues; + } + + bool KTX::read(Storage* src) { + resetStorage(src); + + return true; + } + + bool KTX::checkStorageHeader(const Storage& src) { + try { + size_t srcSize = src.size(); + const uint8_t* srcBytes = src.data(); + + // validation + if (srcSize < sizeof(Header)) { + throw Exception("length is too short for header"); + } + const Header* header = reinterpret_cast(srcBytes); + + if (!checkIdentifier(header->identifier)) { + throw Exception("identifier field invalid"); + } + + bool endianMatch { true }; + if (!checkEndianness(header->endianness, endianMatch)) { + throw Exception("endianness field has invalid value"); + } + + // TODO: endian conversion if !endianMatch - for now, this is for local use and is unnecessary + + + // TODO: calculated bytesOfTexData + if (srcSize < (sizeof(Header) + header->bytesOfKeyValueData)) { + throw Exception("length is too short for metadata"); + } + + size_t bytesOfTexData = 0; + if (srcSize < (sizeof(Header) + header->bytesOfKeyValueData + bytesOfTexData)) { + + throw Exception("length is too short for data"); + } + + + // read metadata + KeyValues keyValues = getKeyValues(header->bytesOfKeyValueData, srcBytes + sizeof(Header)); + + // prepare gpu::Texture using header & key-values + // TODO + + // read data + // TODO + + return true; + } catch (Exception& e) { + qWarning(e.what()); + return false; + } + } + + KTX* KTX::create(const Storage* data) { + try { + if (!checkStorageHeader(*data)) { + + } + + auto result = new KTX(); + result->resetStorage(const_cast(data)); + + // read metadata + KeyValues keyValues = getKeyValues(result->getHeader()->bytesOfKeyValueData, result->getKeyValueData()); + + return nullptr; + } catch (Exception& e) { + qWarning(e.what()); + return nullptr; + } + } +} \ No newline at end of file diff --git a/libraries/ktx/src/ktx/Writer.cpp b/libraries/ktx/src/ktx/Writer.cpp new file mode 100644 index 0000000000..d502a4a29a --- /dev/null +++ b/libraries/ktx/src/ktx/Writer.cpp @@ -0,0 +1,21 @@ +// +// Writer.cpp +// ktx/src/ktx +// +// Created by Zach Pomerantz on 2/08/2017. +// Copyright 2017 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 "KTX.h" + + +namespace ktx { + /* size_t serialize(const gpu::Texture& texture, uint8_t* data) { + return 0; + }*/ + /* KTX serialize(const gpu::Texture& texture) { + return KTX(0, nullptr); + }*/ +} From 6888b0f09913f52e54b0765ad78da4a55e607a6a Mon Sep 17 00:00:00 2001 From: samcake Date: Wed, 15 Feb 2017 16:24:06 -0800 Subject: [PATCH 006/302] DOne with the parsing side of things --- libraries/ktx/src/ktx/KTX.cpp | 29 +++++++++++- libraries/ktx/src/ktx/KTX.h | 49 +++++++++++++++------ libraries/ktx/src/ktx/Reader.cpp | 75 ++++++++++++++++++++++---------- 3 files changed, 114 insertions(+), 39 deletions(-) diff --git a/libraries/ktx/src/ktx/KTX.cpp b/libraries/ktx/src/ktx/KTX.cpp index 2d5e0bc812..4ce4c94c69 100644 --- a/libraries/ktx/src/ktx/KTX.cpp +++ b/libraries/ktx/src/ktx/KTX.cpp @@ -49,8 +49,8 @@ size_t Header::evalRowSize(uint32_t level) const { auto pixelWidth = evalPixelWidth(level); auto pixSize = evalPixelSize(); auto netSize = pixelWidth * pixSize; - auto packing = netSize % 4; - return netSize + (packing ? 4 - packing : 0); + auto packing = netSize % PACKING_SIZE; + return netSize + (packing ? PACKING_SIZE - packing : 0); } size_t Header::evalFaceSize(uint32_t level) const { auto pixelHeight = evalPixelHeight(level); @@ -83,6 +83,23 @@ const Header* KTX::getHeader() const { } } + +size_t KTX::getKeyValueDataSize() const { + if (_storage) { + return getHeader()->bytesOfKeyValueData; + } else { + return 0; + } +} + +size_t KTX::getTexelsDataSize() const { + if (_storage) { + return _storage->size() - sizeof(Header) + getKeyValueDataSize(); + } else { + return 0; + } +} + const Byte* KTX::getKeyValueData() const { if (_storage) { return (_storage->_bytes + sizeof(Header)); @@ -90,3 +107,11 @@ const Byte* KTX::getKeyValueData() const { return nullptr; } } + +const Byte* KTX::getTexelsData() const { + if (_storage) { + return (_storage->_bytes + sizeof(Header) + getKeyValueDataSize()); + } else { + return nullptr; + } +} diff --git a/libraries/ktx/src/ktx/KTX.h b/libraries/ktx/src/ktx/KTX.h index 003d3f0d1b..b6a9240972 100644 --- a/libraries/ktx/src/ktx/KTX.h +++ b/libraries/ktx/src/ktx/KTX.h @@ -66,6 +66,7 @@ end namespace ktx { + const uint32_t PACKING_SIZE { sizeof(uint32_t) }; enum GLType : uint32_t { COMPRESSED_TYPE = 0, @@ -317,6 +318,17 @@ namespace ktx { { if (_size && _bytes) { _bytes = bytes; } } + Storage(size_t size, const Byte* bytes) : + Storage(size) + { + if (_size && _bytes && bytes) { + memcpy(_bytes, bytes, size); + } + } + Storage(const Storage& src) : + Storage(src.size(), src.data()) + {} + }; // Header @@ -364,8 +376,14 @@ namespace ktx { struct Mip { - uint32_t imageSize; + uint32_t _imageSize; + uint32_t _padding; const Byte* _bytes; + + Mip(uint32_t imageSize, uint32_t padding, const Byte* bytes) : + _imageSize(imageSize), + _padding(padding), + _bytes(bytes) {} }; using Mips = std::vector; @@ -375,21 +393,26 @@ namespace ktx { public: KTX(); + ~KTX(); - bool read(const Storage* src); - bool read(Storage* src); - - std::unique_ptr _storage; - - const Header* getHeader() const; - const Byte* getKeyValueData() const; - - KeyValues _keyValues; - - Mips _mips; + // parse a block of memory and create a KTX object from it + static std::unique_ptr create(const Storage& src); + static std::unique_ptr create(std::unique_ptr& src); static bool checkStorageHeader(const Storage& storage); - static KTX* create(const Storage* src); + + // Access raw pointers to the main sections of the KTX + const Header* getHeader() const; + const Byte* getKeyValueData() const; + const Byte* getTexelsData() const; + + size_t getKeyValueDataSize() const; + size_t getTexelsDataSize() const; + + + std::unique_ptr _storage; + KeyValues _keyValues; + Mips _mips; }; } diff --git a/libraries/ktx/src/ktx/Reader.cpp b/libraries/ktx/src/ktx/Reader.cpp index 8ec4de7686..ff682d5bdb 100644 --- a/libraries/ktx/src/ktx/Reader.cpp +++ b/libraries/ktx/src/ktx/Reader.cpp @@ -73,7 +73,7 @@ namespace ktx { std::move(std::string(reinterpret_cast(src + keyLength), keyValueByteSize - keyLength))); // advance offset/src - uint32_t keyValuePadding = 3 - ((keyValueByteSize + 3) % 4); + uint32_t keyValuePadding = 3 - ((keyValueByteSize + 3) % PACKING_SIZE); offset += keyValueByteSize + keyValuePadding; src += keyValueByteSize + keyValuePadding; } @@ -81,16 +81,39 @@ namespace ktx { return keyValues; } - bool KTX::read(Storage* src) { - resetStorage(src); + Mips getMipsTable(const Header& header, size_t mipsDataSize, const Byte* mipsData) { + Mips mips; + auto currentPtr = mipsData; + auto numMips = header.numberOfMipmapLevels + 1; - return true; + // Keep identifying new mip as long as we can at list query the next imageSize + while ((currentPtr - mipsData) + sizeof(uint32_t) <= (mipsDataSize)) { + + // Grab the imageSize coming up + auto imageSize = *reinterpret_cast(currentPtr); + currentPtr += sizeof(uint32_t); + + // If enough data ahead then capture the pointer + if ((currentPtr - mipsData) + imageSize <= (mipsDataSize)) { + auto padding = imageSize % PACKING_SIZE; + padding = (padding ? 4 - padding : 0); + + mips.emplace_back(Mip(imageSize, padding, currentPtr)); + + currentPtr += imageSize + padding; + } else { + break; + } + } + + return mips; } + bool KTX::checkStorageHeader(const Storage& src) { try { - size_t srcSize = src.size(); - const uint8_t* srcBytes = src.data(); + auto srcSize = src.size(); + auto srcBytes = src.data(); // validation if (srcSize < sizeof(Header)) { @@ -121,16 +144,6 @@ namespace ktx { throw Exception("length is too short for data"); } - - // read metadata - KeyValues keyValues = getKeyValues(header->bytesOfKeyValueData, srcBytes + sizeof(Header)); - - // prepare gpu::Texture using header & key-values - // TODO - - // read data - // TODO - return true; } catch (Exception& e) { qWarning(e.what()); @@ -138,20 +151,34 @@ namespace ktx { } } - KTX* KTX::create(const Storage* data) { + std::unique_ptr KTX::create(const Storage& src) { + auto srcCopy = std::make_unique(src); + + return create(srcCopy); + } + + std::unique_ptr KTX::create(std::unique_ptr& src) { + if (!src) { + return nullptr; + } + try { - if (!checkStorageHeader(*data)) { - + if (!checkStorageHeader(*src)) { + } - auto result = new KTX(); - result->resetStorage(const_cast(data)); + std::unique_ptr result(new KTX()); + result->resetStorage(src.release()); // read metadata - KeyValues keyValues = getKeyValues(result->getHeader()->bytesOfKeyValueData, result->getKeyValueData()); + result->_keyValues = getKeyValues(result->getHeader()->bytesOfKeyValueData, result->getKeyValueData()); - return nullptr; - } catch (Exception& e) { + // populate mip table + result->_mips = getMipsTable(*result->getHeader(), result->getTexelsDataSize(), result->getTexelsData()); + + return result; + } + catch (Exception& e) { qWarning(e.what()); return nullptr; } From 7343c2f90b97516109c3c72464761cd2bfec60ea Mon Sep 17 00:00:00 2001 From: samcake Date: Wed, 15 Feb 2017 17:04:40 -0800 Subject: [PATCH 007/302] Starting to work on the writer side --- libraries/ktx/src/ktx/KTX.cpp | 10 +++++-- libraries/ktx/src/ktx/KTX.h | 23 ++++++++++------ libraries/ktx/src/ktx/Reader.cpp | 42 ++++++++++++++--------------- libraries/ktx/src/ktx/Writer.cpp | 46 +++++++++++++++++++++++++++----- 4 files changed, 84 insertions(+), 37 deletions(-) diff --git a/libraries/ktx/src/ktx/KTX.cpp b/libraries/ktx/src/ktx/KTX.cpp index 4ce4c94c69..d2872e76b5 100644 --- a/libraries/ktx/src/ktx/KTX.cpp +++ b/libraries/ktx/src/ktx/KTX.cpp @@ -15,6 +15,11 @@ using namespace ktx; +uint32_t evalPadding(size_t byteSize) { + auto padding = byteSize % PACKING_SIZE; + return (padding ? PACKING_SIZE - padding : 0); +} + const Header::Identifier ktx::Header::IDENTIFIER {{ 0xAB, 0x4B, 0x54, 0x58, 0x20, 0x31, 0x31, 0xBB, 0x0D, 0x0A, 0x1A, 0x0A }}; @@ -49,8 +54,8 @@ size_t Header::evalRowSize(uint32_t level) const { auto pixelWidth = evalPixelWidth(level); auto pixSize = evalPixelSize(); auto netSize = pixelWidth * pixSize; - auto packing = netSize % PACKING_SIZE; - return netSize + (packing ? PACKING_SIZE - packing : 0); + auto padding = evalPadding(netSize); + return netSize + padding; } size_t Header::evalFaceSize(uint32_t level) const { auto pixelHeight = evalPixelHeight(level); @@ -115,3 +120,4 @@ const Byte* KTX::getTexelsData() const { return nullptr; } } + diff --git a/libraries/ktx/src/ktx/KTX.h b/libraries/ktx/src/ktx/KTX.h index b6a9240972..5b72c4c1ce 100644 --- a/libraries/ktx/src/ktx/KTX.h +++ b/libraries/ktx/src/ktx/KTX.h @@ -67,6 +67,8 @@ end namespace ktx { const uint32_t PACKING_SIZE { sizeof(uint32_t) }; + using Byte = uint8_t; + uint32_t evalPadding(size_t byteSize); enum GLType : uint32_t { COMPRESSED_TYPE = 0, @@ -289,7 +291,6 @@ namespace ktx { NUM_CUBEMAPFACES = 6, }; - using Byte = uint8_t; // Chunk of data struct Storage { @@ -375,31 +376,37 @@ namespace ktx { using KeyValues = std::list; - struct Mip { + struct Image { uint32_t _imageSize; uint32_t _padding; const Byte* _bytes; - Mip(uint32_t imageSize, uint32_t padding, const Byte* bytes) : + Image(uint32_t imageSize, uint32_t padding, const Byte* bytes) : _imageSize(imageSize), _padding(padding), _bytes(bytes) {} }; - using Mips = std::vector; + using Images = std::vector; + class KTX { void resetStorage(Storage* src); + KTX(); public: - KTX(); ~KTX(); - // parse a block of memory and create a KTX object from it + // Define a KTX object manually to write it somewhere (in a file on disk?) + // This path allocate the Storage where to store header, keyvalues and copy mips + // Then COPY all the data + static std::unique_ptr create(const Header& header, const KeyValues& keyValues, const Images& images); + + // Parse a block of memory and create a KTX object from it static std::unique_ptr create(const Storage& src); static std::unique_ptr create(std::unique_ptr& src); - static bool checkStorageHeader(const Storage& storage); + static bool checkHeaderFromStorage(const Storage& storage); // Access raw pointers to the main sections of the KTX const Header* getHeader() const; @@ -412,7 +419,7 @@ namespace ktx { std::unique_ptr _storage; KeyValues _keyValues; - Mips _mips; + Images _images; }; } diff --git a/libraries/ktx/src/ktx/Reader.cpp b/libraries/ktx/src/ktx/Reader.cpp index ff682d5bdb..2b45d30786 100644 --- a/libraries/ktx/src/ktx/Reader.cpp +++ b/libraries/ktx/src/ktx/Reader.cpp @@ -14,9 +14,9 @@ #include namespace ktx { - class Exception: public std::exception { + class ReaderException: public std::exception { public: - Exception(std::string explanation) : _explanation(explanation) {} + ReaderException(std::string explanation) : _explanation(explanation) {} const char* what() const override { return ("KTX deserialization error: " + _explanation).c_str(); } @@ -55,7 +55,7 @@ namespace ktx { uint32_t keyValueByteSize; memcpy(&keyValueByteSize, src, sizeof(uint32_t)); if (keyValueByteSize > length - offset) { - throw Exception("invalid key-value size"); + throw ReaderException("invalid key-value size"); } // find the first null character \0 @@ -63,7 +63,7 @@ namespace ktx { while (reinterpret_cast(src[++keyLength]) != '\0') { if (keyLength == keyValueByteSize) { // key must be null-terminated, and there must be space for the value - throw Exception("invalid key-value " + std::string(reinterpret_cast(src), keyLength)); + throw ReaderException("invalid key-value " + std::string(reinterpret_cast(src), keyLength)); } } @@ -81,8 +81,8 @@ namespace ktx { return keyValues; } - Mips getMipsTable(const Header& header, size_t mipsDataSize, const Byte* mipsData) { - Mips mips; + Images getImagesTable(const Header& header, size_t mipsDataSize, const Byte* mipsData) { + Images images; auto currentPtr = mipsData; auto numMips = header.numberOfMipmapLevels + 1; @@ -95,10 +95,9 @@ namespace ktx { // If enough data ahead then capture the pointer if ((currentPtr - mipsData) + imageSize <= (mipsDataSize)) { - auto padding = imageSize % PACKING_SIZE; - padding = (padding ? 4 - padding : 0); + auto padding = evalPadding(imageSize); - mips.emplace_back(Mip(imageSize, padding, currentPtr)); + images.emplace_back(Image(imageSize, padding, currentPtr)); currentPtr += imageSize + padding; } else { @@ -106,28 +105,28 @@ namespace ktx { } } - return mips; + return images; } - bool KTX::checkStorageHeader(const Storage& src) { + bool KTX::checkHeaderFromStorage(const Storage& src) { try { auto srcSize = src.size(); auto srcBytes = src.data(); // validation if (srcSize < sizeof(Header)) { - throw Exception("length is too short for header"); + throw ReaderException("length is too short for header"); } const Header* header = reinterpret_cast(srcBytes); if (!checkIdentifier(header->identifier)) { - throw Exception("identifier field invalid"); + throw ReaderException("identifier field invalid"); } bool endianMatch { true }; if (!checkEndianness(header->endianness, endianMatch)) { - throw Exception("endianness field has invalid value"); + throw ReaderException("endianness field has invalid value"); } // TODO: endian conversion if !endianMatch - for now, this is for local use and is unnecessary @@ -135,17 +134,18 @@ namespace ktx { // TODO: calculated bytesOfTexData if (srcSize < (sizeof(Header) + header->bytesOfKeyValueData)) { - throw Exception("length is too short for metadata"); + throw ReaderException("length is too short for metadata"); } size_t bytesOfTexData = 0; if (srcSize < (sizeof(Header) + header->bytesOfKeyValueData + bytesOfTexData)) { - throw Exception("length is too short for data"); + throw ReaderException("length is too short for data"); } return true; - } catch (Exception& e) { + } + catch (ReaderException& e) { qWarning(e.what()); return false; } @@ -163,7 +163,7 @@ namespace ktx { } try { - if (!checkStorageHeader(*src)) { + if (!checkHeaderFromStorage(*src)) { } @@ -173,12 +173,12 @@ namespace ktx { // read metadata result->_keyValues = getKeyValues(result->getHeader()->bytesOfKeyValueData, result->getKeyValueData()); - // populate mip table - result->_mips = getMipsTable(*result->getHeader(), result->getTexelsDataSize(), result->getTexelsData()); + // populate image table + result->_images = getImagesTable(*result->getHeader(), result->getTexelsDataSize(), result->getTexelsData()); return result; } - catch (Exception& e) { + catch (ReaderException& e) { qWarning(e.what()); return nullptr; } diff --git a/libraries/ktx/src/ktx/Writer.cpp b/libraries/ktx/src/ktx/Writer.cpp index d502a4a29a..a2b3d55178 100644 --- a/libraries/ktx/src/ktx/Writer.cpp +++ b/libraries/ktx/src/ktx/Writer.cpp @@ -12,10 +12,44 @@ namespace ktx { - /* size_t serialize(const gpu::Texture& texture, uint8_t* data) { - return 0; - }*/ - /* KTX serialize(const gpu::Texture& texture) { - return KTX(0, nullptr); - }*/ + + class WriterException : public std::exception { + public: + WriterException(std::string explanation) : _explanation(explanation) {} + const char* what() const override { + return ("KTX serialization error: " + _explanation).c_str(); + } + private: + std::string _explanation; + }; + + std::unique_ptr generateStorage(const Header& header, const KeyValues& keyValues, const Images& images) { + size_t storageSize = sizeof(Header); + auto numMips = header.numberOfMipmapLevels + 1; + + for (uint32_t l = 0; l < numMips; l++) { + if (images.size() > l) { + + storageSize += images[l]._imageSize; + storageSize += images[l]._imageSize; + } + + } + + } + + std::unique_ptr KTX::create(const Header& header, const KeyValues& keyValues, const Images& images) { + + std::unique_ptr result(new KTX()); + result->resetStorage(generateStorage(header, keyValues, images).release()); + + // read metadata + result->_keyValues = keyValues; + + // populate image table + result->_images = images; + + return result; + } + } From 0d28d17e40f7ef1e6c72d432e0ce1f5b66280e1e Mon Sep 17 00:00:00 2001 From: sam Date: Thu, 16 Feb 2017 02:45:53 -0800 Subject: [PATCH 008/302] Maybe saving the first ktx textures, testing the save pipeline --- interface/CMakeLists.txt | 2 +- libraries/gpu/src/gpu/Texture.cpp | 25 +++++++++ libraries/gpu/src/gpu/Texture.h | 8 +++ libraries/ktx/src/ktx/KTX.cpp | 38 ++++++++----- libraries/ktx/src/ktx/KTX.h | 71 ++++++++++++++++-------- libraries/ktx/src/ktx/Reader.cpp | 6 +- libraries/ktx/src/ktx/Writer.cpp | 55 ++++++++++++++++-- libraries/model/CMakeLists.txt | 2 +- libraries/model/src/model/TextureMap.cpp | 17 ++++++ libraries/render-utils/CMakeLists.txt | 2 +- libraries/render/CMakeLists.txt | 2 +- 11 files changed, 181 insertions(+), 47 deletions(-) diff --git a/interface/CMakeLists.txt b/interface/CMakeLists.txt index 868a2cf933..03b7a6b7e9 100644 --- a/interface/CMakeLists.txt +++ b/interface/CMakeLists.txt @@ -178,7 +178,7 @@ endif() # link required hifi libraries link_hifi_libraries( - shared octree gpu gl gpu-gl procedural model render + shared octree ktx gpu gl gpu-gl procedural model render recording fbx networking model-networking entities avatars audio audio-client animation script-engine physics render-utils entities-renderer ui auto-updater diff --git a/libraries/gpu/src/gpu/Texture.cpp b/libraries/gpu/src/gpu/Texture.cpp index 5b0c4c876a..80d636eec2 100755 --- a/libraries/gpu/src/gpu/Texture.cpp +++ b/libraries/gpu/src/gpu/Texture.cpp @@ -1001,3 +1001,28 @@ Texture::ExternalUpdates Texture::getUpdates() const { } return result; } + +#include + +ktx::KTXUniquePointer Texture::serialize(const Texture& texture) { + + ktx::Header header; + header.setUncompressed(ktx::GLType::UNSIGNED_BYTE, 4, ktx::GLFormat::BGRA, ktx::GLInternalFormat_Uncompressed::RGBA8, ktx::GLBaseInternalFormat::RGBA); + header.pixelWidth = texture.getWidth(); + header.pixelHeight = texture.getHeight(); + header.numberOfMipmapLevels = texture.mipLevels(); + + ktx::Images images; + for (int level = 0; level < header.numberOfMipmapLevels; level++) { + auto mip = texture.accessStoredMipFace(level); + if (mip) { + images.emplace_back(ktx::Image(mip->getSize(), 0, mip->readData())); + } + } + + auto ktxBuffer = ktx::KTX::create(header, ktx::KeyValues(), images); + return ktxBuffer; +} +TexturePointer Texture::unserialize(const ktx::KTXUniquePointer& srcData) { + return nullptr; +} \ No newline at end of file diff --git a/libraries/gpu/src/gpu/Texture.h b/libraries/gpu/src/gpu/Texture.h index 856bd4983d..62966b58ae 100755 --- a/libraries/gpu/src/gpu/Texture.h +++ b/libraries/gpu/src/gpu/Texture.h @@ -20,6 +20,11 @@ #include "Forward.h" #include "Resource.h" +namespace ktx { + class KTX; + using KTXUniquePointer = std::unique_ptr; +} + namespace gpu { // THe spherical harmonics is a nice tool for cubemap, so if required, the irradiance SH can be automatically generated @@ -475,6 +480,9 @@ public: ExternalUpdates getUpdates() const; + static ktx::KTXUniquePointer serialize(const Texture& texture); + static TexturePointer unserialize(const ktx::KTXUniquePointer& srcData); + protected: // Should only be accessed internally or by the backend sync function mutable Mutex _externalMutex; diff --git a/libraries/ktx/src/ktx/KTX.cpp b/libraries/ktx/src/ktx/KTX.cpp index d2872e76b5..f8f315d784 100644 --- a/libraries/ktx/src/ktx/KTX.cpp +++ b/libraries/ktx/src/ktx/KTX.cpp @@ -15,11 +15,12 @@ using namespace ktx; -uint32_t evalPadding(size_t byteSize) { +uint32_t Header::evalPadding(size_t byteSize) { auto padding = byteSize % PACKING_SIZE; - return (padding ? PACKING_SIZE - padding : 0); + return (uint32_t) (padding ? PACKING_SIZE - padding : 0); } + const Header::Identifier ktx::Header::IDENTIFIER {{ 0xAB, 0x4B, 0x54, 0x58, 0x20, 0x31, 0x31, 0xBB, 0x0D, 0x0A, 0x1A, 0x0A }}; @@ -29,7 +30,7 @@ Header::Header() { } uint32_t Header::evalMaxDimension() const { - return std::max(pixelWidth, std::max(pixelHeight, pixelDepth)); + return std::max(getPixelWidth(), std::max(getPixelHeight(), getPixelDepth())); } uint32_t Header::evalMaxLevel() const { @@ -37,13 +38,13 @@ uint32_t Header::evalMaxLevel() const { } uint32_t Header::evalPixelWidth(uint32_t level) const { - return std::max(pixelWidth >> level, 1U); + return std::max(getPixelWidth() >> level, 1U); } uint32_t Header::evalPixelHeight(uint32_t level) const { - return std::max(pixelHeight >> level, 1U); + return std::max(getPixelHeight() >> level, 1U); } uint32_t Header::evalPixelDepth(uint32_t level) const { - return std::max(pixelDepth >> level, 1U); + return std::max(getPixelDepth() >> level, 1U); } size_t Header::evalPixelSize() const { @@ -51,24 +52,24 @@ size_t Header::evalPixelSize() const { } size_t Header::evalRowSize(uint32_t level) const { - auto pixelWidth = evalPixelWidth(level); + auto pixWidth = evalPixelWidth(level); auto pixSize = evalPixelSize(); - auto netSize = pixelWidth * pixSize; + auto netSize = pixWidth * pixSize; auto padding = evalPadding(netSize); return netSize + padding; } size_t Header::evalFaceSize(uint32_t level) const { - auto pixelHeight = evalPixelHeight(level); - auto pixelDepth = evalPixelDepth(level); + auto pixHeight = evalPixelHeight(level); + auto pixDepth = evalPixelDepth(level); auto rowSize = evalRowSize(level); - return pixelDepth * pixelHeight * rowSize; + return pixDepth * pixHeight * rowSize; } size_t Header::evalImageSize(uint32_t level) const { auto faceSize = evalFaceSize(level); if (numberOfFaces == 6 && numberOfArrayElements == 0) { return faceSize; } else { - return (numberOfArrayElements * numberOfFaces * faceSize); + return (getNumberOfSlices() * numberOfFaces * faceSize); } } @@ -76,6 +77,9 @@ size_t Header::evalImageSize(uint32_t level) const { KTX::KTX() { } +KTX::~KTX() { +} + void KTX::resetStorage(Storage* storage) { _storage.reset(storage); } @@ -99,7 +103,8 @@ size_t KTX::getKeyValueDataSize() const { size_t KTX::getTexelsDataSize() const { if (_storage) { - return _storage->size() - sizeof(Header) + getKeyValueDataSize(); + //return _storage->size() - (sizeof(Header) + getKeyValueDataSize()); + return (_storage->_bytes + _storage->_size) - getTexelsData(); } else { return 0; } @@ -121,3 +126,10 @@ const Byte* KTX::getTexelsData() const { } } +Byte* KTX::getTexelsData() { + if (_storage) { + return (_storage->_bytes + sizeof(Header) + getKeyValueDataSize()); + } else { + return nullptr; + } +} diff --git a/libraries/ktx/src/ktx/KTX.h b/libraries/ktx/src/ktx/KTX.h index 5b72c4c1ce..1006124693 100644 --- a/libraries/ktx/src/ktx/KTX.h +++ b/libraries/ktx/src/ktx/KTX.h @@ -68,9 +68,8 @@ end namespace ktx { const uint32_t PACKING_SIZE { sizeof(uint32_t) }; using Byte = uint8_t; - uint32_t evalPadding(size_t byteSize); - enum GLType : uint32_t { + enum class GLType : uint32_t { COMPRESSED_TYPE = 0, // GL 4.4 Table 8.2 @@ -102,7 +101,7 @@ namespace ktx { NUM_GLTYPES = 25, }; - enum GLFormat : uint32_t { + enum class GLFormat : uint32_t { COMPRESSED_FORMAT = 0, // GL 4.4 Table 8.3 @@ -131,7 +130,7 @@ namespace ktx { NUM_GLFORMATS = 20, }; - enum GLInternalFormat_Uncompressed : uint32_t { + enum class GLInternalFormat_Uncompressed : uint32_t { // GL 4.4 Table 8.12 R8 = 0x8229, R8_SNORM = 0x8F94, @@ -233,7 +232,7 @@ namespace ktx { NUM_UNCOMPRESSED_GLINTERNALFORMATS = 74, }; - enum GLInternalFormat_Compressed : uint32_t { + enum class GLInternalFormat_Compressed : uint32_t { // GL 4.4 Table 8.14 COMPRESSED_RED = 0x8225, COMPRESSED_RG = 0x8226, @@ -268,15 +267,15 @@ namespace ktx { NUM_COMPRESSED_GLINTERNALFORMATS = 24, }; - enum GLBaseInternalFormat : uint32_t { + enum class GLBaseInternalFormat : uint32_t { // GL 4.4 Table 8.11 - BIF_DEPTH_COMPONENT = 0x1902, - BIF_DEPTH_STENCIL = 0x84F9, - BIF_RED = 0x1903, - BIF_RG = 0x8227, - BIF_RGB = 0x1907, - BIF_RGBA = 0x1908, - BIF_STENCIL_INDEX = 0x1901, + DEPTH_COMPONENT = 0x1902, + DEPTH_STENCIL = 0x84F9, + RED = 0x1903, + RG = 0x8227, + RGB = 0x1907, + RGBA = 0x1908, + STENCIL_INDEX = 0x1901, NUM_GLBASEINTERNALFORMATS = 7, }; @@ -341,22 +340,33 @@ namespace ktx { static const uint32_t ENDIAN_TEST = 0x04030201; static const uint32_t REVERSE_ENDIAN_TEST = 0x01020304; + static uint32_t evalPadding(size_t byteSize); + Header(); Byte identifier[IDENTIFIER_LENGTH]; uint32_t endianness { ENDIAN_TEST }; + uint32_t glType; - uint32_t glTypeSize; + uint32_t glTypeSize { 0 }; uint32_t glFormat; uint32_t glInternalFormat; uint32_t glBaseInternalFormat; - uint32_t pixelWidth; - uint32_t pixelHeight; - uint32_t pixelDepth; - uint32_t numberOfArrayElements; - uint32_t numberOfFaces; - uint32_t numberOfMipmapLevels; - uint32_t bytesOfKeyValueData; + + uint32_t pixelWidth { 0 }; + uint32_t pixelHeight { 0 }; + uint32_t pixelDepth { 0 }; + uint32_t numberOfArrayElements { 0 }; + uint32_t numberOfFaces { 1 }; + uint32_t numberOfMipmapLevels { 1 }; + + uint32_t bytesOfKeyValueData { 0 }; + + uint32_t getPixelWidth() const { return (pixelWidth ? pixelWidth : 1); } + uint32_t getPixelHeight() const { return (pixelHeight ? pixelHeight : 1); } + uint32_t getPixelDepth() const { return (pixelDepth ? pixelDepth : 1); } + uint32_t getNumberOfSlices() const { return (numberOfArrayElements ? numberOfArrayElements : 1); } + uint32_t getNumberOfLevels() const { return (numberOfMipmapLevels ? numberOfMipmapLevels : 1); } uint32_t evalMaxDimension() const; uint32_t evalMaxLevel() const; @@ -369,6 +379,20 @@ namespace ktx { size_t evalFaceSize(uint32_t level) const; size_t evalImageSize(uint32_t level) const; + void setUncompressed(GLType type, uint32_t typeSize, GLFormat format, GLInternalFormat_Uncompressed internalFormat, GLBaseInternalFormat baseInternalFormat) { + glType = (uint32_t) type; + glTypeSize = 0; + glFormat = (uint32_t) format; + glInternalFormat = (uint32_t) internalFormat; + glBaseInternalFormat = (uint32_t) baseInternalFormat; + } + void setCompressed(GLInternalFormat_Compressed internalFormat, GLBaseInternalFormat baseInternalFormat) { + glType = (uint32_t) GLType::COMPRESSED_TYPE; + glTypeSize = 1; + glFormat = (uint32_t) GLFormat::COMPRESSED_FORMAT; + glInternalFormat = (uint32_t) internalFormat; + glBaseInternalFormat = (uint32_t) baseInternalFormat; + } }; // Key Values @@ -392,6 +416,9 @@ namespace ktx { class KTX { void resetStorage(Storage* src); + void resetHeader(const Header& header); + void resetImages(const Images& images); + KTX(); public: @@ -412,11 +439,11 @@ namespace ktx { const Header* getHeader() const; const Byte* getKeyValueData() const; const Byte* getTexelsData() const; + Byte* getTexelsData(); size_t getKeyValueDataSize() const; size_t getTexelsDataSize() const; - std::unique_ptr _storage; KeyValues _keyValues; Images _images; diff --git a/libraries/ktx/src/ktx/Reader.cpp b/libraries/ktx/src/ktx/Reader.cpp index 2b45d30786..9586a9c033 100644 --- a/libraries/ktx/src/ktx/Reader.cpp +++ b/libraries/ktx/src/ktx/Reader.cpp @@ -84,18 +84,18 @@ namespace ktx { Images getImagesTable(const Header& header, size_t mipsDataSize, const Byte* mipsData) { Images images; auto currentPtr = mipsData; - auto numMips = header.numberOfMipmapLevels + 1; + auto numMips = header.getNumberOfLevels(); // Keep identifying new mip as long as we can at list query the next imageSize while ((currentPtr - mipsData) + sizeof(uint32_t) <= (mipsDataSize)) { // Grab the imageSize coming up - auto imageSize = *reinterpret_cast(currentPtr); + size_t imageSize = *reinterpret_cast(currentPtr); currentPtr += sizeof(uint32_t); // If enough data ahead then capture the pointer if ((currentPtr - mipsData) + imageSize <= (mipsDataSize)) { - auto padding = evalPadding(imageSize); + auto padding = Header::evalPadding(imageSize); images.emplace_back(Image(imageSize, padding, currentPtr)); diff --git a/libraries/ktx/src/ktx/Writer.cpp b/libraries/ktx/src/ktx/Writer.cpp index a2b3d55178..ae2dfc2d6d 100644 --- a/libraries/ktx/src/ktx/Writer.cpp +++ b/libraries/ktx/src/ktx/Writer.cpp @@ -25,17 +25,60 @@ namespace ktx { std::unique_ptr generateStorage(const Header& header, const KeyValues& keyValues, const Images& images) { size_t storageSize = sizeof(Header); - auto numMips = header.numberOfMipmapLevels + 1; + auto numMips = header.getNumberOfLevels(); for (uint32_t l = 0; l < numMips; l++) { if (images.size() > l) { - - storageSize += images[l]._imageSize; + storageSize += sizeof(uint32_t); storageSize += images[l]._imageSize; + storageSize += Header::evalPadding(images[l]._imageSize); } - } + std::unique_ptr storage(new Storage(storageSize)); + return storage; + } + + + void KTX::resetHeader(const Header& header) { + if (!_storage) { + return; + } + memcpy(_storage->_bytes, &header, sizeof(Header)); + } + void KTX::resetImages(const Images& srcImages) { + auto imagesDataPtr = getTexelsData(); + if (!imagesDataPtr) { + return; + } + auto allocatedImagesDataSize = getTexelsDataSize(); + size_t currentDataSize = 0; + auto currentPtr = imagesDataPtr; + + _images.clear(); + + + for (uint32_t l = 0; l < srcImages.size(); l++) { + if (currentDataSize + sizeof(uint32_t) < allocatedImagesDataSize) { + size_t imageSize = srcImages[l]._imageSize; + *(reinterpret_cast (currentPtr)) = imageSize; + currentPtr += sizeof(uint32_t); + currentDataSize += sizeof(uint32_t); + + // If enough data ahead then capture the copy source pointer + if (currentDataSize + imageSize <= (allocatedImagesDataSize)) { + + auto copied = memcpy(currentPtr, srcImages[l]._bytes, imageSize); + + auto padding = Header::evalPadding(imageSize); + + _images.emplace_back(Image(imageSize, padding, currentPtr)); + + currentPtr += imageSize + padding; + currentDataSize += imageSize + padding; + } + } + } } std::unique_ptr KTX::create(const Header& header, const KeyValues& keyValues, const Images& images) { @@ -43,11 +86,13 @@ namespace ktx { std::unique_ptr result(new KTX()); result->resetStorage(generateStorage(header, keyValues, images).release()); + result->resetHeader(header); + // read metadata result->_keyValues = keyValues; // populate image table - result->_images = images; + result->resetImages(images); return result; } diff --git a/libraries/model/CMakeLists.txt b/libraries/model/CMakeLists.txt index 63f632e484..021aa3d027 100755 --- a/libraries/model/CMakeLists.txt +++ b/libraries/model/CMakeLists.txt @@ -1,5 +1,5 @@ set(TARGET_NAME model) AUTOSCRIBE_SHADER_LIB(gpu model) setup_hifi_library() -link_hifi_libraries(shared gpu) +link_hifi_libraries(shared ktx gpu) diff --git a/libraries/model/src/model/TextureMap.cpp b/libraries/model/src/model/TextureMap.cpp index d1fbaf767a..42f908c754 100755 --- a/libraries/model/src/model/TextureMap.cpp +++ b/libraries/model/src/model/TextureMap.cpp @@ -10,6 +10,8 @@ // #include "TextureMap.h" +#include + #include #include #include @@ -264,8 +266,23 @@ gpu::Texture* TextureUsage::process2DTextureColorFromImage(const QImage& srcImag if (generateMips) { ::generateMips(theTexture, image, formatMip, false); } + auto theKTX = Texture::serialize(*theTexture); + if (theKTX) { + // save that! + std::string filename("C://temp//ktxCache//texmex"); + filename += std::to_string((size_t) theTexture); + filename += ".ktx"; + + FILE* file = fopen (filename.c_str(),"wb"); + if (file != nullptr) { + fwrite(theKTX->_storage->data(), 1, theKTX->_storage->size(), file); + fclose (file); + } + + } } + return theTexture; } diff --git a/libraries/render-utils/CMakeLists.txt b/libraries/render-utils/CMakeLists.txt index ecafb8f565..3bf389973a 100644 --- a/libraries/render-utils/CMakeLists.txt +++ b/libraries/render-utils/CMakeLists.txt @@ -3,7 +3,7 @@ AUTOSCRIBE_SHADER_LIB(gpu model render) # pull in the resources.qrc file qt5_add_resources(QT_RESOURCES_FILE "${CMAKE_CURRENT_SOURCE_DIR}/res/fonts/fonts.qrc") setup_hifi_library(Widgets OpenGL Network Qml Quick Script) -link_hifi_libraries(shared gpu model model-networking render animation fbx entities) +link_hifi_libraries(shared ktx gpu model model-networking render animation fbx entities) if (NOT ANDROID) target_nsight() diff --git a/libraries/render/CMakeLists.txt b/libraries/render/CMakeLists.txt index 735bb7f086..8fd05bd320 100644 --- a/libraries/render/CMakeLists.txt +++ b/libraries/render/CMakeLists.txt @@ -3,6 +3,6 @@ AUTOSCRIBE_SHADER_LIB(gpu model) setup_hifi_library() # render needs octree only for getAccuracyAngle(float, int) -link_hifi_libraries(shared gpu model octree) +link_hifi_libraries(shared ktx gpu model octree) target_nsight() From 2f7181fb322407d6b9fe0f7cb84c684a0036cf97 Mon Sep 17 00:00:00 2001 From: samcake Date: Thu, 16 Feb 2017 13:27:16 -0800 Subject: [PATCH 009/302] Address review comments --- libraries/gpu/src/gpu/Texture.cpp | 24 ----- libraries/gpu/src/gpu/Texture_ktx.cpp | 42 ++++++++ libraries/ktx/src/ktx/KTX.h | 17 +++- libraries/ktx/src/ktx/Reader.cpp | 6 +- libraries/ktx/src/ktx/Writer.cpp | 118 ++++++++++++++++------- libraries/model/src/model/TextureMap.cpp | 15 ++- 6 files changed, 156 insertions(+), 66 deletions(-) create mode 100644 libraries/gpu/src/gpu/Texture_ktx.cpp diff --git a/libraries/gpu/src/gpu/Texture.cpp b/libraries/gpu/src/gpu/Texture.cpp index 80d636eec2..d7ae202e7d 100755 --- a/libraries/gpu/src/gpu/Texture.cpp +++ b/libraries/gpu/src/gpu/Texture.cpp @@ -1002,27 +1002,3 @@ Texture::ExternalUpdates Texture::getUpdates() const { return result; } -#include - -ktx::KTXUniquePointer Texture::serialize(const Texture& texture) { - - ktx::Header header; - header.setUncompressed(ktx::GLType::UNSIGNED_BYTE, 4, ktx::GLFormat::BGRA, ktx::GLInternalFormat_Uncompressed::RGBA8, ktx::GLBaseInternalFormat::RGBA); - header.pixelWidth = texture.getWidth(); - header.pixelHeight = texture.getHeight(); - header.numberOfMipmapLevels = texture.mipLevels(); - - ktx::Images images; - for (int level = 0; level < header.numberOfMipmapLevels; level++) { - auto mip = texture.accessStoredMipFace(level); - if (mip) { - images.emplace_back(ktx::Image(mip->getSize(), 0, mip->readData())); - } - } - - auto ktxBuffer = ktx::KTX::create(header, ktx::KeyValues(), images); - return ktxBuffer; -} -TexturePointer Texture::unserialize(const ktx::KTXUniquePointer& srcData) { - return nullptr; -} \ No newline at end of file diff --git a/libraries/gpu/src/gpu/Texture_ktx.cpp b/libraries/gpu/src/gpu/Texture_ktx.cpp new file mode 100644 index 0000000000..d06454b933 --- /dev/null +++ b/libraries/gpu/src/gpu/Texture_ktx.cpp @@ -0,0 +1,42 @@ +// +// Texture_ktx.cpp +// libraries/gpu/src/gpu +// +// Created by Sam Gateau on 2/16/2017. +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + + +#include "Texture.h" + +#include +using namespace gpu; + +ktx::KTXUniquePointer Texture::serialize(const Texture& texture) { + + ktx::Header header; + header.setUncompressed(ktx::GLType::UNSIGNED_BYTE, 4, ktx::GLFormat::BGRA, ktx::GLInternalFormat_Uncompressed::RGBA8, ktx::GLBaseInternalFormat::RGBA); + header.pixelWidth = texture.getWidth(); + header.pixelHeight = texture.getHeight(); + header.numberOfMipmapLevels = texture.mipLevels(); + + ktx::Images images; + for (int level = 0; level < header.numberOfMipmapLevels; level++) { + auto mip = texture.accessStoredMipFace(level); + if (mip) { + images.emplace_back(ktx::Image(mip->getSize(), 0, mip->readData())); + } + } + + auto ktxBuffer = ktx::KTX::create(header, images); + return ktxBuffer; +} +TexturePointer Texture::unserialize(const ktx::KTXUniquePointer& srcData) { + + const auto& header = *srcData->getHeader(); + + return nullptr; +} \ No newline at end of file diff --git a/libraries/ktx/src/ktx/KTX.h b/libraries/ktx/src/ktx/KTX.h index 1006124693..41032c8222 100644 --- a/libraries/ktx/src/ktx/KTX.h +++ b/libraries/ktx/src/ktx/KTX.h @@ -427,7 +427,22 @@ namespace ktx { // Define a KTX object manually to write it somewhere (in a file on disk?) // This path allocate the Storage where to store header, keyvalues and copy mips // Then COPY all the data - static std::unique_ptr create(const Header& header, const KeyValues& keyValues, const Images& images); + static std::unique_ptr create(const Header& header, const Images& images, const KeyValues& keyValues = KeyValues()); + + // Instead of creating a full Copy of the src data in a KTX object, the write serialization can be performed with the + // following two functions + // size_t sizeNeeded = KTX::evalStorageSize(header, images); + // + // //allocate a buffer of size "sizeNeeded" or map a file with enough capacity + // Byte* destBytes = new Byte[sizeNeeded]; + // + // // THen perform the writing of the src data to the destinnation buffer + // write(destBytes, sizeNeeded, header, images); + // + // This is exactly what is done in the create function + static size_t evalStorageSize(const Header& header, const Images& images, const KeyValues& keyValues = KeyValues()); + static size_t write(Byte* destBytes, size_t destByteSize, const Header& header, const Images& images, const KeyValues& keyValues = KeyValues()); + static Images writeImages(Byte* destBytes, size_t destByteSize, const Images& images); // Parse a block of memory and create a KTX object from it static std::unique_ptr create(const Storage& src); diff --git a/libraries/ktx/src/ktx/Reader.cpp b/libraries/ktx/src/ktx/Reader.cpp index 9586a9c033..4099aa6ef8 100644 --- a/libraries/ktx/src/ktx/Reader.cpp +++ b/libraries/ktx/src/ktx/Reader.cpp @@ -16,12 +16,12 @@ namespace ktx { class ReaderException: public std::exception { public: - ReaderException(std::string explanation) : _explanation(explanation) {} + ReaderException(const std::string& explanation) : _explanation("KTX deserialization error: " + explanation) {} const char* what() const override { - return ("KTX deserialization error: " + _explanation).c_str(); + return _explanation.c_str(); } private: - std::string _explanation; + const std::string _explanation; }; bool checkEndianness(uint32_t endianness, bool& matching) { diff --git a/libraries/ktx/src/ktx/Writer.cpp b/libraries/ktx/src/ktx/Writer.cpp index ae2dfc2d6d..75836ad5b5 100644 --- a/libraries/ktx/src/ktx/Writer.cpp +++ b/libraries/ktx/src/ktx/Writer.cpp @@ -15,30 +15,14 @@ namespace ktx { class WriterException : public std::exception { public: - WriterException(std::string explanation) : _explanation(explanation) {} + WriterException(const std::string& explanation) : _explanation("KTX serialization error: " + explanation) {} const char* what() const override { - return ("KTX serialization error: " + _explanation).c_str(); + return _explanation.c_str(); } private: - std::string _explanation; + const std::string _explanation; }; - std::unique_ptr generateStorage(const Header& header, const KeyValues& keyValues, const Images& images) { - size_t storageSize = sizeof(Header); - auto numMips = header.getNumberOfLevels(); - - for (uint32_t l = 0; l < numMips; l++) { - if (images.size() > l) { - storageSize += sizeof(uint32_t); - storageSize += images[l]._imageSize; - storageSize += Header::evalPadding(images[l]._imageSize); - } - } - - std::unique_ptr storage(new Storage(storageSize)); - return storage; - } - void KTX::resetHeader(const Header& header) { if (!_storage) { @@ -52,10 +36,84 @@ namespace ktx { return; } auto allocatedImagesDataSize = getTexelsDataSize(); + + // Just copy in our storage + _images = writeImages(imagesDataPtr, allocatedImagesDataSize, srcImages); + } + + std::unique_ptr KTX::create(const Header& header, const Images& images, const KeyValues& keyValues) { + auto storageSize = evalStorageSize(header, images, keyValues); + + std::unique_ptr result(new KTX()); + + result->resetStorage(new Storage(storageSize)); + + result->resetHeader(header); + + // read metadata + result->_keyValues = keyValues; + + // populate image table + result->resetImages(images); + + return result; + } + + size_t KTX::evalStorageSize(const Header& header, const Images& images, const KeyValues& keyValues) { + size_t storageSize = sizeof(Header); + + if (header.bytesOfKeyValueData && !keyValues.empty()) { + + } + + auto numMips = header.getNumberOfLevels(); + for (uint32_t l = 0; l < numMips; l++) { + if (images.size() > l) { + storageSize += sizeof(uint32_t); + storageSize += images[l]._imageSize; + storageSize += Header::evalPadding(images[l]._imageSize); + } + } + return storageSize; + } + + size_t KTX::write(Byte* destBytes, size_t destByteSize, const Header& header, const Images& srcImages, const KeyValues& keyValues) { + // Check again that we have enough destination capacity + if (!destBytes || (destByteSize < evalStorageSize(header, srcImages, keyValues))) { + return 0; + } + + auto currentDestPtr = destBytes; + // Header + auto destHeader = reinterpret_cast(currentDestPtr); + memcpy(currentDestPtr, &header, sizeof(Header)); + currentDestPtr += sizeof(Header); + + // KeyValues + // Skip for now + if (header.bytesOfKeyValueData && !keyValues.empty()) { + + } + destHeader->bytesOfKeyValueData = 0; + currentDestPtr += destHeader->bytesOfKeyValueData; + + // Images + auto destImages = writeImages(currentDestPtr, destByteSize - sizeof(Header) - destHeader->bytesOfKeyValueData, srcImages); + // We chould check here that the amoutn of dest IMages generated is the same as the source + + return destByteSize; + } + + Images KTX::writeImages(Byte* destBytes, size_t destByteSize, const Images& srcImages) { + Images destImages; + auto imagesDataPtr = destBytes; + if (!imagesDataPtr) { + return destImages; + } + auto allocatedImagesDataSize = destByteSize; size_t currentDataSize = 0; auto currentPtr = imagesDataPtr; - _images.clear(); for (uint32_t l = 0; l < srcImages.size(); l++) { @@ -72,29 +130,15 @@ namespace ktx { auto padding = Header::evalPadding(imageSize); - _images.emplace_back(Image(imageSize, padding, currentPtr)); + destImages.emplace_back(Image(imageSize, padding, currentPtr)); currentPtr += imageSize + padding; currentDataSize += imageSize + padding; } } } - } - - std::unique_ptr KTX::create(const Header& header, const KeyValues& keyValues, const Images& images) { - - std::unique_ptr result(new KTX()); - result->resetStorage(generateStorage(header, keyValues, images).release()); - - result->resetHeader(header); - - // read metadata - result->_keyValues = keyValues; - - // populate image table - result->resetImages(images); - - return result; + + return destImages; } } diff --git a/libraries/model/src/model/TextureMap.cpp b/libraries/model/src/model/TextureMap.cpp index 42f908c754..37c35ef250 100755 --- a/libraries/model/src/model/TextureMap.cpp +++ b/libraries/model/src/model/TextureMap.cpp @@ -15,6 +15,9 @@ #include #include #include +#include +#include +#include #include @@ -269,7 +272,17 @@ gpu::Texture* TextureUsage::process2DTextureColorFromImage(const QImage& srcImag auto theKTX = Texture::serialize(*theTexture); if (theKTX) { // save that! - std::string filename("C://temp//ktxCache//texmex"); + QString path("hifi_ktx/"); + QFileInfo originalFileInfo(path); + QString docsLocation = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation); + path = docsLocation + "/" + path; + QFileInfo info(path); + if (!info.absoluteDir().exists()) { + QString originalRelativePath = originalFileInfo.path(); + QDir(docsLocation).mkpath(originalRelativePath); + } + + std::string filename(path.toStdString()); filename += std::to_string((size_t) theTexture); filename += ".ktx"; From 8ee5defc6086a2cd2165b8e96672f489f965f40e Mon Sep 17 00:00:00 2001 From: sam Date: Thu, 16 Feb 2017 16:03:55 -0800 Subject: [PATCH 010/302] Adding the reading path --- libraries/gpu/src/gpu/Texture.h | 2 +- libraries/gpu/src/gpu/Texture_ktx.cpp | 16 ++++++++++-- libraries/model/src/model/TextureMap.cpp | 32 +++++++++++++++++++++--- 3 files changed, 43 insertions(+), 7 deletions(-) diff --git a/libraries/gpu/src/gpu/Texture.h b/libraries/gpu/src/gpu/Texture.h index 62966b58ae..b99e9da371 100755 --- a/libraries/gpu/src/gpu/Texture.h +++ b/libraries/gpu/src/gpu/Texture.h @@ -481,7 +481,7 @@ public: ExternalUpdates getUpdates() const; static ktx::KTXUniquePointer serialize(const Texture& texture); - static TexturePointer unserialize(const ktx::KTXUniquePointer& srcData); + static Texture* unserialize(const ktx::KTXUniquePointer& srcData); protected: // Should only be accessed internally or by the backend sync function diff --git a/libraries/gpu/src/gpu/Texture_ktx.cpp b/libraries/gpu/src/gpu/Texture_ktx.cpp index d06454b933..6380b23903 100644 --- a/libraries/gpu/src/gpu/Texture_ktx.cpp +++ b/libraries/gpu/src/gpu/Texture_ktx.cpp @@ -34,9 +34,21 @@ ktx::KTXUniquePointer Texture::serialize(const Texture& texture) { auto ktxBuffer = ktx::KTX::create(header, images); return ktxBuffer; } -TexturePointer Texture::unserialize(const ktx::KTXUniquePointer& srcData) { +Texture* Texture::unserialize(const ktx::KTXUniquePointer& srcData) { + if (!srcData) { + return nullptr; + } const auto& header = *srcData->getHeader(); - return nullptr; + Format pixelFormat = Format::COLOR_RGBA_32; + + auto tex = Texture::create2D(pixelFormat, header.getPixelWidth(), header.getPixelHeight()); + uint16_t level = 0; + for (auto& image : srcData->_images) { + tex->assignStoredMip(level, pixelFormat, image._imageSize, image._bytes); + level++; + } + + return tex; } \ No newline at end of file diff --git a/libraries/model/src/model/TextureMap.cpp b/libraries/model/src/model/TextureMap.cpp index 37c35ef250..0416c22d5e 100755 --- a/libraries/model/src/model/TextureMap.cpp +++ b/libraries/model/src/model/TextureMap.cpp @@ -286,12 +286,36 @@ gpu::Texture* TextureUsage::process2DTextureColorFromImage(const QImage& srcImag filename += std::to_string((size_t) theTexture); filename += ".ktx"; - FILE* file = fopen (filename.c_str(),"wb"); - if (file != nullptr) { - fwrite(theKTX->_storage->data(), 1, theKTX->_storage->size(), file); - fclose (file); + { + FILE* file = fopen (filename.c_str(),"wb"); + if (file != nullptr) { + fwrite(theKTX->_storage->data(), 1, theKTX->_storage->size(), file); + fclose (file); + } } + { + FILE* file = fopen (filename.c_str(),"rb"); + if (file != nullptr) { + // obtain file size: + fseek (file , 0 , SEEK_END); + auto size = ftell(file); + rewind(file); + + std::unique_ptr storage(new ktx::Storage(size)); + fread(storage->_bytes, 1, storage->_size, file); + fclose (file); + + //then create a new texture out of the ktx + auto theNewTexure = Texture::unserialize(ktx::KTX::create(storage)); + + if (theNewTexure) { + auto srcTexture = theTexture; + theTexture = theNewTexure; + delete srcTexture; + } + } + } } } From 2ee3f05713722d134b820d7cd69854c35df872b2 Mon Sep 17 00:00:00 2001 From: sam Date: Thu, 16 Feb 2017 17:28:02 -0800 Subject: [PATCH 011/302] CLeaning the read case --- libraries/gpu/src/gpu/Format.cpp | 4 + libraries/gpu/src/gpu/Format.h | 2 + libraries/gpu/src/gpu/Texture_ktx.cpp | 7 +- libraries/ktx/src/ktx/KTX.h | 4 +- libraries/ktx/src/ktx/Reader.cpp | 105 +++++++++++--------------- 5 files changed, 57 insertions(+), 65 deletions(-) diff --git a/libraries/gpu/src/gpu/Format.cpp b/libraries/gpu/src/gpu/Format.cpp index 2a8185bf94..0f5e85c907 100644 --- a/libraries/gpu/src/gpu/Format.cpp +++ b/libraries/gpu/src/gpu/Format.cpp @@ -12,6 +12,10 @@ using namespace gpu; const Element Element::COLOR_RGBA_32{ VEC4, NUINT8, RGBA }; const Element Element::COLOR_SRGBA_32{ VEC4, NUINT8, SRGBA }; + +const Element Element::COLOR_BGRA_32{ VEC4, NUINT8, BGRA }; +const Element Element::COLOR_SBGRA_32{ VEC4, NUINT8, SBGRA }; + const Element Element::COLOR_R11G11B10{ SCALAR, FLOAT, R11G11B10 }; const Element Element::VEC4F_COLOR_RGBA{ VEC4, FLOAT, RGBA }; const Element Element::VEC2F_UV{ VEC2, FLOAT, UV }; diff --git a/libraries/gpu/src/gpu/Format.h b/libraries/gpu/src/gpu/Format.h index 13809a41e6..c7dc2eed8d 100644 --- a/libraries/gpu/src/gpu/Format.h +++ b/libraries/gpu/src/gpu/Format.h @@ -229,6 +229,8 @@ public: static const Element COLOR_RGBA_32; static const Element COLOR_SRGBA_32; + static const Element COLOR_BGRA_32; + static const Element COLOR_SBGRA_32; static const Element COLOR_R11G11B10; static const Element VEC4F_COLOR_RGBA; static const Element VEC2F_UV; diff --git a/libraries/gpu/src/gpu/Texture_ktx.cpp b/libraries/gpu/src/gpu/Texture_ktx.cpp index 6380b23903..623a3493da 100644 --- a/libraries/gpu/src/gpu/Texture_ktx.cpp +++ b/libraries/gpu/src/gpu/Texture_ktx.cpp @@ -24,7 +24,7 @@ ktx::KTXUniquePointer Texture::serialize(const Texture& texture) { header.numberOfMipmapLevels = texture.mipLevels(); ktx::Images images; - for (int level = 0; level < header.numberOfMipmapLevels; level++) { + for (uint32_t level = 0; level < header.numberOfMipmapLevels; level++) { auto mip = texture.accessStoredMipFace(level); if (mip) { images.emplace_back(ktx::Image(mip->getSize(), 0, mip->readData())); @@ -41,12 +41,13 @@ Texture* Texture::unserialize(const ktx::KTXUniquePointer& srcData) { const auto& header = *srcData->getHeader(); - Format pixelFormat = Format::COLOR_RGBA_32; + Format mipFormat = Format::COLOR_SBGRA_32; + Format pixelFormat = Format::COLOR_SRGBA_32; auto tex = Texture::create2D(pixelFormat, header.getPixelWidth(), header.getPixelHeight()); uint16_t level = 0; for (auto& image : srcData->_images) { - tex->assignStoredMip(level, pixelFormat, image._imageSize, image._bytes); + tex->assignStoredMip(level, mipFormat, image._imageSize, image._bytes); level++; } diff --git a/libraries/ktx/src/ktx/KTX.h b/libraries/ktx/src/ktx/KTX.h index 41032c8222..3a4c19971a 100644 --- a/libraries/ktx/src/ktx/KTX.h +++ b/libraries/ktx/src/ktx/KTX.h @@ -445,10 +445,10 @@ namespace ktx { static Images writeImages(Byte* destBytes, size_t destByteSize, const Images& images); // Parse a block of memory and create a KTX object from it - static std::unique_ptr create(const Storage& src); static std::unique_ptr create(std::unique_ptr& src); - static bool checkHeaderFromStorage(const Storage& storage); + static bool checkHeaderFromStorage(size_t srcSize, const Byte* srcBytes); + static Images parseImages(const Header& header, size_t srcSize, const Byte* srcBytes); // Access raw pointers to the main sections of the KTX const Header* getHeader() const; diff --git a/libraries/ktx/src/ktx/Reader.cpp b/libraries/ktx/src/ktx/Reader.cpp index 4099aa6ef8..00c0c4e19a 100644 --- a/libraries/ktx/src/ktx/Reader.cpp +++ b/libraries/ktx/src/ktx/Reader.cpp @@ -38,12 +38,17 @@ namespace ktx { } break; default: + throw ReaderException("endianness field has invalid value"); return false; } } bool checkIdentifier(const Byte* identifier) { - return memcmp(identifier, Header::IDENTIFIER.data(), Header::IDENTIFIER_LENGTH); + if (!(0 == memcmp(identifier, Header::IDENTIFIER.data(), Header::IDENTIFIER_LENGTH))) { + throw ReaderException("identifier field invalid"); + return false; + } + return true; } KeyValues getKeyValues(size_t length, const Byte* src) { @@ -81,53 +86,18 @@ namespace ktx { return keyValues; } - Images getImagesTable(const Header& header, size_t mipsDataSize, const Byte* mipsData) { - Images images; - auto currentPtr = mipsData; - auto numMips = header.getNumberOfLevels(); - - // Keep identifying new mip as long as we can at list query the next imageSize - while ((currentPtr - mipsData) + sizeof(uint32_t) <= (mipsDataSize)) { - - // Grab the imageSize coming up - size_t imageSize = *reinterpret_cast(currentPtr); - currentPtr += sizeof(uint32_t); - - // If enough data ahead then capture the pointer - if ((currentPtr - mipsData) + imageSize <= (mipsDataSize)) { - auto padding = Header::evalPadding(imageSize); - - images.emplace_back(Image(imageSize, padding, currentPtr)); - - currentPtr += imageSize + padding; - } else { - break; - } - } - - return images; - } - - - bool KTX::checkHeaderFromStorage(const Storage& src) { + bool KTX::checkHeaderFromStorage(size_t srcSize, const Byte* srcBytes) { try { - auto srcSize = src.size(); - auto srcBytes = src.data(); - // validation if (srcSize < sizeof(Header)) { throw ReaderException("length is too short for header"); } const Header* header = reinterpret_cast(srcBytes); - if (!checkIdentifier(header->identifier)) { - throw ReaderException("identifier field invalid"); - } + checkIdentifier(header->identifier); bool endianMatch { true }; - if (!checkEndianness(header->endianness, endianMatch)) { - throw ReaderException("endianness field has invalid value"); - } + checkEndianness(header->endianness, endianMatch); // TODO: endian conversion if !endianMatch - for now, this is for local use and is unnecessary @@ -151,10 +121,31 @@ namespace ktx { } } - std::unique_ptr KTX::create(const Storage& src) { - auto srcCopy = std::make_unique(src); + Images KTX::parseImages(const Header& header, size_t srcSize, const Byte* srcBytes) { + Images images; + auto currentPtr = srcBytes; + auto numMips = header.getNumberOfLevels(); - return create(srcCopy); + // Keep identifying new mip as long as we can at list query the next imageSize + while ((currentPtr - srcBytes) + sizeof(uint32_t) <= (srcSize)) { + + // Grab the imageSize coming up + size_t imageSize = *reinterpret_cast(currentPtr); + currentPtr += sizeof(uint32_t); + + // If enough data ahead then capture the pointer + if ((currentPtr - srcBytes) + imageSize <= (srcSize)) { + auto padding = Header::evalPadding(imageSize); + + images.emplace_back(Image(imageSize, padding, currentPtr)); + + currentPtr += imageSize + padding; + } else { + break; + } + } + + return images; } std::unique_ptr KTX::create(std::unique_ptr& src) { @@ -162,25 +153,19 @@ namespace ktx { return nullptr; } - try { - if (!checkHeaderFromStorage(*src)) { - - } - - std::unique_ptr result(new KTX()); - result->resetStorage(src.release()); - - // read metadata - result->_keyValues = getKeyValues(result->getHeader()->bytesOfKeyValueData, result->getKeyValueData()); - - // populate image table - result->_images = getImagesTable(*result->getHeader(), result->getTexelsDataSize(), result->getTexelsData()); - - return result; - } - catch (ReaderException& e) { - qWarning(e.what()); + if (!checkHeaderFromStorage(src->size(), src->data())) { return nullptr; } + + std::unique_ptr result(new KTX()); + result->resetStorage(src.release()); + + // read metadata + // result->_keyValues = getKeyValues(result->getHeader()->bytesOfKeyValueData, result->getKeyValueData()); + + // populate image table + result->_images = parseImages(*result->getHeader(), result->getTexelsDataSize(), result->getTexelsData()); + + return result; } } \ No newline at end of file From 56d11206541c93fef18eecd9fd4ce4d9e1fc28d3 Mon Sep 17 00:00:00 2001 From: sam Date: Fri, 17 Feb 2017 01:15:27 -0800 Subject: [PATCH 012/302] progressing on io with ktx --- libraries/gpu/src/gpu/Texture.cpp | 20 ++-- libraries/gpu/src/gpu/Texture.h | 17 ++-- libraries/gpu/src/gpu/Texture_ktx.cpp | 84 ++++++++++++++-- libraries/ktx/src/ktx/KTX.h | 19 +++- libraries/model/src/model/TextureMap.cpp | 120 +++++++++++++---------- 5 files changed, 183 insertions(+), 77 deletions(-) diff --git a/libraries/gpu/src/gpu/Texture.cpp b/libraries/gpu/src/gpu/Texture.cpp index d7ae202e7d..ba7723001a 100755 --- a/libraries/gpu/src/gpu/Texture.cpp +++ b/libraries/gpu/src/gpu/Texture.cpp @@ -264,19 +264,19 @@ Texture* Texture::createExternal2D(const ExternalRecycler& recycler, const Sampl } Texture* Texture::create1D(const Element& texelFormat, uint16 width, const Sampler& sampler) { - return create(TEX_1D, texelFormat, width, 1, 1, 1, 1, sampler); + return create(TEX_1D, texelFormat, width, 1, 1, 1, 0, sampler); } Texture* Texture::create2D(const Element& texelFormat, uint16 width, uint16 height, const Sampler& sampler) { - return create(TEX_2D, texelFormat, width, height, 1, 1, 1, sampler); + return create(TEX_2D, texelFormat, width, height, 1, 1, 0, sampler); } Texture* Texture::create3D(const Element& texelFormat, uint16 width, uint16 height, uint16 depth, const Sampler& sampler) { - return create(TEX_3D, texelFormat, width, height, depth, 1, 1, sampler); + return create(TEX_3D, texelFormat, width, height, depth, 1, 0, sampler); } Texture* Texture::createCube(const Element& texelFormat, uint16 width, const Sampler& sampler) { - return create(TEX_CUBE, texelFormat, width, width, 1, 1, 1, sampler); + return create(TEX_CUBE, texelFormat, width, width, 1, 1, 0, sampler); } Texture* Texture::create(Type type, const Element& texelFormat, uint16 width, uint16 height, uint16 depth, uint16 numSamples, uint16 numSlices, const Sampler& sampler) @@ -321,7 +321,7 @@ Texture::~Texture() } Texture::Size Texture::resize(Type type, const Element& texelFormat, uint16 width, uint16 height, uint16 depth, uint16 numSamples, uint16 numSlices) { - if (width && height && depth && numSamples && numSlices) { + if (width && height && depth && numSamples) { bool changed = false; if ( _type != type) { @@ -382,20 +382,20 @@ Texture::Size Texture::resize(Type type, const Element& texelFormat, uint16 widt } Texture::Size Texture::resize1D(uint16 width, uint16 numSamples) { - return resize(TEX_1D, getTexelFormat(), width, 1, 1, numSamples, 1); + return resize(TEX_1D, getTexelFormat(), width, 1, 1, numSamples, 0); } Texture::Size Texture::resize2D(uint16 width, uint16 height, uint16 numSamples) { - return resize(TEX_2D, getTexelFormat(), width, height, 1, numSamples, 1); + return resize(TEX_2D, getTexelFormat(), width, height, 1, numSamples, 0); } Texture::Size Texture::resize3D(uint16 width, uint16 height, uint16 depth, uint16 numSamples) { - return resize(TEX_3D, getTexelFormat(), width, height, depth, numSamples, 1); + return resize(TEX_3D, getTexelFormat(), width, height, depth, numSamples, 0); } Texture::Size Texture::resizeCube(uint16 width, uint16 numSamples) { - return resize(TEX_CUBE, getTexelFormat(), width, 1, 1, numSamples, 1); + return resize(TEX_CUBE, getTexelFormat(), width, 1, 1, numSamples, 0); } Texture::Size Texture::reformat(const Element& texelFormat) { - return resize(_type, texelFormat, getWidth(), getHeight(), getDepth(), getNumSamples(), getNumSlices()); + return resize(_type, texelFormat, getWidth(), getHeight(), getDepth(), getNumSamples(), _numSlices); } bool Texture::isColorRenderTarget() const { diff --git a/libraries/gpu/src/gpu/Texture.h b/libraries/gpu/src/gpu/Texture.h index b99e9da371..fb8d94150d 100755 --- a/libraries/gpu/src/gpu/Texture.h +++ b/libraries/gpu/src/gpu/Texture.h @@ -352,7 +352,12 @@ public: uint32 getNumTexels() const { return _width * _height * _depth * getNumFaces(); } - uint16 getNumSlices() const { return _numSlices; } + // The texture is an array if the _numSlices is not 0. + // otherwise, if _numSLices is 0, then the texture is NOT an array + // The number of slices returned is 1 at the minimum (if not an array) or the actual _numSlices. + bool isArray() const { return _numSlices > 0; } + uint16 getNumSlices() const { return (isArray() ? _numSlices : 1); } + uint16 getNumSamples() const { return _numSamples; } @@ -502,12 +507,12 @@ protected: uint32 _size = 0; Element _texelFormat; - uint16 _width = 1; - uint16 _height = 1; - uint16 _depth = 1; + uint16 _width { 1 }; + uint16 _height { 1 }; + uint16 _depth { 1 }; - uint16 _numSamples = 1; - uint16 _numSlices = 1; + uint16 _numSamples { 1 }; + uint16 _numSlices { 0 }; // if _numSlices is 0, the texture is not an "Array", the getNumSlices reported is 1 uint16 _maxMip { 0 }; uint16 _minMip { 0 }; diff --git a/libraries/gpu/src/gpu/Texture_ktx.cpp b/libraries/gpu/src/gpu/Texture_ktx.cpp index 623a3493da..c36c0f14d8 100644 --- a/libraries/gpu/src/gpu/Texture_ktx.cpp +++ b/libraries/gpu/src/gpu/Texture_ktx.cpp @@ -16,12 +16,57 @@ using namespace gpu; ktx::KTXUniquePointer Texture::serialize(const Texture& texture) { - ktx::Header header; + + // From texture format to ktx format description + auto texelFormat = texture.getTexelFormat(); + if ( !( (texelFormat == Format::COLOR_RGBA_32) + || (texelFormat == Format::COLOR_SRGBA_32) + )) + return nullptr; + header.setUncompressed(ktx::GLType::UNSIGNED_BYTE, 4, ktx::GLFormat::BGRA, ktx::GLInternalFormat_Uncompressed::RGBA8, ktx::GLBaseInternalFormat::RGBA); - header.pixelWidth = texture.getWidth(); - header.pixelHeight = texture.getHeight(); - header.numberOfMipmapLevels = texture.mipLevels(); + + // Set Dimensions + switch (texture.getType()) { + case TEX_1D: { + if (texture.isArray()) { + header.set1DArray(texture.getWidth(), texture.getNumSlices()); + } else { + header.set1D(texture.getWidth()); + } + break; + } + case TEX_2D: { + if (texture.isArray()) { + header.set2DArray(texture.getWidth(), texture.getHeight(), texture.getNumSlices()); + } else { + header.set2D(texture.getWidth(), texture.getHeight()); + } + break; + } + case TEX_3D: { + if (texture.isArray()) { + header.set3DArray(texture.getWidth(), texture.getHeight(), texture.getDepth(), texture.getNumSlices()); + } else { + header.set3D(texture.getWidth(), texture.getHeight(), texture.getDepth()); + } + break; + } + case TEX_CUBE: { + if (texture.isArray()) { + header.setCubeArray(texture.getWidth(), texture.getHeight(), texture.getNumSlices()); + } else { + header.setCube(texture.getWidth(), texture.getHeight()); + } + break; + } + default: + return nullptr; + } + + // Number level of mips coming + header.numberOfMipmapLevels = texture.maxMip(); ktx::Images images; for (uint32_t level = 0; level < header.numberOfMipmapLevels; level++) { @@ -34,17 +79,42 @@ ktx::KTXUniquePointer Texture::serialize(const Texture& texture) { auto ktxBuffer = ktx::KTX::create(header, images); return ktxBuffer; } + Texture* Texture::unserialize(const ktx::KTXUniquePointer& srcData) { if (!srcData) { return nullptr; } - const auto& header = *srcData->getHeader(); Format mipFormat = Format::COLOR_SBGRA_32; - Format pixelFormat = Format::COLOR_SRGBA_32; + Format texelFormat = Format::COLOR_SRGBA_32; - auto tex = Texture::create2D(pixelFormat, header.getPixelWidth(), header.getPixelHeight()); + // Find Texture Type based on dimensions + Type type = TEX_1D; + if (header.pixelWidth == 0) { + return nullptr; + } else if (header.pixelHeight == 0) { + type = TEX_1D; + } else if (header.pixelDepth == 0) { + if (header.numberOfFaces == ktx::NUM_CUBEMAPFACES) { + type = TEX_CUBE; + } else { + type = TEX_2D; + } + } else { + type = TEX_3D; + } + + auto tex = Texture::create( type, + texelFormat, + header.getPixelWidth(), + header.getPixelHeight(), + header.getPixelDepth(), + 1, // num Samples + header.getNumberOfSlices(), + Sampler()); + + // Assing the mips availables uint16_t level = 0; for (auto& image : srcData->_images) { tex->assignStoredMip(level, mipFormat, image._imageSize, image._bytes); diff --git a/libraries/ktx/src/ktx/KTX.h b/libraries/ktx/src/ktx/KTX.h index 3a4c19971a..8e79d90dc0 100644 --- a/libraries/ktx/src/ktx/KTX.h +++ b/libraries/ktx/src/ktx/KTX.h @@ -353,7 +353,7 @@ namespace ktx { uint32_t glInternalFormat; uint32_t glBaseInternalFormat; - uint32_t pixelWidth { 0 }; + uint32_t pixelWidth { 1 }; uint32_t pixelHeight { 0 }; uint32_t pixelDepth { 0 }; uint32_t numberOfArrayElements { 0 }; @@ -393,6 +393,23 @@ namespace ktx { glInternalFormat = (uint32_t) internalFormat; glBaseInternalFormat = (uint32_t) baseInternalFormat; } + + void setDimensions(uint32_t width, uint32_t height = 0, uint32_t depth = 0, uint32_t numSlices = 0, uint32_t numFaces = 1) { + pixelWidth = (width > 0 ? width : 1); + pixelHeight = height; + pixelDepth = depth; + numberOfArrayElements = numSlices; + numberOfFaces = ((numFaces == 1) || (numFaces == NUM_CUBEMAPFACES) ? numFaces : 1); + } + void set1D(uint32_t width) { setDimensions(width); } + void set1DArray(uint32_t width, uint32_t numSlices) { setDimensions(width, 0, 0, (numSlices > 0 ? numSlices : 1)); } + void set2D(uint32_t width, uint32_t height) { setDimensions(width, height); } + void set2DArray(uint32_t width, uint32_t height, uint32_t numSlices) { setDimensions(width, height, 0, (numSlices > 0 ? numSlices : 1)); } + void set3D(uint32_t width, uint32_t height, uint32_t depth) { setDimensions(width, height, depth); } + void set3DArray(uint32_t width, uint32_t height, uint32_t depth, uint32_t numSlices) { setDimensions(width, height, depth, (numSlices > 0 ? numSlices : 1)); } + void setCube(uint32_t width, uint32_t height) { setDimensions(width, height, 0, 0, NUM_CUBEMAPFACES); } + void setCubeArray(uint32_t width, uint32_t height, uint32_t numSlices) { setDimensions(width, height, 0, (numSlices > 0 ? numSlices : 1), NUM_CUBEMAPFACES); } + }; // Key Values diff --git a/libraries/model/src/model/TextureMap.cpp b/libraries/model/src/model/TextureMap.cpp index 0416c22d5e..8e27c4f4f0 100755 --- a/libraries/model/src/model/TextureMap.cpp +++ b/libraries/model/src/model/TextureMap.cpp @@ -85,6 +85,61 @@ QImage processSourceImage(const QImage& srcImage, bool cubemap) { return srcImage; } +gpu::Texture* cacheTexture(const std::string& name, gpu::Texture* srcTexture, bool write = true, bool read = true) { + if (!srcTexture) { + return nullptr; + } + gpu::Texture* returnedTexture = srcTexture; + + auto theKTX = Texture::serialize(*srcTexture); + if (theKTX) { + // Prepare cache directory + QString path("hifi_ktx/"); + QFileInfo originalFileInfo(path); + QString docsLocation = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation); + path = docsLocation + "/" + path; + QFileInfo info(path); + if (!info.absoluteDir().exists()) { + QString originalRelativePath = originalFileInfo.path(); + QDir(docsLocation).mkpath(originalRelativePath); + } + std::string filename(path.toStdString()); + filename += name; + filename += ".ktx"; + + if (write) { + FILE* file = fopen (filename.c_str(),"wb"); + if (file != nullptr) { + fwrite(theKTX->_storage->data(), 1, theKTX->_storage->size(), file); + fclose (file); + } + } + + if (read) { + FILE* file = fopen (filename.c_str(),"rb"); + if (file != nullptr) { + // obtain file size: + fseek (file , 0 , SEEK_END); + auto size = ftell(file); + rewind(file); + + std::unique_ptr storage(new ktx::Storage(size)); + fread(storage->_bytes, 1, storage->_size, file); + fclose (file); + + //then create a new texture out of the ktx + auto theNewTexure = Texture::unserialize(ktx::KTX::create(storage)); + + if (theNewTexure) { + returnedTexture = theNewTexure; + delete srcTexture; + } + } + } + } + return returnedTexture; +} + void TextureMap::setTextureSource(TextureSourcePointer& textureSource) { _textureSource = textureSource; } @@ -269,57 +324,10 @@ gpu::Texture* TextureUsage::process2DTextureColorFromImage(const QImage& srcImag if (generateMips) { ::generateMips(theTexture, image, formatMip, false); } - auto theKTX = Texture::serialize(*theTexture); - if (theKTX) { - // save that! - QString path("hifi_ktx/"); - QFileInfo originalFileInfo(path); - QString docsLocation = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation); - path = docsLocation + "/" + path; - QFileInfo info(path); - if (!info.absoluteDir().exists()) { - QString originalRelativePath = originalFileInfo.path(); - QDir(docsLocation).mkpath(originalRelativePath); - } - std::string filename(path.toStdString()); - filename += std::to_string((size_t) theTexture); - filename += ".ktx"; - - { - FILE* file = fopen (filename.c_str(),"wb"); - if (file != nullptr) { - fwrite(theKTX->_storage->data(), 1, theKTX->_storage->size(), file); - fclose (file); - } - } - - { - FILE* file = fopen (filename.c_str(),"rb"); - if (file != nullptr) { - // obtain file size: - fseek (file , 0 , SEEK_END); - auto size = ftell(file); - rewind(file); - - std::unique_ptr storage(new ktx::Storage(size)); - fread(storage->_bytes, 1, storage->_size, file); - fclose (file); - - //then create a new texture out of the ktx - auto theNewTexure = Texture::unserialize(ktx::KTX::create(storage)); - - if (theNewTexure) { - auto srcTexture = theTexture; - theTexture = theNewTexure; - delete srcTexture; - } - } - } - } + theTexture = cacheTexture(std::to_string((size_t) theTexture), theTexture); } - return theTexture; } @@ -360,6 +368,8 @@ gpu::Texture* TextureUsage::createNormalTextureFromNormalImage(const QImage& src theTexture->setSource(srcImageName); theTexture->assignStoredMip(0, formatMip, image.byteCount(), image.constBits()); generateMips(theTexture, image, formatMip, true); + + theTexture = cacheTexture(std::to_string((size_t) theTexture), theTexture); } return theTexture; @@ -446,6 +456,8 @@ gpu::Texture* TextureUsage::createNormalTextureFromBumpImage(const QImage& srcIm theTexture->setSource(srcImageName); theTexture->assignStoredMip(0, formatMip, image.byteCount(), image.constBits()); generateMips(theTexture, image, formatMip, true); + + theTexture = cacheTexture(std::to_string((size_t) theTexture), theTexture); } return theTexture; @@ -479,8 +491,8 @@ gpu::Texture* TextureUsage::createRoughnessTextureFromImage(const QImage& srcIma theTexture->setSource(srcImageName); theTexture->assignStoredMip(0, formatMip, image.byteCount(), image.constBits()); generateMips(theTexture, image, formatMip, true); - - // FIXME queue for transfer to GPU and block on completion + + theTexture = cacheTexture(std::to_string((size_t) theTexture), theTexture); } return theTexture; @@ -518,8 +530,8 @@ gpu::Texture* TextureUsage::createRoughnessTextureFromGlossImage(const QImage& s theTexture->setSource(srcImageName); theTexture->assignStoredMip(0, formatMip, image.byteCount(), image.constBits()); generateMips(theTexture, image, formatMip, true); - - // FIXME queue for transfer to GPU and block on completion + + theTexture = cacheTexture(std::to_string((size_t) theTexture), theTexture); } return theTexture; @@ -555,7 +567,7 @@ gpu::Texture* TextureUsage::createMetallicTextureFromImage(const QImage& srcImag theTexture->assignStoredMip(0, formatMip, image.byteCount(), image.constBits()); generateMips(theTexture, image, formatMip, true); - // FIXME queue for transfer to GPU and block on completion + theTexture = cacheTexture(std::to_string((size_t) theTexture), theTexture); } return theTexture; @@ -886,6 +898,8 @@ gpu::Texture* TextureUsage::processCubeTextureColorFromImage(const QImage& srcIm PROFILE_RANGE(resource_parse, "generateIrradiance"); theTexture->generateIrradiance(); } + + theTexture = cacheTexture(std::to_string((size_t) theTexture), theTexture); } } From 7fb7aa87eb8c64b11a99eb864541743f7c797630 Mon Sep 17 00:00:00 2001 From: Bradley Austin Davis Date: Tue, 24 Jan 2017 15:37:18 -0800 Subject: [PATCH 013/302] Working on new texture management strategy --- interface/src/ui/ApplicationOverlay.cpp | 6 +- interface/src/ui/overlays/Web3DOverlay.cpp | 2 +- .../display-plugins/OpenGLDisplayPlugin.cpp | 3 +- .../display-plugins/hmd/HmdDisplayPlugin.cpp | 32 +- .../src/RenderableWebEntityItem.cpp | 2 +- libraries/gpu-gl/src/gpu/gl/GLBackend.cpp | 7 +- libraries/gpu-gl/src/gpu/gl/GLBackend.h | 13 +- .../gpu-gl/src/gpu/gl/GLBackendTexture.cpp | 54 +- libraries/gpu-gl/src/gpu/gl/GLFramebuffer.cpp | 9 +- libraries/gpu-gl/src/gpu/gl/GLFramebuffer.h | 2 +- libraries/gpu-gl/src/gpu/gl/GLTexture.cpp | 233 +---- libraries/gpu-gl/src/gpu/gl/GLTexture.h | 207 +--- .../gpu-gl/src/gpu/gl/GLTextureTransfer.cpp | 208 ---- .../gpu-gl/src/gpu/gl/GLTextureTransfer.h | 78 -- libraries/gpu-gl/src/gpu/gl41/GL41Backend.h | 33 +- .../gpu-gl/src/gpu/gl41/GL41BackendOutput.cpp | 10 +- .../src/gpu/gl41/GL41BackendTexture.cpp | 187 ++-- libraries/gpu-gl/src/gpu/gl45/GL45Backend.cpp | 5 - libraries/gpu-gl/src/gpu/gl45/GL45Backend.h | 177 +++- .../gpu-gl/src/gpu/gl45/GL45BackendOutput.cpp | 10 +- .../src/gpu/gl45/GL45BackendTexture.cpp | 545 +++-------- .../gpu/gl45/GL45BackendVariableTexture.cpp | 888 ++++++++++++++++++ libraries/gpu/src/gpu/Batch.cpp | 7 - libraries/gpu/src/gpu/Framebuffer.cpp | 12 +- libraries/gpu/src/gpu/Texture.cpp | 35 +- libraries/gpu/src/gpu/Texture.h | 23 +- .../src/model-networking/TextureCache.cpp | 15 +- .../src/model-networking/TextureCache.h | 1 + libraries/model/src/model/TextureMap.cpp | 13 +- libraries/model/src/model/TextureMap.h | 3 +- .../render-utils/src/AntialiasingEffect.cpp | 2 +- .../render-utils/src/DeferredFramebuffer.cpp | 10 +- .../src/DeferredLightingEffect.cpp | 4 +- .../render-utils/src/RenderDeferredTask.cpp | 2 +- .../render-utils/src/SubsurfaceScattering.cpp | 6 +- .../render-utils/src/SurfaceGeometryPass.cpp | 12 +- .../src/OculusLegacyDisplayPlugin.cpp | 2 +- plugins/openvr/src/OpenVrDisplayPlugin.cpp | 8 +- tests/render-perf/src/main.cpp | 1 - tests/render-texture-load/src/main.cpp | 1 + 40 files changed, 1508 insertions(+), 1360 deletions(-) delete mode 100644 libraries/gpu-gl/src/gpu/gl/GLTextureTransfer.cpp delete mode 100644 libraries/gpu-gl/src/gpu/gl/GLTextureTransfer.h create mode 100644 libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp diff --git a/interface/src/ui/ApplicationOverlay.cpp b/interface/src/ui/ApplicationOverlay.cpp index 364dff52a3..730e21139f 100644 --- a/interface/src/ui/ApplicationOverlay.cpp +++ b/interface/src/ui/ApplicationOverlay.cpp @@ -99,7 +99,7 @@ void ApplicationOverlay::renderQmlUi(RenderArgs* renderArgs) { PROFILE_RANGE(app, __FUNCTION__); if (!_uiTexture) { - _uiTexture = gpu::TexturePointer(gpu::Texture::createExternal2D(OffscreenQmlSurface::getDiscardLambda())); + _uiTexture = gpu::TexturePointer(gpu::Texture::createExternal(OffscreenQmlSurface::getDiscardLambda())); _uiTexture->setSource(__FUNCTION__); } // Once we move UI rendering and screen rendering to different @@ -272,13 +272,13 @@ void ApplicationOverlay::buildFramebufferObject() { auto width = uiSize.x; auto height = uiSize.y; if (!_overlayFramebuffer->getDepthStencilBuffer()) { - auto overlayDepthTexture = gpu::TexturePointer(gpu::Texture::create2D(DEPTH_FORMAT, width, height, DEFAULT_SAMPLER)); + auto overlayDepthTexture = gpu::TexturePointer(gpu::Texture::createRenderBuffer(DEPTH_FORMAT, width, height, DEFAULT_SAMPLER)); _overlayFramebuffer->setDepthStencilBuffer(overlayDepthTexture, DEPTH_FORMAT); } if (!_overlayFramebuffer->getRenderBuffer(0)) { const gpu::Sampler OVERLAY_SAMPLER(gpu::Sampler::FILTER_MIN_MAG_LINEAR, gpu::Sampler::WRAP_CLAMP); - auto colorBuffer = gpu::TexturePointer(gpu::Texture::create2D(COLOR_FORMAT, width, height, OVERLAY_SAMPLER)); + auto colorBuffer = gpu::TexturePointer(gpu::Texture::createRenderBuffer(COLOR_FORMAT, width, height, OVERLAY_SAMPLER)); _overlayFramebuffer->setRenderBuffer(0, colorBuffer); } } diff --git a/interface/src/ui/overlays/Web3DOverlay.cpp b/interface/src/ui/overlays/Web3DOverlay.cpp index cb649e8766..f67acb0304 100644 --- a/interface/src/ui/overlays/Web3DOverlay.cpp +++ b/interface/src/ui/overlays/Web3DOverlay.cpp @@ -251,7 +251,7 @@ void Web3DOverlay::render(RenderArgs* args) { if (!_texture) { auto webSurface = _webSurface; - _texture = gpu::TexturePointer(gpu::Texture::createExternal2D(OffscreenQmlSurface::getDiscardLambda())); + _texture = gpu::TexturePointer(gpu::Texture::createExternal(OffscreenQmlSurface::getDiscardLambda())); _texture->setSource(__FUNCTION__); } OffscreenQmlSurface::TextureAndFence newTextureAndFence; diff --git a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp index cf6b39812a..1bfe4f3dcc 100644 --- a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp @@ -355,7 +355,7 @@ void OpenGLDisplayPlugin::customizeContext() { if ((image.width() > 0) && (image.height() > 0)) { cursorData.texture.reset( - gpu::Texture::create2D( + gpu::Texture::createStrict( gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA), image.width(), image.height(), gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR))); @@ -363,6 +363,7 @@ void OpenGLDisplayPlugin::customizeContext() { auto usage = gpu::Texture::Usage::Builder().withColor().withAlpha(); cursorData.texture->setUsage(usage.build()); cursorData.texture->assignStoredMip(0, gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA), image.byteCount(), image.constBits()); + cursorData.texture->autoGenerateMips(-1); } } } diff --git a/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.cpp index a8b8ba3618..24f4e429ef 100644 --- a/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.cpp @@ -296,7 +296,7 @@ void HmdDisplayPlugin::internalPresent() { image = image.convertToFormat(QImage::Format_RGBA8888); if (!_previewTexture) { _previewTexture.reset( - gpu::Texture::create2D( + gpu::Texture::createStrict( gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA), image.width(), image.height(), gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR))); @@ -306,23 +306,21 @@ void HmdDisplayPlugin::internalPresent() { _previewTexture->autoGenerateMips(-1); } - if (getGLBackend()->isTextureReady(_previewTexture)) { - auto viewport = getViewportForSourceSize(uvec2(_previewTexture->getDimensions())); + auto viewport = getViewportForSourceSize(uvec2(_previewTexture->getDimensions())); - render([&](gpu::Batch& batch) { - batch.enableStereo(false); - batch.resetViewTransform(); - batch.setFramebuffer(gpu::FramebufferPointer()); - batch.clearColorFramebuffer(gpu::Framebuffer::BUFFER_COLOR0, vec4(0)); - batch.setStateScissorRect(viewport); - batch.setViewportTransform(viewport); - batch.setResourceTexture(0, _previewTexture); - batch.setPipeline(_presentPipeline); - batch.draw(gpu::TRIANGLE_STRIP, 4); - }); - _clearPreviewFlag = false; - swapBuffers(); - } + render([&](gpu::Batch& batch) { + batch.enableStereo(false); + batch.resetViewTransform(); + batch.setFramebuffer(gpu::FramebufferPointer()); + batch.clearColorFramebuffer(gpu::Framebuffer::BUFFER_COLOR0, vec4(0)); + batch.setStateScissorRect(viewport); + batch.setViewportTransform(viewport); + batch.setResourceTexture(0, _previewTexture); + batch.setPipeline(_presentPipeline); + batch.draw(gpu::TRIANGLE_STRIP, 4); + }); + _clearPreviewFlag = false; + swapBuffers(); } postPreview(); diff --git a/libraries/entities-renderer/src/RenderableWebEntityItem.cpp b/libraries/entities-renderer/src/RenderableWebEntityItem.cpp index 972c23d534..b3978b9356 100644 --- a/libraries/entities-renderer/src/RenderableWebEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableWebEntityItem.cpp @@ -214,7 +214,7 @@ void RenderableWebEntityItem::render(RenderArgs* args) { if (!_texture) { auto webSurface = _webSurface; - _texture = gpu::TexturePointer(gpu::Texture::createExternal2D(OffscreenQmlSurface::getDiscardLambda())); + _texture = gpu::TexturePointer(gpu::Texture::createExternal(OffscreenQmlSurface::getDiscardLambda())); _texture->setSource(__FUNCTION__); } OffscreenQmlSurface::TextureAndFence newTextureAndFence; diff --git a/libraries/gpu-gl/src/gpu/gl/GLBackend.cpp b/libraries/gpu-gl/src/gpu/gl/GLBackend.cpp index c51f468908..76cc64f3e3 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLBackend.cpp +++ b/libraries/gpu-gl/src/gpu/gl/GLBackend.cpp @@ -62,8 +62,6 @@ BackendPointer GLBackend::createBackend() { INSTANCE = result.get(); void* voidInstance = &(*result); qApp->setProperty(hifi::properties::gl::BACKEND, QVariant::fromValue(voidInstance)); - - gl::GLTexture::initTextureTransferHelper(); return result; } @@ -623,6 +621,7 @@ void GLBackend::queueLambda(const std::function lambda) const { } void GLBackend::recycle() const { + PROFILE_RANGE(render_gpu_gl, __FUNCTION__) { std::list> lamdbasTrash; { @@ -745,10 +744,6 @@ void GLBackend::recycle() const { glDeleteQueries((GLsizei)ids.size(), ids.data()); } } - -#ifndef THREADED_TEXTURE_TRANSFER - gl::GLTexture::_textureTransferHelper->process(); -#endif } void GLBackend::setCameraCorrection(const Mat4& correction) { diff --git a/libraries/gpu-gl/src/gpu/gl/GLBackend.h b/libraries/gpu-gl/src/gpu/gl/GLBackend.h index 950ac65a3f..76c950ec2b 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLBackend.h +++ b/libraries/gpu-gl/src/gpu/gl/GLBackend.h @@ -187,10 +187,15 @@ public: virtual void do_setStateScissorRect(const Batch& batch, size_t paramOffset) final; virtual GLuint getFramebufferID(const FramebufferPointer& framebuffer) = 0; - virtual GLuint getTextureID(const TexturePointer& texture, bool needTransfer = true) = 0; + virtual GLuint getTextureID(const TexturePointer& texture) final; virtual GLuint getBufferID(const Buffer& buffer) = 0; virtual GLuint getQueryID(const QueryPointer& query) = 0; - virtual bool isTextureReady(const TexturePointer& texture); + + virtual GLFramebuffer* syncGPUObject(const Framebuffer& framebuffer) = 0; + virtual GLBuffer* syncGPUObject(const Buffer& buffer) = 0; + virtual GLTexture* syncGPUObject(const TexturePointer& texture); + virtual GLQuery* syncGPUObject(const Query& query) = 0; + //virtual bool isTextureReady(const TexturePointer& texture); virtual void releaseBuffer(GLuint id, Size size) const; virtual void releaseExternalTexture(GLuint id, const Texture::ExternalRecycler& recycler) const; @@ -206,10 +211,6 @@ public: protected: void recycle() const override; - virtual GLFramebuffer* syncGPUObject(const Framebuffer& framebuffer) = 0; - virtual GLBuffer* syncGPUObject(const Buffer& buffer) = 0; - virtual GLTexture* syncGPUObject(const TexturePointer& texture, bool sync = true) = 0; - virtual GLQuery* syncGPUObject(const Query& query) = 0; static const size_t INVALID_OFFSET = (size_t)-1; bool _inRenderTransferPass { false }; diff --git a/libraries/gpu-gl/src/gpu/gl/GLBackendTexture.cpp b/libraries/gpu-gl/src/gpu/gl/GLBackendTexture.cpp index f51eac0e33..ca4e328612 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLBackendTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl/GLBackendTexture.cpp @@ -14,12 +14,56 @@ using namespace gpu; using namespace gpu::gl; -bool GLBackend::isTextureReady(const TexturePointer& texture) { - // DO not transfer the texture, this call is expected for rendering texture - GLTexture* object = syncGPUObject(texture, true); - return object && object->isReady(); + +GLuint GLBackend::getTextureID(const TexturePointer& texture) { + GLTexture* object = syncGPUObject(texture); + + if (!object) { + return 0; + } + + return object->_id; } +GLTexture* GLBackend::syncGPUObject(const TexturePointer& texturePointer) { + const Texture& texture = *texturePointer; + // Special case external textures + if (TextureUsageType::EXTERNAL == texture.getUsageType()) { + Texture::ExternalUpdates updates = texture.getUpdates(); + if (!updates.empty()) { + Texture::ExternalRecycler recycler = texture.getExternalRecycler(); + Q_ASSERT(recycler); + // Discard any superfluous updates + while (updates.size() > 1) { + const auto& update = updates.front(); + // Superfluous updates will never have been read, but we want to ensure the previous + // writes to them are complete before they're written again, so return them with the + // same fences they arrived with. This can happen on any thread because no GL context + // work is involved + recycler(update.first, update.second); + updates.pop_front(); + } + + // The last texture remaining is the one we'll use to create the GLTexture + const auto& update = updates.front(); + // Check for a fence, and if it exists, inject a wait into the command stream, then destroy the fence + if (update.second) { + GLsync fence = static_cast(update.second); + glWaitSync(fence, 0, GL_TIMEOUT_IGNORED); + glDeleteSync(fence); + } + + // Create the new texture object (replaces any previous texture object) + new GLExternalTexture(shared_from_this(), texture, update.first); + } + + // Return the texture object (if any) associated with the texture, without extensive logic + // (external textures are + return Backend::getGPUObject(texture); + } + + return nullptr; +} void GLBackend::do_generateTextureMips(const Batch& batch, size_t paramOffset) { TexturePointer resourceTexture = batch._textures.get(batch._params[paramOffset + 0]._uint); @@ -28,7 +72,7 @@ void GLBackend::do_generateTextureMips(const Batch& batch, size_t paramOffset) { } // DO not transfer the texture, this call is expected for rendering texture - GLTexture* object = syncGPUObject(resourceTexture, false); + GLTexture* object = syncGPUObject(resourceTexture); if (!object) { return; } diff --git a/libraries/gpu-gl/src/gpu/gl/GLFramebuffer.cpp b/libraries/gpu-gl/src/gpu/gl/GLFramebuffer.cpp index 85cf069062..2ac7e9d060 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLFramebuffer.cpp +++ b/libraries/gpu-gl/src/gpu/gl/GLFramebuffer.cpp @@ -21,13 +21,12 @@ GLFramebuffer::~GLFramebuffer() { } } -bool GLFramebuffer::checkStatus(GLenum target) const { - bool result = false; +bool GLFramebuffer::checkStatus() const { switch (_status) { case GL_FRAMEBUFFER_COMPLETE: // Success ! - result = true; - break; + return true; + case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT: qCWarning(gpugllogging) << "GLFramebuffer::syncGPUObject : Framebuffer not valid, GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT."; break; @@ -44,5 +43,5 @@ bool GLFramebuffer::checkStatus(GLenum target) const { qCWarning(gpugllogging) << "GLFramebuffer::syncGPUObject : Framebuffer not valid, GL_FRAMEBUFFER_UNSUPPORTED."; break; } - return result; + return false; } diff --git a/libraries/gpu-gl/src/gpu/gl/GLFramebuffer.h b/libraries/gpu-gl/src/gpu/gl/GLFramebuffer.h index 9b4f9703fc..c0633cfdef 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLFramebuffer.h +++ b/libraries/gpu-gl/src/gpu/gl/GLFramebuffer.h @@ -64,7 +64,7 @@ public: protected: GLenum _status { GL_FRAMEBUFFER_COMPLETE }; virtual void update() = 0; - bool checkStatus(GLenum target) const; + bool checkStatus() const; GLFramebuffer(const std::weak_ptr& backend, const Framebuffer& framebuffer, GLuint id) : GLObject(backend, framebuffer, id) {} ~GLFramebuffer(); diff --git a/libraries/gpu-gl/src/gpu/gl/GLTexture.cpp b/libraries/gpu-gl/src/gpu/gl/GLTexture.cpp index 1e0dd08ae1..1de820e1df 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl/GLTexture.cpp @@ -10,15 +10,13 @@ #include -#include "GLTextureTransfer.h" #include "GLBackend.h" using namespace gpu; using namespace gpu::gl; -std::shared_ptr GLTexture::_textureTransferHelper; -const GLenum GLTexture::CUBE_FACE_LAYOUT[6] = { +const GLenum GLTexture::CUBE_FACE_LAYOUT[GLTexture::TEXTURE_CUBE_NUM_FACES] = { GL_TEXTURE_CUBE_MAP_POSITIVE_X, GL_TEXTURE_CUBE_MAP_NEGATIVE_X, GL_TEXTURE_CUBE_MAP_POSITIVE_Y, GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, GL_TEXTURE_CUBE_MAP_POSITIVE_Z, GL_TEXTURE_CUBE_MAP_NEGATIVE_Z @@ -67,6 +65,17 @@ GLenum GLTexture::getGLTextureType(const Texture& texture) { } +uint8_t GLTexture::getFaceCount(GLenum target) { + switch (target) { + case GL_TEXTURE_2D: + return TEXTURE_2D_NUM_FACES; + case GL_TEXTURE_CUBE_MAP: + return TEXTURE_CUBE_NUM_FACES; + default: + Q_UNREACHABLE(); + break; + } +} const std::vector& GLTexture::getFaceTargets(GLenum target) { static std::vector cubeFaceTargets { GL_TEXTURE_CUBE_MAP_POSITIVE_X, GL_TEXTURE_CUBE_MAP_NEGATIVE_X, @@ -89,216 +98,34 @@ const std::vector& GLTexture::getFaceTargets(GLenum target) { return faceTargets; } -// Default texture memory = GPU total memory - 2GB -#define GPU_MEMORY_RESERVE_BYTES MB_TO_BYTES(2048) -// Minimum texture memory = 1GB -#define TEXTURE_MEMORY_MIN_BYTES MB_TO_BYTES(1024) - - -float GLTexture::getMemoryPressure() { - // Check for an explicit memory limit - auto availableTextureMemory = Texture::getAllowedGPUMemoryUsage(); - - - // If no memory limit has been set, use a percentage of the total dedicated memory - if (!availableTextureMemory) { -#if 0 - auto totalMemory = getDedicatedMemory(); - if ((GPU_MEMORY_RESERVE_BYTES + TEXTURE_MEMORY_MIN_BYTES) > totalMemory) { - availableTextureMemory = TEXTURE_MEMORY_MIN_BYTES; - } else { - availableTextureMemory = totalMemory - GPU_MEMORY_RESERVE_BYTES; - } -#else - // Hardcode texture limit for sparse textures at 1 GB for now - availableTextureMemory = TEXTURE_MEMORY_MIN_BYTES; -#endif - } - - // Return the consumed texture memory divided by the available texture memory. - auto consumedGpuMemory = Context::getTextureGPUMemoryUsage() - Context::getTextureGPUFramebufferMemoryUsage(); - float memoryPressure = (float)consumedGpuMemory / (float)availableTextureMemory; - static Context::Size lastConsumedGpuMemory = 0; - if (memoryPressure > 1.0f && lastConsumedGpuMemory != consumedGpuMemory) { - lastConsumedGpuMemory = consumedGpuMemory; - qCDebug(gpugllogging) << "Exceeded max allowed texture memory: " << consumedGpuMemory << " / " << availableTextureMemory; - } - return memoryPressure; -} - - -// Create the texture and allocate storage -GLTexture::GLTexture(const std::weak_ptr& backend, const Texture& texture, GLuint id, bool transferrable) : - GLObject(backend, texture, id), - _external(false), - _source(texture.source()), - _storageStamp(texture.getStamp()), - _target(getGLTextureType(texture)), - _internalFormat(gl::GLTexelFormat::evalGLTexelFormatInternal(texture.getTexelFormat())), - _maxMip(texture.maxMip()), - _minMip(texture.minMip()), - _virtualSize(texture.evalTotalSize()), - _transferrable(transferrable) -{ - auto strongBackend = _backend.lock(); - strongBackend->recycle(); - Backend::incrementTextureGPUCount(); - Backend::updateTextureGPUVirtualMemoryUsage(0, _virtualSize); - Backend::setGPUObject(texture, this); -} - GLTexture::GLTexture(const std::weak_ptr& backend, const Texture& texture, GLuint id) : GLObject(backend, texture, id), - _external(true), _source(texture.source()), - _storageStamp(0), - _target(getGLTextureType(texture)), - _internalFormat(GL_RGBA8), - // FIXME force mips to 0? - _maxMip(texture.maxMip()), - _minMip(texture.minMip()), - _virtualSize(0), - _transferrable(false) + _target(getGLTextureType(texture)) { Backend::setGPUObject(texture, this); - - // FIXME Is this necessary? - //withPreservedTexture([this] { - // syncSampler(); - // if (_gpuObject.isAutogenerateMips()) { - // generateMips(); - // } - //}); } GLTexture::~GLTexture() { + auto backend = _backend.lock(); + if (backend && _id) { + backend->releaseTexture(_id, 0); + } +} + + +GLExternalTexture::GLExternalTexture(const std::weak_ptr& backend, const Texture& texture, GLuint id) + : Parent(backend, texture, id) { } + +GLExternalTexture::~GLExternalTexture() { auto backend = _backend.lock(); if (backend) { - if (_external) { - auto recycler = _gpuObject.getExternalRecycler(); - if (recycler) { - backend->releaseExternalTexture(_id, recycler); - } else { - qWarning() << "No recycler available for texture " << _id << " possible leak"; - } - } else if (_id) { - // WARNING! Sparse textures do not use this code path. See GL45BackendTexture for - // the GL45Texture destructor for doing any required work tracking GPU stats - backend->releaseTexture(_id, _size); + auto recycler = _gpuObject.getExternalRecycler(); + if (recycler) { + backend->releaseExternalTexture(_id, recycler); + } else { + qWarning() << "No recycler available for texture " << _id << " possible leak"; } - - if (!_external && !_transferrable) { - Backend::updateTextureGPUFramebufferMemoryUsage(_size, 0); - } - } - Backend::updateTextureGPUVirtualMemoryUsage(_virtualSize, 0); -} - -void GLTexture::createTexture() { - withPreservedTexture([&] { - allocateStorage(); - (void)CHECK_GL_ERROR(); - syncSampler(); - (void)CHECK_GL_ERROR(); - }); -} - -void GLTexture::withPreservedTexture(std::function f) const { - GLint boundTex = -1; - switch (_target) { - case GL_TEXTURE_2D: - glGetIntegerv(GL_TEXTURE_BINDING_2D, &boundTex); - break; - - case GL_TEXTURE_CUBE_MAP: - glGetIntegerv(GL_TEXTURE_BINDING_CUBE_MAP, &boundTex); - break; - - default: - qFatal("Unsupported texture type"); - } - (void)CHECK_GL_ERROR(); - - glBindTexture(_target, _texture); - f(); - glBindTexture(_target, boundTex); - (void)CHECK_GL_ERROR(); -} - -void GLTexture::setSize(GLuint size) const { - if (!_external && !_transferrable) { - Backend::updateTextureGPUFramebufferMemoryUsage(_size, size); - } - Backend::updateTextureGPUMemoryUsage(_size, size); - const_cast(_size) = size; -} - -bool GLTexture::isInvalid() const { - return _storageStamp < _gpuObject.getStamp(); -} - -bool GLTexture::isOutdated() const { - return GLSyncState::Idle == _syncState && _contentStamp < _gpuObject.getDataStamp(); -} - -bool GLTexture::isReady() const { - // If we have an invalid texture, we're never ready - if (isInvalid()) { - return false; - } - - auto syncState = _syncState.load(); - if (isOutdated() || Idle != syncState) { - return false; - } - - return true; -} - - -// Do any post-transfer operations that might be required on the main context / rendering thread -void GLTexture::postTransfer() { - setSyncState(GLSyncState::Idle); - ++_transferCount; - - // At this point the mip pixels have been loaded, we can notify the gpu texture to abandon it's memory - switch (_gpuObject.getType()) { - case Texture::TEX_2D: - for (uint16_t i = 0; i < Sampler::MAX_MIP_LEVEL; ++i) { - if (_gpuObject.isStoredMipFaceAvailable(i)) { - _gpuObject.notifyMipFaceGPULoaded(i); - } - } - break; - - case Texture::TEX_CUBE: - // transfer pixels from each faces - for (uint8_t f = 0; f < CUBE_NUM_FACES; f++) { - for (uint16_t i = 0; i < Sampler::MAX_MIP_LEVEL; ++i) { - if (_gpuObject.isStoredMipFaceAvailable(i, f)) { - _gpuObject.notifyMipFaceGPULoaded(i, f); - } - } - } - break; - - default: - qCWarning(gpugllogging) << __FUNCTION__ << " case for Texture Type " << _gpuObject.getType() << " not supported"; - break; + const_cast(_id) = 0; } } - -void GLTexture::initTextureTransferHelper() { - _textureTransferHelper = std::make_shared(); -} - -void GLTexture::startTransfer() { - createTexture(); -} - -void GLTexture::finishTransfer() { - if (_gpuObject.isAutogenerateMips()) { - generateMips(); - } -} - diff --git a/libraries/gpu-gl/src/gpu/gl/GLTexture.h b/libraries/gpu-gl/src/gpu/gl/GLTexture.h index 0f75a6fe51..1f91e17157 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLTexture.h +++ b/libraries/gpu-gl/src/gpu/gl/GLTexture.h @@ -9,7 +9,6 @@ #define hifi_gpu_gl_GLTexture_h #include "GLShared.h" -#include "GLTextureTransfer.h" #include "GLBackend.h" #include "GLTexelFormat.h" @@ -20,210 +19,48 @@ struct GLFilterMode { GLint magFilter; }; - class GLTexture : public GLObject { + using Parent = GLObject; + friend class GLBackend; public: static const uint16_t INVALID_MIP { (uint16_t)-1 }; static const uint8_t INVALID_FACE { (uint8_t)-1 }; - static void initTextureTransferHelper(); - static std::shared_ptr _textureTransferHelper; - - template - static GLTexture* sync(GLBackend& backend, const TexturePointer& texturePointer, bool needTransfer) { - const Texture& texture = *texturePointer; - - // Special case external textures - if (texture.getUsage().isExternal()) { - Texture::ExternalUpdates updates = texture.getUpdates(); - if (!updates.empty()) { - Texture::ExternalRecycler recycler = texture.getExternalRecycler(); - Q_ASSERT(recycler); - // Discard any superfluous updates - while (updates.size() > 1) { - const auto& update = updates.front(); - // Superfluous updates will never have been read, but we want to ensure the previous - // writes to them are complete before they're written again, so return them with the - // same fences they arrived with. This can happen on any thread because no GL context - // work is involved - recycler(update.first, update.second); - updates.pop_front(); - } - - // The last texture remaining is the one we'll use to create the GLTexture - const auto& update = updates.front(); - // Check for a fence, and if it exists, inject a wait into the command stream, then destroy the fence - if (update.second) { - GLsync fence = static_cast(update.second); - glWaitSync(fence, 0, GL_TIMEOUT_IGNORED); - glDeleteSync(fence); - } - - // Create the new texture object (replaces any previous texture object) - new GLTextureType(backend.shared_from_this(), texture, update.first); - } - - // Return the texture object (if any) associated with the texture, without extensive logic - // (external textures are - return Backend::getGPUObject(texture); - } - - if (!texture.isDefined()) { - // NO texture definition yet so let's avoid thinking - return nullptr; - } - - // If the object hasn't been created, or the object definition is out of date, drop and re-create - GLTexture* object = Backend::getGPUObject(texture); - - // Create the texture if need be (force re-creation if the storage stamp changes - // for easier use of immutable storage) - if (!object || object->isInvalid()) { - // This automatically any previous texture - object = new GLTextureType(backend.shared_from_this(), texture, needTransfer); - if (!object->_transferrable) { - object->createTexture(); - object->_contentStamp = texture.getDataStamp(); - object->updateSize(); - object->postTransfer(); - } - } - - // Object maybe doens't neet to be tranasferred after creation - if (!object->_transferrable) { - return object; - } - - // If we just did a transfer, return the object after doing post-transfer work - if (GLSyncState::Transferred == object->getSyncState()) { - object->postTransfer(); - } - - if (object->isOutdated()) { - // Object might be outdated, if so, start the transfer - // (outdated objects that are already in transfer will have reported 'true' for ready() - _textureTransferHelper->transferTexture(texturePointer); - return nullptr; - } - - if (!object->isReady()) { - return nullptr; - } - - ((GLTexture*)object)->updateMips(); - - return object; - } - - template - static GLuint getId(GLBackend& backend, const TexturePointer& texture, bool shouldSync) { - if (!texture) { - return 0; - } - GLTexture* object { nullptr }; - if (shouldSync) { - object = sync(backend, texture, shouldSync); - } else { - object = Backend::getGPUObject(*texture); - } - - if (!object) { - return 0; - } - - if (!shouldSync) { - return object->_id; - } - - // Don't return textures that are in transfer state - if ((object->getSyncState() != GLSyncState::Idle) || - // Don't return transferrable textures that have never completed transfer - (!object->_transferrable || 0 != object->_transferCount)) { - return 0; - } - - return object->_id; - } - ~GLTexture(); - // Is this texture generated outside the GPU library? - const bool _external; const GLuint& _texture { _id }; const std::string _source; - const Stamp _storageStamp; const GLenum _target; - const GLenum _internalFormat; - const uint16 _maxMip; - uint16 _minMip; - const GLuint _virtualSize; // theoretical size as expected - Stamp _contentStamp { 0 }; - const bool _transferrable; - Size _transferCount { 0 }; - GLuint size() const { return _size; } - GLSyncState getSyncState() const { return _syncState; } - // Is the storage out of date relative to the gpu texture? - bool isInvalid() const; + static const std::vector& getFaceTargets(GLenum textureType); + static uint8_t getFaceCount(GLenum textureType); + static GLenum getGLTextureType(const Texture& texture); - // Is the content out of date relative to the gpu texture? - bool isOutdated() const; - - // Is the texture in a state where it can be rendered with no work? - bool isReady() const; - - // Execute any post-move operations that must occur only on the main thread - virtual void postTransfer(); - - uint16 usedMipLevels() const { return (_maxMip - _minMip) + 1; } - - static const size_t CUBE_NUM_FACES = 6; - static const GLenum CUBE_FACE_LAYOUT[6]; + static const uint8_t TEXTURE_2D_NUM_FACES = 1; + static const uint8_t TEXTURE_CUBE_NUM_FACES = 6; + static const GLenum CUBE_FACE_LAYOUT[TEXTURE_CUBE_NUM_FACES]; static const GLFilterMode FILTER_MODES[Sampler::NUM_FILTERS]; static const GLenum WRAP_MODES[Sampler::NUM_WRAP_MODES]; - // Return a floating point value indicating how much of the allowed - // texture memory we are currently consuming. A value of 0 indicates - // no texture memory usage, while a value of 1 indicates all available / allowed memory - // is consumed. A value above 1 indicates that there is a problem. - static float getMemoryPressure(); protected: - - static const std::vector& getFaceTargets(GLenum textureType); - - static GLenum getGLTextureType(const Texture& texture); - - - const GLuint _size { 0 }; // true size as reported by the gl api - std::atomic _syncState { GLSyncState::Idle }; - - GLTexture(const std::weak_ptr& backend, const Texture& texture, GLuint id, bool transferrable); - GLTexture(const std::weak_ptr& backend, const Texture& texture, GLuint id); - - void setSyncState(GLSyncState syncState) { _syncState = syncState; } - - void createTexture(); - - virtual void updateMips() {} - virtual void allocateStorage() const = 0; - virtual void updateSize() const = 0; - virtual void syncSampler() const = 0; + virtual uint32 size() const = 0; virtual void generateMips() const = 0; - virtual void withPreservedTexture(std::function f) const; -protected: - void setSize(GLuint size) const; - - virtual void startTransfer(); - // Returns true if this is the last block required to complete transfer - virtual bool continueTransfer() { return false; } - virtual void finishTransfer(); - -private: - friend class GLTextureTransferHelper; - friend class GLBackend; + GLTexture(const std::weak_ptr& backend, const Texture& texture, GLuint id); }; +class GLExternalTexture : public GLTexture { + using Parent = GLTexture; + friend class GLBackend; +public: + ~GLExternalTexture(); +protected: + GLExternalTexture(const std::weak_ptr& backend, const Texture& texture, GLuint id); + void generateMips() const override {} + uint32 size() const override { return 0; } +}; + + } } #endif diff --git a/libraries/gpu-gl/src/gpu/gl/GLTextureTransfer.cpp b/libraries/gpu-gl/src/gpu/gl/GLTextureTransfer.cpp deleted file mode 100644 index 9dac2986e3..0000000000 --- a/libraries/gpu-gl/src/gpu/gl/GLTextureTransfer.cpp +++ /dev/null @@ -1,208 +0,0 @@ -// -// Created by Bradley Austin Davis on 2016/04/03 -// Copyright 2013-2016 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 "GLTextureTransfer.h" - -#include -#include - -#include - -#include "GLShared.h" -#include "GLTexture.h" - -#ifdef HAVE_NSIGHT -#include "nvToolsExt.h" -std::unordered_map _map; -#endif - - -#ifdef TEXTURE_TRANSFER_PBOS -#define TEXTURE_TRANSFER_BLOCK_SIZE (64 * 1024) -#define TEXTURE_TRANSFER_PBO_COUNT 128 -#endif - -using namespace gpu; -using namespace gpu::gl; - -GLTextureTransferHelper::GLTextureTransferHelper() { -#ifdef THREADED_TEXTURE_TRANSFER - setObjectName("TextureTransferThread"); - _context.create(); - initialize(true, QThread::LowPriority); - // Clean shutdown on UNIX, otherwise _canvas is freed early - connect(qApp, &QCoreApplication::aboutToQuit, [&] { terminate(); }); -#else - initialize(false, QThread::LowPriority); -#endif -} - -GLTextureTransferHelper::~GLTextureTransferHelper() { -#ifdef THREADED_TEXTURE_TRANSFER - if (isStillRunning()) { - terminate(); - } -#else - terminate(); -#endif -} - -void GLTextureTransferHelper::transferTexture(const gpu::TexturePointer& texturePointer) { - GLTexture* object = Backend::getGPUObject(*texturePointer); - - Backend::incrementTextureGPUTransferCount(); - object->setSyncState(GLSyncState::Pending); - Lock lock(_mutex); - _pendingTextures.push_back(texturePointer); -} - -void GLTextureTransferHelper::setup() { -#ifdef THREADED_TEXTURE_TRANSFER - _context.makeCurrent(); - -#ifdef TEXTURE_TRANSFER_FORCE_DRAW - // FIXME don't use opengl 4.5 DSA functionality without verifying it's present - glCreateRenderbuffers(1, &_drawRenderbuffer); - glNamedRenderbufferStorage(_drawRenderbuffer, GL_RGBA8, 128, 128); - glCreateFramebuffers(1, &_drawFramebuffer); - glNamedFramebufferRenderbuffer(_drawFramebuffer, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, _drawRenderbuffer); - glCreateFramebuffers(1, &_readFramebuffer); -#endif - -#ifdef TEXTURE_TRANSFER_PBOS - std::array pbos; - glCreateBuffers(TEXTURE_TRANSFER_PBO_COUNT, &pbos[0]); - for (uint32_t i = 0; i < TEXTURE_TRANSFER_PBO_COUNT; ++i) { - TextureTransferBlock newBlock; - newBlock._pbo = pbos[i]; - glNamedBufferStorage(newBlock._pbo, TEXTURE_TRANSFER_BLOCK_SIZE, 0, GL_MAP_WRITE_BIT | GL_MAP_PERSISTENT_BIT | GL_MAP_COHERENT_BIT); - newBlock._mapped = glMapNamedBufferRange(newBlock._pbo, 0, TEXTURE_TRANSFER_BLOCK_SIZE, GL_MAP_WRITE_BIT | GL_MAP_PERSISTENT_BIT | GL_MAP_COHERENT_BIT); - _readyQueue.push(newBlock); - } -#endif -#endif -} - -void GLTextureTransferHelper::shutdown() { -#ifdef THREADED_TEXTURE_TRANSFER - _context.makeCurrent(); -#endif - -#ifdef TEXTURE_TRANSFER_FORCE_DRAW - glNamedFramebufferRenderbuffer(_drawFramebuffer, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, 0); - glDeleteFramebuffers(1, &_drawFramebuffer); - _drawFramebuffer = 0; - glDeleteFramebuffers(1, &_readFramebuffer); - _readFramebuffer = 0; - - glNamedFramebufferTexture(_readFramebuffer, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0); - glDeleteRenderbuffers(1, &_drawRenderbuffer); - _drawRenderbuffer = 0; -#endif -} - -void GLTextureTransferHelper::queueExecution(VoidLambda lambda) { - Lock lock(_mutex); - _pendingCommands.push_back(lambda); -} - -#define MAX_TRANSFERS_PER_PASS 2 - -bool GLTextureTransferHelper::process() { - // Take any new textures or commands off the queue - VoidLambdaList pendingCommands; - TextureList newTransferTextures; - { - Lock lock(_mutex); - newTransferTextures.swap(_pendingTextures); - pendingCommands.swap(_pendingCommands); - } - - if (!pendingCommands.empty()) { - for (auto command : pendingCommands) { - command(); - } - glFlush(); - } - - if (!newTransferTextures.empty()) { - for (auto& texturePointer : newTransferTextures) { -#ifdef HAVE_NSIGHT - _map[texturePointer] = nvtxRangeStart("TextureTansfer"); -#endif - GLTexture* object = Backend::getGPUObject(*texturePointer); - object->startTransfer(); - _transferringTextures.push_back(texturePointer); - _textureIterator = _transferringTextures.begin(); - } - _transferringTextures.sort([](const gpu::TexturePointer& a, const gpu::TexturePointer& b)->bool { - return a->getSize() < b->getSize(); - }); - } - - // No transfers in progress, sleep - if (_transferringTextures.empty()) { -#ifdef THREADED_TEXTURE_TRANSFER - QThread::usleep(1); -#endif - return true; - } - PROFILE_COUNTER_IF_CHANGED(render_gpu_gl, "transferringTextures", int, (int) _transferringTextures.size()) - - static auto lastReport = usecTimestampNow(); - auto now = usecTimestampNow(); - auto lastReportInterval = now - lastReport; - if (lastReportInterval > USECS_PER_SECOND * 4) { - lastReport = now; - qCDebug(gpulogging) << "Texture list " << _transferringTextures.size(); - } - - size_t transferCount = 0; - for (_textureIterator = _transferringTextures.begin(); _textureIterator != _transferringTextures.end();) { - if (++transferCount > MAX_TRANSFERS_PER_PASS) { - break; - } - auto texture = *_textureIterator; - GLTexture* gltexture = Backend::getGPUObject(*texture); - if (gltexture->continueTransfer()) { - ++_textureIterator; - continue; - } - - gltexture->finishTransfer(); - -#ifdef TEXTURE_TRANSFER_FORCE_DRAW - // FIXME force a draw on the texture transfer thread before passing the texture to the main thread for use -#endif - -#ifdef THREADED_TEXTURE_TRANSFER - clientWait(); -#endif - gltexture->_contentStamp = gltexture->_gpuObject.getDataStamp(); - gltexture->updateSize(); - gltexture->setSyncState(gpu::gl::GLSyncState::Transferred); - Backend::decrementTextureGPUTransferCount(); -#ifdef HAVE_NSIGHT - // Mark the texture as transferred - nvtxRangeEnd(_map[texture]); - _map.erase(texture); -#endif - _textureIterator = _transferringTextures.erase(_textureIterator); - } - -#ifdef THREADED_TEXTURE_TRANSFER - if (!_transferringTextures.empty()) { - // Don't saturate the GPU - clientWait(); - } else { - // Don't saturate the CPU - QThread::msleep(1); - } -#endif - - return true; -} diff --git a/libraries/gpu-gl/src/gpu/gl/GLTextureTransfer.h b/libraries/gpu-gl/src/gpu/gl/GLTextureTransfer.h deleted file mode 100644 index a23c282fd4..0000000000 --- a/libraries/gpu-gl/src/gpu/gl/GLTextureTransfer.h +++ /dev/null @@ -1,78 +0,0 @@ -// -// Created by Bradley Austin Davis on 2016/04/03 -// Copyright 2013-2016 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// -#ifndef hifi_gpu_gl_GLTextureTransfer_h -#define hifi_gpu_gl_GLTextureTransfer_h - -#include -#include - -#include - -#include - -#include "GLShared.h" - -#ifdef Q_OS_WIN -#define THREADED_TEXTURE_TRANSFER -#endif - -#ifdef THREADED_TEXTURE_TRANSFER -// FIXME when sparse textures are enabled, it's harder to force a draw on the transfer thread -// also, the current draw code is implicitly using OpenGL 4.5 functionality -//#define TEXTURE_TRANSFER_FORCE_DRAW -// FIXME PBO's increase the complexity and don't seem to work reliably -//#define TEXTURE_TRANSFER_PBOS -#endif - -namespace gpu { namespace gl { - -using TextureList = std::list; -using TextureListIterator = TextureList::iterator; - -class GLTextureTransferHelper : public GenericThread { -public: - using VoidLambda = std::function; - using VoidLambdaList = std::list; - using Pointer = std::shared_ptr; - GLTextureTransferHelper(); - ~GLTextureTransferHelper(); - void transferTexture(const gpu::TexturePointer& texturePointer); - void queueExecution(VoidLambda lambda); - - void setup() override; - void shutdown() override; - bool process() override; - -private: -#ifdef THREADED_TEXTURE_TRANSFER - ::gl::OffscreenContext _context; -#endif - -#ifdef TEXTURE_TRANSFER_FORCE_DRAW - // Framebuffers / renderbuffers for forcing access to the texture on the transfer thread - GLuint _drawRenderbuffer { 0 }; - GLuint _drawFramebuffer { 0 }; - GLuint _readFramebuffer { 0 }; -#endif - - // A mutex for protecting items access on the render and transfer threads - Mutex _mutex; - // Commands that have been submitted for execution on the texture transfer thread - VoidLambdaList _pendingCommands; - // Textures that have been submitted for transfer - TextureList _pendingTextures; - // Textures currently in the transfer process - // Only used on the transfer thread - TextureList _transferringTextures; - TextureListIterator _textureIterator; - -}; - -} } - -#endif \ No newline at end of file diff --git a/libraries/gpu-gl/src/gpu/gl41/GL41Backend.h b/libraries/gpu-gl/src/gpu/gl41/GL41Backend.h index 72e2f5a804..6d2f91c436 100644 --- a/libraries/gpu-gl/src/gpu/gl41/GL41Backend.h +++ b/libraries/gpu-gl/src/gpu/gl41/GL41Backend.h @@ -40,18 +40,28 @@ public: class GL41Texture : public GLTexture { using Parent = GLTexture; - GLuint allocate(); - public: - GL41Texture(const std::weak_ptr& backend, const Texture& buffer, GLuint externalId); - GL41Texture(const std::weak_ptr& backend, const Texture& buffer, bool transferrable); + static GLuint allocate(); + + public: + ~GL41Texture(); + + private: + GL41Texture(const std::weak_ptr& backend, const Texture& buffer); - protected: - void transferMip(uint16_t mipLevel, uint8_t face) const; - void startTransfer() override; - void allocateStorage() const override; - void updateSize() const override; - void syncSampler() const override; void generateMips() const override; + uint32 size() const override; + + friend class GL41Backend; + const Stamp _storageStamp; + mutable Stamp _contentStamp { 0 }; + mutable Stamp _samplerStamp { 0 }; + const uint32 _size; + + + bool isOutdated() const; + void withPreservedTexture(std::function f) const; + void syncContent() const; + void syncSampler() const; }; @@ -62,8 +72,7 @@ protected: GLuint getBufferID(const Buffer& buffer) override; GLBuffer* syncGPUObject(const Buffer& buffer) override; - GLuint getTextureID(const TexturePointer& texture, bool needTransfer = true) override; - GLTexture* syncGPUObject(const TexturePointer& texture, bool sync = true) override; + GLTexture* syncGPUObject(const TexturePointer& texture) override; GLuint getQueryID(const QueryPointer& query) override; GLQuery* syncGPUObject(const Query& query) override; diff --git a/libraries/gpu-gl/src/gpu/gl41/GL41BackendOutput.cpp b/libraries/gpu-gl/src/gpu/gl41/GL41BackendOutput.cpp index 6d11a52035..195b155bf3 100644 --- a/libraries/gpu-gl/src/gpu/gl41/GL41BackendOutput.cpp +++ b/libraries/gpu-gl/src/gpu/gl41/GL41BackendOutput.cpp @@ -53,10 +53,12 @@ public: GL_COLOR_ATTACHMENT15 }; int unit = 0; + auto backend = _backend.lock(); for (auto& b : _gpuObject.getRenderBuffers()) { surface = b._texture; if (surface) { - gltexture = gl::GLTexture::sync(*_backend.lock().get(), surface, false); // Grab the gltexture and don't transfer + Q_ASSERT(TextureUsageType::RENDERBUFFER == surface->getUsageType()); + gltexture = backend->syncGPUObject(surface); } else { gltexture = nullptr; } @@ -81,9 +83,11 @@ public: } if (_gpuObject.getDepthStamp() != _depthStamp) { + auto backend = _backend.lock(); auto surface = _gpuObject.getDepthStencilBuffer(); if (_gpuObject.hasDepthStencil() && surface) { - gltexture = gl::GLTexture::sync(*_backend.lock().get(), surface, false); // Grab the gltexture and don't transfer + Q_ASSERT(TextureUsageType::RENDERBUFFER == surface->getUsageType()); + gltexture = backend->syncGPUObject(surface); } if (gltexture) { @@ -110,7 +114,7 @@ public: glBindFramebuffer(GL_DRAW_FRAMEBUFFER, currentFBO); } - checkStatus(GL_DRAW_FRAMEBUFFER); + checkStatus(); } diff --git a/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp b/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp index 65c45111db..65c4dda202 100644 --- a/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp @@ -29,20 +29,97 @@ GLuint GL41Texture::allocate() { return result; } -GLuint GL41Backend::getTextureID(const TexturePointer& texture, bool transfer) { - return GL41Texture::getId(*this, texture, transfer); +GLTexture* GL41Backend::syncGPUObject(const TexturePointer& texturePointer) { + if (!texturePointer) { + return nullptr; + } + const Texture& texture = *texturePointer; + if (TextureUsageType::EXTERNAL == texture.getUsageType()) { + return Parent::syncGPUObject(texturePointer); + } + + if (!texture.isDefined()) { + // NO texture definition yet so let's avoid thinking + return nullptr; + } + + // If the object hasn't been created, or the object definition is out of date, drop and re-create + GL41Texture* object = Backend::getGPUObject(texture); + if (!object || object->_storageStamp < texture.getStamp()) { + // This automatically any previous texture + object = new GL41Texture(shared_from_this(), texture); + } + + // FIXME internalize to GL41Texture 'sync' function + if (object->isOutdated()) { + object->withPreservedTexture([&] { + if (object->_contentStamp < texture.getDataStamp()) { + // FIXME implement synchronous texture transfer here + object->syncContent(); + } + + if (object->_samplerStamp < texture.getSamplerStamp()) { + object->syncSampler(); + } + }); + } + + return object; } -GLTexture* GL41Backend::syncGPUObject(const TexturePointer& texture, bool transfer) { - return GL41Texture::sync(*this, texture, transfer); +GL41Texture::GL41Texture(const std::weak_ptr& backend, const Texture& texture) + : GLTexture(backend, texture, allocate()), _storageStamp { texture.getStamp() }, _size(texture.evalTotalSize()) { + + withPreservedTexture([&] { + GLTexelFormat texelFormat = GLTexelFormat::evalGLTexelFormat(_gpuObject.getTexelFormat()); + const Sampler& sampler = _gpuObject.getSampler(); + auto minMip = sampler.getMinMip(); + auto maxMip = sampler.getMaxMip(); + for (uint16_t l = minMip; l <= maxMip; l++) { + // Get the mip level dimensions, accounting for the downgrade level + Vec3u dimensions = _gpuObject.evalMipDimensions(l); + for (GLenum target : getFaceTargets(_target)) { + glTexImage2D(target, l - minMip, texelFormat.internalFormat, dimensions.x, dimensions.y, 0, texelFormat.format, texelFormat.type, NULL); + (void)CHECK_GL_ERROR(); + } + } + }); } -GL41Texture::GL41Texture(const std::weak_ptr& backend, const Texture& texture, GLuint externalId) - : GLTexture(backend, texture, externalId) { +GL41Texture::~GL41Texture() { + } -GL41Texture::GL41Texture(const std::weak_ptr& backend, const Texture& texture, bool transferrable) - : GLTexture(backend, texture, allocate(), transferrable) { +bool GL41Texture::isOutdated() const { + if (_samplerStamp <= _gpuObject.getSamplerStamp()) { + return true; + } + if (TextureUsageType::RESOURCE == _gpuObject.getUsageType() && _contentStamp <= _gpuObject.getDataStamp()) { + return true; + } + return false; +} + +void GL41Texture::withPreservedTexture(std::function f) const { + GLint boundTex = -1; + switch (_target) { + case GL_TEXTURE_2D: + glGetIntegerv(GL_TEXTURE_BINDING_2D, &boundTex); + break; + + case GL_TEXTURE_CUBE_MAP: + glGetIntegerv(GL_TEXTURE_BINDING_CUBE_MAP, &boundTex); + break; + + default: + qFatal("Unsupported texture type"); + } + (void)CHECK_GL_ERROR(); + + glBindTexture(_target, _texture); + f(); + glBindTexture(_target, boundTex); + (void)CHECK_GL_ERROR(); } void GL41Texture::generateMips() const { @@ -52,94 +129,12 @@ void GL41Texture::generateMips() const { (void)CHECK_GL_ERROR(); } -void GL41Texture::allocateStorage() const { - GLTexelFormat texelFormat = GLTexelFormat::evalGLTexelFormat(_gpuObject.getTexelFormat()); - glTexParameteri(_target, GL_TEXTURE_BASE_LEVEL, 0); - (void)CHECK_GL_ERROR(); - glTexParameteri(_target, GL_TEXTURE_MAX_LEVEL, _maxMip - _minMip); - (void)CHECK_GL_ERROR(); - if (GLEW_VERSION_4_2 && !_gpuObject.getTexelFormat().isCompressed()) { - // Get the dimensions, accounting for the downgrade level - Vec3u dimensions = _gpuObject.evalMipDimensions(_minMip); - glTexStorage2D(_target, usedMipLevels(), texelFormat.internalFormat, dimensions.x, dimensions.y); - (void)CHECK_GL_ERROR(); - } else { - for (uint16_t l = _minMip; l <= _maxMip; l++) { - // Get the mip level dimensions, accounting for the downgrade level - Vec3u dimensions = _gpuObject.evalMipDimensions(l); - for (GLenum target : getFaceTargets(_target)) { - glTexImage2D(target, l - _minMip, texelFormat.internalFormat, dimensions.x, dimensions.y, 0, texelFormat.format, texelFormat.type, NULL); - (void)CHECK_GL_ERROR(); - } - } - } +void GL41Texture::syncContent() const { + // FIXME actually copy the texture data + _contentStamp = _gpuObject.getDataStamp() + 1; } -void GL41Texture::updateSize() const { - setSize(_virtualSize); - if (!_id) { - return; - } - - if (_gpuObject.getTexelFormat().isCompressed()) { - GLenum proxyType = GL_TEXTURE_2D; - GLuint numFaces = 1; - if (_gpuObject.getType() == gpu::Texture::TEX_CUBE) { - proxyType = CUBE_FACE_LAYOUT[0]; - numFaces = (GLuint)CUBE_NUM_FACES; - } - GLint gpuSize{ 0 }; - glGetTexLevelParameteriv(proxyType, 0, GL_TEXTURE_COMPRESSED, &gpuSize); - (void)CHECK_GL_ERROR(); - - if (gpuSize) { - for (GLuint level = _minMip; level < _maxMip; level++) { - GLint levelSize{ 0 }; - glGetTexLevelParameteriv(proxyType, level, GL_TEXTURE_COMPRESSED_IMAGE_SIZE, &levelSize); - levelSize *= numFaces; - - if (levelSize <= 0) { - break; - } - gpuSize += levelSize; - } - (void)CHECK_GL_ERROR(); - setSize(gpuSize); - return; - } - } -} - -// Move content bits from the CPU to the GPU for a given mip / face -void GL41Texture::transferMip(uint16_t mipLevel, uint8_t face) const { - auto mip = _gpuObject.accessStoredMipFace(mipLevel, face); - GLTexelFormat texelFormat = GLTexelFormat::evalGLTexelFormat(_gpuObject.getTexelFormat(), mip->getFormat()); - //GLenum target = getFaceTargets()[face]; - GLenum target = _target == GL_TEXTURE_2D ? GL_TEXTURE_2D : CUBE_FACE_LAYOUT[face]; - auto size = _gpuObject.evalMipDimensions(mipLevel); - glTexSubImage2D(target, mipLevel, 0, 0, size.x, size.y, texelFormat.format, texelFormat.type, mip->readData()); - (void)CHECK_GL_ERROR(); -} - -void GL41Texture::startTransfer() { - PROFILE_RANGE(render_gpu_gl, __FUNCTION__); - Parent::startTransfer(); - - glBindTexture(_target, _id); - (void)CHECK_GL_ERROR(); - - // transfer pixels from each faces - uint8_t numFaces = (Texture::TEX_CUBE == _gpuObject.getType()) ? CUBE_NUM_FACES : 1; - for (uint8_t f = 0; f < numFaces; f++) { - for (uint16_t i = 0; i < Sampler::MAX_MIP_LEVEL; ++i) { - if (_gpuObject.isStoredMipFaceAvailable(i, f)) { - transferMip(i, f); - } - } - } -} - -void GL41Backend::GL41Texture::syncSampler() const { +void GL41Texture::syncSampler() const { const Sampler& sampler = _gpuObject.getSampler(); const auto& fm = FILTER_MODES[sampler.getFilter()]; glTexParameteri(_target, GL_TEXTURE_MIN_FILTER, fm.minFilter); @@ -161,5 +156,9 @@ void GL41Backend::GL41Texture::syncSampler() const { glTexParameterf(_target, GL_TEXTURE_MIN_LOD, (float)sampler.getMinMip()); glTexParameterf(_target, GL_TEXTURE_MAX_LOD, (sampler.getMaxMip() == Sampler::MAX_MIP_LEVEL ? 1000.f : sampler.getMaxMip())); glTexParameterf(_target, GL_TEXTURE_MAX_ANISOTROPY_EXT, sampler.getMaxAnisotropy()); + _samplerStamp = _gpuObject.getSamplerStamp() + 1; } +uint32 GL41Texture::size() const { + return _size; +} diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45Backend.cpp b/libraries/gpu-gl/src/gpu/gl45/GL45Backend.cpp index d7dde8b7d6..f0ef2ac7a8 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45Backend.cpp +++ b/libraries/gpu-gl/src/gpu/gl45/GL45Backend.cpp @@ -163,8 +163,3 @@ void GL45Backend::do_multiDrawIndexedIndirect(const Batch& batch, size_t paramOf _stats._DSNumAPIDrawcalls++; (void)CHECK_GL_ERROR(); } - -void GL45Backend::recycle() const { - Parent::recycle(); - derezTextures(); -} diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h b/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h index 2242bba5d9..e44f71e3e5 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h +++ b/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h @@ -8,6 +8,7 @@ // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +#pragma once #ifndef hifi_gpu_45_GL45Backend_h #define hifi_gpu_45_GL45Backend_h @@ -19,6 +20,7 @@ namespace gpu { namespace gl45 { using namespace gpu::gl; +using TextureWeakPointer = std::weak_ptr; class GL45Backend : public GLBackend { using Parent = GLBackend; @@ -31,60 +33,152 @@ public: class GL45Texture : public GLTexture { using Parent = GLTexture; + friend class GL45Backend; static GLuint allocate(const Texture& texture); - static const uint32_t DEFAULT_PAGE_DIMENSION = 128; - static const uint32_t DEFAULT_MAX_SPARSE_LEVEL = 0xFFFF; + protected: + GL45Texture(const std::weak_ptr& backend, const Texture& texture); + void generateMips() const override; + void copyMipFromTexture(uint16_t sourceMip, uint16_t targetMip) const; + virtual void syncSampler() const; + }; + + // + // Textures that have fixed allocation sizes and cannot be managed at runtime + // + + class GL45FixedAllocationTexture : public GL45Texture { + using Parent = GL45Texture; + friend class GL45Backend; public: - GL45Texture(const std::weak_ptr& backend, const Texture& texture, GLuint externalId); - GL45Texture(const std::weak_ptr& backend, const Texture& texture, bool transferrable); - ~GL45Texture(); + GL45FixedAllocationTexture(const std::weak_ptr& backend, const Texture& texture); + ~GL45FixedAllocationTexture(); - void postTransfer() override; + protected: + uint32 size() const override { return _size; } + void allocateStorage() const; + void syncSampler() const override; + const uint32 _size { 0 }; + }; - struct SparseInfo { - SparseInfo(GL45Texture& texture); - void maybeMakeSparse(); - void update(); - uvec3 getPageCounts(const uvec3& dimensions) const; - uint32_t getPageCount(const uvec3& dimensions) const; - uint32_t getSize() const; + class GL45AttachmentTexture : public GL45FixedAllocationTexture { + using Parent = GL45FixedAllocationTexture; + friend class GL45Backend; + protected: + GL45AttachmentTexture(const std::weak_ptr& backend, const Texture& texture); + ~GL45AttachmentTexture(); + }; - GL45Texture& texture; - bool sparse { false }; - uvec3 pageDimensions { DEFAULT_PAGE_DIMENSION }; - GLuint maxSparseLevel { DEFAULT_MAX_SPARSE_LEVEL }; - uint32_t allocatedPages { 0 }; - uint32_t maxPages { 0 }; - uint32_t pageBytes { 0 }; - GLint pageDimensionsIndex { 0 }; + class GL45StrictResourceTexture : public GL45FixedAllocationTexture { + using Parent = GL45FixedAllocationTexture; + friend class GL45Backend; + protected: + GL45StrictResourceTexture(const std::weak_ptr& backend, const Texture& texture); + }; + + // + // Textures that can be managed at runtime to increase or decrease their memory load + // + + class GL45VariableAllocationTexture : public GL45Texture { + using Parent = GL45Texture; + friend class GL45Backend; + using PromoteLambda = std::function; + + public: + enum class MemoryPressureState { + Idle, + Transfer, + Oversubscribed, + Undersubscribed, }; protected: - void updateMips() override; - void stripToMip(uint16_t newMinMip); - void startTransfer() override; - bool continueTransfer() override; - void finishTransfer() override; - void incrementalTransfer(const uvec3& size, const gpu::Texture::PixelsPointer& mip, std::function f) const; - void transferMip(uint16_t mipLevel, uint8_t face = 0) const; - void allocateMip(uint16_t mipLevel, uint8_t face = 0) const; - void allocateStorage() const override; - void updateSize() const override; - void syncSampler() const override; - void generateMips() const override; - void withPreservedTexture(std::function f) const override; - void derez(); + static std::atomic _memoryPressureStateStale; + static MemoryPressureState _memoryPressureState; + static std::list _memoryManagedTextures; + static const uvec3 INITIAL_MIP_TRANSFER_DIMENSIONS; - SparseInfo _sparseInfo; - uint16_t _mipOffset { 0 }; - friend class GL45Backend; + static void updateMemoryPressure(); + static void processWorkQueues(); + static void addMemoryManagedTexture(const TexturePointer& texturePointer); + + static void manageMemory(); + + protected: + GL45VariableAllocationTexture(const std::weak_ptr& backend, const Texture& texture); + ~GL45VariableAllocationTexture(); + //bool canPromoteNoAllocate() const { return _allocatedMip < _populatedMip; } + bool canPromote() const { return _allocatedMip > 0; } + bool canDemote() const { return _allocatedMip < _maxAllocatedMip; } + bool hasPendingTransfers() const { return !_pendingTransfers.empty(); } + void executeNextTransfer(); + uint32 size() const override { return _size; } + virtual void populateTransferQueue() = 0; + virtual void promote() = 0; + virtual void demote() = 0; + + uint16 _populatedMip { 0 }; + uint16 _allocatedMip { 0 }; + uint16 _maxAllocatedMip { 0 }; + uint32 _size { 0 }; + std::queue _pendingTransfers; }; + class GL45ResourceTexture : public GL45VariableAllocationTexture { + using Parent = GL45VariableAllocationTexture; + friend class GL45Backend; + protected: + GL45ResourceTexture(const std::weak_ptr& backend, const Texture& texture); + + void syncSampler() const override; + void promote() override; + void demote() override; + void populateTransferQueue() override; + + void allocateStorage(uint16 mip); + void copyMipsFromTexture(); + private: + }; + +#if 0 + class GL45SparseResourceTexture : public GL45VariableAllocationTexture { + using Parent = GL45VariableAllocationTexture; + friend class GL45Backend; + using TextureTypeFormat = std::pair; + using PageDimensions = std::vector; + using PageDimensionsMap = std::map; + static PageDimensionsMap pageDimensionsByFormat; + static Mutex pageDimensionsMutex; + + static bool isSparseEligible(const Texture& texture); + static PageDimensions getPageDimensionsForFormat(const TextureTypeFormat& typeFormat); + static PageDimensions getPageDimensionsForFormat(GLenum type, GLenum format); + static const uint32_t DEFAULT_PAGE_DIMENSION = 128; + static const uint32_t DEFAULT_MAX_SPARSE_LEVEL = 0xFFFF; + + protected: + GL45SparseResourceTexture(const std::weak_ptr& backend, const Texture& texture); + ~GL45SparseResourceTexture(); + uint32 size() const override { return _allocatedPages * _pageBytes; } + void promote() override; + void demote() override; + + private: + uvec3 getPageCounts(const uvec3& dimensions) const; + uint32_t getPageCount(const uvec3& dimensions) const; + + uint32_t _allocatedPages { 0 }; + uint32_t _pageBytes { 0 }; + uvec3 _pageDimensions { DEFAULT_PAGE_DIMENSION }; + GLuint _maxSparseLevel { DEFAULT_MAX_SPARSE_LEVEL }; + }; +#endif + protected: + void recycle() const override; - void derezTextures() const; GLuint getFramebufferID(const FramebufferPointer& framebuffer) override; GLFramebuffer* syncGPUObject(const Framebuffer& framebuffer) override; @@ -92,8 +186,7 @@ protected: GLuint getBufferID(const Buffer& buffer) override; GLBuffer* syncGPUObject(const Buffer& buffer) override; - GLuint getTextureID(const TexturePointer& texture, bool needTransfer = true) override; - GLTexture* syncGPUObject(const TexturePointer& texture, bool sync = true) override; + GLTexture* syncGPUObject(const TexturePointer& texture) override; GLuint getQueryID(const QueryPointer& query) override; GLQuery* syncGPUObject(const Query& query) override; @@ -126,5 +219,5 @@ protected: Q_DECLARE_LOGGING_CATEGORY(gpugl45logging) - #endif + diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45BackendOutput.cpp b/libraries/gpu-gl/src/gpu/gl45/GL45BackendOutput.cpp index c5b84b7deb..9648af9b21 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45BackendOutput.cpp +++ b/libraries/gpu-gl/src/gpu/gl45/GL45BackendOutput.cpp @@ -49,10 +49,12 @@ public: GL_COLOR_ATTACHMENT15 }; int unit = 0; + auto backend = _backend.lock(); for (auto& b : _gpuObject.getRenderBuffers()) { surface = b._texture; if (surface) { - gltexture = gl::GLTexture::sync(*_backend.lock().get(), surface, false); // Grab the gltexture and don't transfer + Q_ASSERT(TextureUsageType::RENDERBUFFER == surface->getUsageType()); + gltexture = backend->syncGPUObject(surface); } else { gltexture = nullptr; } @@ -78,8 +80,10 @@ public: if (_gpuObject.getDepthStamp() != _depthStamp) { auto surface = _gpuObject.getDepthStencilBuffer(); + auto backend = _backend.lock(); if (_gpuObject.hasDepthStencil() && surface) { - gltexture = gl::GLTexture::sync(*_backend.lock().get(), surface, false); // Grab the gltexture and don't transfer + Q_ASSERT(TextureUsageType::RENDERBUFFER == surface->getUsageType()); + gltexture = backend->syncGPUObject(surface); } if (gltexture) { @@ -102,7 +106,7 @@ public: _status = glCheckNamedFramebufferStatus(_id, GL_DRAW_FRAMEBUFFER); // restore the current framebuffer - checkStatus(GL_DRAW_FRAMEBUFFER); + checkStatus(); } diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp b/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp index 6948a045a2..c46c07ee37 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp @@ -8,9 +8,10 @@ // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -#include "GL45Backend.h" +#include "GL45Backend.h" #include +#include #include #include #include @@ -19,142 +20,73 @@ #include #include +#include #include "../gl/GLTexelFormat.h" using namespace gpu; using namespace gpu::gl; using namespace gpu::gl45; -// Allocate 1 MB of buffer space for paged transfers -#define DEFAULT_PAGE_BUFFER_SIZE (1024*1024) -#define DEFAULT_GL_PIXEL_ALIGNMENT 4 - -using GL45Texture = GL45Backend::GL45Texture; - -static std::map> texturesByMipCounts; -static Mutex texturesByMipCountsMutex; -using TextureTypeFormat = std::pair; -std::map> sparsePageDimensionsByFormat; -Mutex sparsePageDimensionsByFormatMutex; - -static std::vector getPageDimensionsForFormat(const TextureTypeFormat& typeFormat) { - { - Lock lock(sparsePageDimensionsByFormatMutex); - if (sparsePageDimensionsByFormat.count(typeFormat)) { - return sparsePageDimensionsByFormat[typeFormat]; - } - } - GLint count = 0; - glGetInternalformativ(typeFormat.first, typeFormat.second, GL_NUM_VIRTUAL_PAGE_SIZES_ARB, 1, &count); - - std::vector result; - if (count > 0) { - std::vector x, y, z; - x.resize(count); - glGetInternalformativ(typeFormat.first, typeFormat.second, GL_VIRTUAL_PAGE_SIZE_X_ARB, 1, &x[0]); - y.resize(count); - glGetInternalformativ(typeFormat.first, typeFormat.second, GL_VIRTUAL_PAGE_SIZE_Y_ARB, 1, &y[0]); - z.resize(count); - glGetInternalformativ(typeFormat.first, typeFormat.second, GL_VIRTUAL_PAGE_SIZE_Z_ARB, 1, &z[0]); - - result.resize(count); - for (GLint i = 0; i < count; ++i) { - result[i] = uvec3(x[i], y[i], z[i]); - } - } - - { - Lock lock(sparsePageDimensionsByFormatMutex); - if (0 == sparsePageDimensionsByFormat.count(typeFormat)) { - sparsePageDimensionsByFormat[typeFormat] = result; - } - } - - return result; -} - -static std::vector getPageDimensionsForFormat(GLenum target, GLenum format) { - return getPageDimensionsForFormat({ target, format }); -} - -GLTexture* GL45Backend::syncGPUObject(const TexturePointer& texture, bool transfer) { - return GL45Texture::sync(*this, texture, transfer); -} - -using SparseInfo = GL45Backend::GL45Texture::SparseInfo; - -SparseInfo::SparseInfo(GL45Texture& texture) - : texture(texture) { -} - -void SparseInfo::maybeMakeSparse() { - // Don't enable sparse for objects with explicitly managed mip levels - if (!texture._gpuObject.isAutogenerateMips()) { - return; - } - return; - - const uvec3 dimensions = texture._gpuObject.getDimensions(); - auto allowedPageDimensions = getPageDimensionsForFormat(texture._target, texture._internalFormat); - // In order to enable sparse the texture size must be an integer multiple of the page size - for (size_t i = 0; i < allowedPageDimensions.size(); ++i) { - pageDimensionsIndex = (uint32_t) i; - pageDimensions = allowedPageDimensions[i]; - // Is this texture an integer multiple of page dimensions? - if (uvec3(0) == (dimensions % pageDimensions)) { - qCDebug(gpugl45logging) << "Enabling sparse for texture " << texture._source.c_str(); - sparse = true; - break; - } - } - - if (sparse) { - glTextureParameteri(texture._id, GL_TEXTURE_SPARSE_ARB, GL_TRUE); - glTextureParameteri(texture._id, GL_VIRTUAL_PAGE_SIZE_INDEX_ARB, pageDimensionsIndex); - } else { - qCDebug(gpugl45logging) << "Size " << dimensions.x << " x " << dimensions.y << - " is not supported by any sparse page size for texture" << texture._source.c_str(); - } -} - #define SPARSE_PAGE_SIZE_OVERHEAD_ESTIMATE 1.3f -// This can only be called after we've established our storage size -void SparseInfo::update() { - if (!sparse) { - return; +GLTexture* GL45Backend::syncGPUObject(const TexturePointer& texturePointer) { + if (!texturePointer) { + return nullptr; } - glGetTextureParameterIuiv(texture._id, GL_NUM_SPARSE_LEVELS_ARB, &maxSparseLevel); - pageBytes = texture._gpuObject.getTexelFormat().getSize(); - pageBytes *= pageDimensions.x * pageDimensions.y * pageDimensions.z; - // Testing with a simple texture allocating app shows an estimated 20% GPU memory overhead for - // sparse textures as compared to non-sparse, so we acount for that here. - pageBytes = (uint32_t)(pageBytes * SPARSE_PAGE_SIZE_OVERHEAD_ESTIMATE); - for (uint16_t mipLevel = 0; mipLevel <= maxSparseLevel; ++mipLevel) { - auto mipDimensions = texture._gpuObject.evalMipDimensions(mipLevel); - auto mipPageCount = getPageCount(mipDimensions); - maxPages += mipPageCount; + const Texture& texture = *texturePointer; + if (std::string("cursor texture") == texture.source()) { + qDebug() << "Loading cursor texture"; } - if (texture._target == GL_TEXTURE_CUBE_MAP) { - maxPages *= GLTexture::CUBE_NUM_FACES; + if (TextureUsageType::EXTERNAL == texture.getUsageType()) { + return Parent::syncGPUObject(texturePointer); } + + if (!texture.isDefined()) { + // NO texture definition yet so let's avoid thinking + return nullptr; + } + + GL45Texture* object = Backend::getGPUObject(texture); + if (!object) { + switch (texture.getUsageType()) { + case TextureUsageType::RENDERBUFFER: + object = new GL45AttachmentTexture(shared_from_this(), texture); + break; + + case TextureUsageType::STRICT_RESOURCE: + qCDebug(gpugllogging) << "Strict texture " << texture.source().c_str(); + object = new GL45StrictResourceTexture(shared_from_this(), texture); + break; + + case TextureUsageType::RESOURCE: { + + GL45VariableAllocationTexture* varObject { nullptr }; +#if 0 + if (isTextureManagementSparseEnabled() && GL45Texture::isSparseEligible(texture)) { + varObject = new GL45SparseResourceTexture(shared_from_this(), texture); + } else { + varObject = new GL45ResourceTexture(shared_from_this(), texture); + } +#else + varObject = new GL45ResourceTexture(shared_from_this(), texture); +#endif + GL45VariableAllocationTexture::addMemoryManagedTexture(texturePointer); + object = varObject; + break; + } + + default: + Q_UNREACHABLE(); + } + } + + return object; } -uvec3 SparseInfo::getPageCounts(const uvec3& dimensions) const { - auto result = (dimensions / pageDimensions) + - glm::clamp(dimensions % pageDimensions, glm::uvec3(0), glm::uvec3(1)); - return result; -} - -uint32_t SparseInfo::getPageCount(const uvec3& dimensions) const { - auto pageCounts = getPageCounts(dimensions); - return pageCounts.x * pageCounts.y * pageCounts.z; -} - - -uint32_t SparseInfo::getSize() const { - return allocatedPages * pageBytes; +void GL45Backend::recycle() const { + Parent::recycle(); + GL45VariableAllocationTexture::manageMemory(); } void GL45Backend::initTextureManagementStage() { @@ -171,6 +103,11 @@ void GL45Backend::initTextureManagementStage() { } } +using GL45Texture = GL45Backend::GL45Texture; + +GL45Texture::GL45Texture(const std::weak_ptr& backend, const Texture& texture) + : GLTexture(backend, texture, allocate(texture)) { +} GLuint GL45Texture::allocate(const Texture& texture) { GLuint result; @@ -178,162 +115,37 @@ GLuint GL45Texture::allocate(const Texture& texture) { return result; } -GLuint GL45Backend::getTextureID(const TexturePointer& texture, bool transfer) { - return GL45Texture::getId(*this, texture, transfer); -} - -GL45Texture::GL45Texture(const std::weak_ptr& backend, const Texture& texture, GLuint externalId) - : GLTexture(backend, texture, externalId), _sparseInfo(*this) -{ -} - -GL45Texture::GL45Texture(const std::weak_ptr& backend, const Texture& texture, bool transferrable) - : GLTexture(backend, texture, allocate(texture), transferrable), _sparseInfo(*this) - { - - auto theBackend = _backend.lock(); - if (_transferrable && theBackend && theBackend->isTextureManagementSparseEnabled()) { - _sparseInfo.maybeMakeSparse(); - if (_sparseInfo.sparse) { - Backend::incrementTextureGPUSparseCount(); - } - } -} - -GL45Texture::~GL45Texture() { - // Remove this texture from the candidate list of derezzable textures - if (_transferrable) { - auto mipLevels = usedMipLevels(); - Lock lock(texturesByMipCountsMutex); - if (texturesByMipCounts.count(mipLevels)) { - auto& textures = texturesByMipCounts[mipLevels]; - textures.erase(this); - if (textures.empty()) { - texturesByMipCounts.erase(mipLevels); - } - } - } - - if (_sparseInfo.sparse) { - Backend::decrementTextureGPUSparseCount(); - - // Experimenation suggests that allocating sparse textures on one context/thread and deallocating - // them on another is buggy. So for sparse textures we need to queue a lambda with the deallocation - // callls to the transfer thread - auto id = _id; - // Set the class _id to 0 so we don't try to double delete - const_cast(_id) = 0; - std::list> destructionFunctions; - - uint8_t maxFace = (uint8_t)((_target == GL_TEXTURE_CUBE_MAP) ? GLTexture::CUBE_NUM_FACES : 1); - auto maxSparseMip = std::min(_maxMip, _sparseInfo.maxSparseLevel); - for (uint16_t mipLevel = _minMip; mipLevel <= maxSparseMip; ++mipLevel) { - auto mipDimensions = _gpuObject.evalMipDimensions(mipLevel); - destructionFunctions.push_back([id, maxFace, mipLevel, mipDimensions] { - glTexturePageCommitmentEXT(id, mipLevel, 0, 0, 0, mipDimensions.x, mipDimensions.y, maxFace, GL_FALSE); - }); - - auto deallocatedPages = _sparseInfo.getPageCount(mipDimensions) * maxFace; - assert(deallocatedPages <= _sparseInfo.allocatedPages); - _sparseInfo.allocatedPages -= deallocatedPages; - } - - if (0 != _sparseInfo.allocatedPages) { - qCWarning(gpugl45logging) << "Allocated pages remaining " << _id << " " << _sparseInfo.allocatedPages; - } - - auto size = _size; - const_cast(_size) = 0; - _textureTransferHelper->queueExecution([id, size, destructionFunctions] { - for (auto function : destructionFunctions) { - function(); - } - glDeleteTextures(1, &id); - Backend::decrementTextureGPUCount(); - Backend::updateTextureGPUMemoryUsage(size, 0); - Backend::updateTextureGPUSparseMemoryUsage(size, 0); - }); - } -} - -void GL45Texture::withPreservedTexture(std::function f) const { - f(); -} - void GL45Texture::generateMips() const { glGenerateTextureMipmap(_id); (void)CHECK_GL_ERROR(); } -void GL45Texture::allocateStorage() const { - if (_gpuObject.getTexelFormat().isCompressed()) { - qFatal("Compressed textures not yet supported"); +void GL45Texture::copyMipFromTexture(uint16_t sourceMip, uint16_t targetMip) const { + const auto& texture = _gpuObject; + if (!texture.isStoredMipFaceAvailable(sourceMip)) { + return; } - glTextureParameteri(_id, GL_TEXTURE_BASE_LEVEL, 0); - glTextureParameteri(_id, GL_TEXTURE_MAX_LEVEL, _maxMip - _minMip); - // Get the dimensions, accounting for the downgrade level - Vec3u dimensions = _gpuObject.evalMipDimensions(_minMip + _mipOffset); - glTextureStorage2D(_id, usedMipLevels(), _internalFormat, dimensions.x, dimensions.y); - (void)CHECK_GL_ERROR(); -} - -void GL45Texture::updateSize() const { - if (_gpuObject.getTexelFormat().isCompressed()) { - qFatal("Compressed textures not yet supported"); - } - - if (_transferrable && _sparseInfo.sparse) { - auto size = _sparseInfo.getSize(); - Backend::updateTextureGPUSparseMemoryUsage(_size, size); - setSize(size); - } else { - setSize(_gpuObject.evalTotalSize(_mipOffset)); - } -} - -void GL45Texture::startTransfer() { - Parent::startTransfer(); - _sparseInfo.update(); -} - -bool GL45Texture::continueTransfer() { - PROFILE_RANGE(render_gpu_gl, "continueTransfer") - size_t maxFace = GL_TEXTURE_CUBE_MAP == _target ? CUBE_NUM_FACES : 1; + size_t maxFace = GLTexture::getFaceCount(_target); for (uint8_t face = 0; face < maxFace; ++face) { - for (uint16_t mipLevel = _minMip; mipLevel <= _maxMip; ++mipLevel) { - auto size = _gpuObject.evalMipDimensions(mipLevel); - if (_sparseInfo.sparse && mipLevel <= _sparseInfo.maxSparseLevel) { - glTexturePageCommitmentEXT(_id, mipLevel, 0, 0, face, size.x, size.y, 1, GL_TRUE); - _sparseInfo.allocatedPages += _sparseInfo.getPageCount(size); - } - if (_gpuObject.isStoredMipFaceAvailable(mipLevel, face)) { - PROFILE_RANGE_EX(render_gpu_gl, "texSubImage", 0x0000ffff, (size.x * size.y * maxFace / 1024)); - - auto mip = _gpuObject.accessStoredMipFace(mipLevel, face); - GLTexelFormat texelFormat = GLTexelFormat::evalGLTexelFormat(_gpuObject.getTexelFormat(), mip->getFormat()); - if (GL_TEXTURE_2D == _target) { - glTextureSubImage2D(_id, mipLevel, 0, 0, size.x, size.y, texelFormat.format, texelFormat.type, mip->readData()); - } else if (GL_TEXTURE_CUBE_MAP == _target) { - // DSA ARB does not work on AMD, so use EXT - // unless EXT is not available on the driver - if (glTextureSubImage2DEXT) { - auto target = CUBE_FACE_LAYOUT[face]; - glTextureSubImage2DEXT(_id, target, mipLevel, 0, 0, size.x, size.y, texelFormat.format, texelFormat.type, mip->readData()); - } else { - glTextureSubImage3D(_id, mipLevel, 0, 0, face, size.x, size.y, 1, texelFormat.format, texelFormat.type, mip->readData()); - } - } else { - Q_ASSERT(false); - } - (void)CHECK_GL_ERROR(); + auto size = texture.evalMipDimensions(sourceMip); + auto mipData = texture.accessStoredMipFace(sourceMip, face); + GLTexelFormat texelFormat = GLTexelFormat::evalGLTexelFormat(texture.getTexelFormat(), mipData->getFormat()); + if (GL_TEXTURE_2D == _target) { + glTextureSubImage2D(_id, targetMip, 0, 0, size.x, size.y, texelFormat.format, texelFormat.type, mipData->readData()); + } else if (GL_TEXTURE_CUBE_MAP == _target) { + // DSA ARB does not work on AMD, so use EXT + // unless EXT is not available on the driver + if (glTextureSubImage2DEXT) { + auto target = GLTexture::CUBE_FACE_LAYOUT[face]; + glTextureSubImage2DEXT(_id, target, targetMip, 0, 0, size.x, size.y, texelFormat.format, texelFormat.type, mipData->readData()); + } else { + glTextureSubImage3D(_id, targetMip, 0, 0, face, size.x, size.y, 1, texelFormat.format, texelFormat.type, mipData->readData()); } + } else { + Q_ASSERT(false); } + (void)CHECK_GL_ERROR(); } - return false; -} - -void GL45Texture::finishTransfer() { - Parent::finishTransfer(); } void GL45Texture::syncSampler() const { @@ -353,163 +165,66 @@ void GL45Texture::syncSampler() const { glTextureParameteri(_id, GL_TEXTURE_WRAP_S, WRAP_MODES[sampler.getWrapModeU()]); glTextureParameteri(_id, GL_TEXTURE_WRAP_T, WRAP_MODES[sampler.getWrapModeV()]); glTextureParameteri(_id, GL_TEXTURE_WRAP_R, WRAP_MODES[sampler.getWrapModeW()]); + glTextureParameterf(_id, GL_TEXTURE_MAX_ANISOTROPY_EXT, sampler.getMaxAnisotropy()); glTextureParameterfv(_id, GL_TEXTURE_BORDER_COLOR, (const float*)&sampler.getBorderColor()); + +#if 0 // FIXME account for mip offsets here auto baseMip = std::max(sampler.getMipOffset(), _minMip); glTextureParameteri(_id, GL_TEXTURE_BASE_LEVEL, baseMip); glTextureParameterf(_id, GL_TEXTURE_MIN_LOD, (float)sampler.getMinMip()); glTextureParameterf(_id, GL_TEXTURE_MAX_LOD, (sampler.getMaxMip() == Sampler::MAX_MIP_LEVEL ? 1000.f : sampler.getMaxMip() - _mipOffset)); - glTextureParameterf(_id, GL_TEXTURE_MAX_ANISOTROPY_EXT, sampler.getMaxAnisotropy()); +#endif } -void GL45Texture::postTransfer() { - Parent::postTransfer(); - auto mipLevels = usedMipLevels(); - if (_transferrable && mipLevels > 1 && _minMip < _sparseInfo.maxSparseLevel) { - Lock lock(texturesByMipCountsMutex); - texturesByMipCounts[mipLevels].insert(this); - } -} +using GL45FixedAllocationTexture = GL45Backend::GL45FixedAllocationTexture; -void GL45Texture::stripToMip(uint16_t newMinMip) { - if (newMinMip < _minMip) { - qCWarning(gpugl45logging) << "Cannot decrease the min mip"; - return; - } - - if (_sparseInfo.sparse && newMinMip > _sparseInfo.maxSparseLevel) { - qCWarning(gpugl45logging) << "Cannot increase the min mip into the mip tail"; - return; - } - - PROFILE_RANGE(render_gpu_gl, "GL45Texture::stripToMip"); - - auto mipLevels = usedMipLevels(); - { - Lock lock(texturesByMipCountsMutex); - assert(0 != texturesByMipCounts.count(mipLevels)); - assert(0 != texturesByMipCounts[mipLevels].count(this)); - texturesByMipCounts[mipLevels].erase(this); - if (texturesByMipCounts[mipLevels].empty()) { - texturesByMipCounts.erase(mipLevels); - } - } - - // If we weren't generating mips before, we need to now that we're stripping down mip levels. - if (!_gpuObject.isAutogenerateMips()) { - qCDebug(gpugl45logging) << "Force mip generation for texture"; - glGenerateTextureMipmap(_id); - } - - - uint8_t maxFace = (uint8_t)((_target == GL_TEXTURE_CUBE_MAP) ? GLTexture::CUBE_NUM_FACES : 1); - if (_sparseInfo.sparse) { - for (uint16_t mip = _minMip; mip < newMinMip; ++mip) { - auto id = _id; - auto mipDimensions = _gpuObject.evalMipDimensions(mip); - _textureTransferHelper->queueExecution([id, mip, mipDimensions, maxFace] { - glTexturePageCommitmentEXT(id, mip, 0, 0, 0, mipDimensions.x, mipDimensions.y, maxFace, GL_FALSE); - }); - - auto deallocatedPages = _sparseInfo.getPageCount(mipDimensions) * maxFace; - assert(deallocatedPages < _sparseInfo.allocatedPages); - _sparseInfo.allocatedPages -= deallocatedPages; - } - _minMip = newMinMip; - } else { - GLuint oldId = _id; - // Find the distance between the old min mip and the new one - uint16 mipDelta = newMinMip - _minMip; - _mipOffset += mipDelta; - const_cast(_maxMip) -= mipDelta; - auto newLevels = usedMipLevels(); - - // Create and setup the new texture (allocate) - { - Vec3u newDimensions = _gpuObject.evalMipDimensions(_mipOffset); - PROFILE_RANGE_EX(render_gpu_gl, "Re-Allocate", 0xff0000ff, (newDimensions.x * newDimensions.y)); - - glCreateTextures(_target, 1, &const_cast(_id)); - glTextureParameteri(_id, GL_TEXTURE_BASE_LEVEL, 0); - glTextureParameteri(_id, GL_TEXTURE_MAX_LEVEL, _maxMip - _minMip); - glTextureStorage2D(_id, newLevels, _internalFormat, newDimensions.x, newDimensions.y); - } - - // Copy the contents of the old texture to the new - { - PROFILE_RANGE(render_gpu_gl, "Blit"); - // Preferred path only available in 4.3 - for (uint16 targetMip = _minMip; targetMip <= _maxMip; ++targetMip) { - uint16 sourceMip = targetMip + mipDelta; - Vec3u mipDimensions = _gpuObject.evalMipDimensions(targetMip + _mipOffset); - for (GLenum target : getFaceTargets(_target)) { - glCopyImageSubData( - oldId, target, sourceMip, 0, 0, 0, - _id, target, targetMip, 0, 0, 0, - mipDimensions.x, mipDimensions.y, 1 - ); - (void)CHECK_GL_ERROR(); - } - } - - glDeleteTextures(1, &oldId); - } - } - - // Re-sync the sampler to force access to the new mip level +GL45FixedAllocationTexture::GL45FixedAllocationTexture(const std::weak_ptr& backend, const Texture& texture) : GL45Texture(backend, texture), _size(texture.evalTotalSize()) { + allocateStorage(); syncSampler(); - updateSize(); +} - // Re-insert into the texture-by-mips map if appropriate - mipLevels = usedMipLevels(); - if (mipLevels > 1 && (!_sparseInfo.sparse || _minMip < _sparseInfo.maxSparseLevel)) { - Lock lock(texturesByMipCountsMutex); - texturesByMipCounts[mipLevels].insert(this); +GL45FixedAllocationTexture::~GL45FixedAllocationTexture() { +} + +void GL45FixedAllocationTexture::allocateStorage() const { + const GLTexelFormat texelFormat = GLTexelFormat::evalGLTexelFormat(_gpuObject.getTexelFormat()); + const auto dimensions = _gpuObject.getDimensions(); + const auto mips = _gpuObject.evalNumMips(); + glTextureStorage2D(_id, mips, texelFormat.internalFormat, dimensions.x, dimensions.y); +} + +void GL45FixedAllocationTexture::syncSampler() const { + Parent::syncSampler(); + const Sampler& sampler = _gpuObject.getSampler(); + auto baseMip = std::max(sampler.getMipOffset(), sampler.getMinMip()); + glTextureParameteri(_id, GL_TEXTURE_BASE_LEVEL, baseMip); + glTextureParameterf(_id, GL_TEXTURE_MIN_LOD, (float)sampler.getMinMip()); + glTextureParameterf(_id, GL_TEXTURE_MAX_LOD, (sampler.getMaxMip() == Sampler::MAX_MIP_LEVEL ? 1000.f : sampler.getMaxMip())); +} + +// Renderbuffer attachment textures +using GL45AttachmentTexture = GL45Backend::GL45AttachmentTexture; + +GL45AttachmentTexture::GL45AttachmentTexture(const std::weak_ptr& backend, const Texture& texture) : GL45FixedAllocationTexture(backend, texture) { + Backend::updateTextureGPUFramebufferMemoryUsage(0, size()); +} + +GL45AttachmentTexture::~GL45AttachmentTexture() { + Backend::updateTextureGPUFramebufferMemoryUsage(size(), 0); +} + +// Strict resource textures +using GL45StrictResourceTexture = GL45Backend::GL45StrictResourceTexture; + +GL45StrictResourceTexture::GL45StrictResourceTexture(const std::weak_ptr& backend, const Texture& texture) : GL45FixedAllocationTexture(backend, texture) { + auto mipLevels = _gpuObject.evalNumMips(); + for (uint16_t sourceMip = 0; sourceMip < mipLevels; ++sourceMip) { + uint16_t targetMip = sourceMip; + copyMipFromTexture(sourceMip, targetMip); + } + if (texture.isAutogenerateMips()) { + generateMips(); } } -void GL45Texture::updateMips() { - if (!_sparseInfo.sparse) { - return; - } - auto newMinMip = std::min(_gpuObject.minMip(), _sparseInfo.maxSparseLevel); - if (_minMip < newMinMip) { - stripToMip(newMinMip); - } -} - -void GL45Texture::derez() { - if (_sparseInfo.sparse) { - assert(_minMip < _sparseInfo.maxSparseLevel); - } - assert(_minMip < _maxMip); - assert(_transferrable); - stripToMip(_minMip + 1); -} - -void GL45Backend::derezTextures() const { - if (GLTexture::getMemoryPressure() < 1.0f) { - return; - } - - Lock lock(texturesByMipCountsMutex); - if (texturesByMipCounts.empty()) { - // No available textures to derez - return; - } - - auto mipLevel = texturesByMipCounts.rbegin()->first; - if (mipLevel <= 1) { - // No mips available to remove - return; - } - - GL45Texture* targetTexture = nullptr; - { - auto& textures = texturesByMipCounts[mipLevel]; - assert(!textures.empty()); - targetTexture = *textures.begin(); - } - lock.unlock(); - targetTexture->derez(); -} diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp b/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp new file mode 100644 index 0000000000..c5db65058d --- /dev/null +++ b/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp @@ -0,0 +1,888 @@ +// +// GL45BackendTexture.cpp +// libraries/gpu/src/gpu +// +// Created by Sam Gateau on 1/19/2015. +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include "GL45Backend.h" +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include "../gl/GLTexelFormat.h" + +using namespace gpu; +using namespace gpu::gl; +using namespace gpu::gl45; + +// Variable sized textures +using GL45VariableAllocationTexture = GL45Backend::GL45VariableAllocationTexture; +using MemoryPressureState = GL45VariableAllocationTexture::MemoryPressureState; + +std::list GL45VariableAllocationTexture::_memoryManagedTextures; +MemoryPressureState GL45VariableAllocationTexture::_memoryPressureState = MemoryPressureState::Idle; +std::atomic GL45VariableAllocationTexture::_memoryPressureStateStale { false }; +const uvec3 GL45VariableAllocationTexture::INITIAL_MIP_TRANSFER_DIMENSIONS { 64, 64, 1 }; + +#define OVERSUBSCRIBED_PRESSURE_VALUE 0.95f +#define UNDERSUBSCRIBED_PRESSURE_VALUE 0.85f +#define DEFAULT_ALLOWED_TEXTURE_MEMORY_MB ((size_t)1024) + +static const size_t DEFAULT_ALLOWED_TEXTURE_MEMORY = MB_TO_BYTES(DEFAULT_ALLOWED_TEXTURE_MEMORY_MB); + +using QueuePair = std::pair; +class QueuePairLess { +public: + bool operator()(const QueuePair& a, const QueuePair& b) { + return a.second < b.second; + } +}; +class QueuePairGreater { +public: + bool operator()(const QueuePair& a, const QueuePair& b) { + return a.second > b.second; + } +}; +using DemoteQueue = std::priority_queue, QueuePairLess>; +using PromoteQueue = std::priority_queue, QueuePairGreater>; +using TransferQueue = std::queue; +static DemoteQueue demoteQueue; +static PromoteQueue promoteQueue; +static TransferQueue transferQueue; + +void GL45VariableAllocationTexture::addMemoryManagedTexture(const TexturePointer& texturePointer) { + _memoryManagedTextures.push_back(texturePointer); + GL45VariableAllocationTexture* object = Backend::getGPUObject(*texturePointer); + switch (_memoryPressureState) { + case MemoryPressureState::Oversubscribed: + if (object->canDemote()) { + demoteQueue.push({ texturePointer, object->size() }); + } + break; + + case MemoryPressureState::Undersubscribed: + if (object->canPromote()) { + promoteQueue.push({ texturePointer, object->size() }); + } + break; + + case MemoryPressureState::Transfer: + if (object->hasPendingTransfers()) { + transferQueue.push( texturePointer ); + } + break; + + case MemoryPressureState::Idle: + break; + + default: + Q_UNREACHABLE(); + } +} + +void GL45VariableAllocationTexture::updateMemoryPressure() { + static size_t lastAllowedMemoryAllocation = gpu::Texture::getAllowedGPUMemoryUsage(); + size_t allowedMemoryAllocation = gpu::Texture::getAllowedGPUMemoryUsage(); + if (allowedMemoryAllocation != lastAllowedMemoryAllocation) { + _memoryPressureStateStale = true; + lastAllowedMemoryAllocation = allowedMemoryAllocation; + } + + if (!_memoryPressureStateStale) { + return; + } + _memoryPressureStateStale = false; + // Clear any defunct textures + _memoryManagedTextures.remove_if([&](const TextureWeakPointer& weakPointer) { + return weakPointer.expired(); + }); + + // Convert weak pointers to strong + std::list strongTextures; { + std::transform( + _memoryManagedTextures.begin(), _memoryManagedTextures.end(), + std::back_inserter(strongTextures), + [](const TextureWeakPointer& p) { return p.lock(); }); + } + + size_t totalVariableMemoryAllocation = 0; + size_t idealMemoryAllocation = 0; + bool canDemote = false; + bool canPromote = false; + bool hasTransfers = false; + for (const auto& texture : strongTextures) { + // Race conditions can still leave nulls in the list, so we need to check + if (!texture) { + continue; + } + idealMemoryAllocation += texture->evalTotalSize(); + GL45VariableAllocationTexture* object = Backend::getGPUObject(*texture); + totalVariableMemoryAllocation += object->size(); + canDemote |= object->canDemote(); + canPromote |= object->canPromote(); + hasTransfers |= object->hasPendingTransfers(); + } + + size_t unallocated = idealMemoryAllocation - totalVariableMemoryAllocation; + if (0 == allowedMemoryAllocation) { + allowedMemoryAllocation = DEFAULT_ALLOWED_TEXTURE_MEMORY; + } + + + float pressure = (float)totalVariableMemoryAllocation / (float)allowedMemoryAllocation; + + auto newState = MemoryPressureState::Idle; + if (pressure > OVERSUBSCRIBED_PRESSURE_VALUE && canDemote) { + newState = MemoryPressureState::Oversubscribed; + } else if (pressure < UNDERSUBSCRIBED_PRESSURE_VALUE && unallocated != 0 && canPromote) { + newState = MemoryPressureState::Undersubscribed; + } else if (hasTransfers) { + newState = MemoryPressureState::Transfer; + } + + if (newState != _memoryPressureState) { + _memoryPressureState = newState; + + demoteQueue = DemoteQueue(); + promoteQueue = PromoteQueue(); + transferQueue = TransferQueue(); + + switch (_memoryPressureState) { + case MemoryPressureState::Idle: + break; + + case MemoryPressureState::Oversubscribed: + for (const auto& texture : strongTextures) { + if (!texture) { + continue; + } + GL45VariableAllocationTexture* object = Backend::getGPUObject(*texture); + if (object->canDemote()) { + demoteQueue.push({ texture, object->size() }); + } + } + break; + + case MemoryPressureState::Undersubscribed: + for (const auto& texture : strongTextures) { + if (!texture) { + continue; + } + GL45VariableAllocationTexture* object = Backend::getGPUObject(*texture); + if (object->canPromote()) { + promoteQueue.push({ texture, object->size() }); + } + } + break; + + case MemoryPressureState::Transfer: + for (const auto& texture : strongTextures) { + if (!texture) { + continue; + } + GL45VariableAllocationTexture* object = Backend::getGPUObject(*texture); + if (object->hasPendingTransfers()) { + transferQueue.push(texture); + } + } + break; + + default: + Q_UNREACHABLE(); + break; + } + } +} + +void GL45VariableAllocationTexture::processWorkQueues() { + switch (_memoryPressureState) { + case MemoryPressureState::Idle: + break; + + case MemoryPressureState::Oversubscribed: + // Grab the first item off the demote queue + while (!demoteQueue.empty()) { + auto demoteTarget = demoteQueue.top(); + demoteQueue.pop(); + auto texture = demoteTarget.first.lock(); + if (!texture) { + continue; + } + + GL45VariableAllocationTexture* object = Backend::getGPUObject(*texture); + if (!object->canDemote()) { + continue; + } + + //qDebug() << "QQQ executing demote for " << texture->source().c_str(); + object->demote(); + // if the object can be further demoted, reinsert into the queue + if (object->canDemote()) { + demoteQueue.push({ demoteTarget.first, object->size() }); + } + break; + } + if (demoteQueue.empty()) { + _memoryPressureState = MemoryPressureState::Idle; + } + break; + + case MemoryPressureState::Undersubscribed: + while (!promoteQueue.empty()) { + auto promoteTarget = promoteQueue.top(); + promoteQueue.pop(); + auto texture = promoteTarget.first.lock(); + if (!texture) { + continue; + } + GL45VariableAllocationTexture* object = Backend::getGPUObject(*texture); + if (!object->canPromote()) { + continue; + } + //qDebug() << "QQQ executing promote for " << texture->source().c_str(); + object->promote(); + if (object->canPromote()) { + promoteQueue.push({ promoteTarget.first, object->size() }); + } + break; + } + if (promoteQueue.empty()) { + _memoryPressureState = MemoryPressureState::Idle; + } + break; + + case MemoryPressureState::Transfer: + while (!transferQueue.empty()) { + auto weakTexture = transferQueue.front(); + transferQueue.pop(); + auto texture = weakTexture.lock(); + if (!texture) { + continue; + } + GL45VariableAllocationTexture* object = Backend::getGPUObject(*texture); + if (!object->hasPendingTransfers()) { + continue; + } + //qDebug() << "QQQ executing transfer for " << texture->source().c_str(); + object->executeNextTransfer(); + if (object->hasPendingTransfers()) { + transferQueue.push(weakTexture); + } + break; + } + if (transferQueue.empty()) { + _memoryPressureState = MemoryPressureState::Idle; + } + break; + + default: + Q_UNREACHABLE(); + break; + } +} + +void GL45VariableAllocationTexture::manageMemory() { + static auto lastProcessTime = usecTimestampNow(); + auto now = usecTimestampNow(); + auto interval = now - lastProcessTime; + if (interval > (USECS_PER_MSEC * 20)) { + lastProcessTime = now; + updateMemoryPressure(); + processWorkQueues(); + } +} + +GL45VariableAllocationTexture::GL45VariableAllocationTexture(const std::weak_ptr& backend, const Texture& texture) : GL45Texture(backend, texture) { +} + +GL45VariableAllocationTexture::~GL45VariableAllocationTexture() { + _memoryPressureStateStale = true; + Backend::updateTextureGPUMemoryUsage(_size, 0); +} + +void GL45VariableAllocationTexture::executeNextTransfer() { + if (!_pendingTransfers.empty()) { + _pendingTransfers.front()(); + _pendingTransfers.pop(); + } +} + +// Managed size resource textures +using GL45ResourceTexture = GL45Backend::GL45ResourceTexture; + +GL45ResourceTexture::GL45ResourceTexture(const std::weak_ptr& backend, const Texture& texture) : GL45VariableAllocationTexture(backend, texture) { + auto mipLevels = texture.evalNumMips(); + _allocatedMip = mipLevels; + uvec3 mipDimensions; + for (uint16_t mip = 0; mip < mipLevels; ++mip) { + if (glm::all(glm::lessThanEqual(texture.evalMipDimensions(mip), INITIAL_MIP_TRANSFER_DIMENSIONS))) { + _maxAllocatedMip = _populatedMip = mip; + break; + } + } + + uint16_t allocatedMip = _populatedMip - std::min(_populatedMip, 2); + allocateStorage(allocatedMip); + _memoryPressureStateStale = true; + copyMipsFromTexture(); + syncSampler(); + +} + +void GL45ResourceTexture::allocateStorage(uint16 allocatedMip) { + _allocatedMip = allocatedMip; + const GLTexelFormat texelFormat = GLTexelFormat::evalGLTexelFormat(_gpuObject.getTexelFormat()); + const auto dimensions = _gpuObject.evalMipDimensions(_allocatedMip); + const auto totalMips = _gpuObject.evalNumMips(); + const auto mips = totalMips - _allocatedMip; + glTextureStorage2D(_id, mips, texelFormat.internalFormat, dimensions.x, dimensions.y); + auto mipLevels = _gpuObject.evalNumMips(); + _size = 0; + for (uint16_t mip = _allocatedMip; mip < mipLevels; ++mip) { + _size += _gpuObject.evalMipSize(mip); + } + Backend::updateTextureGPUMemoryUsage(0, _size); + +} + +void GL45ResourceTexture::copyMipsFromTexture() { + auto mipLevels = _gpuObject.evalNumMips(); + for (uint16_t sourceMip = _populatedMip; sourceMip < mipLevels; ++sourceMip) { + uint16_t targetMip = sourceMip - _allocatedMip; + copyMipFromTexture(sourceMip, targetMip); + } +} + +void GL45ResourceTexture::syncSampler() const { + Parent::syncSampler(); + const Sampler& sampler = _gpuObject.getSampler(); + uint16_t maxMip = _gpuObject.evalNumMips() - _allocatedMip; + auto minMip = std::max(sampler.getMipOffset(), sampler.getMinMip()); + minMip = std::min(minMip, maxMip); + glTextureParameteri(_id, GL_TEXTURE_BASE_LEVEL, _populatedMip - _allocatedMip); + glTextureParameterf(_id, GL_TEXTURE_MIN_LOD, (float)minMip); + glTextureParameterf(_id, GL_TEXTURE_MAX_LOD, (float)maxMip); +} + +void GL45ResourceTexture::promote() { + Q_ASSERT(_allocatedMip > 0); + GLuint oldId = _id; + uint32_t oldSize = _size; + // create new texture + const_cast(_id) = allocate(_gpuObject); + uint16_t oldAllocatedMip = _allocatedMip; + // allocate storage for new level + allocateStorage(_allocatedMip - std::min(_allocatedMip, 2)); + uint16_t mips = _gpuObject.evalNumMips(); + // copy pre-existing mips + for (uint16_t mip = _populatedMip; mip < mips; ++mip) { + auto mipDimensions = _gpuObject.evalMipDimensions(mip); + uint16_t targetMip = mip - _allocatedMip; + uint16_t sourceMip = mip - oldAllocatedMip; + auto faces = getFaceCount(_target); + for (uint8_t face = 0; face < faces; ++face) { + glCopyImageSubData( + oldId, _target, sourceMip, 0, 0, face, + _id, _target, targetMip, 0, 0, face, + mipDimensions.x, mipDimensions.y, 1 + ); + (void)CHECK_GL_ERROR(); + } + } + // destroy the old texture + glDeleteTextures(1, &oldId); + // update the memory usage + Backend::updateTextureGPUMemoryUsage(oldSize, 0); + _memoryPressureStateStale = true; + syncSampler(); + populateTransferQueue(); +} + +void GL45ResourceTexture::demote() { + Q_ASSERT(_allocatedMip < _maxAllocatedMip); + auto oldId = _id; + auto oldSize = _size; + const_cast(_id) = allocate(_gpuObject); + allocateStorage(_allocatedMip + 1); + _populatedMip = std::max(_populatedMip, _allocatedMip); + uint16_t mips = _gpuObject.evalNumMips(); + // copy pre-existing mips + for (uint16_t mip = _populatedMip; mip < mips; ++mip) { + auto mipDimensions = _gpuObject.evalMipDimensions(mip); + uint16_t targetMip = mip - _allocatedMip; + uint16_t sourceMip = targetMip + 1; + auto faces = getFaceCount(_target); + for (uint8_t face = 0; face < faces; ++face) { + glCopyImageSubData( + oldId, _target, sourceMip, 0, 0, face, + _id, _target, targetMip, 0, 0, face, + mipDimensions.x, mipDimensions.y, 1 + ); + (void)CHECK_GL_ERROR(); + } + } + // destroy the old texture + glDeleteTextures(1, &oldId); + // update the memory usage + Backend::updateTextureGPUMemoryUsage(oldSize, 0); + _memoryPressureStateStale = true; + syncSampler(); + populateTransferQueue(); +} + +void GL45ResourceTexture::populateTransferQueue() { + _pendingTransfers = std::queue(); + if (_populatedMip <= _allocatedMip) { + return; + } + + for (int16_t mip = _populatedMip - 1; mip >= _allocatedMip; --mip) { + // FIXME break down the transfers into chunks so that no single transfer is + // consuming more than X bandwidth + _pendingTransfers.push([mip, this] { + Q_ASSERT(mip >= _allocatedMip); + // FIXME modify the copy mechanism to be incremental + copyMipFromTexture(mip, mip - _allocatedMip); + _populatedMip = mip; + syncSampler(); + }); + } +} + +// Sparsely allocated, managed size resource textures +#if 0 +#define SPARSE_PAGE_SIZE_OVERHEAD_ESTIMATE 1.3f + +using GL45SparseResourceTexture = GL45Backend::GL45SparseResourceTexture; + +GL45Texture::PageDimensionsMap GL45Texture::pageDimensionsByFormat; +Mutex GL45Texture::pageDimensionsMutex; + +GL45Texture::PageDimensions GL45Texture::getPageDimensionsForFormat(const TextureTypeFormat& typeFormat) { + { + Lock lock(pageDimensionsMutex); + if (pageDimensionsByFormat.count(typeFormat)) { + return pageDimensionsByFormat[typeFormat]; + } + } + + GLint count = 0; + glGetInternalformativ(typeFormat.first, typeFormat.second, GL_NUM_VIRTUAL_PAGE_SIZES_ARB, 1, &count); + + std::vector result; + if (count > 0) { + std::vector x, y, z; + x.resize(count); + glGetInternalformativ(typeFormat.first, typeFormat.second, GL_VIRTUAL_PAGE_SIZE_X_ARB, 1, &x[0]); + y.resize(count); + glGetInternalformativ(typeFormat.first, typeFormat.second, GL_VIRTUAL_PAGE_SIZE_Y_ARB, 1, &y[0]); + z.resize(count); + glGetInternalformativ(typeFormat.first, typeFormat.second, GL_VIRTUAL_PAGE_SIZE_Z_ARB, 1, &z[0]); + + result.resize(count); + for (GLint i = 0; i < count; ++i) { + result[i] = uvec3(x[i], y[i], z[i]); + } + } + + { + Lock lock(pageDimensionsMutex); + if (0 == pageDimensionsByFormat.count(typeFormat)) { + pageDimensionsByFormat[typeFormat] = result; + } + } + + return result; +} + +GL45Texture::PageDimensions GL45Texture::getPageDimensionsForFormat(GLenum target, GLenum format) { + return getPageDimensionsForFormat({ target, format }); +} +bool GL45Texture::isSparseEligible(const Texture& texture) { + Q_ASSERT(TextureUsageType::RESOURCE == texture.getUsageType()); + + // Disabling sparse for the momemnt + return false; + + const auto allowedPageDimensions = getPageDimensionsForFormat(getGLTextureType(texture), + gl::GLTexelFormat::evalGLTexelFormatInternal(texture.getTexelFormat())); + const auto textureDimensions = texture.getDimensions(); + for (const auto& pageDimensions : allowedPageDimensions) { + if (uvec3(0) == (textureDimensions % pageDimensions)) { + return true; + } + } + + return false; +} + + +GL45SparseResourceTexture::GL45SparseResourceTexture(const std::weak_ptr& backend, const Texture& texture) : GL45VariableAllocationTexture(backend, texture) { + const GLTexelFormat texelFormat = GLTexelFormat::evalGLTexelFormat(_gpuObject.getTexelFormat()); + const uvec3 dimensions = _gpuObject.getDimensions(); + auto allowedPageDimensions = getPageDimensionsForFormat(_target, texelFormat.internalFormat); + uint32_t pageDimensionsIndex = 0; + // In order to enable sparse the texture size must be an integer multiple of the page size + for (size_t i = 0; i < allowedPageDimensions.size(); ++i) { + pageDimensionsIndex = (uint32_t)i; + _pageDimensions = allowedPageDimensions[i]; + // Is this texture an integer multiple of page dimensions? + if (uvec3(0) == (dimensions % _pageDimensions)) { + qCDebug(gpugl45logging) << "Enabling sparse for texture " << _gpuObject.source().c_str(); + break; + } + } + glTextureParameteri(_id, GL_TEXTURE_SPARSE_ARB, GL_TRUE); + glTextureParameteri(_id, GL_VIRTUAL_PAGE_SIZE_INDEX_ARB, pageDimensionsIndex); + glGetTextureParameterIuiv(_id, GL_NUM_SPARSE_LEVELS_ARB, &_maxSparseLevel); + + _pageBytes = _gpuObject.getTexelFormat().getSize(); + _pageBytes *= _pageDimensions.x * _pageDimensions.y * _pageDimensions.z; + // Testing with a simple texture allocating app shows an estimated 20% GPU memory overhead for + // sparse textures as compared to non-sparse, so we acount for that here. + _pageBytes = (uint32_t)(_pageBytes * SPARSE_PAGE_SIZE_OVERHEAD_ESTIMATE); + + //allocateStorage(); + syncSampler(); +} + +GL45SparseResourceTexture::~GL45SparseResourceTexture() { + Backend::updateTextureGPUVirtualMemoryUsage(size(), 0); +} + +uvec3 GL45SparseResourceTexture::getPageCounts(const uvec3& dimensions) const { + auto result = (dimensions / _pageDimensions) + + glm::clamp(dimensions % _pageDimensions, glm::uvec3(0), glm::uvec3(1)); + return result; +} + +uint32_t GL45SparseResourceTexture::getPageCount(const uvec3& dimensions) const { + auto pageCounts = getPageCounts(dimensions); + return pageCounts.x * pageCounts.y * pageCounts.z; +} + +void GL45SparseResourceTexture::promote() { +} + +void GL45SparseResourceTexture::demote() { +} + +SparseInfo::SparseInfo(GL45Texture& texture) + : texture(texture) { +} + +void SparseInfo::maybeMakeSparse() { + // Don't enable sparse for objects with explicitly managed mip levels + if (!texture._gpuObject.isAutogenerateMips()) { + return; + } + + const uvec3 dimensions = texture._gpuObject.getDimensions(); + auto allowedPageDimensions = getPageDimensionsForFormat(texture._target, texture._internalFormat); + // In order to enable sparse the texture size must be an integer multiple of the page size + for (size_t i = 0; i < allowedPageDimensions.size(); ++i) { + pageDimensionsIndex = (uint32_t)i; + pageDimensions = allowedPageDimensions[i]; + // Is this texture an integer multiple of page dimensions? + if (uvec3(0) == (dimensions % pageDimensions)) { + qCDebug(gpugl45logging) << "Enabling sparse for texture " << texture._source.c_str(); + sparse = true; + break; + } + } + + if (sparse) { + glTextureParameteri(texture._id, GL_TEXTURE_SPARSE_ARB, GL_TRUE); + glTextureParameteri(texture._id, GL_VIRTUAL_PAGE_SIZE_INDEX_ARB, pageDimensionsIndex); + } else { + qCDebug(gpugl45logging) << "Size " << dimensions.x << " x " << dimensions.y << + " is not supported by any sparse page size for texture" << texture._source.c_str(); + } +} + + +// This can only be called after we've established our storage size +void SparseInfo::update() { + if (!sparse) { + return; + } + glGetTextureParameterIuiv(texture._id, GL_NUM_SPARSE_LEVELS_ARB, &maxSparseLevel); + + for (uint16_t mipLevel = 0; mipLevel <= maxSparseLevel; ++mipLevel) { + auto mipDimensions = texture._gpuObject.evalMipDimensions(mipLevel); + auto mipPageCount = getPageCount(mipDimensions); + maxPages += mipPageCount; + } + if (texture._target == GL_TEXTURE_CUBE_MAP) { + maxPages *= GLTexture::CUBE_NUM_FACES; + } +} + + +void SparseInfo::allocateToMip(uint16_t targetMip) { + // Not sparse, do nothing + if (!sparse) { + return; + } + + if (allocatedMip == INVALID_MIP) { + allocatedMip = maxSparseLevel + 1; + } + + // Don't try to allocate below the maximum sparse level + if (targetMip > maxSparseLevel) { + targetMip = maxSparseLevel; + } + + // Already allocated this level + if (allocatedMip <= targetMip) { + return; + } + + uint32_t maxFace = (uint32_t)(GL_TEXTURE_CUBE_MAP == texture._target ? CUBE_NUM_FACES : 1); + for (uint16_t mip = targetMip; mip < allocatedMip; ++mip) { + auto size = texture._gpuObject.evalMipDimensions(mip); + glTexturePageCommitmentEXT(texture._id, mip, 0, 0, 0, size.x, size.y, maxFace, GL_TRUE); + allocatedPages += getPageCount(size); + } + allocatedMip = targetMip; +} + +uint32_t SparseInfo::getSize() const { + return allocatedPages * pageBytes; +} +using SparseInfo = GL45Backend::GL45Texture::SparseInfo; + +void GL45Texture::updateSize() const { + if (_gpuObject.getTexelFormat().isCompressed()) { + qFatal("Compressed textures not yet supported"); + } + + if (_transferrable && _sparseInfo.sparse) { + auto size = _sparseInfo.getSize(); + Backend::updateTextureGPUSparseMemoryUsage(_size, size); + setSize(size); + } else { + setSize(_gpuObject.evalTotalSize(_mipOffset)); + } +} + +void GL45Texture::startTransfer() { + Parent::startTransfer(); + _sparseInfo.update(); + _populatedMip = _maxMip + 1; +} + +bool GL45Texture::continueTransfer() { + size_t maxFace = GL_TEXTURE_CUBE_MAP == _target ? CUBE_NUM_FACES : 1; + if (_populatedMip == _minMip) { + return false; + } + + uint16_t targetMip = _populatedMip - 1; + while (targetMip > 0 && !_gpuObject.isStoredMipFaceAvailable(targetMip)) { + --targetMip; + } + + _sparseInfo.allocateToMip(targetMip); + for (uint8_t face = 0; face < maxFace; ++face) { + auto size = _gpuObject.evalMipDimensions(targetMip); + if (_gpuObject.isStoredMipFaceAvailable(targetMip, face)) { + auto mip = _gpuObject.accessStoredMipFace(targetMip, face); + GLTexelFormat texelFormat = GLTexelFormat::evalGLTexelFormat(_gpuObject.getTexelFormat(), mip->getFormat()); + if (GL_TEXTURE_2D == _target) { + glTextureSubImage2D(_id, targetMip, 0, 0, size.x, size.y, texelFormat.format, texelFormat.type, mip->readData()); + } else if (GL_TEXTURE_CUBE_MAP == _target) { + // DSA ARB does not work on AMD, so use EXT + // unless EXT is not available on the driver + if (glTextureSubImage2DEXT) { + auto target = CUBE_FACE_LAYOUT[face]; + glTextureSubImage2DEXT(_id, target, targetMip, 0, 0, size.x, size.y, texelFormat.format, texelFormat.type, mip->readData()); + } else { + glTextureSubImage3D(_id, targetMip, 0, 0, face, size.x, size.y, 1, texelFormat.format, texelFormat.type, mip->readData()); + } + } else { + Q_ASSERT(false); + } + (void)CHECK_GL_ERROR(); + break; + } + } + _populatedMip = targetMip; + return _populatedMip != _minMip; +} + +void GL45Texture::finishTransfer() { + Parent::finishTransfer(); +} + +void GL45Texture::postTransfer() { + Parent::postTransfer(); +} + +void GL45Texture::stripToMip(uint16_t newMinMip) { + if (newMinMip < _minMip) { + qCWarning(gpugl45logging) << "Cannot decrease the min mip"; + return; + } + + if (_sparseInfo.sparse && newMinMip > _sparseInfo.maxSparseLevel) { + qCWarning(gpugl45logging) << "Cannot increase the min mip into the mip tail"; + return; + } + + // If we weren't generating mips before, we need to now that we're stripping down mip levels. + if (!_gpuObject.isAutogenerateMips()) { + qCDebug(gpugl45logging) << "Force mip generation for texture"; + glGenerateTextureMipmap(_id); + } + + + uint8_t maxFace = (uint8_t)((_target == GL_TEXTURE_CUBE_MAP) ? GLTexture::CUBE_NUM_FACES : 1); + if (_sparseInfo.sparse) { + for (uint16_t mip = _minMip; mip < newMinMip; ++mip) { + auto id = _id; + auto mipDimensions = _gpuObject.evalMipDimensions(mip); + glTexturePageCommitmentEXT(id, mip, 0, 0, 0, mipDimensions.x, mipDimensions.y, maxFace, GL_FALSE); + auto deallocatedPages = _sparseInfo.getPageCount(mipDimensions) * maxFace; + assert(deallocatedPages < _sparseInfo.allocatedPages); + _sparseInfo.allocatedPages -= deallocatedPages; + } + _minMip = newMinMip; + } else { + GLuint oldId = _id; + // Find the distance between the old min mip and the new one + uint16 mipDelta = newMinMip - _minMip; + _mipOffset += mipDelta; + const_cast(_maxMip) -= mipDelta; + auto newLevels = usedMipLevels(); + + // Create and setup the new texture (allocate) + glCreateTextures(_target, 1, &const_cast(_id)); + glTextureParameteri(_id, GL_TEXTURE_BASE_LEVEL, 0); + glTextureParameteri(_id, GL_TEXTURE_MAX_LEVEL, _maxMip - _minMip); + Vec3u newDimensions = _gpuObject.evalMipDimensions(_mipOffset); + glTextureStorage2D(_id, newLevels, _internalFormat, newDimensions.x, newDimensions.y); + + // Copy the contents of the old texture to the new + GLuint fbo { 0 }; + glCreateFramebuffers(1, &fbo); + glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo); + for (uint16 targetMip = _minMip; targetMip <= _maxMip; ++targetMip) { + uint16 sourceMip = targetMip + mipDelta; + Vec3u mipDimensions = _gpuObject.evalMipDimensions(targetMip + _mipOffset); + for (GLenum target : getFaceTargets(_target)) { + glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, target, oldId, sourceMip); + (void)CHECK_GL_ERROR(); + glCopyTextureSubImage2D(_id, targetMip, 0, 0, 0, 0, mipDimensions.x, mipDimensions.y); + (void)CHECK_GL_ERROR(); + } + } + glBindFramebuffer(GL_READ_FRAMEBUFFER, 0); + glDeleteFramebuffers(1, &fbo); + glDeleteTextures(1, &oldId); + } + + // Re-sync the sampler to force access to the new mip level + syncSampler(); + updateSize(); +} + +bool GL45Texture::derezable() const { + if (_external) { + return false; + } + auto maxMinMip = _sparseInfo.sparse ? _sparseInfo.maxSparseLevel : _maxMip; + return _transferrable && (_targetMinMip < maxMinMip); +} + +size_t GL45Texture::getMipByteCount(uint16_t mip) const { + if (!_sparseInfo.sparse) { + return Parent::getMipByteCount(mip); + } + + auto dimensions = _gpuObject.evalMipDimensions(_targetMinMip); + return _sparseInfo.getPageCount(dimensions) * _sparseInfo.pageBytes; +} + +std::pair GL45Texture::preDerez() { + assert(!_sparseInfo.sparse || _targetMinMip < _sparseInfo.maxSparseLevel); + size_t freedMemory = getMipByteCount(_targetMinMip); + bool liveMip = _populatedMip != INVALID_MIP && _populatedMip <= _targetMinMip; + ++_targetMinMip; + return { freedMemory, liveMip }; +} + +void GL45Texture::derez() { + if (_sparseInfo.sparse) { + assert(_minMip < _sparseInfo.maxSparseLevel); + } + assert(_minMip < _maxMip); + assert(_transferrable); + stripToMip(_minMip + 1); +} + +size_t GL45Texture::getCurrentGpuSize() const { + if (!_sparseInfo.sparse) { + return Parent::getCurrentGpuSize(); + } + + return _sparseInfo.getSize(); +} + +size_t GL45Texture::getTargetGpuSize() const { + if (!_sparseInfo.sparse) { + return Parent::getTargetGpuSize(); + } + + size_t result = 0; + for (auto mip = _targetMinMip; mip <= _sparseInfo.maxSparseLevel; ++mip) { + result += (_sparseInfo.pageBytes * _sparseInfo.getPageCount(_gpuObject.evalMipDimensions(mip))); + } + + return result; +} + +GL45Texture::~GL45Texture() { + if (_sparseInfo.sparse) { + uint8_t maxFace = (uint8_t)((_target == GL_TEXTURE_CUBE_MAP) ? GLTexture::CUBE_NUM_FACES : 1); + auto maxSparseMip = std::min(_maxMip, _sparseInfo.maxSparseLevel); + for (uint16_t mipLevel = _minMip; mipLevel <= maxSparseMip; ++mipLevel) { + auto mipDimensions = _gpuObject.evalMipDimensions(mipLevel); + glTexturePageCommitmentEXT(_texture, mipLevel, 0, 0, 0, mipDimensions.x, mipDimensions.y, maxFace, GL_FALSE); + auto deallocatedPages = _sparseInfo.getPageCount(mipDimensions) * maxFace; + assert(deallocatedPages <= _sparseInfo.allocatedPages); + _sparseInfo.allocatedPages -= deallocatedPages; + } + + if (0 != _sparseInfo.allocatedPages) { + qCWarning(gpugl45logging) << "Allocated pages remaining " << _id << " " << _sparseInfo.allocatedPages; + } + Backend::decrementTextureGPUSparseCount(); + } +} +GL45Texture::GL45Texture(const std::weak_ptr& backend, const Texture& texture) + : GLTexture(backend, texture, allocate(texture)), _sparseInfo(*this), _targetMinMip(_minMip) +{ + + auto theBackend = _backend.lock(); + if (_transferrable && theBackend && theBackend->isTextureManagementSparseEnabled()) { + _sparseInfo.maybeMakeSparse(); + if (_sparseInfo.sparse) { + Backend::incrementTextureGPUSparseCount(); + } + } +} +#endif diff --git a/libraries/gpu/src/gpu/Batch.cpp b/libraries/gpu/src/gpu/Batch.cpp index c15da61800..f822da129b 100644 --- a/libraries/gpu/src/gpu/Batch.cpp +++ b/libraries/gpu/src/gpu/Batch.cpp @@ -292,15 +292,8 @@ void Batch::setUniformBuffer(uint32 slot, const BufferView& view) { setUniformBuffer(slot, view._buffer, view._offset, view._size); } - void Batch::setResourceTexture(uint32 slot, const TexturePointer& texture) { - if (texture && texture->getUsage().isExternal()) { - auto recycler = texture->getExternalRecycler(); - Q_ASSERT(recycler); - } - ADD_COMMAND(setResourceTexture); - _params.emplace_back(_textures.cache(texture)); _params.emplace_back(slot); } diff --git a/libraries/gpu/src/gpu/Framebuffer.cpp b/libraries/gpu/src/gpu/Framebuffer.cpp index e8ccfce3b2..0d3291a74d 100755 --- a/libraries/gpu/src/gpu/Framebuffer.cpp +++ b/libraries/gpu/src/gpu/Framebuffer.cpp @@ -32,7 +32,7 @@ Framebuffer* Framebuffer::create(const std::string& name) { Framebuffer* Framebuffer::create(const std::string& name, const Format& colorBufferFormat, uint16 width, uint16 height) { auto framebuffer = Framebuffer::create(name); - auto colorTexture = TexturePointer(Texture::create2D(colorBufferFormat, width, height, Sampler(Sampler::FILTER_MIN_MAG_POINT))); + auto colorTexture = TexturePointer(Texture::createRenderBuffer(colorBufferFormat, width, height, Sampler(Sampler::FILTER_MIN_MAG_POINT))); colorTexture->setSource("Framebuffer::colorTexture"); framebuffer->setRenderBuffer(0, colorTexture); @@ -43,8 +43,8 @@ Framebuffer* Framebuffer::create(const std::string& name, const Format& colorBuf Framebuffer* Framebuffer::create(const std::string& name, const Format& colorBufferFormat, const Format& depthStencilBufferFormat, uint16 width, uint16 height) { auto framebuffer = Framebuffer::create(name); - auto colorTexture = TexturePointer(Texture::create2D(colorBufferFormat, width, height, Sampler(Sampler::FILTER_MIN_MAG_POINT))); - auto depthTexture = TexturePointer(Texture::create2D(depthStencilBufferFormat, width, height, Sampler(Sampler::FILTER_MIN_MAG_POINT))); + auto colorTexture = TexturePointer(Texture::createRenderBuffer(colorBufferFormat, width, height, Sampler(Sampler::FILTER_MIN_MAG_POINT))); + auto depthTexture = TexturePointer(Texture::createRenderBuffer(depthStencilBufferFormat, width, height, Sampler(Sampler::FILTER_MIN_MAG_POINT))); framebuffer->setRenderBuffer(0, colorTexture); framebuffer->setDepthStencilBuffer(depthTexture, depthStencilBufferFormat); @@ -55,7 +55,7 @@ Framebuffer* Framebuffer::createShadowmap(uint16 width) { auto framebuffer = Framebuffer::create("Shadowmap"); auto depthFormat = Element(gpu::SCALAR, gpu::FLOAT, gpu::DEPTH); // Depth32 texel format - auto depthTexture = TexturePointer(Texture::create2D(depthFormat, width, width)); + auto depthTexture = TexturePointer(Texture::createRenderBuffer(depthFormat, width, width)); Sampler::Desc samplerDesc; samplerDesc._borderColor = glm::vec4(1.0f); samplerDesc._wrapModeU = Sampler::WRAP_BORDER; @@ -143,6 +143,8 @@ int Framebuffer::setRenderBuffer(uint32 slot, const TexturePointer& texture, uin return -1; } + Q_ASSERT(!texture || TextureUsageType::RENDERBUFFER == texture->getUsageType()); + // Check for the slot if (slot >= getMaxNumRenderBuffers()) { return -1; @@ -222,6 +224,8 @@ bool Framebuffer::setDepthStencilBuffer(const TexturePointer& texture, const For return false; } + Q_ASSERT(!texture || TextureUsageType::RENDERBUFFER == texture->getUsageType()); + // Check for the compatibility of size if (texture) { if (!validateTargetCompatibility(*texture)) { diff --git a/libraries/gpu/src/gpu/Texture.cpp b/libraries/gpu/src/gpu/Texture.cpp index 5b0c4c876a..9db3fb60c3 100755 --- a/libraries/gpu/src/gpu/Texture.cpp +++ b/libraries/gpu/src/gpu/Texture.cpp @@ -253,35 +253,42 @@ bool Texture::Storage::assignMipFaceData(uint16 level, const Element& format, Si return allocated == size; } -Texture* Texture::createExternal2D(const ExternalRecycler& recycler, const Sampler& sampler) { - Texture* tex = new Texture(); +Texture* Texture::createExternal(const ExternalRecycler& recycler, const Sampler& sampler) { + Texture* tex = new Texture(TextureUsageType::EXTERNAL); tex->_type = TEX_2D; tex->_maxMip = 0; tex->_sampler = sampler; - tex->setUsage(Usage::Builder().withExternal().withColor()); tex->setExternalRecycler(recycler); return tex; } +Texture* Texture::createRenderBuffer(const Element& texelFormat, uint16 width, uint16 height, const Sampler& sampler) { + return create(TextureUsageType::RENDERBUFFER, TEX_2D, texelFormat, width, height, 1, 1, 1, sampler); +} + Texture* Texture::create1D(const Element& texelFormat, uint16 width, const Sampler& sampler) { - return create(TEX_1D, texelFormat, width, 1, 1, 1, 1, sampler); + return create(TextureUsageType::RESOURCE, TEX_1D, texelFormat, width, 1, 1, 1, 1, sampler); } Texture* Texture::create2D(const Element& texelFormat, uint16 width, uint16 height, const Sampler& sampler) { - return create(TEX_2D, texelFormat, width, height, 1, 1, 1, sampler); + return create(TextureUsageType::RESOURCE, TEX_2D, texelFormat, width, height, 1, 1, 1, sampler); +} + +Texture* Texture::createStrict(const Element& texelFormat, uint16 width, uint16 height, const Sampler& sampler) { + return create(TextureUsageType::STRICT_RESOURCE, TEX_2D, texelFormat, width, height, 1, 1, 1, sampler); } Texture* Texture::create3D(const Element& texelFormat, uint16 width, uint16 height, uint16 depth, const Sampler& sampler) { - return create(TEX_3D, texelFormat, width, height, depth, 1, 1, sampler); + return create(TextureUsageType::RESOURCE, TEX_3D, texelFormat, width, height, depth, 1, 1, sampler); } Texture* Texture::createCube(const Element& texelFormat, uint16 width, const Sampler& sampler) { - return create(TEX_CUBE, texelFormat, width, width, 1, 1, 1, sampler); + return create(TextureUsageType::RESOURCE, TEX_CUBE, texelFormat, width, width, 1, 1, 1, sampler); } -Texture* Texture::create(Type type, const Element& texelFormat, uint16 width, uint16 height, uint16 depth, uint16 numSamples, uint16 numSlices, const Sampler& sampler) +Texture* Texture::create(TextureUsageType usageType, Type type, const Element& texelFormat, uint16 width, uint16 height, uint16 depth, uint16 numSamples, uint16 numSlices, const Sampler& sampler) { - Texture* tex = new Texture(); + Texture* tex = new Texture(usageType); tex->_storage.reset(new Storage()); tex->_type = type; tex->_storage->assignTexture(tex); @@ -293,16 +300,14 @@ Texture* Texture::create(Type type, const Element& texelFormat, uint16 width, ui return tex; } -Texture::Texture(): - Resource() -{ +Texture::Texture(TextureUsageType usageType) : + Resource(), _usageType(usageType) { _textureCPUCount++; } -Texture::~Texture() -{ +Texture::~Texture() { _textureCPUCount--; - if (getUsage().isExternal()) { + if (_usageType == TextureUsageType::EXTERNAL) { Texture::ExternalUpdates externalUpdates; { Lock lock(_externalMutex); diff --git a/libraries/gpu/src/gpu/Texture.h b/libraries/gpu/src/gpu/Texture.h index 856bd4983d..3c6c34c68d 100755 --- a/libraries/gpu/src/gpu/Texture.h +++ b/libraries/gpu/src/gpu/Texture.h @@ -139,6 +139,13 @@ protected: Desc _desc; }; +enum class TextureUsageType { + RENDERBUFFER, // Used as attachments to a framebuffer + RESOURCE, // Resource textures, like materials... subject to memory manipulation + STRICT_RESOURCE, // Resource textures not subject to manipulation, like the normal fitting texture + EXTERNAL, +}; + class Texture : public Resource { static std::atomic _textureCPUCount; static std::atomic _textureCPUMemoryUsage; @@ -173,9 +180,9 @@ public: NORMAL, // Texture is a normal map ALPHA, // Texture has an alpha channel ALPHA_MASK, // Texture alpha channel is a Mask 0/1 - EXTERNAL, NUM_FLAGS, }; + typedef std::bitset Flags; // The key is the Flags @@ -199,7 +206,6 @@ public: Builder& withNormal() { _flags.set(NORMAL); return (*this); } Builder& withAlpha() { _flags.set(ALPHA); return (*this); } Builder& withAlphaMask() { _flags.set(ALPHA_MASK); return (*this); } - Builder& withExternal() { _flags.set(EXTERNAL); return (*this); } }; Usage(const Builder& builder) : Usage(builder._flags) {} @@ -208,8 +214,6 @@ public: bool isAlpha() const { return _flags[ALPHA]; } bool isAlphaMask() const { return _flags[ALPHA_MASK]; } - bool isExternal() const { return _flags[EXTERNAL]; } - bool operator==(const Usage& usage) { return (_flags == usage._flags); } bool operator!=(const Usage& usage) { return (_flags != usage._flags); } @@ -298,9 +302,11 @@ public: static Texture* create2D(const Element& texelFormat, uint16 width, uint16 height, const Sampler& sampler = Sampler()); static Texture* create3D(const Element& texelFormat, uint16 width, uint16 height, uint16 depth, const Sampler& sampler = Sampler()); static Texture* createCube(const Element& texelFormat, uint16 width, const Sampler& sampler = Sampler()); - static Texture* createExternal2D(const ExternalRecycler& recycler, const Sampler& sampler = Sampler()); + static Texture* createRenderBuffer(const Element& texelFormat, uint16 width, uint16 height, const Sampler& sampler = Sampler()); + static Texture* createStrict(const Element& texelFormat, uint16 width, uint16 height, const Sampler& sampler = Sampler()); + static Texture* createExternal(const ExternalRecycler& recycler, const Sampler& sampler = Sampler()); - Texture(); + Texture(TextureUsageType usageType); Texture(const Texture& buf); // deep copy of the sysmem texture Texture& operator=(const Texture& buf); // deep copy of the sysmem texture ~Texture(); @@ -325,6 +331,7 @@ public: // Size and format Type getType() const { return _type; } + TextureUsageType getUsageType() const { return _usageType; } bool isColorRenderTarget() const; bool isDepthStencilRenderTarget() const; @@ -476,6 +483,8 @@ public: ExternalUpdates getUpdates() const; protected: + const TextureUsageType _usageType; + // Should only be accessed internally or by the backend sync function mutable Mutex _externalMutex; mutable std::list _externalUpdates; @@ -513,7 +522,7 @@ protected: bool _isIrradianceValid = false; bool _defined = false; - static Texture* create(Type type, const Element& texelFormat, uint16 width, uint16 height, uint16 depth, uint16 numSamples, uint16 numSlices, const Sampler& sampler); + static Texture* create(TextureUsageType usageType, Type type, const Element& texelFormat, uint16 width, uint16 height, uint16 depth, uint16 numSamples, uint16 numSlices, const Sampler& sampler); Size resize(Type type, const Element& texelFormat, uint16 width, uint16 height, uint16 depth, uint16 numSamples, uint16 numSlices); }; diff --git a/libraries/model-networking/src/model-networking/TextureCache.cpp b/libraries/model-networking/src/model-networking/TextureCache.cpp index f371207981..6a84fc960f 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.cpp +++ b/libraries/model-networking/src/model-networking/TextureCache.cpp @@ -120,7 +120,7 @@ const unsigned char OPAQUE_BLACK[] = { 0x00, 0x00, 0x00, 0xFF }; const gpu::TexturePointer& TextureCache::getWhiteTexture() { if (!_whiteTexture) { - _whiteTexture = gpu::TexturePointer(gpu::Texture::create2D(gpu::Element::COLOR_RGBA_32, 1, 1)); + _whiteTexture = gpu::TexturePointer(gpu::Texture::createStrict(gpu::Element::COLOR_RGBA_32, 1, 1)); _whiteTexture->setSource("TextureCache::_whiteTexture"); _whiteTexture->assignStoredMip(0, _whiteTexture->getTexelFormat(), sizeof(OPAQUE_WHITE), OPAQUE_WHITE); } @@ -129,7 +129,7 @@ const gpu::TexturePointer& TextureCache::getWhiteTexture() { const gpu::TexturePointer& TextureCache::getGrayTexture() { if (!_grayTexture) { - _grayTexture = gpu::TexturePointer(gpu::Texture::create2D(gpu::Element::COLOR_RGBA_32, 1, 1)); + _grayTexture = gpu::TexturePointer(gpu::Texture::createStrict(gpu::Element::COLOR_RGBA_32, 1, 1)); _grayTexture->setSource("TextureCache::_grayTexture"); _grayTexture->assignStoredMip(0, _grayTexture->getTexelFormat(), sizeof(OPAQUE_GRAY), OPAQUE_GRAY); } @@ -138,7 +138,7 @@ const gpu::TexturePointer& TextureCache::getGrayTexture() { const gpu::TexturePointer& TextureCache::getBlueTexture() { if (!_blueTexture) { - _blueTexture = gpu::TexturePointer(gpu::Texture::create2D(gpu::Element::COLOR_RGBA_32, 1, 1)); + _blueTexture = gpu::TexturePointer(gpu::Texture::createStrict(gpu::Element::COLOR_RGBA_32, 1, 1)); _blueTexture->setSource("TextureCache::_blueTexture"); _blueTexture->assignStoredMip(0, _blueTexture->getTexelFormat(), sizeof(OPAQUE_BLUE), OPAQUE_BLUE); } @@ -147,7 +147,7 @@ const gpu::TexturePointer& TextureCache::getBlueTexture() { const gpu::TexturePointer& TextureCache::getBlackTexture() { if (!_blackTexture) { - _blackTexture = gpu::TexturePointer(gpu::Texture::create2D(gpu::Element::COLOR_RGBA_32, 1, 1)); + _blackTexture = gpu::TexturePointer(gpu::Texture::createStrict(gpu::Element::COLOR_RGBA_32, 1, 1)); _blackTexture->setSource("TextureCache::_blackTexture"); _blackTexture->assignStoredMip(0, _blackTexture->getTexelFormat(), sizeof(OPAQUE_BLACK), OPAQUE_BLACK); } @@ -157,7 +157,7 @@ const gpu::TexturePointer& TextureCache::getBlackTexture() { const gpu::TexturePointer& TextureCache::getNormalFittingTexture() { if (!_normalFittingTexture) { - _normalFittingTexture = getImageTexture(PathUtils::resourcesPath() + "images/normalFittingScale.dds"); + _normalFittingTexture = getImageTexture(PathUtils::resourcesPath() + "images/normalFittingScale.dds", NetworkTexture::STRICT_TEXTURE); } return _normalFittingTexture; } @@ -227,11 +227,16 @@ NetworkTexture::TextureLoaderFunc getTextureLoaderForType(NetworkTexture::Type t return model::TextureUsage::createMetallicTextureFromImage; break; } + case Type::STRICT_TEXTURE: { + return model::TextureUsage::createStrict2DTextureFromImage; + break; + } case Type::CUSTOM_TEXTURE: { Q_ASSERT(false); return NetworkTexture::TextureLoaderFunc(); break; } + case Type::DEFAULT_TEXTURE: default: { return model::TextureUsage::create2DTextureFromImage; diff --git a/libraries/model-networking/src/model-networking/TextureCache.h b/libraries/model-networking/src/model-networking/TextureCache.h index cb509490c6..749b5a2ebb 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.h +++ b/libraries/model-networking/src/model-networking/TextureCache.h @@ -43,6 +43,7 @@ class NetworkTexture : public Resource, public Texture { public: enum Type { DEFAULT_TEXTURE, + STRICT_TEXTURE, ALBEDO_TEXTURE, NORMAL_TEXTURE, BUMP_TEXTURE, diff --git a/libraries/model/src/model/TextureMap.cpp b/libraries/model/src/model/TextureMap.cpp index d1fbaf767a..6a9446f2ef 100755 --- a/libraries/model/src/model/TextureMap.cpp +++ b/libraries/model/src/model/TextureMap.cpp @@ -235,7 +235,7 @@ void generateFaceMips(gpu::Texture* texture, QImage& image, gpu::Element formatM #endif } -gpu::Texture* TextureUsage::process2DTextureColorFromImage(const QImage& srcImage, const std::string& srcImageName, bool isLinear, bool doCompress, bool generateMips) { +gpu::Texture* TextureUsage::process2DTextureColorFromImage(const QImage& srcImage, const std::string& srcImageName, bool isLinear, bool doCompress, bool generateMips, bool isStrict) { PROFILE_RANGE(resource_parse, "process2DTextureColorFromImage"); bool validAlpha = false; bool alphaAsMask = true; @@ -248,7 +248,11 @@ gpu::Texture* TextureUsage::process2DTextureColorFromImage(const QImage& srcImag gpu::Element formatMip; defineColorTexelFormats(formatGPU, formatMip, image, isLinear, doCompress); - theTexture = (gpu::Texture::create2D(formatGPU, image.width(), image.height(), gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR))); + if (isStrict) { + theTexture = (gpu::Texture::createStrict(formatGPU, image.width(), image.height(), gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR))); + } else { + theTexture = (gpu::Texture::create2D(formatGPU, image.width(), image.height(), gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR))); + } theTexture->setSource(srcImageName); auto usage = gpu::Texture::Usage::Builder().withColor(); if (validAlpha) { @@ -269,11 +273,14 @@ gpu::Texture* TextureUsage::process2DTextureColorFromImage(const QImage& srcImag return theTexture; } +gpu::Texture* TextureUsage::createStrict2DTextureFromImage(const QImage& srcImage, const std::string& srcImageName) { + return process2DTextureColorFromImage(srcImage, srcImageName, false, false, true, true); +} + gpu::Texture* TextureUsage::create2DTextureFromImage(const QImage& srcImage, const std::string& srcImageName) { return process2DTextureColorFromImage(srcImage, srcImageName, false, false, true); } - gpu::Texture* TextureUsage::createAlbedoTextureFromImage(const QImage& srcImage, const std::string& srcImageName) { return process2DTextureColorFromImage(srcImage, srcImageName, false, true, true); } diff --git a/libraries/model/src/model/TextureMap.h b/libraries/model/src/model/TextureMap.h index 220ee57a97..a4bb861502 100755 --- a/libraries/model/src/model/TextureMap.h +++ b/libraries/model/src/model/TextureMap.h @@ -32,6 +32,7 @@ public: int _environmentUsage = 0; static gpu::Texture* create2DTextureFromImage(const QImage& image, const std::string& srcImageName); + static gpu::Texture* createStrict2DTextureFromImage(const QImage& image, const std::string& srcImageName); static gpu::Texture* createAlbedoTextureFromImage(const QImage& image, const std::string& srcImageName); static gpu::Texture* createEmissiveTextureFromImage(const QImage& image, const std::string& srcImageName); static gpu::Texture* createNormalTextureFromNormalImage(const QImage& image, const std::string& srcImageName); @@ -47,7 +48,7 @@ public: static const QImage process2DImageColor(const QImage& srcImage, bool& validAlpha, bool& alphaAsMask); static void defineColorTexelFormats(gpu::Element& formatGPU, gpu::Element& formatMip, const QImage& srcImage, bool isLinear, bool doCompress); - static gpu::Texture* process2DTextureColorFromImage(const QImage& srcImage, const std::string& srcImageName, bool isLinear, bool doCompress, bool generateMips); + static gpu::Texture* process2DTextureColorFromImage(const QImage& srcImage, const std::string& srcImageName, bool isLinear, bool doCompress, bool generateMips, bool isStrict = false); static gpu::Texture* processCubeTextureColorFromImage(const QImage& srcImage, const std::string& srcImageName, bool isLinear, bool doCompress, bool generateMips, bool generateIrradiance); }; diff --git a/libraries/render-utils/src/AntialiasingEffect.cpp b/libraries/render-utils/src/AntialiasingEffect.cpp index 2941197e6d..f95d45de04 100644 --- a/libraries/render-utils/src/AntialiasingEffect.cpp +++ b/libraries/render-utils/src/AntialiasingEffect.cpp @@ -52,7 +52,7 @@ const gpu::PipelinePointer& Antialiasing::getAntialiasingPipeline() { _antialiasingBuffer = gpu::FramebufferPointer(gpu::Framebuffer::create("antialiasing")); auto format = gpu::Element::COLOR_SRGBA_32; // DependencyManager::get()->getLightingTexture()->getTexelFormat(); auto defaultSampler = gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_POINT); - _antialiasingTexture = gpu::TexturePointer(gpu::Texture::create2D(format, width, height, defaultSampler)); + _antialiasingTexture = gpu::TexturePointer(gpu::Texture::createRenderBuffer(format, width, height, defaultSampler)); _antialiasingBuffer->setRenderBuffer(0, _antialiasingTexture); } diff --git a/libraries/render-utils/src/DeferredFramebuffer.cpp b/libraries/render-utils/src/DeferredFramebuffer.cpp index e8783e0e0d..40c22beba4 100644 --- a/libraries/render-utils/src/DeferredFramebuffer.cpp +++ b/libraries/render-utils/src/DeferredFramebuffer.cpp @@ -53,9 +53,9 @@ void DeferredFramebuffer::allocate() { auto defaultSampler = gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_POINT); - _deferredColorTexture = gpu::TexturePointer(gpu::Texture::create2D(colorFormat, width, height, defaultSampler)); - _deferredNormalTexture = gpu::TexturePointer(gpu::Texture::create2D(linearFormat, width, height, defaultSampler)); - _deferredSpecularTexture = gpu::TexturePointer(gpu::Texture::create2D(colorFormat, width, height, defaultSampler)); + _deferredColorTexture = gpu::TexturePointer(gpu::Texture::createRenderBuffer(colorFormat, width, height, defaultSampler)); + _deferredNormalTexture = gpu::TexturePointer(gpu::Texture::createRenderBuffer(linearFormat, width, height, defaultSampler)); + _deferredSpecularTexture = gpu::TexturePointer(gpu::Texture::createRenderBuffer(colorFormat, width, height, defaultSampler)); _deferredFramebuffer->setRenderBuffer(0, _deferredColorTexture); _deferredFramebuffer->setRenderBuffer(1, _deferredNormalTexture); @@ -65,7 +65,7 @@ void DeferredFramebuffer::allocate() { auto depthFormat = gpu::Element(gpu::SCALAR, gpu::UINT32, gpu::DEPTH_STENCIL); // Depth24_Stencil8 texel format if (!_primaryDepthTexture) { - _primaryDepthTexture = gpu::TexturePointer(gpu::Texture::create2D(depthFormat, width, height, defaultSampler)); + _primaryDepthTexture = gpu::TexturePointer(gpu::Texture::createRenderBuffer(depthFormat, width, height, defaultSampler)); } _deferredFramebuffer->setDepthStencilBuffer(_primaryDepthTexture, depthFormat); @@ -75,7 +75,7 @@ void DeferredFramebuffer::allocate() { auto smoothSampler = gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR); - _lightingTexture = gpu::TexturePointer(gpu::Texture::create2D(gpu::Element(gpu::SCALAR, gpu::FLOAT, gpu::R11G11B10), width, height, defaultSampler)); + _lightingTexture = gpu::TexturePointer(gpu::Texture::createRenderBuffer(gpu::Element(gpu::SCALAR, gpu::FLOAT, gpu::R11G11B10), width, height, defaultSampler)); _lightingFramebuffer = gpu::FramebufferPointer(gpu::Framebuffer::create("lighting")); _lightingFramebuffer->setRenderBuffer(0, _lightingTexture); _lightingFramebuffer->setDepthStencilBuffer(_primaryDepthTexture, depthFormat); diff --git a/libraries/render-utils/src/DeferredLightingEffect.cpp b/libraries/render-utils/src/DeferredLightingEffect.cpp index 6f1152ac16..ce340583ee 100644 --- a/libraries/render-utils/src/DeferredLightingEffect.cpp +++ b/libraries/render-utils/src/DeferredLightingEffect.cpp @@ -496,14 +496,14 @@ void PreparePrimaryFramebuffer::run(const SceneContextPointer& sceneContext, con auto colorFormat = gpu::Element::COLOR_SRGBA_32; auto defaultSampler = gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_POINT); - auto primaryColorTexture = gpu::TexturePointer(gpu::Texture::create2D(colorFormat, frameSize.x, frameSize.y, defaultSampler)); + auto primaryColorTexture = gpu::TexturePointer(gpu::Texture::createRenderBuffer(colorFormat, frameSize.x, frameSize.y, defaultSampler)); _primaryFramebuffer->setRenderBuffer(0, primaryColorTexture); auto depthFormat = gpu::Element(gpu::SCALAR, gpu::UINT32, gpu::DEPTH_STENCIL); // Depth24_Stencil8 texel format - auto primaryDepthTexture = gpu::TexturePointer(gpu::Texture::create2D(depthFormat, frameSize.x, frameSize.y, defaultSampler)); + auto primaryDepthTexture = gpu::TexturePointer(gpu::Texture::createRenderBuffer(depthFormat, frameSize.x, frameSize.y, defaultSampler)); _primaryFramebuffer->setDepthStencilBuffer(primaryDepthTexture, depthFormat); } diff --git a/libraries/render-utils/src/RenderDeferredTask.cpp b/libraries/render-utils/src/RenderDeferredTask.cpp index 55a9c8b9e4..d1a7080eca 100644 --- a/libraries/render-utils/src/RenderDeferredTask.cpp +++ b/libraries/render-utils/src/RenderDeferredTask.cpp @@ -190,7 +190,7 @@ RenderDeferredTask::RenderDeferredTask(RenderFetchCullSortTask::Output items) { { // Grab a texture map representing the different status icons and assign that to the drawStatsuJob auto iconMapPath = PathUtils::resourcesPath() + "icons/statusIconAtlas.svg"; - auto statusIconMap = DependencyManager::get()->getImageTexture(iconMapPath); + auto statusIconMap = DependencyManager::get()->getImageTexture(iconMapPath, NetworkTexture::STRICT_TEXTURE); addJob("DrawStatus", opaques, DrawStatus(statusIconMap)); } } diff --git a/libraries/render-utils/src/SubsurfaceScattering.cpp b/libraries/render-utils/src/SubsurfaceScattering.cpp index 188381b822..25a01bff1b 100644 --- a/libraries/render-utils/src/SubsurfaceScattering.cpp +++ b/libraries/render-utils/src/SubsurfaceScattering.cpp @@ -414,7 +414,7 @@ gpu::TexturePointer SubsurfaceScatteringResource::generateScatteringProfile(Rend const int PROFILE_RESOLUTION = 512; // const auto pixelFormat = gpu::Element::COLOR_SRGBA_32; const auto pixelFormat = gpu::Element::COLOR_R11G11B10; - auto profileMap = gpu::TexturePointer(gpu::Texture::create2D(pixelFormat, PROFILE_RESOLUTION, 1, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR, gpu::Sampler::WRAP_CLAMP))); + auto profileMap = gpu::TexturePointer(gpu::Texture::createRenderBuffer(pixelFormat, PROFILE_RESOLUTION, 1, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR, gpu::Sampler::WRAP_CLAMP))); profileMap->setSource("Generated Scattering Profile"); diffuseProfileGPU(profileMap, args); return profileMap; @@ -425,7 +425,7 @@ gpu::TexturePointer SubsurfaceScatteringResource::generatePreIntegratedScatterin const int TABLE_RESOLUTION = 512; // const auto pixelFormat = gpu::Element::COLOR_SRGBA_32; const auto pixelFormat = gpu::Element::COLOR_R11G11B10; - auto scatteringLUT = gpu::TexturePointer(gpu::Texture::create2D(pixelFormat, TABLE_RESOLUTION, TABLE_RESOLUTION, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR, gpu::Sampler::WRAP_CLAMP))); + auto scatteringLUT = gpu::TexturePointer(gpu::Texture::createRenderBuffer(pixelFormat, TABLE_RESOLUTION, TABLE_RESOLUTION, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR, gpu::Sampler::WRAP_CLAMP))); //diffuseScatter(scatteringLUT); scatteringLUT->setSource("Generated pre-integrated scattering"); diffuseScatterGPU(profile, scatteringLUT, args); @@ -434,7 +434,7 @@ gpu::TexturePointer SubsurfaceScatteringResource::generatePreIntegratedScatterin gpu::TexturePointer SubsurfaceScatteringResource::generateScatteringSpecularBeckmann(RenderArgs* args) { const int SPECULAR_RESOLUTION = 256; - auto beckmannMap = gpu::TexturePointer(gpu::Texture::create2D(gpu::Element::COLOR_RGBA_32 /*gpu::Element(gpu::SCALAR, gpu::HALF, gpu::RGB)*/, SPECULAR_RESOLUTION, SPECULAR_RESOLUTION, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR, gpu::Sampler::WRAP_CLAMP))); + auto beckmannMap = gpu::TexturePointer(gpu::Texture::createRenderBuffer(gpu::Element::COLOR_RGBA_32 /*gpu::Element(gpu::SCALAR, gpu::HALF, gpu::RGB)*/, SPECULAR_RESOLUTION, SPECULAR_RESOLUTION, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR, gpu::Sampler::WRAP_CLAMP))); beckmannMap->setSource("Generated beckmannMap"); computeSpecularBeckmannGPU(beckmannMap, args); return beckmannMap; diff --git a/libraries/render-utils/src/SurfaceGeometryPass.cpp b/libraries/render-utils/src/SurfaceGeometryPass.cpp index f0ac56ac26..3a23e70664 100644 --- a/libraries/render-utils/src/SurfaceGeometryPass.cpp +++ b/libraries/render-utils/src/SurfaceGeometryPass.cpp @@ -72,18 +72,18 @@ void LinearDepthFramebuffer::allocate() { auto height = _frameSize.y; // For Linear Depth: - _linearDepthTexture = gpu::TexturePointer(gpu::Texture::create2D(gpu::Element(gpu::SCALAR, gpu::FLOAT, gpu::RGB), width, height, + _linearDepthTexture = gpu::TexturePointer(gpu::Texture::createRenderBuffer(gpu::Element(gpu::SCALAR, gpu::FLOAT, gpu::RGB), width, height, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_LINEAR_MIP_POINT))); _linearDepthFramebuffer = gpu::FramebufferPointer(gpu::Framebuffer::create("linearDepth")); _linearDepthFramebuffer->setRenderBuffer(0, _linearDepthTexture); _linearDepthFramebuffer->setDepthStencilBuffer(_primaryDepthTexture, _primaryDepthTexture->getTexelFormat()); // For Downsampling: - _halfLinearDepthTexture = gpu::TexturePointer(gpu::Texture::create2D(gpu::Element(gpu::SCALAR, gpu::FLOAT, gpu::RGB), _halfFrameSize.x, _halfFrameSize.y, + _halfLinearDepthTexture = gpu::TexturePointer(gpu::Texture::createRenderBuffer(gpu::Element(gpu::SCALAR, gpu::FLOAT, gpu::RGB), _halfFrameSize.x, _halfFrameSize.y, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_LINEAR_MIP_POINT))); _halfLinearDepthTexture->autoGenerateMips(5); - _halfNormalTexture = gpu::TexturePointer(gpu::Texture::create2D(gpu::Element(gpu::VEC3, gpu::NUINT8, gpu::RGB), _halfFrameSize.x, _halfFrameSize.y, + _halfNormalTexture = gpu::TexturePointer(gpu::Texture::createRenderBuffer(gpu::Element(gpu::VEC3, gpu::NUINT8, gpu::RGB), _halfFrameSize.x, _halfFrameSize.y, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_LINEAR_MIP_POINT))); _downsampleFramebuffer = gpu::FramebufferPointer(gpu::Framebuffer::create("halfLinearDepth")); @@ -304,15 +304,15 @@ void SurfaceGeometryFramebuffer::allocate() { auto width = _frameSize.x; auto height = _frameSize.y; - _curvatureTexture = gpu::TexturePointer(gpu::Texture::create2D(gpu::Element::COLOR_RGBA_32, width, height, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_LINEAR_MIP_POINT))); + _curvatureTexture = gpu::TexturePointer(gpu::Texture::createRenderBuffer(gpu::Element::COLOR_RGBA_32, width, height, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_LINEAR_MIP_POINT))); _curvatureFramebuffer = gpu::FramebufferPointer(gpu::Framebuffer::create("surfaceGeometry::curvature")); _curvatureFramebuffer->setRenderBuffer(0, _curvatureTexture); - _lowCurvatureTexture = gpu::TexturePointer(gpu::Texture::create2D(gpu::Element::COLOR_RGBA_32, width, height, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_LINEAR_MIP_POINT))); + _lowCurvatureTexture = gpu::TexturePointer(gpu::Texture::createRenderBuffer(gpu::Element::COLOR_RGBA_32, width, height, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_LINEAR_MIP_POINT))); _lowCurvatureFramebuffer = gpu::FramebufferPointer(gpu::Framebuffer::create("surfaceGeometry::lowCurvature")); _lowCurvatureFramebuffer->setRenderBuffer(0, _lowCurvatureTexture); - _blurringTexture = gpu::TexturePointer(gpu::Texture::create2D(gpu::Element::COLOR_RGBA_32, width, height, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_LINEAR_MIP_POINT))); + _blurringTexture = gpu::TexturePointer(gpu::Texture::createRenderBuffer(gpu::Element::COLOR_RGBA_32, width, height, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_LINEAR_MIP_POINT))); _blurringFramebuffer = gpu::FramebufferPointer(gpu::Framebuffer::create("surfaceGeometry::blurring")); _blurringFramebuffer->setRenderBuffer(0, _blurringTexture); } diff --git a/plugins/oculusLegacy/src/OculusLegacyDisplayPlugin.cpp b/plugins/oculusLegacy/src/OculusLegacyDisplayPlugin.cpp index 09f3e6dc8c..b759a06aee 100644 --- a/plugins/oculusLegacy/src/OculusLegacyDisplayPlugin.cpp +++ b/plugins/oculusLegacy/src/OculusLegacyDisplayPlugin.cpp @@ -255,7 +255,7 @@ void OculusLegacyDisplayPlugin::hmdPresent() { memset(eyePoses, 0, sizeof(ovrPosef) * 2); eyePoses[0].Orientation = eyePoses[1].Orientation = ovrRotation; - GLint texture = getGLBackend()->getTextureID(_compositeFramebuffer->getRenderBuffer(0), false); + GLint texture = getGLBackend()->getTextureID(_compositeFramebuffer->getRenderBuffer(0)); auto sync = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); glFlush(); if (_hmdWindow->makeCurrent()) { diff --git a/plugins/openvr/src/OpenVrDisplayPlugin.cpp b/plugins/openvr/src/OpenVrDisplayPlugin.cpp index 42f3ece9cd..f9c69da4d6 100644 --- a/plugins/openvr/src/OpenVrDisplayPlugin.cpp +++ b/plugins/openvr/src/OpenVrDisplayPlugin.cpp @@ -494,9 +494,9 @@ void OpenVrDisplayPlugin::customizeContext() { _compositeInfos[0].texture = _compositeFramebuffer->getRenderBuffer(0); for (size_t i = 0; i < COMPOSITING_BUFFER_SIZE; ++i) { if (0 != i) { - _compositeInfos[i].texture = gpu::TexturePointer(gpu::Texture::create2D(gpu::Element::COLOR_RGBA_32, _renderTargetSize.x, _renderTargetSize.y, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_POINT))); + _compositeInfos[i].texture = gpu::TexturePointer(gpu::Texture::createRenderBuffer(gpu::Element::COLOR_RGBA_32, _renderTargetSize.x, _renderTargetSize.y, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_POINT))); } - _compositeInfos[i].textureID = getGLBackend()->getTextureID(_compositeInfos[i].texture, false); + _compositeInfos[i].textureID = getGLBackend()->getTextureID(_compositeInfos[i].texture); } _submitThread->_canvas = _submitCanvas; _submitThread->start(QThread::HighPriority); @@ -624,7 +624,7 @@ void OpenVrDisplayPlugin::compositeLayers() { glFlush(); if (!newComposite.textureID) { - newComposite.textureID = getGLBackend()->getTextureID(newComposite.texture, false); + newComposite.textureID = getGLBackend()->getTextureID(newComposite.texture); } withPresentThreadLock([&] { _submitThread->update(newComposite); @@ -638,7 +638,7 @@ void OpenVrDisplayPlugin::hmdPresent() { if (_threadedSubmit) { _submitThread->waitForPresent(); } else { - GLuint glTexId = getGLBackend()->getTextureID(_compositeFramebuffer->getRenderBuffer(0), false); + GLuint glTexId = getGLBackend()->getTextureID(_compositeFramebuffer->getRenderBuffer(0)); vr::Texture_t vrTexture { (void*)glTexId, vr::API_OpenGL, vr::ColorSpace_Auto }; vr::VRCompositor()->Submit(vr::Eye_Left, &vrTexture, &OPENVR_TEXTURE_BOUNDS_LEFT); vr::VRCompositor()->Submit(vr::Eye_Right, &vrTexture, &OPENVR_TEXTURE_BOUNDS_RIGHT); diff --git a/tests/render-perf/src/main.cpp b/tests/render-perf/src/main.cpp index 7e9d2c426f..522fe79b10 100644 --- a/tests/render-perf/src/main.cpp +++ b/tests/render-perf/src/main.cpp @@ -642,7 +642,6 @@ protected: gpu::Texture::setAllowedGPUMemoryUsage(MB_TO_BYTES(64)); return; - default: break; } diff --git a/tests/render-texture-load/src/main.cpp b/tests/render-texture-load/src/main.cpp index 09a420f018..d924f76232 100644 --- a/tests/render-texture-load/src/main.cpp +++ b/tests/render-texture-load/src/main.cpp @@ -48,6 +48,7 @@ #include #include +#include #include #include #include From 1238edd0d7b8dcf8bfd34f02f3adf494f0578ced Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Thu, 9 Feb 2017 10:20:24 -0800 Subject: [PATCH 014/302] Add incremental transfers for large mips --- libraries/gpu-gl/src/gpu/gl45/GL45Backend.h | 3 +- .../src/gpu/gl45/GL45BackendTexture.cpp | 47 ++++++++------ .../gpu/gl45/GL45BackendVariableTexture.cpp | 63 ++++++++++++++++--- 3 files changed, 82 insertions(+), 31 deletions(-) diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h b/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h index e44f71e3e5..f811b26d94 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h +++ b/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h @@ -38,7 +38,8 @@ public: protected: GL45Texture(const std::weak_ptr& backend, const Texture& texture); void generateMips() const override; - void copyMipFromTexture(uint16_t sourceMip, uint16_t targetMip) const; + void copyMipFaceFromTexture(uint16_t sourceMip, uint16_t targetMip, uint8_t face) const; + void copyMipFaceLinesFromTexture(uint16_t sourceMip, uint16_t targetMip, uint8_t face, uint32_t lineOffset, uint32_t lines, size_t dataOffset) const; virtual void syncSampler() const; }; diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp b/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp index c46c07ee37..21c211ca20 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp @@ -120,32 +120,36 @@ void GL45Texture::generateMips() const { (void)CHECK_GL_ERROR(); } -void GL45Texture::copyMipFromTexture(uint16_t sourceMip, uint16_t targetMip) const { +void GL45Texture::copyMipFaceLinesFromTexture(uint16_t sourceMip, uint16_t targetMip, uint8_t face, uint32_t lineOffset, uint32_t lines, size_t dataOffset) const { const auto& texture = _gpuObject; if (!texture.isStoredMipFaceAvailable(sourceMip)) { return; } - size_t maxFace = GLTexture::getFaceCount(_target); - for (uint8_t face = 0; face < maxFace; ++face) { - auto size = texture.evalMipDimensions(sourceMip); - auto mipData = texture.accessStoredMipFace(sourceMip, face); - GLTexelFormat texelFormat = GLTexelFormat::evalGLTexelFormat(texture.getTexelFormat(), mipData->getFormat()); - if (GL_TEXTURE_2D == _target) { - glTextureSubImage2D(_id, targetMip, 0, 0, size.x, size.y, texelFormat.format, texelFormat.type, mipData->readData()); - } else if (GL_TEXTURE_CUBE_MAP == _target) { - // DSA ARB does not work on AMD, so use EXT - // unless EXT is not available on the driver - if (glTextureSubImage2DEXT) { - auto target = GLTexture::CUBE_FACE_LAYOUT[face]; - glTextureSubImage2DEXT(_id, target, targetMip, 0, 0, size.x, size.y, texelFormat.format, texelFormat.type, mipData->readData()); - } else { - glTextureSubImage3D(_id, targetMip, 0, 0, face, size.x, size.y, 1, texelFormat.format, texelFormat.type, mipData->readData()); - } + auto mipDimensions = texture.evalMipDimensions(sourceMip); + glm::uvec3 size = { mipDimensions.x, lines, mipDimensions.z }; + auto mipData = texture.accessStoredMipFace(sourceMip, face); + auto sourcePointer = mipData->readData() + dataOffset; + GLTexelFormat texelFormat = GLTexelFormat::evalGLTexelFormat(texture.getTexelFormat(), mipData->getFormat()); + if (GL_TEXTURE_2D == _target) { + glTextureSubImage2D(_id, targetMip, 0, lineOffset, size.x, size.y, texelFormat.format, texelFormat.type, sourcePointer); + } else if (GL_TEXTURE_CUBE_MAP == _target) { + // DSA ARB does not work on AMD, so use EXT + // unless EXT is not available on the driver + if (glTextureSubImage2DEXT) { + auto target = GLTexture::CUBE_FACE_LAYOUT[face]; + glTextureSubImage2DEXT(_id, target, targetMip, 0, lineOffset, size.x, size.y, texelFormat.format, texelFormat.type, sourcePointer); } else { - Q_ASSERT(false); + glTextureSubImage3D(_id, targetMip, 0, lineOffset, face, size.x, size.y, 1, texelFormat.format, texelFormat.type, sourcePointer); } - (void)CHECK_GL_ERROR(); + } else { + Q_ASSERT(false); } + (void)CHECK_GL_ERROR(); +} + +void GL45Texture::copyMipFaceFromTexture(uint16_t sourceMip, uint16_t targetMip, uint8_t face) const { + auto size = _gpuObject.evalMipDimensions(sourceMip); + copyMipFaceLinesFromTexture(sourceMip, targetMip, face, 0, size.y, 0); } void GL45Texture::syncSampler() const { @@ -221,7 +225,10 @@ GL45StrictResourceTexture::GL45StrictResourceTexture(const std::weak_ptr= _allocatedMip; --mip) { - // FIXME break down the transfers into chunks so that no single transfer is - // consuming more than X bandwidth - _pendingTransfers.push([mip, this] { - Q_ASSERT(mip >= _allocatedMip); - // FIXME modify the copy mechanism to be incremental - copyMipFromTexture(mip, mip - _allocatedMip); - _populatedMip = mip; + static const uvec3 MAX_TRANSFER_DIMENSIONS { 512, 512, 1 }; + static const size_t MAX_TRANSFER_SIZE = MAX_TRANSFER_DIMENSIONS.x * MAX_TRANSFER_DIMENSIONS.y * 4; + const uint8_t maxFace = GLTexture::getFaceCount(_target); + + uint16_t sourceMip = _populatedMip; + do { + --sourceMip; + auto targetMip = sourceMip - _allocatedMip; + auto mipDimensions = _gpuObject.evalMipDimensions(sourceMip); + for (uint8_t face = 0; face < maxFace; ++face) { + if (!_gpuObject.isStoredMipFaceAvailable(sourceMip, face)) { + continue; + } + + // If the mip is less than the max transfer size, then just do it in one transfer + if (glm::all(glm::lessThanEqual(mipDimensions, MAX_TRANSFER_DIMENSIONS))) { + // Can the mip be transferred in one go + _pendingTransfers.push([=] { + Q_ASSERT(sourceMip >= _allocatedMip); + // FIXME modify the copy mechanism to be incremental + copyMipFaceFromTexture(sourceMip, targetMip, face); + }); + continue; + } + + // break down the transfers into chunks so that no single transfer is + // consuming more than X bandwidth + auto mipData = _gpuObject.accessStoredMipFace(sourceMip, face); + const auto lines = mipDimensions.y; + auto bytesPerLine = (uint32_t)mipData->getSize() / lines; + Q_ASSERT(0 == (mipData->getSize() % lines)); + auto linesPerTransfer = MAX_TRANSFER_SIZE / bytesPerLine; + size_t offset = 0; + uint32_t lineOffset = 0; + while (lineOffset < lines) { + uint32_t linesToCopy = std::min(lines - lineOffset, linesPerTransfer); + uvec3 size { mipDimensions.x, linesToCopy, 1 }; + _pendingTransfers.push([=] { + copyMipFaceLinesFromTexture(sourceMip, targetMip, face, lineOffset, linesToCopy, offset); + }); + lineOffset += linesToCopy; + offset += (linesToCopy * bytesPerLine); + } + } + + // queue up the sampler and populated mip change for after the transfer has completed + _pendingTransfers.push([=] { + _populatedMip = targetMip; syncSampler(); }); - } + } while (sourceMip != _allocatedMip); } // Sparsely allocated, managed size resource textures From fe5c511eeb310f5ef7181a0cf61353f27cff1b59 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Thu, 9 Feb 2017 12:09:59 -0800 Subject: [PATCH 015/302] Fix texture count --- libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp b/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp index a6ab13c258..67ab1b5bb5 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp @@ -385,6 +385,7 @@ void GL45ResourceTexture::promote() { uint32_t oldSize = _size; // create new texture const_cast(_id) = allocate(_gpuObject); + incrementTextureGPUCount(); uint16_t oldAllocatedMip = _allocatedMip; // allocate storage for new level allocateStorage(_allocatedMip - std::min(_allocatedMip, 2)); @@ -418,6 +419,7 @@ void GL45ResourceTexture::demote() { auto oldId = _id; auto oldSize = _size; const_cast(_id) = allocate(_gpuObject); + incrementTextureGPUCount(); allocateStorage(_allocatedMip + 1); _populatedMip = std::max(_populatedMip, _allocatedMip); uint16_t mips = _gpuObject.evalNumMips(); @@ -482,7 +484,7 @@ void GL45ResourceTexture::populateTransferQueue() { const auto lines = mipDimensions.y; auto bytesPerLine = (uint32_t)mipData->getSize() / lines; Q_ASSERT(0 == (mipData->getSize() % lines)); - auto linesPerTransfer = MAX_TRANSFER_SIZE / bytesPerLine; + uint32_t linesPerTransfer = (uint32_t)(MAX_TRANSFER_SIZE / bytesPerLine); size_t offset = 0; uint32_t lineOffset = 0; while (lineOffset < lines) { From 0d89b3a922c86c9f3598729a4526a1a508020b19 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Thu, 9 Feb 2017 15:29:59 -0800 Subject: [PATCH 016/302] Better sampler handling --- libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp | 10 ++-------- .../gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp | 6 ------ 2 files changed, 2 insertions(+), 14 deletions(-) diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp b/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp index 21c211ca20..23ab9b203c 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp @@ -171,14 +171,8 @@ void GL45Texture::syncSampler() const { glTextureParameteri(_id, GL_TEXTURE_WRAP_R, WRAP_MODES[sampler.getWrapModeW()]); glTextureParameterf(_id, GL_TEXTURE_MAX_ANISOTROPY_EXT, sampler.getMaxAnisotropy()); glTextureParameterfv(_id, GL_TEXTURE_BORDER_COLOR, (const float*)&sampler.getBorderColor()); - -#if 0 - // FIXME account for mip offsets here - auto baseMip = std::max(sampler.getMipOffset(), _minMip); - glTextureParameteri(_id, GL_TEXTURE_BASE_LEVEL, baseMip); - glTextureParameterf(_id, GL_TEXTURE_MIN_LOD, (float)sampler.getMinMip()); - glTextureParameterf(_id, GL_TEXTURE_MAX_LOD, (sampler.getMaxMip() == Sampler::MAX_MIP_LEVEL ? 1000.f : sampler.getMaxMip() - _mipOffset)); -#endif + glTextureParameterf(_id, GL_TEXTURE_MIN_LOD, sampler.getMinMip()); + glTextureParameterf(_id, GL_TEXTURE_MAX_LOD, (sampler.getMaxMip() == Sampler::MAX_MIP_LEVEL ? 1000.f : sampler.getMaxMip())); } using GL45FixedAllocationTexture = GL45Backend::GL45FixedAllocationTexture; diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp b/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp index 67ab1b5bb5..b6c72aab77 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp @@ -370,13 +370,7 @@ void GL45ResourceTexture::copyMipsFromTexture() { void GL45ResourceTexture::syncSampler() const { Parent::syncSampler(); - const Sampler& sampler = _gpuObject.getSampler(); - uint16_t maxMip = _gpuObject.evalNumMips() - _allocatedMip; - auto minMip = std::max(sampler.getMipOffset(), sampler.getMinMip()); - minMip = std::min(minMip, maxMip); glTextureParameteri(_id, GL_TEXTURE_BASE_LEVEL, _populatedMip - _allocatedMip); - glTextureParameterf(_id, GL_TEXTURE_MIN_LOD, (float)minMip); - glTextureParameterf(_id, GL_TEXTURE_MAX_LOD, (float)maxMip); } void GL45ResourceTexture::promote() { From 283ff01038e60e61f3a03b03c05cb6840403da5c Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Thu, 9 Feb 2017 18:29:20 -0800 Subject: [PATCH 017/302] Remove duplicate code, polish --- .../src/gpu/gl41/GL41BackendTexture.cpp | 2 +- libraries/gpu-gl/src/gpu/gl45/GL45Backend.h | 12 + .../src/gpu/gl45/GL45BackendTexture.cpp | 1 + .../gpu/gl45/GL45BackendVariableTexture.cpp | 229 ++++++------------ 4 files changed, 82 insertions(+), 162 deletions(-) diff --git a/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp b/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp index 65c4dda202..cf389c83f3 100644 --- a/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp @@ -69,7 +69,7 @@ GLTexture* GL41Backend::syncGPUObject(const TexturePointer& texturePointer) { GL41Texture::GL41Texture(const std::weak_ptr& backend, const Texture& texture) : GLTexture(backend, texture, allocate()), _storageStamp { texture.getStamp() }, _size(texture.evalTotalSize()) { - + incrementTextureGPUCount(); withPreservedTexture([&] { GLTexelFormat texelFormat = GLTexelFormat::evalGLTexelFormat(_gpuObject.getTexelFormat()); const Sampler& sampler = _gpuObject.getSampler(); diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h b/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h index f811b26d94..e6a61e9498 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h +++ b/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h @@ -94,15 +94,27 @@ public: Undersubscribed, }; + using QueuePair = std::pair; + class QueuePairLess { + public: + bool operator()(const QueuePair& a, const QueuePair& b) { + return a.second < b.second; + } + }; + using WorkQueue = std::priority_queue, QueuePairLess>; + protected: static std::atomic _memoryPressureStateStale; static MemoryPressureState _memoryPressureState; static std::list _memoryManagedTextures; + static WorkQueue _workQueue; static const uvec3 INITIAL_MIP_TRANSFER_DIMENSIONS; + static void updateMemoryPressure(); static void processWorkQueues(); static void addMemoryManagedTexture(const TexturePointer& texturePointer); + static void addToWorkQueue(const TexturePointer& texture); static void manageMemory(); diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp b/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp index 23ab9b203c..87eb5228a4 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp @@ -107,6 +107,7 @@ using GL45Texture = GL45Backend::GL45Texture; GL45Texture::GL45Texture(const std::weak_ptr& backend, const Texture& texture) : GLTexture(backend, texture, allocate(texture)) { + incrementTextureGPUCount(); } GLuint GL45Texture::allocate(const Texture& texture) { diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp b/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp index b6c72aab77..0f14c9cc43 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp @@ -30,11 +30,13 @@ using namespace gpu::gl45; // Variable sized textures using GL45VariableAllocationTexture = GL45Backend::GL45VariableAllocationTexture; using MemoryPressureState = GL45VariableAllocationTexture::MemoryPressureState; +using WorkQueue = GL45VariableAllocationTexture::WorkQueue; std::list GL45VariableAllocationTexture::_memoryManagedTextures; MemoryPressureState GL45VariableAllocationTexture::_memoryPressureState = MemoryPressureState::Idle; std::atomic GL45VariableAllocationTexture::_memoryPressureStateStale { false }; const uvec3 GL45VariableAllocationTexture::INITIAL_MIP_TRANSFER_DIMENSIONS { 64, 64, 1 }; +WorkQueue GL45VariableAllocationTexture::_workQueue; #define OVERSUBSCRIBED_PRESSURE_VALUE 0.95f #define UNDERSUBSCRIBED_PRESSURE_VALUE 0.85f @@ -42,45 +44,29 @@ const uvec3 GL45VariableAllocationTexture::INITIAL_MIP_TRANSFER_DIMENSIONS { 64, static const size_t DEFAULT_ALLOWED_TEXTURE_MEMORY = MB_TO_BYTES(DEFAULT_ALLOWED_TEXTURE_MEMORY_MB); -using QueuePair = std::pair; -class QueuePairLess { -public: - bool operator()(const QueuePair& a, const QueuePair& b) { - return a.second < b.second; - } -}; -class QueuePairGreater { -public: - bool operator()(const QueuePair& a, const QueuePair& b) { - return a.second > b.second; - } -}; -using DemoteQueue = std::priority_queue, QueuePairLess>; -using PromoteQueue = std::priority_queue, QueuePairGreater>; -using TransferQueue = std::queue; -static DemoteQueue demoteQueue; -static PromoteQueue promoteQueue; -static TransferQueue transferQueue; - void GL45VariableAllocationTexture::addMemoryManagedTexture(const TexturePointer& texturePointer) { _memoryManagedTextures.push_back(texturePointer); + addToWorkQueue(texturePointer); +} + +void GL45VariableAllocationTexture::addToWorkQueue(const TexturePointer& texturePointer) { GL45VariableAllocationTexture* object = Backend::getGPUObject(*texturePointer); switch (_memoryPressureState) { case MemoryPressureState::Oversubscribed: if (object->canDemote()) { - demoteQueue.push({ texturePointer, object->size() }); + _workQueue.push({ texturePointer, (float) object->size() }); } break; case MemoryPressureState::Undersubscribed: if (object->canPromote()) { - promoteQueue.push({ texturePointer, object->size() }); + _workQueue.push({ texturePointer, 1.0f / (float)object->size() }); } break; case MemoryPressureState::Transfer: if (object->hasPendingTransfers()) { - transferQueue.push( texturePointer ); + _workQueue.push({ texturePointer, 1.0f / (float)object->_gpuObject.evalMipSize(object->_populatedMip) }); } break; @@ -94,23 +80,32 @@ void GL45VariableAllocationTexture::addMemoryManagedTexture(const TexturePointer void GL45VariableAllocationTexture::updateMemoryPressure() { static size_t lastAllowedMemoryAllocation = gpu::Texture::getAllowedGPUMemoryUsage(); + size_t allowedMemoryAllocation = gpu::Texture::getAllowedGPUMemoryUsage(); + if (0 == allowedMemoryAllocation) { + allowedMemoryAllocation = DEFAULT_ALLOWED_TEXTURE_MEMORY; + } + + // If the user explicitly changed the allowed memory usage, we need to mark ourselves stale + // so that we react if (allowedMemoryAllocation != lastAllowedMemoryAllocation) { _memoryPressureStateStale = true; lastAllowedMemoryAllocation = allowedMemoryAllocation; } - if (!_memoryPressureStateStale) { + if (!_memoryPressureStateStale.exchange(false)) { return; } - _memoryPressureStateStale = false; - // Clear any defunct textures + + // Clear any defunct textures (weak pointers that no longer have a valid texture) _memoryManagedTextures.remove_if([&](const TextureWeakPointer& weakPointer) { return weakPointer.expired(); }); - // Convert weak pointers to strong - std::list strongTextures; { + // Convert weak pointers to strong. This new list may still contain nulls if a texture was + // deleted on another thread between the previous line and this one + std::vector strongTextures; { + strongTextures.reserve(_memoryManagedTextures.size()); std::transform( _memoryManagedTextures.begin(), _memoryManagedTextures.end(), std::back_inserter(strongTextures), @@ -127,8 +122,10 @@ void GL45VariableAllocationTexture::updateMemoryPressure() { if (!texture) { continue; } - idealMemoryAllocation += texture->evalTotalSize(); GL45VariableAllocationTexture* object = Backend::getGPUObject(*texture); + // Track how much the texture thinks it should be using + idealMemoryAllocation += texture->evalTotalSize(); + // Track how much we're actually using totalVariableMemoryAllocation += object->size(); canDemote |= object->canDemote(); canPromote |= object->canPromote(); @@ -136,11 +133,6 @@ void GL45VariableAllocationTexture::updateMemoryPressure() { } size_t unallocated = idealMemoryAllocation - totalVariableMemoryAllocation; - if (0 == allowedMemoryAllocation) { - allowedMemoryAllocation = DEFAULT_ALLOWED_TEXTURE_MEMORY; - } - - float pressure = (float)totalVariableMemoryAllocation / (float)allowedMemoryAllocation; auto newState = MemoryPressureState::Idle; @@ -154,142 +146,59 @@ void GL45VariableAllocationTexture::updateMemoryPressure() { if (newState != _memoryPressureState) { _memoryPressureState = newState; - - demoteQueue = DemoteQueue(); - promoteQueue = PromoteQueue(); - transferQueue = TransferQueue(); - - switch (_memoryPressureState) { - case MemoryPressureState::Idle: - break; - - case MemoryPressureState::Oversubscribed: - for (const auto& texture : strongTextures) { - if (!texture) { - continue; - } - GL45VariableAllocationTexture* object = Backend::getGPUObject(*texture); - if (object->canDemote()) { - demoteQueue.push({ texture, object->size() }); - } - } - break; - - case MemoryPressureState::Undersubscribed: - for (const auto& texture : strongTextures) { - if (!texture) { - continue; - } - GL45VariableAllocationTexture* object = Backend::getGPUObject(*texture); - if (object->canPromote()) { - promoteQueue.push({ texture, object->size() }); - } - } - break; - - case MemoryPressureState::Transfer: - for (const auto& texture : strongTextures) { - if (!texture) { - continue; - } - GL45VariableAllocationTexture* object = Backend::getGPUObject(*texture); - if (object->hasPendingTransfers()) { - transferQueue.push(texture); - } - } - break; - - default: - Q_UNREACHABLE(); - break; + // Clear the existing queue + _workQueue = WorkQueue(); + // Populate the existing textures into the queue + for (const auto& texture : strongTextures) { + addToWorkQueue(texture); } } } void GL45VariableAllocationTexture::processWorkQueues() { - switch (_memoryPressureState) { - case MemoryPressureState::Idle: - break; + if (MemoryPressureState::Idle == _memoryPressureState) { + return; + } - case MemoryPressureState::Oversubscribed: - // Grab the first item off the demote queue - while (!demoteQueue.empty()) { - auto demoteTarget = demoteQueue.top(); - demoteQueue.pop(); - auto texture = demoteTarget.first.lock(); - if (!texture) { - continue; - } + while (!_workQueue.empty()) { + auto workTarget = _workQueue.top(); + _workQueue.pop(); + auto texture = workTarget.first.lock(); + if (!texture) { + continue; + } - GL45VariableAllocationTexture* object = Backend::getGPUObject(*texture); - if (!object->canDemote()) { - continue; - } - - //qDebug() << "QQQ executing demote for " << texture->source().c_str(); - object->demote(); - // if the object can be further demoted, reinsert into the queue - if (object->canDemote()) { - demoteQueue.push({ demoteTarget.first, object->size() }); - } - break; + // Grab the first item off the demote queue + GL45VariableAllocationTexture* object = Backend::getGPUObject(*texture); + if (MemoryPressureState::Oversubscribed == _memoryPressureState) { + if (!object->canDemote()) { + continue; } - if (demoteQueue.empty()) { - _memoryPressureState = MemoryPressureState::Idle; + //qDebug() << "QQQ executing demote for " << texture->source().c_str(); + object->demote(); + } else if (MemoryPressureState::Undersubscribed == _memoryPressureState) { + if (!object->canPromote()) { + continue; } - break; - - case MemoryPressureState::Undersubscribed: - while (!promoteQueue.empty()) { - auto promoteTarget = promoteQueue.top(); - promoteQueue.pop(); - auto texture = promoteTarget.first.lock(); - if (!texture) { - continue; - } - GL45VariableAllocationTexture* object = Backend::getGPUObject(*texture); - if (!object->canPromote()) { - continue; - } - //qDebug() << "QQQ executing promote for " << texture->source().c_str(); - object->promote(); - if (object->canPromote()) { - promoteQueue.push({ promoteTarget.first, object->size() }); - } - break; + //qDebug() << "QQQ executing promote for " << texture->source().c_str(); + object->promote(); + } else if (MemoryPressureState::Transfer == _memoryPressureState) { + if (!object->hasPendingTransfers()) { + continue; } - if (promoteQueue.empty()) { - _memoryPressureState = MemoryPressureState::Idle; - } - break; - - case MemoryPressureState::Transfer: - while (!transferQueue.empty()) { - auto weakTexture = transferQueue.front(); - transferQueue.pop(); - auto texture = weakTexture.lock(); - if (!texture) { - continue; - } - GL45VariableAllocationTexture* object = Backend::getGPUObject(*texture); - if (!object->hasPendingTransfers()) { - continue; - } - //qDebug() << "QQQ executing transfer for " << texture->source().c_str(); - object->executeNextTransfer(); - if (object->hasPendingTransfers()) { - transferQueue.push(weakTexture); - } - break; - } - if (transferQueue.empty()) { - _memoryPressureState = MemoryPressureState::Idle; - } - break; - - default: + //qDebug() << "QQQ executing transfer for " << texture->source().c_str(); + object->executeNextTransfer(); + } else { Q_UNREACHABLE(); - break; + } + + // Reinject into the queue if more work to be done + addToWorkQueue(texture); + break; + } + + if (_workQueue.empty()) { + _memoryPressureState = MemoryPressureState::Idle; } } @@ -379,7 +288,6 @@ void GL45ResourceTexture::promote() { uint32_t oldSize = _size; // create new texture const_cast(_id) = allocate(_gpuObject); - incrementTextureGPUCount(); uint16_t oldAllocatedMip = _allocatedMip; // allocate storage for new level allocateStorage(_allocatedMip - std::min(_allocatedMip, 2)); @@ -413,7 +321,6 @@ void GL45ResourceTexture::demote() { auto oldId = _id; auto oldSize = _size; const_cast(_id) = allocate(_gpuObject); - incrementTextureGPUCount(); allocateStorage(_allocatedMip + 1); _populatedMip = std::max(_populatedMip, _allocatedMip); uint16_t mips = _gpuObject.evalNumMips(); From 35a6359d5976b61fab4f21f17188872a6e6122b5 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Fri, 10 Feb 2017 10:56:01 -0800 Subject: [PATCH 018/302] Fixing transfer logic, adding stats display & profile ranges --- interface/resources/qml/Stats.qml | 11 +-- interface/src/ui/Stats.cpp | 3 + interface/src/ui/Stats.h | 2 + libraries/gpu-gl/src/gpu/gl45/GL45Backend.h | 7 +- .../src/gpu/gl45/GL45BackendTexture.cpp | 3 - .../gpu/gl45/GL45BackendVariableTexture.cpp | 70 ++++++++++++++++--- 6 files changed, 72 insertions(+), 24 deletions(-) diff --git a/interface/resources/qml/Stats.qml b/interface/resources/qml/Stats.qml index faf37d5366..4cba41f6cc 100644 --- a/interface/resources/qml/Stats.qml +++ b/interface/resources/qml/Stats.qml @@ -241,7 +241,7 @@ Item { text: "GPU Textures: "; } StatText { - text: " Sparse Enabled: " + (0 == root.gpuSparseTextureEnabled ? "false" : "true"); + text: " Pressure State: " + root.gpuTextureMemoryPressureState; } StatText { text: " Count: " + root.gpuTextures; @@ -253,14 +253,7 @@ Item { text: " Decimated: " + root.decimatedTextureCount; } StatText { - text: " Sparse Count: " + root.gpuTexturesSparse; - visible: 0 != root.gpuSparseTextureEnabled; - } - StatText { - text: " Virtual Memory: " + root.gpuTextureVirtualMemory + " MB"; - } - StatText { - text: " Commited Memory: " + root.gpuTextureMemory + " MB"; + text: " Resource Memory: " + root.gpuTextureMemory + " MB"; } StatText { text: " Framebuffer Memory: " + root.gpuTextureFramebufferMemory + " MB"; diff --git a/interface/src/ui/Stats.cpp b/interface/src/ui/Stats.cpp index e82f99bed2..7af454b702 100644 --- a/interface/src/ui/Stats.cpp +++ b/interface/src/ui/Stats.cpp @@ -38,6 +38,8 @@ using namespace std; static Stats* INSTANCE{ nullptr }; +QString getTextureMemoryPressureModeString(); + Stats* Stats::getInstance() { if (!INSTANCE) { Stats::registerType(); @@ -323,6 +325,7 @@ void Stats::updateStats(bool force) { STAT_UPDATE(gpuTextureVirtualMemory, (int)BYTES_TO_MB(gpu::Texture::getTextureGPUVirtualMemoryUsage())); STAT_UPDATE(gpuTextureFramebufferMemory, (int)BYTES_TO_MB(gpu::Texture::getTextureGPUFramebufferMemoryUsage())); STAT_UPDATE(gpuTextureSparseMemory, (int)BYTES_TO_MB(gpu::Texture::getTextureGPUSparseMemoryUsage())); + STAT_UPDATE(gpuTextureMemoryPressureState, getTextureMemoryPressureModeString()); STAT_UPDATE(gpuSparseTextureEnabled, gpuContext->getBackend()->isTextureManagementSparseEnabled() ? 1 : 0); STAT_UPDATE(gpuFreeMemory, (int)BYTES_TO_MB(gpu::Context::getFreeGPUMemory())); STAT_UPDATE(rectifiedTextureCount, (int)RECTIFIED_TEXTURE_COUNT.load()); diff --git a/interface/src/ui/Stats.h b/interface/src/ui/Stats.h index f501f4b09a..069429d639 100644 --- a/interface/src/ui/Stats.h +++ b/interface/src/ui/Stats.h @@ -110,6 +110,7 @@ class Stats : public QQuickItem { STATS_PROPERTY(int, gpuTextureFramebufferMemory, 0) STATS_PROPERTY(int, gpuTextureSparseMemory, 0) STATS_PROPERTY(int, gpuSparseTextureEnabled, 0) + STATS_PROPERTY(QString, gpuTextureMemoryPressureState, QString()) STATS_PROPERTY(int, gpuFreeMemory, 0) STATS_PROPERTY(float, gpuFrameTime, 0) STATS_PROPERTY(float, batchFrameTime, 0) @@ -217,6 +218,7 @@ signals: void gpuTextureVirtualMemoryChanged(); void gpuTextureFramebufferMemoryChanged(); void gpuTextureSparseMemoryChanged(); + void gpuTextureMemoryPressureStateChanged(); void gpuSparseTextureEnabledChanged(); void gpuFreeMemoryChanged(); void gpuFrameTimeChanged(); diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h b/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h index e6a61e9498..1983088ca5 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h +++ b/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h @@ -103,11 +103,13 @@ public: }; using WorkQueue = std::priority_queue, QueuePairLess>; + static MemoryPressureState _memoryPressureState; protected: static std::atomic _memoryPressureStateStale; - static MemoryPressureState _memoryPressureState; static std::list _memoryManagedTextures; - static WorkQueue _workQueue; + static WorkQueue _transferQueue; + static WorkQueue _promoteQueue; + static WorkQueue _demoteQueue; static const uvec3 INITIAL_MIP_TRANSFER_DIMENSIONS; @@ -115,6 +117,7 @@ public: static void processWorkQueues(); static void addMemoryManagedTexture(const TexturePointer& texturePointer); static void addToWorkQueue(const TexturePointer& texture); + static WorkQueue& getActiveWorkQueue(); static void manageMemory(); diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp b/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp index 87eb5228a4..c344b453a9 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp @@ -35,9 +35,6 @@ GLTexture* GL45Backend::syncGPUObject(const TexturePointer& texturePointer) { } const Texture& texture = *texturePointer; - if (std::string("cursor texture") == texture.source()) { - qDebug() << "Loading cursor texture"; - } if (TextureUsageType::EXTERNAL == texture.getUsageType()) { return Parent::syncGPUObject(texturePointer); } diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp b/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp index 0f14c9cc43..282ec2161c 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp @@ -36,7 +36,9 @@ std::list GL45VariableAllocationTexture::_memoryManagedTextu MemoryPressureState GL45VariableAllocationTexture::_memoryPressureState = MemoryPressureState::Idle; std::atomic GL45VariableAllocationTexture::_memoryPressureStateStale { false }; const uvec3 GL45VariableAllocationTexture::INITIAL_MIP_TRANSFER_DIMENSIONS { 64, 64, 1 }; -WorkQueue GL45VariableAllocationTexture::_workQueue; +WorkQueue GL45VariableAllocationTexture::_transferQueue; +WorkQueue GL45VariableAllocationTexture::_promoteQueue; +WorkQueue GL45VariableAllocationTexture::_demoteQueue; #define OVERSUBSCRIBED_PRESSURE_VALUE 0.95f #define UNDERSUBSCRIBED_PRESSURE_VALUE 0.85f @@ -54,19 +56,19 @@ void GL45VariableAllocationTexture::addToWorkQueue(const TexturePointer& texture switch (_memoryPressureState) { case MemoryPressureState::Oversubscribed: if (object->canDemote()) { - _workQueue.push({ texturePointer, (float) object->size() }); + _demoteQueue.push({ texturePointer, (float)object->size() }); } break; case MemoryPressureState::Undersubscribed: if (object->canPromote()) { - _workQueue.push({ texturePointer, 1.0f / (float)object->size() }); + _promoteQueue.push({ texturePointer, 1.0f / (float)object->size() }); } break; case MemoryPressureState::Transfer: if (object->hasPendingTransfers()) { - _workQueue.push({ texturePointer, 1.0f / (float)object->_gpuObject.evalMipSize(object->_populatedMip) }); + _transferQueue.push({ texturePointer, 1.0f / (float)object->_gpuObject.evalMipSize(object->_populatedMip) }); } break; @@ -78,6 +80,44 @@ void GL45VariableAllocationTexture::addToWorkQueue(const TexturePointer& texture } } +WorkQueue& GL45VariableAllocationTexture::getActiveWorkQueue() { + static WorkQueue empty; + switch (_memoryPressureState) { + case MemoryPressureState::Oversubscribed: + return _demoteQueue; + + case MemoryPressureState::Undersubscribed: + return _promoteQueue; + + case MemoryPressureState::Transfer: + return _transferQueue; + + default: + break; + } + Q_UNREACHABLE(); + return empty; +} + +// FIXME hack for stats display +QString getTextureMemoryPressureModeString() { + switch (GL45VariableAllocationTexture::_memoryPressureState) { + case MemoryPressureState::Oversubscribed: + return "Oversubscribed"; + + case MemoryPressureState::Undersubscribed: + return "Undersubscribed"; + + case MemoryPressureState::Transfer: + return "Transfer"; + + case MemoryPressureState::Idle: + return "Idle"; + } + Q_UNREACHABLE(); + return "Unknown"; +} + void GL45VariableAllocationTexture::updateMemoryPressure() { static size_t lastAllowedMemoryAllocation = gpu::Texture::getAllowedGPUMemoryUsage(); @@ -97,6 +137,8 @@ void GL45VariableAllocationTexture::updateMemoryPressure() { return; } + PROFILE_RANGE(render_gpu_gl, __FUNCTION__); + // Clear any defunct textures (weak pointers that no longer have a valid texture) _memoryManagedTextures.remove_if([&](const TextureWeakPointer& weakPointer) { return weakPointer.expired(); @@ -147,7 +189,9 @@ void GL45VariableAllocationTexture::updateMemoryPressure() { if (newState != _memoryPressureState) { _memoryPressureState = newState; // Clear the existing queue - _workQueue = WorkQueue(); + _transferQueue = WorkQueue(); + _promoteQueue = WorkQueue(); + _demoteQueue = WorkQueue(); // Populate the existing textures into the queue for (const auto& texture : strongTextures) { addToWorkQueue(texture); @@ -160,9 +204,11 @@ void GL45VariableAllocationTexture::processWorkQueues() { return; } - while (!_workQueue.empty()) { - auto workTarget = _workQueue.top(); - _workQueue.pop(); + auto& workQueue = getActiveWorkQueue(); + PROFILE_RANGE(render_gpu_gl, __FUNCTION__); + while (!workQueue.empty()) { + auto workTarget = workQueue.top(); + workQueue.pop(); auto texture = workTarget.first.lock(); if (!texture) { continue; @@ -197,7 +243,7 @@ void GL45VariableAllocationTexture::processWorkQueues() { break; } - if (_workQueue.empty()) { + if (workQueue.empty()) { _memoryPressureState = MemoryPressureState::Idle; } } @@ -208,6 +254,7 @@ void GL45VariableAllocationTexture::manageMemory() { auto interval = now - lastProcessTime; if (interval > (USECS_PER_MSEC * 20)) { lastProcessTime = now; + PROFILE_RANGE(render_gpu_gl, __FUNCTION__); updateMemoryPressure(); processWorkQueues(); } @@ -283,6 +330,7 @@ void GL45ResourceTexture::syncSampler() const { } void GL45ResourceTexture::promote() { + PROFILE_RANGE(render_gpu_gl, __FUNCTION__); Q_ASSERT(_allocatedMip > 0); GLuint oldId = _id; uint32_t oldSize = _size; @@ -317,6 +365,7 @@ void GL45ResourceTexture::promote() { } void GL45ResourceTexture::demote() { + PROFILE_RANGE(render_gpu_gl, __FUNCTION__); Q_ASSERT(_allocatedMip < _maxAllocatedMip); auto oldId = _id; auto oldSize = _size; @@ -349,6 +398,7 @@ void GL45ResourceTexture::demote() { } void GL45ResourceTexture::populateTransferQueue() { + PROFILE_RANGE(render_gpu_gl, __FUNCTION__); _pendingTransfers = std::queue(); if (_populatedMip <= _allocatedMip) { return; @@ -401,7 +451,7 @@ void GL45ResourceTexture::populateTransferQueue() { // queue up the sampler and populated mip change for after the transfer has completed _pendingTransfers.push([=] { - _populatedMip = targetMip; + _populatedMip = sourceMip; syncSampler(); }); } while (sourceMip != _allocatedMip); From 439cb388f2205ca9733217822301b4b73d00841c Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Fri, 10 Feb 2017 11:15:22 -0800 Subject: [PATCH 019/302] Increase the rate of work queue processing --- .../src/gpu/gl45/GL45BackendVariableTexture.cpp | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp b/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp index 282ec2161c..22bb26cc10 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp @@ -249,15 +249,9 @@ void GL45VariableAllocationTexture::processWorkQueues() { } void GL45VariableAllocationTexture::manageMemory() { - static auto lastProcessTime = usecTimestampNow(); - auto now = usecTimestampNow(); - auto interval = now - lastProcessTime; - if (interval > (USECS_PER_MSEC * 20)) { - lastProcessTime = now; - PROFILE_RANGE(render_gpu_gl, __FUNCTION__); - updateMemoryPressure(); - processWorkQueues(); - } + PROFILE_RANGE(render_gpu_gl, __FUNCTION__); + updateMemoryPressure(); + processWorkQueues(); } GL45VariableAllocationTexture::GL45VariableAllocationTexture(const std::weak_ptr& backend, const Texture& texture) : GL45Texture(backend, texture) { @@ -404,7 +398,7 @@ void GL45ResourceTexture::populateTransferQueue() { return; } - static const uvec3 MAX_TRANSFER_DIMENSIONS { 512, 512, 1 }; + static const uvec3 MAX_TRANSFER_DIMENSIONS { 1024, 1024, 1 }; static const size_t MAX_TRANSFER_SIZE = MAX_TRANSFER_DIMENSIONS.x * MAX_TRANSFER_DIMENSIONS.y * 4; const uint8_t maxFace = GLTexture::getFaceCount(_target); From 066a6483a0badf9b533d2e77b4b3b7a6e8768971 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Fri, 10 Feb 2017 11:23:37 -0800 Subject: [PATCH 020/302] Code comments --- libraries/gpu-gl/src/gpu/gl45/GL45Backend.h | 13 +++++++++++-- .../src/gpu/gl45/GL45BackendVariableTexture.cpp | 3 +++ 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h b/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h index 1983088ca5..d2d17160ba 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h +++ b/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h @@ -134,10 +134,20 @@ public: virtual void promote() = 0; virtual void demote() = 0; - uint16 _populatedMip { 0 }; + // The allocated mip level, relative to the number of mips in the gpu::Texture object + // The relationship between a given glMip to the original gpu::Texture mip is always + // glMip + _allocatedMip uint16 _allocatedMip { 0 }; + // The populated mip level, relative to the number of mips in the gpu::Texture object + // This must always be >= the allocated mip + uint16 _populatedMip { 0 }; + // The highest (lowest resolution) mip that we will support, relative to the number + // of mips in the gpu::Texture object uint16 _maxAllocatedMip { 0 }; uint32 _size { 0 }; + // Contains a series of lambdas that when executed will transfer data to the GPU, modify + // the _populatedMip and update the sampler in order to fully populate the allocated texture + // until _populatedMip == _allocatedMip std::queue _pendingTransfers; }; @@ -154,7 +164,6 @@ public: void allocateStorage(uint16 mip); void copyMipsFromTexture(); - private: }; #if 0 diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp b/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp index 22bb26cc10..597e35750a 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp @@ -56,18 +56,21 @@ void GL45VariableAllocationTexture::addToWorkQueue(const TexturePointer& texture switch (_memoryPressureState) { case MemoryPressureState::Oversubscribed: if (object->canDemote()) { + // Demote largest first _demoteQueue.push({ texturePointer, (float)object->size() }); } break; case MemoryPressureState::Undersubscribed: if (object->canPromote()) { + // Promote smallest first _promoteQueue.push({ texturePointer, 1.0f / (float)object->size() }); } break; case MemoryPressureState::Transfer: if (object->hasPendingTransfers()) { + // Transfer priority given to smaller mips first _transferQueue.push({ texturePointer, 1.0f / (float)object->_gpuObject.evalMipSize(object->_populatedMip) }); } break; From 75c17e89a27d5ce287b2a1af5191bb5d514326d0 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Mon, 13 Feb 2017 12:37:42 -0800 Subject: [PATCH 021/302] Fix OpenGL 4.1 texture loading --- .../gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp b/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp index cf389c83f3..efbc6903b1 100644 --- a/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp @@ -73,14 +73,21 @@ GL41Texture::GL41Texture(const std::weak_ptr& backend, const Texture& withPreservedTexture([&] { GLTexelFormat texelFormat = GLTexelFormat::evalGLTexelFormat(_gpuObject.getTexelFormat()); const Sampler& sampler = _gpuObject.getSampler(); - auto minMip = sampler.getMinMip(); - auto maxMip = sampler.getMaxMip(); - for (uint16_t l = minMip; l <= maxMip; l++) { + auto numMips = _gpuObject.evalNumMips(); + for (uint16_t mipLevel = 0; mipLevel < numMips; ++mipLevel) { // Get the mip level dimensions, accounting for the downgrade level - Vec3u dimensions = _gpuObject.evalMipDimensions(l); + Vec3u dimensions = _gpuObject.evalMipDimensions(mipLevel); + uint8_t face = 0; for (GLenum target : getFaceTargets(_target)) { - glTexImage2D(target, l - minMip, texelFormat.internalFormat, dimensions.x, dimensions.y, 0, texelFormat.format, texelFormat.type, NULL); + const Byte* mipData = nullptr; + if (_gpuObject.isStoredMipFaceAvailable(mipLevel, face)) { + auto mip = _gpuObject.accessStoredMipFace(mipLevel, face); + mipData = mip->readData(); + texelFormat = GLTexelFormat::evalGLTexelFormat(_gpuObject.getTexelFormat(), mip->getFormat()); + } + glTexImage2D(target, mipLevel, texelFormat.internalFormat, dimensions.x, dimensions.y, 0, texelFormat.format, texelFormat.type, mipData); (void)CHECK_GL_ERROR(); + ++face; } } }); From 1f058f069e16976a35f39ed7d587fd0c92289e39 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Tue, 14 Feb 2017 17:58:41 -0800 Subject: [PATCH 022/302] First pass at new texture transfer logic --- libraries/gpu-gl/src/gpu/gl45/GL45Backend.h | 48 +++++- .../src/gpu/gl45/GL45BackendTexture.cpp | 24 ++- .../gpu/gl45/GL45BackendVariableTexture.cpp | 163 +++++++++++++++--- 3 files changed, 192 insertions(+), 43 deletions(-) diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h b/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h index d2d17160ba..4f299d417f 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h +++ b/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h @@ -14,6 +14,7 @@ #include "../gl/GLBackend.h" #include "../gl/GLTexture.h" +#include #define INCREMENTAL_TRANSFER 0 @@ -39,7 +40,7 @@ public: GL45Texture(const std::weak_ptr& backend, const Texture& texture); void generateMips() const override; void copyMipFaceFromTexture(uint16_t sourceMip, uint16_t targetMip, uint8_t face) const; - void copyMipFaceLinesFromTexture(uint16_t sourceMip, uint16_t targetMip, uint8_t face, uint32_t lineOffset, uint32_t lines, size_t dataOffset) const; + void copyMipFaceLinesFromTexture(uint16_t mip, uint8_t face, const uvec3& size, uint32_t yOffset, GLenum format, GLenum type, const void* sourcePointer) const; virtual void syncSampler() const; }; @@ -95,14 +96,50 @@ public: }; using QueuePair = std::pair; - class QueuePairLess { - public: + struct QueuePairLess { bool operator()(const QueuePair& a, const QueuePair& b) { return a.second < b.second; } }; using WorkQueue = std::priority_queue, QueuePairLess>; + class TransferJob { + using VoidLambda = std::function; + using VoidLambdaQueue = std::queue; + using ThreadPointer = std::shared_ptr; + const GL45VariableAllocationTexture& _parent; + const uint16_t _sourceMip; + const uint16_t _targetMip; + const uint8_t _face; + const uint32_t _lines; + const uint32_t _lineOffset; + // Holds the contents to transfer to the GPU in CPU memory + std::vector _buffer; + // Indicates if a transfer from backing storage to interal storage has started + bool _bufferingStarted { false }; + bool _transferOnly { false }; + bool _bufferingCompleted { false }; + VoidLambda _transferLambda; + VoidLambda _bufferingLambda; + static ThreadPointer _bufferThread; + static Mutex _mutex; + static VoidLambdaQueue _bufferLambdaQueue; + static std::atomic _shutdownBufferingThread; + static void bufferLoop(); + + public: + TransferJob(const GL45VariableAllocationTexture& parent, std::function transferLambda); + TransferJob(const GL45VariableAllocationTexture& parent, uint16_t sourceMip, uint16_t targetMip, uint8_t face, uint32_t lines = 0, uint32_t lineOffset = 0); + bool tryTransfer(); + static void startTransferLoop(); + static void stopTransferLoop(); + + private: + void startBuffering(); + void transfer(); + }; + + using TransferQueue = std::queue; static MemoryPressureState _memoryPressureState; protected: static std::atomic _memoryPressureStateStale; @@ -110,6 +147,7 @@ public: static WorkQueue _transferQueue; static WorkQueue _promoteQueue; static WorkQueue _demoteQueue; + static TexturePointer _currentTransferTexture; static const uvec3 INITIAL_MIP_TRANSFER_DIMENSIONS; @@ -128,7 +166,7 @@ public: bool canPromote() const { return _allocatedMip > 0; } bool canDemote() const { return _allocatedMip < _maxAllocatedMip; } bool hasPendingTransfers() const { return !_pendingTransfers.empty(); } - void executeNextTransfer(); + void executeNextTransfer(const TexturePointer& currentTexture); uint32 size() const override { return _size; } virtual void populateTransferQueue() = 0; virtual void promote() = 0; @@ -148,7 +186,7 @@ public: // Contains a series of lambdas that when executed will transfer data to the GPU, modify // the _populatedMip and update the sampler in order to fully populate the allocated texture // until _populatedMip == _allocatedMip - std::queue _pendingTransfers; + TransferQueue _pendingTransfers; }; class GL45ResourceTexture : public GL45VariableAllocationTexture { diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp b/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp index c344b453a9..6dd1d6aea3 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp @@ -118,26 +118,17 @@ void GL45Texture::generateMips() const { (void)CHECK_GL_ERROR(); } -void GL45Texture::copyMipFaceLinesFromTexture(uint16_t sourceMip, uint16_t targetMip, uint8_t face, uint32_t lineOffset, uint32_t lines, size_t dataOffset) const { - const auto& texture = _gpuObject; - if (!texture.isStoredMipFaceAvailable(sourceMip)) { - return; - } - auto mipDimensions = texture.evalMipDimensions(sourceMip); - glm::uvec3 size = { mipDimensions.x, lines, mipDimensions.z }; - auto mipData = texture.accessStoredMipFace(sourceMip, face); - auto sourcePointer = mipData->readData() + dataOffset; - GLTexelFormat texelFormat = GLTexelFormat::evalGLTexelFormat(texture.getTexelFormat(), mipData->getFormat()); +void GL45Texture::copyMipFaceLinesFromTexture(uint16_t mip, uint8_t face, const uvec3& size, uint32_t yOffset, GLenum format, GLenum type, const void* sourcePointer) const { if (GL_TEXTURE_2D == _target) { - glTextureSubImage2D(_id, targetMip, 0, lineOffset, size.x, size.y, texelFormat.format, texelFormat.type, sourcePointer); + glTextureSubImage2D(_id, mip, 0, yOffset, size.x, size.y, format, type, sourcePointer); } else if (GL_TEXTURE_CUBE_MAP == _target) { // DSA ARB does not work on AMD, so use EXT // unless EXT is not available on the driver if (glTextureSubImage2DEXT) { auto target = GLTexture::CUBE_FACE_LAYOUT[face]; - glTextureSubImage2DEXT(_id, target, targetMip, 0, lineOffset, size.x, size.y, texelFormat.format, texelFormat.type, sourcePointer); + glTextureSubImage2DEXT(_id, target, mip, 0, yOffset, size.x, size.y, format, type, sourcePointer); } else { - glTextureSubImage3D(_id, targetMip, 0, lineOffset, face, size.x, size.y, 1, texelFormat.format, texelFormat.type, sourcePointer); + glTextureSubImage3D(_id, mip, 0, yOffset, face, size.x, size.y, 1, format, type, sourcePointer); } } else { Q_ASSERT(false); @@ -146,8 +137,13 @@ void GL45Texture::copyMipFaceLinesFromTexture(uint16_t sourceMip, uint16_t targe } void GL45Texture::copyMipFaceFromTexture(uint16_t sourceMip, uint16_t targetMip, uint8_t face) const { + if (!_gpuObject.isStoredMipFaceAvailable(sourceMip)) { + return; + } auto size = _gpuObject.evalMipDimensions(sourceMip); - copyMipFaceLinesFromTexture(sourceMip, targetMip, face, 0, size.y, 0); + auto mipData = _gpuObject.accessStoredMipFace(sourceMip, face); + GLTexelFormat texelFormat = GLTexelFormat::evalGLTexelFormat(_gpuObject.getTexelFormat(), mipData->getFormat()); + copyMipFaceLinesFromTexture(targetMip, face, size, 0, texelFormat.format, texelFormat.type, mipData->readData()); } void GL45Texture::syncSampler() const { diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp b/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp index 597e35750a..e26a5c262f 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp @@ -39,6 +39,7 @@ const uvec3 GL45VariableAllocationTexture::INITIAL_MIP_TRANSFER_DIMENSIONS { 64, WorkQueue GL45VariableAllocationTexture::_transferQueue; WorkQueue GL45VariableAllocationTexture::_promoteQueue; WorkQueue GL45VariableAllocationTexture::_demoteQueue; +TexturePointer GL45VariableAllocationTexture::_currentTransferTexture; #define OVERSUBSCRIBED_PRESSURE_VALUE 0.95f #define UNDERSUBSCRIBED_PRESSURE_VALUE 0.85f @@ -46,6 +47,123 @@ WorkQueue GL45VariableAllocationTexture::_demoteQueue; static const size_t DEFAULT_ALLOWED_TEXTURE_MEMORY = MB_TO_BYTES(DEFAULT_ALLOWED_TEXTURE_MEMORY_MB); +using TransferJob = GL45VariableAllocationTexture::TransferJob; + +static const uvec3 MAX_TRANSFER_DIMENSIONS { 1024, 1024, 1 }; +static const size_t MAX_TRANSFER_SIZE = MAX_TRANSFER_DIMENSIONS.x * MAX_TRANSFER_DIMENSIONS.y * 4; + +std::shared_ptr TransferJob::_bufferThread { nullptr }; +std::atomic TransferJob::_shutdownBufferingThread { false }; +Mutex TransferJob::_mutex; +TransferJob::VoidLambdaQueue TransferJob::_bufferLambdaQueue; + +void TransferJob::startTransferLoop() { + if (_bufferThread) { + return; + } + _shutdownBufferingThread = false; + _bufferThread = std::make_shared([] { + TransferJob::bufferLoop(); + }); +} + +void TransferJob::stopTransferLoop() { + if (!_bufferThread) { + return; + } + _shutdownBufferingThread = true; + _bufferThread->join(); + _bufferThread.reset(); + _shutdownBufferingThread = false; +} + +TransferJob::TransferJob(const GL45VariableAllocationTexture& parent, uint16_t sourceMip, uint16_t targetMip, uint8_t face, uint32_t lines, uint32_t lineOffset) + : _parent(parent), _sourceMip(sourceMip), _targetMip(targetMip), _face(face), _lines(lines), _lineOffset(lineOffset) { + + if (0 == lines) { + _bufferingLambda = [this] { + auto mipData = _parent._gpuObject.accessStoredMipFace(_sourceMip, _face); + auto size = mipData->getSize(); + _buffer.resize(size); + memcpy(&_buffer[0], mipData->readData(), size); + _bufferingCompleted = true; + }; + + } else { + _bufferingLambda = [this] { + auto mipData = _parent._gpuObject.accessStoredMipFace(_sourceMip, _face); + auto dimensions = _parent._gpuObject.evalMipDimensions(_sourceMip); + auto mipSize = mipData->getSize(); + auto bytesPerLine = (uint32_t)mipSize / dimensions.y; + auto transferSize = bytesPerLine * _lines; + auto sourceOffset = bytesPerLine * _lineOffset; + _buffer.resize(transferSize); + memcpy(&_buffer[0], mipData->readData() + sourceOffset, transferSize); + _bufferingCompleted = true; + }; + } + + _transferLambda = [this] { + auto mipData = _parent._gpuObject.accessStoredMipFace(_sourceMip, _face); + auto dimensions = _parent._gpuObject.evalMipDimensions(_sourceMip); + GLTexelFormat texelFormat = GLTexelFormat::evalGLTexelFormat(_parent._gpuObject.getTexelFormat(), mipData->getFormat()); + _parent.copyMipFaceLinesFromTexture(_targetMip, _face, dimensions, _lineOffset, texelFormat.format, texelFormat.type, &_buffer[0]); + _buffer.swap(std::vector()); + }; +} + +TransferJob::TransferJob(const GL45VariableAllocationTexture& parent, std::function transferLambda) + : _parent(parent), _sourceMip(0), _targetMip(0), _face(0), _lines(0), _lineOffset(0), _bufferingCompleted(true), _transferLambda(transferLambda) { + if (!_bufferThread) { + _bufferThread = std::make_shared([] { + TransferJob::bufferLoop(); + }); + } +} + +bool TransferJob::tryTransfer() { + // Are we ready to transfer + if (_bufferingCompleted) { + _transferLambda(); + return true; + } + + startBuffering(); + return false; +} + +void TransferJob::startBuffering() { + if (_bufferingStarted) { + return; + } + _bufferingStarted = true; + { + Lock lock(_mutex); + _bufferLambdaQueue.push(_bufferingLambda); + } +} + +void TransferJob::bufferLoop() { + while (!_shutdownBufferingThread) { + VoidLambdaQueue workingQueue; + { + Lock lock(_mutex); + _bufferLambdaQueue.swap(workingQueue); + } + + if (workingQueue.empty()) { + QThread::msleep(5); + continue; + } + + while (!workingQueue.empty()) { + workingQueue.front()(); + workingQueue.pop(); + } + } +} + + void GL45VariableAllocationTexture::addMemoryManagedTexture(const TexturePointer& texturePointer) { _memoryManagedTextures.push_back(texturePointer); addToWorkQueue(texturePointer); @@ -190,7 +308,14 @@ void GL45VariableAllocationTexture::updateMemoryPressure() { } if (newState != _memoryPressureState) { + if (MemoryPressureState::Transfer == _memoryPressureState) { + TransferJob::stopTransferLoop(); + } _memoryPressureState = newState; + if (MemoryPressureState::Transfer == _memoryPressureState) { + TransferJob::startTransferLoop(); + } + // Clear the existing queue _transferQueue = WorkQueue(); _promoteQueue = WorkQueue(); @@ -223,20 +348,17 @@ void GL45VariableAllocationTexture::processWorkQueues() { if (!object->canDemote()) { continue; } - //qDebug() << "QQQ executing demote for " << texture->source().c_str(); object->demote(); } else if (MemoryPressureState::Undersubscribed == _memoryPressureState) { if (!object->canPromote()) { continue; } - //qDebug() << "QQQ executing promote for " << texture->source().c_str(); object->promote(); } else if (MemoryPressureState::Transfer == _memoryPressureState) { if (!object->hasPendingTransfers()) { continue; } - //qDebug() << "QQQ executing transfer for " << texture->source().c_str(); - object->executeNextTransfer(); + object->executeNextTransfer(texture); } else { Q_UNREACHABLE(); } @@ -265,10 +387,14 @@ GL45VariableAllocationTexture::~GL45VariableAllocationTexture() { Backend::updateTextureGPUMemoryUsage(_size, 0); } -void GL45VariableAllocationTexture::executeNextTransfer() { +void GL45VariableAllocationTexture::executeNextTransfer(const TexturePointer& currentTexture) { if (!_pendingTransfers.empty()) { - _pendingTransfers.front()(); - _pendingTransfers.pop(); + // Keeping hold of a strong pointer during the transfer ensures that the transfer thread cannot try to access a destroyed texture + _currentTransferTexture = currentTexture; + if (_pendingTransfers.front().tryTransfer()) { + _pendingTransfers.pop(); + _currentTransferTexture.reset(); + } } } @@ -394,17 +520,15 @@ void GL45ResourceTexture::demote() { populateTransferQueue(); } + void GL45ResourceTexture::populateTransferQueue() { PROFILE_RANGE(render_gpu_gl, __FUNCTION__); - _pendingTransfers = std::queue(); if (_populatedMip <= _allocatedMip) { return; } + _pendingTransfers = TransferQueue(); - static const uvec3 MAX_TRANSFER_DIMENSIONS { 1024, 1024, 1 }; - static const size_t MAX_TRANSFER_SIZE = MAX_TRANSFER_DIMENSIONS.x * MAX_TRANSFER_DIMENSIONS.y * 4; const uint8_t maxFace = GLTexture::getFaceCount(_target); - uint16_t sourceMip = _populatedMip; do { --sourceMip; @@ -418,11 +542,7 @@ void GL45ResourceTexture::populateTransferQueue() { // If the mip is less than the max transfer size, then just do it in one transfer if (glm::all(glm::lessThanEqual(mipDimensions, MAX_TRANSFER_DIMENSIONS))) { // Can the mip be transferred in one go - _pendingTransfers.push([=] { - Q_ASSERT(sourceMip >= _allocatedMip); - // FIXME modify the copy mechanism to be incremental - copyMipFaceFromTexture(sourceMip, targetMip, face); - }); + _pendingTransfers.emplace(*this, sourceMip, targetMip, face); continue; } @@ -433,24 +553,19 @@ void GL45ResourceTexture::populateTransferQueue() { auto bytesPerLine = (uint32_t)mipData->getSize() / lines; Q_ASSERT(0 == (mipData->getSize() % lines)); uint32_t linesPerTransfer = (uint32_t)(MAX_TRANSFER_SIZE / bytesPerLine); - size_t offset = 0; uint32_t lineOffset = 0; while (lineOffset < lines) { uint32_t linesToCopy = std::min(lines - lineOffset, linesPerTransfer); - uvec3 size { mipDimensions.x, linesToCopy, 1 }; - _pendingTransfers.push([=] { - copyMipFaceLinesFromTexture(sourceMip, targetMip, face, lineOffset, linesToCopy, offset); - }); + _pendingTransfers.emplace(TransferJob(*this, sourceMip, targetMip, face, linesToCopy, lineOffset)); lineOffset += linesToCopy; - offset += (linesToCopy * bytesPerLine); } } // queue up the sampler and populated mip change for after the transfer has completed - _pendingTransfers.push([=] { + _pendingTransfers.emplace(TransferJob(*this, [=] { _populatedMip = sourceMip; syncSampler(); - }); + })); } while (sourceMip != _allocatedMip); } From 115251542d1b4c7cbf7ad95b5a1820f23e1923f9 Mon Sep 17 00:00:00 2001 From: sam Date: Wed, 15 Feb 2017 00:54:16 -0800 Subject: [PATCH 023/302] Adding the ktx library --- libraries/gpu/CMakeLists.txt | 2 +- libraries/ktx/CMakeLists.txt | 3 + libraries/ktx/src/ktx/KTX.cpp | 92 +++++++ libraries/ktx/src/ktx/KTX.h | 397 +++++++++++++++++++++++++++++++ libraries/ktx/src/ktx/Reader.cpp | 159 +++++++++++++ libraries/ktx/src/ktx/Writer.cpp | 21 ++ 6 files changed, 673 insertions(+), 1 deletion(-) create mode 100644 libraries/ktx/CMakeLists.txt create mode 100644 libraries/ktx/src/ktx/KTX.cpp create mode 100644 libraries/ktx/src/ktx/KTX.h create mode 100644 libraries/ktx/src/ktx/Reader.cpp create mode 100644 libraries/ktx/src/ktx/Writer.cpp diff --git a/libraries/gpu/CMakeLists.txt b/libraries/gpu/CMakeLists.txt index 384c5709ee..207431d8c7 100644 --- a/libraries/gpu/CMakeLists.txt +++ b/libraries/gpu/CMakeLists.txt @@ -1,6 +1,6 @@ set(TARGET_NAME gpu) autoscribe_shader_lib(gpu) setup_hifi_library() -link_hifi_libraries(shared) +link_hifi_libraries(shared ktx) target_nsight() diff --git a/libraries/ktx/CMakeLists.txt b/libraries/ktx/CMakeLists.txt new file mode 100644 index 0000000000..404660b247 --- /dev/null +++ b/libraries/ktx/CMakeLists.txt @@ -0,0 +1,3 @@ +set(TARGET_NAME ktx) +setup_hifi_library() +link_hifi_libraries() \ No newline at end of file diff --git a/libraries/ktx/src/ktx/KTX.cpp b/libraries/ktx/src/ktx/KTX.cpp new file mode 100644 index 0000000000..2d5e0bc812 --- /dev/null +++ b/libraries/ktx/src/ktx/KTX.cpp @@ -0,0 +1,92 @@ +// +// KTX.cpp +// ktx/src/ktx +// +// Created by Zach Pomerantz on 2/08/2017. +// Copyright 2017 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 "KTX.h" + +#include //min max and more + +using namespace ktx; + +const Header::Identifier ktx::Header::IDENTIFIER {{ + 0xAB, 0x4B, 0x54, 0x58, 0x20, 0x31, 0x31, 0xBB, 0x0D, 0x0A, 0x1A, 0x0A +}}; + +Header::Header() { + memcpy(identifier, IDENTIFIER.data(), IDENTIFIER_LENGTH); +} + +uint32_t Header::evalMaxDimension() const { + return std::max(pixelWidth, std::max(pixelHeight, pixelDepth)); +} + +uint32_t Header::evalMaxLevel() const { + return 1 + log2(evalMaxDimension()); +} + +uint32_t Header::evalPixelWidth(uint32_t level) const { + return std::max(pixelWidth >> level, 1U); +} +uint32_t Header::evalPixelHeight(uint32_t level) const { + return std::max(pixelHeight >> level, 1U); +} +uint32_t Header::evalPixelDepth(uint32_t level) const { + return std::max(pixelDepth >> level, 1U); +} + +size_t Header::evalPixelSize() const { + return glTypeSize; // Really we should generate the size from the FOrmat etc +} + +size_t Header::evalRowSize(uint32_t level) const { + auto pixelWidth = evalPixelWidth(level); + auto pixSize = evalPixelSize(); + auto netSize = pixelWidth * pixSize; + auto packing = netSize % 4; + return netSize + (packing ? 4 - packing : 0); +} +size_t Header::evalFaceSize(uint32_t level) const { + auto pixelHeight = evalPixelHeight(level); + auto pixelDepth = evalPixelDepth(level); + auto rowSize = evalRowSize(level); + return pixelDepth * pixelHeight * rowSize; +} +size_t Header::evalImageSize(uint32_t level) const { + auto faceSize = evalFaceSize(level); + if (numberOfFaces == 6 && numberOfArrayElements == 0) { + return faceSize; + } else { + return (numberOfArrayElements * numberOfFaces * faceSize); + } +} + + +KTX::KTX() { +} + +void KTX::resetStorage(Storage* storage) { + _storage.reset(storage); +} + +const Header* KTX::getHeader() const { + if (_storage) { + return reinterpret_cast (_storage->_bytes); + } else { + return nullptr; + } +} + +const Byte* KTX::getKeyValueData() const { + if (_storage) { + return (_storage->_bytes + sizeof(Header)); + } else { + return nullptr; + } +} diff --git a/libraries/ktx/src/ktx/KTX.h b/libraries/ktx/src/ktx/KTX.h new file mode 100644 index 0000000000..003d3f0d1b --- /dev/null +++ b/libraries/ktx/src/ktx/KTX.h @@ -0,0 +1,397 @@ +// +// KTX.h +// ktx/src/ktx +// +// Created by Zach Pomerantz on 2/08/2017. +// Copyright 2017 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 +// +#pragma once +#ifndef hifi_ktx_KTX_h +#define hifi_ktx_KTX_h + +#include +#include +#include +#include +#include + +/* KTX Spec: + +Byte[12] identifier +UInt32 endianness +UInt32 glType +UInt32 glTypeSize +UInt32 glFormat +Uint32 glInternalFormat +Uint32 glBaseInternalFormat +UInt32 pixelWidth +UInt32 pixelHeight +UInt32 pixelDepth +UInt32 numberOfArrayElements +UInt32 numberOfFaces +UInt32 numberOfMipmapLevels +UInt32 bytesOfKeyValueData + +for each keyValuePair that fits in bytesOfKeyValueData + UInt32 keyAndValueByteSize + Byte keyAndValue[keyAndValueByteSize] + Byte valuePadding[3 - ((keyAndValueByteSize + 3) % 4)] +end + +for each mipmap_level in numberOfMipmapLevels* + UInt32 imageSize; + for each array_element in numberOfArrayElements* + for each face in numberOfFaces + for each z_slice in pixelDepth* + for each row or row_of_blocks in pixelHeight* + for each pixel or block_of_pixels in pixelWidth + Byte data[format-specific-number-of-bytes]** + end + end + end + Byte cubePadding[0-3] + end + end + Byte mipPadding[3 - ((imageSize + 3) % 4)] +end + +* Replace with 1 if this field is 0. + +** Uncompressed texture data matches a GL_UNPACK_ALIGNMENT of 4. +*/ + + + +namespace ktx { + + enum GLType : uint32_t { + COMPRESSED_TYPE = 0, + + // GL 4.4 Table 8.2 + UNSIGNED_BYTE = 0x1401, + BYTE = 0x1400, + UNSIGNED_SHORT = 0x1403, + SHORT = 0x1402, + UNSIGNED_INT = 0x1405, + INT = 0x1404, + HALF_FLOAT = 0x140B, + FLOAT = 0x1406, + UNSIGNED_BYTE_3_3_2 = 0x8032, + UNSIGNED_BYTE_2_3_3_REV = 0x8362, + UNSIGNED_SHORT_5_6_5 = 0x8363, + UNSIGNED_SHORT_5_6_5_REV = 0x8364, + UNSIGNED_SHORT_4_4_4_4 = 0x8033, + UNSIGNED_SHORT_4_4_4_4_REV = 0x8365, + UNSIGNED_SHORT_5_5_5_1 = 0x8034, + UNSIGNED_SHORT_1_5_5_5_REV = 0x8366, + UNSIGNED_INT_8_8_8_8 = 0x8035, + UNSIGNED_INT_8_8_8_8_REV = 0x8367, + UNSIGNED_INT_10_10_10_2 = 0x8036, + UNSIGNED_INT_2_10_10_10_REV = 0x8368, + UNSIGNED_INT_24_8 = 0x84FA, + UNSIGNED_INT_10F_11F_11F_REV = 0x8C3B, + UNSIGNED_INT_5_9_9_9_REV = 0x8C3E, + FLOAT_32_UNSIGNED_INT_24_8_REV = 0x8DAD, + + NUM_GLTYPES = 25, + }; + + enum GLFormat : uint32_t { + COMPRESSED_FORMAT = 0, + + // GL 4.4 Table 8.3 + STENCIL_INDEX = 0x1901, + DEPTH_COMPONENT = 0x1902, + DEPTH_STENCIL = 0x84F9, + + RED = 0x1903, + GREEN = 0x1904, + BLUE = 0x1905, + RG = 0x8227, + RGB = 0x1907, + RGBA = 0x1908, + BGR = 0x80E0, + BGRA = 0x80E1, + + RG_INTEGER = 0x8228, + RED_INTEGER = 0x8D94, + GREEN_INTEGER = 0x8D95, + BLUE_INTEGER = 0x8D96, + RGB_INTEGER = 0x8D98, + RGBA_INTEGER = 0x8D99, + BGR_INTEGER = 0x8D9A, + BGRA_INTEGER = 0x8D9B, + + NUM_GLFORMATS = 20, + }; + + enum GLInternalFormat_Uncompressed : uint32_t { + // GL 4.4 Table 8.12 + R8 = 0x8229, + R8_SNORM = 0x8F94, + + R16 = 0x822A, + R16_SNORM = 0x8F98, + + RG8 = 0x822B, + RG8_SNORM = 0x8F95, + + RG16 = 0x822C, + RG16_SNORM = 0x8F99, + + R3_G3_B2 = 0x2A10, + RGB4 = 0x804F, + RGB5 = 0x8050, + RGB565 = 0x8D62, + + RGB8 = 0x8051, + RGB8_SNORM = 0x8F96, + RGB10 = 0x8052, + RGB12 = 0x8053, + + RGB16 = 0x8054, + RGB16_SNORM = 0x8F9A, + + RGBA2 = 0x8055, + RGBA4 = 0x8056, + RGB5_A1 = 0x8057, + RGBA8 = 0x8058, + RGBA8_SNORM = 0x8F97, + + RGB10_A2 = 0x8059, + RGB10_A2UI = 0x906F, + + RGBA12 = 0x805A, + RGBA16 = 0x805B, + RGBA16_SNORM = 0x8F9B, + + SRGB8 = 0x8C41, + SRGB8_ALPHA8 = 0x8C43, + + R16F = 0x822D, + RG16F = 0x822F, + RGB16F = 0x881B, + RGBA16F = 0x881A, + + R32F = 0x822E, + RG32F = 0x8230, + RGB32F = 0x8815, + RGBA32F = 0x8814, + + R11F_G11F_B10F = 0x8C3A, + RGB9_E5 = 0x8C3D, + + + R8I = 0x8231, + R8UI = 0x8232, + R16I = 0x8233, + R16UI = 0x8234, + R32I = 0x8235, + R32UI = 0x8236, + RG8I = 0x8237, + RG8UI = 0x8238, + RG16I = 0x8239, + RG16UI = 0x823A, + RG32I = 0x823B, + RG32UI = 0x823C, + + RGB8I = 0x8D8F, + RGB8UI = 0x8D7D, + RGB16I = 0x8D89, + RGB16UI = 0x8D77, + + RGB32I = 0x8D83, + RGB32UI = 0x8D71, + RGBA8I = 0x8D8E, + RGBA8UI = 0x8D7C, + RGBA16I = 0x8D88, + RGBA16UI = 0x8D76, + RGBA32I = 0x8D82, + + RGBA32UI = 0x8D70, + + // GL 4.4 Table 8.13 + DEPTH_COMPONENT16 = 0x81A5, + DEPTH_COMPONENT24 = 0x81A6, + DEPTH_COMPONENT32 = 0x81A7, + + DEPTH_COMPONENT32F = 0x8CAC, + DEPTH24_STENCIL8 = 0x88F0, + DEPTH32F_STENCIL8 = 0x8CAD, + + STENCIL_INDEX1 = 0x8D46, + STENCIL_INDEX4 = 0x8D47, + STENCIL_INDEX8 = 0x8D48, + STENCIL_INDEX16 = 0x8D49, + + NUM_UNCOMPRESSED_GLINTERNALFORMATS = 74, + }; + + enum GLInternalFormat_Compressed : uint32_t { + // GL 4.4 Table 8.14 + COMPRESSED_RED = 0x8225, + COMPRESSED_RG = 0x8226, + COMPRESSED_RGB = 0x84ED, + COMPRESSED_RGBA = 0x84EE, + + COMPRESSED_SRGB = 0x8C48, + COMPRESSED_SRGB_ALPHA = 0x8C49, + + COMPRESSED_RED_RGTC1 = 0x8DBB, + COMPRESSED_SIGNED_RED_RGTC1 = 0x8DBC, + COMPRESSED_RG_RGTC2 = 0x8DBD, + COMPRESSED_SIGNED_RG_RGTC2 = 0x8DBE, + + COMPRESSED_RGBA_BPTC_UNORM = 0x8E8C, + COMPRESSED_SRGB_ALPHA_BPTC_UNORM = 0x8E8D, + COMPRESSED_RGB_BPTC_SIGNED_FLOAT = 0x8E8E, + COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT = 0x8E8F, + + COMPRESSED_RGB8_ETC2 = 0x9274, + COMPRESSED_SRGB8_ETC2 = 0x9275, + COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2 = 0x9276, + COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2 = 0x9277, + COMPRESSED_RGBA8_ETC2_EAC = 0x9278, + COMPRESSED_SRGB8_ALPHA8_ETC2_EAC = 0x9279, + + COMPRESSED_R11_EAC = 0x9270, + COMPRESSED_SIGNED_R11_EAC = 0x9271, + COMPRESSED_RG11_EAC = 0x9272, + COMPRESSED_SIGNED_RG11_EAC = 0x9273, + + NUM_COMPRESSED_GLINTERNALFORMATS = 24, + }; + + enum GLBaseInternalFormat : uint32_t { + // GL 4.4 Table 8.11 + BIF_DEPTH_COMPONENT = 0x1902, + BIF_DEPTH_STENCIL = 0x84F9, + BIF_RED = 0x1903, + BIF_RG = 0x8227, + BIF_RGB = 0x1907, + BIF_RGBA = 0x1908, + BIF_STENCIL_INDEX = 0x1901, + + NUM_GLBASEINTERNALFORMATS = 7, + }; + + enum CubeMapFace { + POS_X = 0, + NEG_X = 1, + POS_Y = 2, + NEG_Y = 3, + POS_Z = 4, + NEG_Z = 5, + NUM_CUBEMAPFACES = 6, + }; + + using Byte = uint8_t; + + // Chunk of data + struct Storage { + size_t _size {0}; + Byte* _bytes {nullptr}; + + Byte* data() { + return _bytes; + } + const Byte* data() const { + return _bytes; + } + size_t size() const { return _size; } + + ~Storage() { if (_bytes) { delete _bytes; } } + + Storage() {} + Storage(size_t size) : + _size(size) + { + if (_size) { _bytes = new Byte[_size]; } + } + + Storage(size_t size, Byte* bytes) : + _size(size) + { + if (_size && _bytes) { _bytes = bytes; } + } + }; + + // Header + struct Header { + static const size_t IDENTIFIER_LENGTH = 12; + using Identifier = std::array; + static const Identifier IDENTIFIER; + + static const uint32_t ENDIAN_TEST = 0x04030201; + static const uint32_t REVERSE_ENDIAN_TEST = 0x01020304; + + Header(); + + Byte identifier[IDENTIFIER_LENGTH]; + uint32_t endianness { ENDIAN_TEST }; + uint32_t glType; + uint32_t glTypeSize; + uint32_t glFormat; + uint32_t glInternalFormat; + uint32_t glBaseInternalFormat; + uint32_t pixelWidth; + uint32_t pixelHeight; + uint32_t pixelDepth; + uint32_t numberOfArrayElements; + uint32_t numberOfFaces; + uint32_t numberOfMipmapLevels; + uint32_t bytesOfKeyValueData; + + uint32_t evalMaxDimension() const; + uint32_t evalMaxLevel() const; + uint32_t evalPixelWidth(uint32_t level) const; + uint32_t evalPixelHeight(uint32_t level) const; + uint32_t evalPixelDepth(uint32_t level) const; + + size_t evalPixelSize() const; + size_t evalRowSize(uint32_t level) const; + size_t evalFaceSize(uint32_t level) const; + size_t evalImageSize(uint32_t level) const; + + }; + + // Key Values + using KeyValue = std::pair; + using KeyValues = std::list; + + + struct Mip { + uint32_t imageSize; + const Byte* _bytes; + }; + using Mips = std::vector; + + class KTX { + void resetStorage(Storage* src); + + public: + + KTX(); + + bool read(const Storage* src); + bool read(Storage* src); + + std::unique_ptr _storage; + + const Header* getHeader() const; + const Byte* getKeyValueData() const; + + KeyValues _keyValues; + + Mips _mips; + + static bool checkStorageHeader(const Storage& storage); + static KTX* create(const Storage* src); + }; + +} + +#endif // hifi_ktx_KTX_h diff --git a/libraries/ktx/src/ktx/Reader.cpp b/libraries/ktx/src/ktx/Reader.cpp new file mode 100644 index 0000000000..8ec4de7686 --- /dev/null +++ b/libraries/ktx/src/ktx/Reader.cpp @@ -0,0 +1,159 @@ +// +// Reader.cpp +// ktx/src/ktx +// +// Created by Zach Pomerantz on 2/08/2017. +// Copyright 2017 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 "KTX.h" + +#include +#include + +namespace ktx { + class Exception: public std::exception { + public: + Exception(std::string explanation) : _explanation(explanation) {} + const char* what() const override { + return ("KTX deserialization error: " + _explanation).c_str(); + } + private: + std::string _explanation; + }; + + bool checkEndianness(uint32_t endianness, bool& matching) { + switch (endianness) { + case Header::ENDIAN_TEST: { + matching = true; + return true; + } + break; + case Header::REVERSE_ENDIAN_TEST: + { + matching = false; + return true; + } + break; + default: + return false; + } + } + + bool checkIdentifier(const Byte* identifier) { + return memcmp(identifier, Header::IDENTIFIER.data(), Header::IDENTIFIER_LENGTH); + } + + KeyValues getKeyValues(size_t length, const Byte* src) { + KeyValues keyValues; + size_t offset = 0; + + while (offset < length) { + // determine byte size + uint32_t keyValueByteSize; + memcpy(&keyValueByteSize, src, sizeof(uint32_t)); + if (keyValueByteSize > length - offset) { + throw Exception("invalid key-value size"); + } + + // find the first null character \0 + int keyLength = 0; + while (reinterpret_cast(src[++keyLength]) != '\0') { + if (keyLength == keyValueByteSize) { + // key must be null-terminated, and there must be space for the value + throw Exception("invalid key-value " + std::string(reinterpret_cast(src), keyLength)); + } + } + + // populate the key-value + keyValues.emplace_back( + std::move(std::string(reinterpret_cast(src), keyLength)), + std::move(std::string(reinterpret_cast(src + keyLength), keyValueByteSize - keyLength))); + + // advance offset/src + uint32_t keyValuePadding = 3 - ((keyValueByteSize + 3) % 4); + offset += keyValueByteSize + keyValuePadding; + src += keyValueByteSize + keyValuePadding; + } + + return keyValues; + } + + bool KTX::read(Storage* src) { + resetStorage(src); + + return true; + } + + bool KTX::checkStorageHeader(const Storage& src) { + try { + size_t srcSize = src.size(); + const uint8_t* srcBytes = src.data(); + + // validation + if (srcSize < sizeof(Header)) { + throw Exception("length is too short for header"); + } + const Header* header = reinterpret_cast(srcBytes); + + if (!checkIdentifier(header->identifier)) { + throw Exception("identifier field invalid"); + } + + bool endianMatch { true }; + if (!checkEndianness(header->endianness, endianMatch)) { + throw Exception("endianness field has invalid value"); + } + + // TODO: endian conversion if !endianMatch - for now, this is for local use and is unnecessary + + + // TODO: calculated bytesOfTexData + if (srcSize < (sizeof(Header) + header->bytesOfKeyValueData)) { + throw Exception("length is too short for metadata"); + } + + size_t bytesOfTexData = 0; + if (srcSize < (sizeof(Header) + header->bytesOfKeyValueData + bytesOfTexData)) { + + throw Exception("length is too short for data"); + } + + + // read metadata + KeyValues keyValues = getKeyValues(header->bytesOfKeyValueData, srcBytes + sizeof(Header)); + + // prepare gpu::Texture using header & key-values + // TODO + + // read data + // TODO + + return true; + } catch (Exception& e) { + qWarning(e.what()); + return false; + } + } + + KTX* KTX::create(const Storage* data) { + try { + if (!checkStorageHeader(*data)) { + + } + + auto result = new KTX(); + result->resetStorage(const_cast(data)); + + // read metadata + KeyValues keyValues = getKeyValues(result->getHeader()->bytesOfKeyValueData, result->getKeyValueData()); + + return nullptr; + } catch (Exception& e) { + qWarning(e.what()); + return nullptr; + } + } +} \ No newline at end of file diff --git a/libraries/ktx/src/ktx/Writer.cpp b/libraries/ktx/src/ktx/Writer.cpp new file mode 100644 index 0000000000..d502a4a29a --- /dev/null +++ b/libraries/ktx/src/ktx/Writer.cpp @@ -0,0 +1,21 @@ +// +// Writer.cpp +// ktx/src/ktx +// +// Created by Zach Pomerantz on 2/08/2017. +// Copyright 2017 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 "KTX.h" + + +namespace ktx { + /* size_t serialize(const gpu::Texture& texture, uint8_t* data) { + return 0; + }*/ + /* KTX serialize(const gpu::Texture& texture) { + return KTX(0, nullptr); + }*/ +} From 894f1b8e665cd6f428a9a3a9bf2c2381e9bd943d Mon Sep 17 00:00:00 2001 From: samcake Date: Wed, 15 Feb 2017 16:24:06 -0800 Subject: [PATCH 024/302] DOne with the parsing side of things --- libraries/ktx/src/ktx/KTX.cpp | 29 +++++++++++- libraries/ktx/src/ktx/KTX.h | 49 +++++++++++++++------ libraries/ktx/src/ktx/Reader.cpp | 75 ++++++++++++++++++++++---------- 3 files changed, 114 insertions(+), 39 deletions(-) diff --git a/libraries/ktx/src/ktx/KTX.cpp b/libraries/ktx/src/ktx/KTX.cpp index 2d5e0bc812..4ce4c94c69 100644 --- a/libraries/ktx/src/ktx/KTX.cpp +++ b/libraries/ktx/src/ktx/KTX.cpp @@ -49,8 +49,8 @@ size_t Header::evalRowSize(uint32_t level) const { auto pixelWidth = evalPixelWidth(level); auto pixSize = evalPixelSize(); auto netSize = pixelWidth * pixSize; - auto packing = netSize % 4; - return netSize + (packing ? 4 - packing : 0); + auto packing = netSize % PACKING_SIZE; + return netSize + (packing ? PACKING_SIZE - packing : 0); } size_t Header::evalFaceSize(uint32_t level) const { auto pixelHeight = evalPixelHeight(level); @@ -83,6 +83,23 @@ const Header* KTX::getHeader() const { } } + +size_t KTX::getKeyValueDataSize() const { + if (_storage) { + return getHeader()->bytesOfKeyValueData; + } else { + return 0; + } +} + +size_t KTX::getTexelsDataSize() const { + if (_storage) { + return _storage->size() - sizeof(Header) + getKeyValueDataSize(); + } else { + return 0; + } +} + const Byte* KTX::getKeyValueData() const { if (_storage) { return (_storage->_bytes + sizeof(Header)); @@ -90,3 +107,11 @@ const Byte* KTX::getKeyValueData() const { return nullptr; } } + +const Byte* KTX::getTexelsData() const { + if (_storage) { + return (_storage->_bytes + sizeof(Header) + getKeyValueDataSize()); + } else { + return nullptr; + } +} diff --git a/libraries/ktx/src/ktx/KTX.h b/libraries/ktx/src/ktx/KTX.h index 003d3f0d1b..b6a9240972 100644 --- a/libraries/ktx/src/ktx/KTX.h +++ b/libraries/ktx/src/ktx/KTX.h @@ -66,6 +66,7 @@ end namespace ktx { + const uint32_t PACKING_SIZE { sizeof(uint32_t) }; enum GLType : uint32_t { COMPRESSED_TYPE = 0, @@ -317,6 +318,17 @@ namespace ktx { { if (_size && _bytes) { _bytes = bytes; } } + Storage(size_t size, const Byte* bytes) : + Storage(size) + { + if (_size && _bytes && bytes) { + memcpy(_bytes, bytes, size); + } + } + Storage(const Storage& src) : + Storage(src.size(), src.data()) + {} + }; // Header @@ -364,8 +376,14 @@ namespace ktx { struct Mip { - uint32_t imageSize; + uint32_t _imageSize; + uint32_t _padding; const Byte* _bytes; + + Mip(uint32_t imageSize, uint32_t padding, const Byte* bytes) : + _imageSize(imageSize), + _padding(padding), + _bytes(bytes) {} }; using Mips = std::vector; @@ -375,21 +393,26 @@ namespace ktx { public: KTX(); + ~KTX(); - bool read(const Storage* src); - bool read(Storage* src); - - std::unique_ptr _storage; - - const Header* getHeader() const; - const Byte* getKeyValueData() const; - - KeyValues _keyValues; - - Mips _mips; + // parse a block of memory and create a KTX object from it + static std::unique_ptr create(const Storage& src); + static std::unique_ptr create(std::unique_ptr& src); static bool checkStorageHeader(const Storage& storage); - static KTX* create(const Storage* src); + + // Access raw pointers to the main sections of the KTX + const Header* getHeader() const; + const Byte* getKeyValueData() const; + const Byte* getTexelsData() const; + + size_t getKeyValueDataSize() const; + size_t getTexelsDataSize() const; + + + std::unique_ptr _storage; + KeyValues _keyValues; + Mips _mips; }; } diff --git a/libraries/ktx/src/ktx/Reader.cpp b/libraries/ktx/src/ktx/Reader.cpp index 8ec4de7686..ff682d5bdb 100644 --- a/libraries/ktx/src/ktx/Reader.cpp +++ b/libraries/ktx/src/ktx/Reader.cpp @@ -73,7 +73,7 @@ namespace ktx { std::move(std::string(reinterpret_cast(src + keyLength), keyValueByteSize - keyLength))); // advance offset/src - uint32_t keyValuePadding = 3 - ((keyValueByteSize + 3) % 4); + uint32_t keyValuePadding = 3 - ((keyValueByteSize + 3) % PACKING_SIZE); offset += keyValueByteSize + keyValuePadding; src += keyValueByteSize + keyValuePadding; } @@ -81,16 +81,39 @@ namespace ktx { return keyValues; } - bool KTX::read(Storage* src) { - resetStorage(src); + Mips getMipsTable(const Header& header, size_t mipsDataSize, const Byte* mipsData) { + Mips mips; + auto currentPtr = mipsData; + auto numMips = header.numberOfMipmapLevels + 1; - return true; + // Keep identifying new mip as long as we can at list query the next imageSize + while ((currentPtr - mipsData) + sizeof(uint32_t) <= (mipsDataSize)) { + + // Grab the imageSize coming up + auto imageSize = *reinterpret_cast(currentPtr); + currentPtr += sizeof(uint32_t); + + // If enough data ahead then capture the pointer + if ((currentPtr - mipsData) + imageSize <= (mipsDataSize)) { + auto padding = imageSize % PACKING_SIZE; + padding = (padding ? 4 - padding : 0); + + mips.emplace_back(Mip(imageSize, padding, currentPtr)); + + currentPtr += imageSize + padding; + } else { + break; + } + } + + return mips; } + bool KTX::checkStorageHeader(const Storage& src) { try { - size_t srcSize = src.size(); - const uint8_t* srcBytes = src.data(); + auto srcSize = src.size(); + auto srcBytes = src.data(); // validation if (srcSize < sizeof(Header)) { @@ -121,16 +144,6 @@ namespace ktx { throw Exception("length is too short for data"); } - - // read metadata - KeyValues keyValues = getKeyValues(header->bytesOfKeyValueData, srcBytes + sizeof(Header)); - - // prepare gpu::Texture using header & key-values - // TODO - - // read data - // TODO - return true; } catch (Exception& e) { qWarning(e.what()); @@ -138,20 +151,34 @@ namespace ktx { } } - KTX* KTX::create(const Storage* data) { + std::unique_ptr KTX::create(const Storage& src) { + auto srcCopy = std::make_unique(src); + + return create(srcCopy); + } + + std::unique_ptr KTX::create(std::unique_ptr& src) { + if (!src) { + return nullptr; + } + try { - if (!checkStorageHeader(*data)) { - + if (!checkStorageHeader(*src)) { + } - auto result = new KTX(); - result->resetStorage(const_cast(data)); + std::unique_ptr result(new KTX()); + result->resetStorage(src.release()); // read metadata - KeyValues keyValues = getKeyValues(result->getHeader()->bytesOfKeyValueData, result->getKeyValueData()); + result->_keyValues = getKeyValues(result->getHeader()->bytesOfKeyValueData, result->getKeyValueData()); - return nullptr; - } catch (Exception& e) { + // populate mip table + result->_mips = getMipsTable(*result->getHeader(), result->getTexelsDataSize(), result->getTexelsData()); + + return result; + } + catch (Exception& e) { qWarning(e.what()); return nullptr; } From cf9fc17c4d640041a16a42296e72ea11a3f472bd Mon Sep 17 00:00:00 2001 From: samcake Date: Wed, 15 Feb 2017 17:04:40 -0800 Subject: [PATCH 025/302] Starting to work on the writer side --- libraries/ktx/src/ktx/KTX.cpp | 10 +++++-- libraries/ktx/src/ktx/KTX.h | 23 ++++++++++------ libraries/ktx/src/ktx/Reader.cpp | 42 ++++++++++++++--------------- libraries/ktx/src/ktx/Writer.cpp | 46 +++++++++++++++++++++++++++----- 4 files changed, 84 insertions(+), 37 deletions(-) diff --git a/libraries/ktx/src/ktx/KTX.cpp b/libraries/ktx/src/ktx/KTX.cpp index 4ce4c94c69..d2872e76b5 100644 --- a/libraries/ktx/src/ktx/KTX.cpp +++ b/libraries/ktx/src/ktx/KTX.cpp @@ -15,6 +15,11 @@ using namespace ktx; +uint32_t evalPadding(size_t byteSize) { + auto padding = byteSize % PACKING_SIZE; + return (padding ? PACKING_SIZE - padding : 0); +} + const Header::Identifier ktx::Header::IDENTIFIER {{ 0xAB, 0x4B, 0x54, 0x58, 0x20, 0x31, 0x31, 0xBB, 0x0D, 0x0A, 0x1A, 0x0A }}; @@ -49,8 +54,8 @@ size_t Header::evalRowSize(uint32_t level) const { auto pixelWidth = evalPixelWidth(level); auto pixSize = evalPixelSize(); auto netSize = pixelWidth * pixSize; - auto packing = netSize % PACKING_SIZE; - return netSize + (packing ? PACKING_SIZE - packing : 0); + auto padding = evalPadding(netSize); + return netSize + padding; } size_t Header::evalFaceSize(uint32_t level) const { auto pixelHeight = evalPixelHeight(level); @@ -115,3 +120,4 @@ const Byte* KTX::getTexelsData() const { return nullptr; } } + diff --git a/libraries/ktx/src/ktx/KTX.h b/libraries/ktx/src/ktx/KTX.h index b6a9240972..5b72c4c1ce 100644 --- a/libraries/ktx/src/ktx/KTX.h +++ b/libraries/ktx/src/ktx/KTX.h @@ -67,6 +67,8 @@ end namespace ktx { const uint32_t PACKING_SIZE { sizeof(uint32_t) }; + using Byte = uint8_t; + uint32_t evalPadding(size_t byteSize); enum GLType : uint32_t { COMPRESSED_TYPE = 0, @@ -289,7 +291,6 @@ namespace ktx { NUM_CUBEMAPFACES = 6, }; - using Byte = uint8_t; // Chunk of data struct Storage { @@ -375,31 +376,37 @@ namespace ktx { using KeyValues = std::list; - struct Mip { + struct Image { uint32_t _imageSize; uint32_t _padding; const Byte* _bytes; - Mip(uint32_t imageSize, uint32_t padding, const Byte* bytes) : + Image(uint32_t imageSize, uint32_t padding, const Byte* bytes) : _imageSize(imageSize), _padding(padding), _bytes(bytes) {} }; - using Mips = std::vector; + using Images = std::vector; + class KTX { void resetStorage(Storage* src); + KTX(); public: - KTX(); ~KTX(); - // parse a block of memory and create a KTX object from it + // Define a KTX object manually to write it somewhere (in a file on disk?) + // This path allocate the Storage where to store header, keyvalues and copy mips + // Then COPY all the data + static std::unique_ptr create(const Header& header, const KeyValues& keyValues, const Images& images); + + // Parse a block of memory and create a KTX object from it static std::unique_ptr create(const Storage& src); static std::unique_ptr create(std::unique_ptr& src); - static bool checkStorageHeader(const Storage& storage); + static bool checkHeaderFromStorage(const Storage& storage); // Access raw pointers to the main sections of the KTX const Header* getHeader() const; @@ -412,7 +419,7 @@ namespace ktx { std::unique_ptr _storage; KeyValues _keyValues; - Mips _mips; + Images _images; }; } diff --git a/libraries/ktx/src/ktx/Reader.cpp b/libraries/ktx/src/ktx/Reader.cpp index ff682d5bdb..2b45d30786 100644 --- a/libraries/ktx/src/ktx/Reader.cpp +++ b/libraries/ktx/src/ktx/Reader.cpp @@ -14,9 +14,9 @@ #include namespace ktx { - class Exception: public std::exception { + class ReaderException: public std::exception { public: - Exception(std::string explanation) : _explanation(explanation) {} + ReaderException(std::string explanation) : _explanation(explanation) {} const char* what() const override { return ("KTX deserialization error: " + _explanation).c_str(); } @@ -55,7 +55,7 @@ namespace ktx { uint32_t keyValueByteSize; memcpy(&keyValueByteSize, src, sizeof(uint32_t)); if (keyValueByteSize > length - offset) { - throw Exception("invalid key-value size"); + throw ReaderException("invalid key-value size"); } // find the first null character \0 @@ -63,7 +63,7 @@ namespace ktx { while (reinterpret_cast(src[++keyLength]) != '\0') { if (keyLength == keyValueByteSize) { // key must be null-terminated, and there must be space for the value - throw Exception("invalid key-value " + std::string(reinterpret_cast(src), keyLength)); + throw ReaderException("invalid key-value " + std::string(reinterpret_cast(src), keyLength)); } } @@ -81,8 +81,8 @@ namespace ktx { return keyValues; } - Mips getMipsTable(const Header& header, size_t mipsDataSize, const Byte* mipsData) { - Mips mips; + Images getImagesTable(const Header& header, size_t mipsDataSize, const Byte* mipsData) { + Images images; auto currentPtr = mipsData; auto numMips = header.numberOfMipmapLevels + 1; @@ -95,10 +95,9 @@ namespace ktx { // If enough data ahead then capture the pointer if ((currentPtr - mipsData) + imageSize <= (mipsDataSize)) { - auto padding = imageSize % PACKING_SIZE; - padding = (padding ? 4 - padding : 0); + auto padding = evalPadding(imageSize); - mips.emplace_back(Mip(imageSize, padding, currentPtr)); + images.emplace_back(Image(imageSize, padding, currentPtr)); currentPtr += imageSize + padding; } else { @@ -106,28 +105,28 @@ namespace ktx { } } - return mips; + return images; } - bool KTX::checkStorageHeader(const Storage& src) { + bool KTX::checkHeaderFromStorage(const Storage& src) { try { auto srcSize = src.size(); auto srcBytes = src.data(); // validation if (srcSize < sizeof(Header)) { - throw Exception("length is too short for header"); + throw ReaderException("length is too short for header"); } const Header* header = reinterpret_cast(srcBytes); if (!checkIdentifier(header->identifier)) { - throw Exception("identifier field invalid"); + throw ReaderException("identifier field invalid"); } bool endianMatch { true }; if (!checkEndianness(header->endianness, endianMatch)) { - throw Exception("endianness field has invalid value"); + throw ReaderException("endianness field has invalid value"); } // TODO: endian conversion if !endianMatch - for now, this is for local use and is unnecessary @@ -135,17 +134,18 @@ namespace ktx { // TODO: calculated bytesOfTexData if (srcSize < (sizeof(Header) + header->bytesOfKeyValueData)) { - throw Exception("length is too short for metadata"); + throw ReaderException("length is too short for metadata"); } size_t bytesOfTexData = 0; if (srcSize < (sizeof(Header) + header->bytesOfKeyValueData + bytesOfTexData)) { - throw Exception("length is too short for data"); + throw ReaderException("length is too short for data"); } return true; - } catch (Exception& e) { + } + catch (ReaderException& e) { qWarning(e.what()); return false; } @@ -163,7 +163,7 @@ namespace ktx { } try { - if (!checkStorageHeader(*src)) { + if (!checkHeaderFromStorage(*src)) { } @@ -173,12 +173,12 @@ namespace ktx { // read metadata result->_keyValues = getKeyValues(result->getHeader()->bytesOfKeyValueData, result->getKeyValueData()); - // populate mip table - result->_mips = getMipsTable(*result->getHeader(), result->getTexelsDataSize(), result->getTexelsData()); + // populate image table + result->_images = getImagesTable(*result->getHeader(), result->getTexelsDataSize(), result->getTexelsData()); return result; } - catch (Exception& e) { + catch (ReaderException& e) { qWarning(e.what()); return nullptr; } diff --git a/libraries/ktx/src/ktx/Writer.cpp b/libraries/ktx/src/ktx/Writer.cpp index d502a4a29a..a2b3d55178 100644 --- a/libraries/ktx/src/ktx/Writer.cpp +++ b/libraries/ktx/src/ktx/Writer.cpp @@ -12,10 +12,44 @@ namespace ktx { - /* size_t serialize(const gpu::Texture& texture, uint8_t* data) { - return 0; - }*/ - /* KTX serialize(const gpu::Texture& texture) { - return KTX(0, nullptr); - }*/ + + class WriterException : public std::exception { + public: + WriterException(std::string explanation) : _explanation(explanation) {} + const char* what() const override { + return ("KTX serialization error: " + _explanation).c_str(); + } + private: + std::string _explanation; + }; + + std::unique_ptr generateStorage(const Header& header, const KeyValues& keyValues, const Images& images) { + size_t storageSize = sizeof(Header); + auto numMips = header.numberOfMipmapLevels + 1; + + for (uint32_t l = 0; l < numMips; l++) { + if (images.size() > l) { + + storageSize += images[l]._imageSize; + storageSize += images[l]._imageSize; + } + + } + + } + + std::unique_ptr KTX::create(const Header& header, const KeyValues& keyValues, const Images& images) { + + std::unique_ptr result(new KTX()); + result->resetStorage(generateStorage(header, keyValues, images).release()); + + // read metadata + result->_keyValues = keyValues; + + // populate image table + result->_images = images; + + return result; + } + } From d56f982decec045c8f012bf33af7532b1edfc123 Mon Sep 17 00:00:00 2001 From: sam Date: Thu, 16 Feb 2017 02:45:53 -0800 Subject: [PATCH 026/302] Maybe saving the first ktx textures, testing the save pipeline --- interface/CMakeLists.txt | 2 +- libraries/gpu/src/gpu/Texture.cpp | 25 +++++++++ libraries/gpu/src/gpu/Texture.h | 8 +++ libraries/ktx/src/ktx/KTX.cpp | 38 ++++++++----- libraries/ktx/src/ktx/KTX.h | 71 ++++++++++++++++-------- libraries/ktx/src/ktx/Reader.cpp | 6 +- libraries/ktx/src/ktx/Writer.cpp | 55 ++++++++++++++++-- libraries/model/CMakeLists.txt | 2 +- libraries/model/src/model/TextureMap.cpp | 17 ++++++ libraries/render-utils/CMakeLists.txt | 2 +- libraries/render/CMakeLists.txt | 2 +- 11 files changed, 181 insertions(+), 47 deletions(-) diff --git a/interface/CMakeLists.txt b/interface/CMakeLists.txt index 868a2cf933..03b7a6b7e9 100644 --- a/interface/CMakeLists.txt +++ b/interface/CMakeLists.txt @@ -178,7 +178,7 @@ endif() # link required hifi libraries link_hifi_libraries( - shared octree gpu gl gpu-gl procedural model render + shared octree ktx gpu gl gpu-gl procedural model render recording fbx networking model-networking entities avatars audio audio-client animation script-engine physics render-utils entities-renderer ui auto-updater diff --git a/libraries/gpu/src/gpu/Texture.cpp b/libraries/gpu/src/gpu/Texture.cpp index 9db3fb60c3..21c32cc55e 100755 --- a/libraries/gpu/src/gpu/Texture.cpp +++ b/libraries/gpu/src/gpu/Texture.cpp @@ -1006,3 +1006,28 @@ Texture::ExternalUpdates Texture::getUpdates() const { } return result; } + +#include + +ktx::KTXUniquePointer Texture::serialize(const Texture& texture) { + + ktx::Header header; + header.setUncompressed(ktx::GLType::UNSIGNED_BYTE, 4, ktx::GLFormat::BGRA, ktx::GLInternalFormat_Uncompressed::RGBA8, ktx::GLBaseInternalFormat::RGBA); + header.pixelWidth = texture.getWidth(); + header.pixelHeight = texture.getHeight(); + header.numberOfMipmapLevels = texture.mipLevels(); + + ktx::Images images; + for (int level = 0; level < header.numberOfMipmapLevels; level++) { + auto mip = texture.accessStoredMipFace(level); + if (mip) { + images.emplace_back(ktx::Image(mip->getSize(), 0, mip->readData())); + } + } + + auto ktxBuffer = ktx::KTX::create(header, ktx::KeyValues(), images); + return ktxBuffer; +} +TexturePointer Texture::unserialize(const ktx::KTXUniquePointer& srcData) { + return nullptr; +} diff --git a/libraries/gpu/src/gpu/Texture.h b/libraries/gpu/src/gpu/Texture.h index 3c6c34c68d..fba4c7079f 100755 --- a/libraries/gpu/src/gpu/Texture.h +++ b/libraries/gpu/src/gpu/Texture.h @@ -20,6 +20,11 @@ #include "Forward.h" #include "Resource.h" +namespace ktx { + class KTX; + using KTXUniquePointer = std::unique_ptr; +} + namespace gpu { // THe spherical harmonics is a nice tool for cubemap, so if required, the irradiance SH can be automatically generated @@ -482,6 +487,9 @@ public: ExternalUpdates getUpdates() const; + static ktx::KTXUniquePointer serialize(const Texture& texture); + static TexturePointer unserialize(const ktx::KTXUniquePointer& srcData); + protected: const TextureUsageType _usageType; diff --git a/libraries/ktx/src/ktx/KTX.cpp b/libraries/ktx/src/ktx/KTX.cpp index d2872e76b5..f8f315d784 100644 --- a/libraries/ktx/src/ktx/KTX.cpp +++ b/libraries/ktx/src/ktx/KTX.cpp @@ -15,11 +15,12 @@ using namespace ktx; -uint32_t evalPadding(size_t byteSize) { +uint32_t Header::evalPadding(size_t byteSize) { auto padding = byteSize % PACKING_SIZE; - return (padding ? PACKING_SIZE - padding : 0); + return (uint32_t) (padding ? PACKING_SIZE - padding : 0); } + const Header::Identifier ktx::Header::IDENTIFIER {{ 0xAB, 0x4B, 0x54, 0x58, 0x20, 0x31, 0x31, 0xBB, 0x0D, 0x0A, 0x1A, 0x0A }}; @@ -29,7 +30,7 @@ Header::Header() { } uint32_t Header::evalMaxDimension() const { - return std::max(pixelWidth, std::max(pixelHeight, pixelDepth)); + return std::max(getPixelWidth(), std::max(getPixelHeight(), getPixelDepth())); } uint32_t Header::evalMaxLevel() const { @@ -37,13 +38,13 @@ uint32_t Header::evalMaxLevel() const { } uint32_t Header::evalPixelWidth(uint32_t level) const { - return std::max(pixelWidth >> level, 1U); + return std::max(getPixelWidth() >> level, 1U); } uint32_t Header::evalPixelHeight(uint32_t level) const { - return std::max(pixelHeight >> level, 1U); + return std::max(getPixelHeight() >> level, 1U); } uint32_t Header::evalPixelDepth(uint32_t level) const { - return std::max(pixelDepth >> level, 1U); + return std::max(getPixelDepth() >> level, 1U); } size_t Header::evalPixelSize() const { @@ -51,24 +52,24 @@ size_t Header::evalPixelSize() const { } size_t Header::evalRowSize(uint32_t level) const { - auto pixelWidth = evalPixelWidth(level); + auto pixWidth = evalPixelWidth(level); auto pixSize = evalPixelSize(); - auto netSize = pixelWidth * pixSize; + auto netSize = pixWidth * pixSize; auto padding = evalPadding(netSize); return netSize + padding; } size_t Header::evalFaceSize(uint32_t level) const { - auto pixelHeight = evalPixelHeight(level); - auto pixelDepth = evalPixelDepth(level); + auto pixHeight = evalPixelHeight(level); + auto pixDepth = evalPixelDepth(level); auto rowSize = evalRowSize(level); - return pixelDepth * pixelHeight * rowSize; + return pixDepth * pixHeight * rowSize; } size_t Header::evalImageSize(uint32_t level) const { auto faceSize = evalFaceSize(level); if (numberOfFaces == 6 && numberOfArrayElements == 0) { return faceSize; } else { - return (numberOfArrayElements * numberOfFaces * faceSize); + return (getNumberOfSlices() * numberOfFaces * faceSize); } } @@ -76,6 +77,9 @@ size_t Header::evalImageSize(uint32_t level) const { KTX::KTX() { } +KTX::~KTX() { +} + void KTX::resetStorage(Storage* storage) { _storage.reset(storage); } @@ -99,7 +103,8 @@ size_t KTX::getKeyValueDataSize() const { size_t KTX::getTexelsDataSize() const { if (_storage) { - return _storage->size() - sizeof(Header) + getKeyValueDataSize(); + //return _storage->size() - (sizeof(Header) + getKeyValueDataSize()); + return (_storage->_bytes + _storage->_size) - getTexelsData(); } else { return 0; } @@ -121,3 +126,10 @@ const Byte* KTX::getTexelsData() const { } } +Byte* KTX::getTexelsData() { + if (_storage) { + return (_storage->_bytes + sizeof(Header) + getKeyValueDataSize()); + } else { + return nullptr; + } +} diff --git a/libraries/ktx/src/ktx/KTX.h b/libraries/ktx/src/ktx/KTX.h index 5b72c4c1ce..1006124693 100644 --- a/libraries/ktx/src/ktx/KTX.h +++ b/libraries/ktx/src/ktx/KTX.h @@ -68,9 +68,8 @@ end namespace ktx { const uint32_t PACKING_SIZE { sizeof(uint32_t) }; using Byte = uint8_t; - uint32_t evalPadding(size_t byteSize); - enum GLType : uint32_t { + enum class GLType : uint32_t { COMPRESSED_TYPE = 0, // GL 4.4 Table 8.2 @@ -102,7 +101,7 @@ namespace ktx { NUM_GLTYPES = 25, }; - enum GLFormat : uint32_t { + enum class GLFormat : uint32_t { COMPRESSED_FORMAT = 0, // GL 4.4 Table 8.3 @@ -131,7 +130,7 @@ namespace ktx { NUM_GLFORMATS = 20, }; - enum GLInternalFormat_Uncompressed : uint32_t { + enum class GLInternalFormat_Uncompressed : uint32_t { // GL 4.4 Table 8.12 R8 = 0x8229, R8_SNORM = 0x8F94, @@ -233,7 +232,7 @@ namespace ktx { NUM_UNCOMPRESSED_GLINTERNALFORMATS = 74, }; - enum GLInternalFormat_Compressed : uint32_t { + enum class GLInternalFormat_Compressed : uint32_t { // GL 4.4 Table 8.14 COMPRESSED_RED = 0x8225, COMPRESSED_RG = 0x8226, @@ -268,15 +267,15 @@ namespace ktx { NUM_COMPRESSED_GLINTERNALFORMATS = 24, }; - enum GLBaseInternalFormat : uint32_t { + enum class GLBaseInternalFormat : uint32_t { // GL 4.4 Table 8.11 - BIF_DEPTH_COMPONENT = 0x1902, - BIF_DEPTH_STENCIL = 0x84F9, - BIF_RED = 0x1903, - BIF_RG = 0x8227, - BIF_RGB = 0x1907, - BIF_RGBA = 0x1908, - BIF_STENCIL_INDEX = 0x1901, + DEPTH_COMPONENT = 0x1902, + DEPTH_STENCIL = 0x84F9, + RED = 0x1903, + RG = 0x8227, + RGB = 0x1907, + RGBA = 0x1908, + STENCIL_INDEX = 0x1901, NUM_GLBASEINTERNALFORMATS = 7, }; @@ -341,22 +340,33 @@ namespace ktx { static const uint32_t ENDIAN_TEST = 0x04030201; static const uint32_t REVERSE_ENDIAN_TEST = 0x01020304; + static uint32_t evalPadding(size_t byteSize); + Header(); Byte identifier[IDENTIFIER_LENGTH]; uint32_t endianness { ENDIAN_TEST }; + uint32_t glType; - uint32_t glTypeSize; + uint32_t glTypeSize { 0 }; uint32_t glFormat; uint32_t glInternalFormat; uint32_t glBaseInternalFormat; - uint32_t pixelWidth; - uint32_t pixelHeight; - uint32_t pixelDepth; - uint32_t numberOfArrayElements; - uint32_t numberOfFaces; - uint32_t numberOfMipmapLevels; - uint32_t bytesOfKeyValueData; + + uint32_t pixelWidth { 0 }; + uint32_t pixelHeight { 0 }; + uint32_t pixelDepth { 0 }; + uint32_t numberOfArrayElements { 0 }; + uint32_t numberOfFaces { 1 }; + uint32_t numberOfMipmapLevels { 1 }; + + uint32_t bytesOfKeyValueData { 0 }; + + uint32_t getPixelWidth() const { return (pixelWidth ? pixelWidth : 1); } + uint32_t getPixelHeight() const { return (pixelHeight ? pixelHeight : 1); } + uint32_t getPixelDepth() const { return (pixelDepth ? pixelDepth : 1); } + uint32_t getNumberOfSlices() const { return (numberOfArrayElements ? numberOfArrayElements : 1); } + uint32_t getNumberOfLevels() const { return (numberOfMipmapLevels ? numberOfMipmapLevels : 1); } uint32_t evalMaxDimension() const; uint32_t evalMaxLevel() const; @@ -369,6 +379,20 @@ namespace ktx { size_t evalFaceSize(uint32_t level) const; size_t evalImageSize(uint32_t level) const; + void setUncompressed(GLType type, uint32_t typeSize, GLFormat format, GLInternalFormat_Uncompressed internalFormat, GLBaseInternalFormat baseInternalFormat) { + glType = (uint32_t) type; + glTypeSize = 0; + glFormat = (uint32_t) format; + glInternalFormat = (uint32_t) internalFormat; + glBaseInternalFormat = (uint32_t) baseInternalFormat; + } + void setCompressed(GLInternalFormat_Compressed internalFormat, GLBaseInternalFormat baseInternalFormat) { + glType = (uint32_t) GLType::COMPRESSED_TYPE; + glTypeSize = 1; + glFormat = (uint32_t) GLFormat::COMPRESSED_FORMAT; + glInternalFormat = (uint32_t) internalFormat; + glBaseInternalFormat = (uint32_t) baseInternalFormat; + } }; // Key Values @@ -392,6 +416,9 @@ namespace ktx { class KTX { void resetStorage(Storage* src); + void resetHeader(const Header& header); + void resetImages(const Images& images); + KTX(); public: @@ -412,11 +439,11 @@ namespace ktx { const Header* getHeader() const; const Byte* getKeyValueData() const; const Byte* getTexelsData() const; + Byte* getTexelsData(); size_t getKeyValueDataSize() const; size_t getTexelsDataSize() const; - std::unique_ptr _storage; KeyValues _keyValues; Images _images; diff --git a/libraries/ktx/src/ktx/Reader.cpp b/libraries/ktx/src/ktx/Reader.cpp index 2b45d30786..9586a9c033 100644 --- a/libraries/ktx/src/ktx/Reader.cpp +++ b/libraries/ktx/src/ktx/Reader.cpp @@ -84,18 +84,18 @@ namespace ktx { Images getImagesTable(const Header& header, size_t mipsDataSize, const Byte* mipsData) { Images images; auto currentPtr = mipsData; - auto numMips = header.numberOfMipmapLevels + 1; + auto numMips = header.getNumberOfLevels(); // Keep identifying new mip as long as we can at list query the next imageSize while ((currentPtr - mipsData) + sizeof(uint32_t) <= (mipsDataSize)) { // Grab the imageSize coming up - auto imageSize = *reinterpret_cast(currentPtr); + size_t imageSize = *reinterpret_cast(currentPtr); currentPtr += sizeof(uint32_t); // If enough data ahead then capture the pointer if ((currentPtr - mipsData) + imageSize <= (mipsDataSize)) { - auto padding = evalPadding(imageSize); + auto padding = Header::evalPadding(imageSize); images.emplace_back(Image(imageSize, padding, currentPtr)); diff --git a/libraries/ktx/src/ktx/Writer.cpp b/libraries/ktx/src/ktx/Writer.cpp index a2b3d55178..ae2dfc2d6d 100644 --- a/libraries/ktx/src/ktx/Writer.cpp +++ b/libraries/ktx/src/ktx/Writer.cpp @@ -25,17 +25,60 @@ namespace ktx { std::unique_ptr generateStorage(const Header& header, const KeyValues& keyValues, const Images& images) { size_t storageSize = sizeof(Header); - auto numMips = header.numberOfMipmapLevels + 1; + auto numMips = header.getNumberOfLevels(); for (uint32_t l = 0; l < numMips; l++) { if (images.size() > l) { - - storageSize += images[l]._imageSize; + storageSize += sizeof(uint32_t); storageSize += images[l]._imageSize; + storageSize += Header::evalPadding(images[l]._imageSize); } - } + std::unique_ptr storage(new Storage(storageSize)); + return storage; + } + + + void KTX::resetHeader(const Header& header) { + if (!_storage) { + return; + } + memcpy(_storage->_bytes, &header, sizeof(Header)); + } + void KTX::resetImages(const Images& srcImages) { + auto imagesDataPtr = getTexelsData(); + if (!imagesDataPtr) { + return; + } + auto allocatedImagesDataSize = getTexelsDataSize(); + size_t currentDataSize = 0; + auto currentPtr = imagesDataPtr; + + _images.clear(); + + + for (uint32_t l = 0; l < srcImages.size(); l++) { + if (currentDataSize + sizeof(uint32_t) < allocatedImagesDataSize) { + size_t imageSize = srcImages[l]._imageSize; + *(reinterpret_cast (currentPtr)) = imageSize; + currentPtr += sizeof(uint32_t); + currentDataSize += sizeof(uint32_t); + + // If enough data ahead then capture the copy source pointer + if (currentDataSize + imageSize <= (allocatedImagesDataSize)) { + + auto copied = memcpy(currentPtr, srcImages[l]._bytes, imageSize); + + auto padding = Header::evalPadding(imageSize); + + _images.emplace_back(Image(imageSize, padding, currentPtr)); + + currentPtr += imageSize + padding; + currentDataSize += imageSize + padding; + } + } + } } std::unique_ptr KTX::create(const Header& header, const KeyValues& keyValues, const Images& images) { @@ -43,11 +86,13 @@ namespace ktx { std::unique_ptr result(new KTX()); result->resetStorage(generateStorage(header, keyValues, images).release()); + result->resetHeader(header); + // read metadata result->_keyValues = keyValues; // populate image table - result->_images = images; + result->resetImages(images); return result; } diff --git a/libraries/model/CMakeLists.txt b/libraries/model/CMakeLists.txt index 63f632e484..021aa3d027 100755 --- a/libraries/model/CMakeLists.txt +++ b/libraries/model/CMakeLists.txt @@ -1,5 +1,5 @@ set(TARGET_NAME model) AUTOSCRIBE_SHADER_LIB(gpu model) setup_hifi_library() -link_hifi_libraries(shared gpu) +link_hifi_libraries(shared ktx gpu) diff --git a/libraries/model/src/model/TextureMap.cpp b/libraries/model/src/model/TextureMap.cpp index 6a9446f2ef..24777b7dd6 100755 --- a/libraries/model/src/model/TextureMap.cpp +++ b/libraries/model/src/model/TextureMap.cpp @@ -10,6 +10,8 @@ // #include "TextureMap.h" +#include + #include #include #include @@ -268,8 +270,23 @@ gpu::Texture* TextureUsage::process2DTextureColorFromImage(const QImage& srcImag if (generateMips) { ::generateMips(theTexture, image, formatMip, false); } + auto theKTX = Texture::serialize(*theTexture); + if (theKTX) { + // save that! + std::string filename("C://temp//ktxCache//texmex"); + filename += std::to_string((size_t) theTexture); + filename += ".ktx"; + + FILE* file = fopen (filename.c_str(),"wb"); + if (file != nullptr) { + fwrite(theKTX->_storage->data(), 1, theKTX->_storage->size(), file); + fclose (file); + } + + } } + return theTexture; } diff --git a/libraries/render-utils/CMakeLists.txt b/libraries/render-utils/CMakeLists.txt index ecafb8f565..3bf389973a 100644 --- a/libraries/render-utils/CMakeLists.txt +++ b/libraries/render-utils/CMakeLists.txt @@ -3,7 +3,7 @@ AUTOSCRIBE_SHADER_LIB(gpu model render) # pull in the resources.qrc file qt5_add_resources(QT_RESOURCES_FILE "${CMAKE_CURRENT_SOURCE_DIR}/res/fonts/fonts.qrc") setup_hifi_library(Widgets OpenGL Network Qml Quick Script) -link_hifi_libraries(shared gpu model model-networking render animation fbx entities) +link_hifi_libraries(shared ktx gpu model model-networking render animation fbx entities) if (NOT ANDROID) target_nsight() diff --git a/libraries/render/CMakeLists.txt b/libraries/render/CMakeLists.txt index 735bb7f086..8fd05bd320 100644 --- a/libraries/render/CMakeLists.txt +++ b/libraries/render/CMakeLists.txt @@ -3,6 +3,6 @@ AUTOSCRIBE_SHADER_LIB(gpu model) setup_hifi_library() # render needs octree only for getAccuracyAngle(float, int) -link_hifi_libraries(shared gpu model octree) +link_hifi_libraries(shared ktx gpu model octree) target_nsight() From 3f0ea1c889f13045a560ecb683c00046e680d29a Mon Sep 17 00:00:00 2001 From: samcake Date: Thu, 16 Feb 2017 13:27:16 -0800 Subject: [PATCH 027/302] Address review comments --- libraries/gpu/src/gpu/Texture_ktx.cpp | 42 ++++++++ libraries/ktx/src/ktx/KTX.h | 17 +++- libraries/ktx/src/ktx/Reader.cpp | 6 +- libraries/ktx/src/ktx/Writer.cpp | 118 ++++++++++++++++------- libraries/model/src/model/TextureMap.cpp | 15 ++- 5 files changed, 156 insertions(+), 42 deletions(-) create mode 100644 libraries/gpu/src/gpu/Texture_ktx.cpp diff --git a/libraries/gpu/src/gpu/Texture_ktx.cpp b/libraries/gpu/src/gpu/Texture_ktx.cpp new file mode 100644 index 0000000000..d06454b933 --- /dev/null +++ b/libraries/gpu/src/gpu/Texture_ktx.cpp @@ -0,0 +1,42 @@ +// +// Texture_ktx.cpp +// libraries/gpu/src/gpu +// +// Created by Sam Gateau on 2/16/2017. +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + + +#include "Texture.h" + +#include +using namespace gpu; + +ktx::KTXUniquePointer Texture::serialize(const Texture& texture) { + + ktx::Header header; + header.setUncompressed(ktx::GLType::UNSIGNED_BYTE, 4, ktx::GLFormat::BGRA, ktx::GLInternalFormat_Uncompressed::RGBA8, ktx::GLBaseInternalFormat::RGBA); + header.pixelWidth = texture.getWidth(); + header.pixelHeight = texture.getHeight(); + header.numberOfMipmapLevels = texture.mipLevels(); + + ktx::Images images; + for (int level = 0; level < header.numberOfMipmapLevels; level++) { + auto mip = texture.accessStoredMipFace(level); + if (mip) { + images.emplace_back(ktx::Image(mip->getSize(), 0, mip->readData())); + } + } + + auto ktxBuffer = ktx::KTX::create(header, images); + return ktxBuffer; +} +TexturePointer Texture::unserialize(const ktx::KTXUniquePointer& srcData) { + + const auto& header = *srcData->getHeader(); + + return nullptr; +} \ No newline at end of file diff --git a/libraries/ktx/src/ktx/KTX.h b/libraries/ktx/src/ktx/KTX.h index 1006124693..41032c8222 100644 --- a/libraries/ktx/src/ktx/KTX.h +++ b/libraries/ktx/src/ktx/KTX.h @@ -427,7 +427,22 @@ namespace ktx { // Define a KTX object manually to write it somewhere (in a file on disk?) // This path allocate the Storage where to store header, keyvalues and copy mips // Then COPY all the data - static std::unique_ptr create(const Header& header, const KeyValues& keyValues, const Images& images); + static std::unique_ptr create(const Header& header, const Images& images, const KeyValues& keyValues = KeyValues()); + + // Instead of creating a full Copy of the src data in a KTX object, the write serialization can be performed with the + // following two functions + // size_t sizeNeeded = KTX::evalStorageSize(header, images); + // + // //allocate a buffer of size "sizeNeeded" or map a file with enough capacity + // Byte* destBytes = new Byte[sizeNeeded]; + // + // // THen perform the writing of the src data to the destinnation buffer + // write(destBytes, sizeNeeded, header, images); + // + // This is exactly what is done in the create function + static size_t evalStorageSize(const Header& header, const Images& images, const KeyValues& keyValues = KeyValues()); + static size_t write(Byte* destBytes, size_t destByteSize, const Header& header, const Images& images, const KeyValues& keyValues = KeyValues()); + static Images writeImages(Byte* destBytes, size_t destByteSize, const Images& images); // Parse a block of memory and create a KTX object from it static std::unique_ptr create(const Storage& src); diff --git a/libraries/ktx/src/ktx/Reader.cpp b/libraries/ktx/src/ktx/Reader.cpp index 9586a9c033..4099aa6ef8 100644 --- a/libraries/ktx/src/ktx/Reader.cpp +++ b/libraries/ktx/src/ktx/Reader.cpp @@ -16,12 +16,12 @@ namespace ktx { class ReaderException: public std::exception { public: - ReaderException(std::string explanation) : _explanation(explanation) {} + ReaderException(const std::string& explanation) : _explanation("KTX deserialization error: " + explanation) {} const char* what() const override { - return ("KTX deserialization error: " + _explanation).c_str(); + return _explanation.c_str(); } private: - std::string _explanation; + const std::string _explanation; }; bool checkEndianness(uint32_t endianness, bool& matching) { diff --git a/libraries/ktx/src/ktx/Writer.cpp b/libraries/ktx/src/ktx/Writer.cpp index ae2dfc2d6d..75836ad5b5 100644 --- a/libraries/ktx/src/ktx/Writer.cpp +++ b/libraries/ktx/src/ktx/Writer.cpp @@ -15,30 +15,14 @@ namespace ktx { class WriterException : public std::exception { public: - WriterException(std::string explanation) : _explanation(explanation) {} + WriterException(const std::string& explanation) : _explanation("KTX serialization error: " + explanation) {} const char* what() const override { - return ("KTX serialization error: " + _explanation).c_str(); + return _explanation.c_str(); } private: - std::string _explanation; + const std::string _explanation; }; - std::unique_ptr generateStorage(const Header& header, const KeyValues& keyValues, const Images& images) { - size_t storageSize = sizeof(Header); - auto numMips = header.getNumberOfLevels(); - - for (uint32_t l = 0; l < numMips; l++) { - if (images.size() > l) { - storageSize += sizeof(uint32_t); - storageSize += images[l]._imageSize; - storageSize += Header::evalPadding(images[l]._imageSize); - } - } - - std::unique_ptr storage(new Storage(storageSize)); - return storage; - } - void KTX::resetHeader(const Header& header) { if (!_storage) { @@ -52,10 +36,84 @@ namespace ktx { return; } auto allocatedImagesDataSize = getTexelsDataSize(); + + // Just copy in our storage + _images = writeImages(imagesDataPtr, allocatedImagesDataSize, srcImages); + } + + std::unique_ptr KTX::create(const Header& header, const Images& images, const KeyValues& keyValues) { + auto storageSize = evalStorageSize(header, images, keyValues); + + std::unique_ptr result(new KTX()); + + result->resetStorage(new Storage(storageSize)); + + result->resetHeader(header); + + // read metadata + result->_keyValues = keyValues; + + // populate image table + result->resetImages(images); + + return result; + } + + size_t KTX::evalStorageSize(const Header& header, const Images& images, const KeyValues& keyValues) { + size_t storageSize = sizeof(Header); + + if (header.bytesOfKeyValueData && !keyValues.empty()) { + + } + + auto numMips = header.getNumberOfLevels(); + for (uint32_t l = 0; l < numMips; l++) { + if (images.size() > l) { + storageSize += sizeof(uint32_t); + storageSize += images[l]._imageSize; + storageSize += Header::evalPadding(images[l]._imageSize); + } + } + return storageSize; + } + + size_t KTX::write(Byte* destBytes, size_t destByteSize, const Header& header, const Images& srcImages, const KeyValues& keyValues) { + // Check again that we have enough destination capacity + if (!destBytes || (destByteSize < evalStorageSize(header, srcImages, keyValues))) { + return 0; + } + + auto currentDestPtr = destBytes; + // Header + auto destHeader = reinterpret_cast(currentDestPtr); + memcpy(currentDestPtr, &header, sizeof(Header)); + currentDestPtr += sizeof(Header); + + // KeyValues + // Skip for now + if (header.bytesOfKeyValueData && !keyValues.empty()) { + + } + destHeader->bytesOfKeyValueData = 0; + currentDestPtr += destHeader->bytesOfKeyValueData; + + // Images + auto destImages = writeImages(currentDestPtr, destByteSize - sizeof(Header) - destHeader->bytesOfKeyValueData, srcImages); + // We chould check here that the amoutn of dest IMages generated is the same as the source + + return destByteSize; + } + + Images KTX::writeImages(Byte* destBytes, size_t destByteSize, const Images& srcImages) { + Images destImages; + auto imagesDataPtr = destBytes; + if (!imagesDataPtr) { + return destImages; + } + auto allocatedImagesDataSize = destByteSize; size_t currentDataSize = 0; auto currentPtr = imagesDataPtr; - _images.clear(); for (uint32_t l = 0; l < srcImages.size(); l++) { @@ -72,29 +130,15 @@ namespace ktx { auto padding = Header::evalPadding(imageSize); - _images.emplace_back(Image(imageSize, padding, currentPtr)); + destImages.emplace_back(Image(imageSize, padding, currentPtr)); currentPtr += imageSize + padding; currentDataSize += imageSize + padding; } } } - } - - std::unique_ptr KTX::create(const Header& header, const KeyValues& keyValues, const Images& images) { - - std::unique_ptr result(new KTX()); - result->resetStorage(generateStorage(header, keyValues, images).release()); - - result->resetHeader(header); - - // read metadata - result->_keyValues = keyValues; - - // populate image table - result->resetImages(images); - - return result; + + return destImages; } } diff --git a/libraries/model/src/model/TextureMap.cpp b/libraries/model/src/model/TextureMap.cpp index 24777b7dd6..cadd985c22 100755 --- a/libraries/model/src/model/TextureMap.cpp +++ b/libraries/model/src/model/TextureMap.cpp @@ -15,6 +15,9 @@ #include #include #include +#include +#include +#include #include @@ -273,7 +276,17 @@ gpu::Texture* TextureUsage::process2DTextureColorFromImage(const QImage& srcImag auto theKTX = Texture::serialize(*theTexture); if (theKTX) { // save that! - std::string filename("C://temp//ktxCache//texmex"); + QString path("hifi_ktx/"); + QFileInfo originalFileInfo(path); + QString docsLocation = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation); + path = docsLocation + "/" + path; + QFileInfo info(path); + if (!info.absoluteDir().exists()) { + QString originalRelativePath = originalFileInfo.path(); + QDir(docsLocation).mkpath(originalRelativePath); + } + + std::string filename(path.toStdString()); filename += std::to_string((size_t) theTexture); filename += ".ktx"; From 6771cc31e1c39928c30ea880e088b407e15e254d Mon Sep 17 00:00:00 2001 From: sam Date: Thu, 16 Feb 2017 16:03:55 -0800 Subject: [PATCH 028/302] Adding the reading path --- libraries/gpu/src/gpu/Texture.h | 2 +- libraries/gpu/src/gpu/Texture_ktx.cpp | 16 ++++++++++-- libraries/model/src/model/TextureMap.cpp | 32 +++++++++++++++++++++--- 3 files changed, 43 insertions(+), 7 deletions(-) diff --git a/libraries/gpu/src/gpu/Texture.h b/libraries/gpu/src/gpu/Texture.h index fba4c7079f..acf063e365 100755 --- a/libraries/gpu/src/gpu/Texture.h +++ b/libraries/gpu/src/gpu/Texture.h @@ -488,7 +488,7 @@ public: ExternalUpdates getUpdates() const; static ktx::KTXUniquePointer serialize(const Texture& texture); - static TexturePointer unserialize(const ktx::KTXUniquePointer& srcData); + static Texture* unserialize(const ktx::KTXUniquePointer& srcData); protected: const TextureUsageType _usageType; diff --git a/libraries/gpu/src/gpu/Texture_ktx.cpp b/libraries/gpu/src/gpu/Texture_ktx.cpp index d06454b933..6380b23903 100644 --- a/libraries/gpu/src/gpu/Texture_ktx.cpp +++ b/libraries/gpu/src/gpu/Texture_ktx.cpp @@ -34,9 +34,21 @@ ktx::KTXUniquePointer Texture::serialize(const Texture& texture) { auto ktxBuffer = ktx::KTX::create(header, images); return ktxBuffer; } -TexturePointer Texture::unserialize(const ktx::KTXUniquePointer& srcData) { +Texture* Texture::unserialize(const ktx::KTXUniquePointer& srcData) { + if (!srcData) { + return nullptr; + } const auto& header = *srcData->getHeader(); - return nullptr; + Format pixelFormat = Format::COLOR_RGBA_32; + + auto tex = Texture::create2D(pixelFormat, header.getPixelWidth(), header.getPixelHeight()); + uint16_t level = 0; + for (auto& image : srcData->_images) { + tex->assignStoredMip(level, pixelFormat, image._imageSize, image._bytes); + level++; + } + + return tex; } \ No newline at end of file diff --git a/libraries/model/src/model/TextureMap.cpp b/libraries/model/src/model/TextureMap.cpp index cadd985c22..4f8a43412e 100755 --- a/libraries/model/src/model/TextureMap.cpp +++ b/libraries/model/src/model/TextureMap.cpp @@ -290,12 +290,36 @@ gpu::Texture* TextureUsage::process2DTextureColorFromImage(const QImage& srcImag filename += std::to_string((size_t) theTexture); filename += ".ktx"; - FILE* file = fopen (filename.c_str(),"wb"); - if (file != nullptr) { - fwrite(theKTX->_storage->data(), 1, theKTX->_storage->size(), file); - fclose (file); + { + FILE* file = fopen (filename.c_str(),"wb"); + if (file != nullptr) { + fwrite(theKTX->_storage->data(), 1, theKTX->_storage->size(), file); + fclose (file); + } } + { + FILE* file = fopen (filename.c_str(),"rb"); + if (file != nullptr) { + // obtain file size: + fseek (file , 0 , SEEK_END); + auto size = ftell(file); + rewind(file); + + std::unique_ptr storage(new ktx::Storage(size)); + fread(storage->_bytes, 1, storage->_size, file); + fclose (file); + + //then create a new texture out of the ktx + auto theNewTexure = Texture::unserialize(ktx::KTX::create(storage)); + + if (theNewTexure) { + auto srcTexture = theTexture; + theTexture = theNewTexure; + delete srcTexture; + } + } + } } } From 0d2e764bfd26cea93c5fdb1f4c4b28969be14bff Mon Sep 17 00:00:00 2001 From: sam Date: Thu, 16 Feb 2017 17:28:02 -0800 Subject: [PATCH 029/302] CLeaning the read case --- libraries/gpu/src/gpu/Format.cpp | 4 + libraries/gpu/src/gpu/Format.h | 2 + libraries/gpu/src/gpu/Texture_ktx.cpp | 7 +- libraries/ktx/src/ktx/KTX.h | 4 +- libraries/ktx/src/ktx/Reader.cpp | 105 +++++++++++--------------- 5 files changed, 57 insertions(+), 65 deletions(-) diff --git a/libraries/gpu/src/gpu/Format.cpp b/libraries/gpu/src/gpu/Format.cpp index 2a8185bf94..0f5e85c907 100644 --- a/libraries/gpu/src/gpu/Format.cpp +++ b/libraries/gpu/src/gpu/Format.cpp @@ -12,6 +12,10 @@ using namespace gpu; const Element Element::COLOR_RGBA_32{ VEC4, NUINT8, RGBA }; const Element Element::COLOR_SRGBA_32{ VEC4, NUINT8, SRGBA }; + +const Element Element::COLOR_BGRA_32{ VEC4, NUINT8, BGRA }; +const Element Element::COLOR_SBGRA_32{ VEC4, NUINT8, SBGRA }; + const Element Element::COLOR_R11G11B10{ SCALAR, FLOAT, R11G11B10 }; const Element Element::VEC4F_COLOR_RGBA{ VEC4, FLOAT, RGBA }; const Element Element::VEC2F_UV{ VEC2, FLOAT, UV }; diff --git a/libraries/gpu/src/gpu/Format.h b/libraries/gpu/src/gpu/Format.h index 13809a41e6..c7dc2eed8d 100644 --- a/libraries/gpu/src/gpu/Format.h +++ b/libraries/gpu/src/gpu/Format.h @@ -229,6 +229,8 @@ public: static const Element COLOR_RGBA_32; static const Element COLOR_SRGBA_32; + static const Element COLOR_BGRA_32; + static const Element COLOR_SBGRA_32; static const Element COLOR_R11G11B10; static const Element VEC4F_COLOR_RGBA; static const Element VEC2F_UV; diff --git a/libraries/gpu/src/gpu/Texture_ktx.cpp b/libraries/gpu/src/gpu/Texture_ktx.cpp index 6380b23903..623a3493da 100644 --- a/libraries/gpu/src/gpu/Texture_ktx.cpp +++ b/libraries/gpu/src/gpu/Texture_ktx.cpp @@ -24,7 +24,7 @@ ktx::KTXUniquePointer Texture::serialize(const Texture& texture) { header.numberOfMipmapLevels = texture.mipLevels(); ktx::Images images; - for (int level = 0; level < header.numberOfMipmapLevels; level++) { + for (uint32_t level = 0; level < header.numberOfMipmapLevels; level++) { auto mip = texture.accessStoredMipFace(level); if (mip) { images.emplace_back(ktx::Image(mip->getSize(), 0, mip->readData())); @@ -41,12 +41,13 @@ Texture* Texture::unserialize(const ktx::KTXUniquePointer& srcData) { const auto& header = *srcData->getHeader(); - Format pixelFormat = Format::COLOR_RGBA_32; + Format mipFormat = Format::COLOR_SBGRA_32; + Format pixelFormat = Format::COLOR_SRGBA_32; auto tex = Texture::create2D(pixelFormat, header.getPixelWidth(), header.getPixelHeight()); uint16_t level = 0; for (auto& image : srcData->_images) { - tex->assignStoredMip(level, pixelFormat, image._imageSize, image._bytes); + tex->assignStoredMip(level, mipFormat, image._imageSize, image._bytes); level++; } diff --git a/libraries/ktx/src/ktx/KTX.h b/libraries/ktx/src/ktx/KTX.h index 41032c8222..3a4c19971a 100644 --- a/libraries/ktx/src/ktx/KTX.h +++ b/libraries/ktx/src/ktx/KTX.h @@ -445,10 +445,10 @@ namespace ktx { static Images writeImages(Byte* destBytes, size_t destByteSize, const Images& images); // Parse a block of memory and create a KTX object from it - static std::unique_ptr create(const Storage& src); static std::unique_ptr create(std::unique_ptr& src); - static bool checkHeaderFromStorage(const Storage& storage); + static bool checkHeaderFromStorage(size_t srcSize, const Byte* srcBytes); + static Images parseImages(const Header& header, size_t srcSize, const Byte* srcBytes); // Access raw pointers to the main sections of the KTX const Header* getHeader() const; diff --git a/libraries/ktx/src/ktx/Reader.cpp b/libraries/ktx/src/ktx/Reader.cpp index 4099aa6ef8..00c0c4e19a 100644 --- a/libraries/ktx/src/ktx/Reader.cpp +++ b/libraries/ktx/src/ktx/Reader.cpp @@ -38,12 +38,17 @@ namespace ktx { } break; default: + throw ReaderException("endianness field has invalid value"); return false; } } bool checkIdentifier(const Byte* identifier) { - return memcmp(identifier, Header::IDENTIFIER.data(), Header::IDENTIFIER_LENGTH); + if (!(0 == memcmp(identifier, Header::IDENTIFIER.data(), Header::IDENTIFIER_LENGTH))) { + throw ReaderException("identifier field invalid"); + return false; + } + return true; } KeyValues getKeyValues(size_t length, const Byte* src) { @@ -81,53 +86,18 @@ namespace ktx { return keyValues; } - Images getImagesTable(const Header& header, size_t mipsDataSize, const Byte* mipsData) { - Images images; - auto currentPtr = mipsData; - auto numMips = header.getNumberOfLevels(); - - // Keep identifying new mip as long as we can at list query the next imageSize - while ((currentPtr - mipsData) + sizeof(uint32_t) <= (mipsDataSize)) { - - // Grab the imageSize coming up - size_t imageSize = *reinterpret_cast(currentPtr); - currentPtr += sizeof(uint32_t); - - // If enough data ahead then capture the pointer - if ((currentPtr - mipsData) + imageSize <= (mipsDataSize)) { - auto padding = Header::evalPadding(imageSize); - - images.emplace_back(Image(imageSize, padding, currentPtr)); - - currentPtr += imageSize + padding; - } else { - break; - } - } - - return images; - } - - - bool KTX::checkHeaderFromStorage(const Storage& src) { + bool KTX::checkHeaderFromStorage(size_t srcSize, const Byte* srcBytes) { try { - auto srcSize = src.size(); - auto srcBytes = src.data(); - // validation if (srcSize < sizeof(Header)) { throw ReaderException("length is too short for header"); } const Header* header = reinterpret_cast(srcBytes); - if (!checkIdentifier(header->identifier)) { - throw ReaderException("identifier field invalid"); - } + checkIdentifier(header->identifier); bool endianMatch { true }; - if (!checkEndianness(header->endianness, endianMatch)) { - throw ReaderException("endianness field has invalid value"); - } + checkEndianness(header->endianness, endianMatch); // TODO: endian conversion if !endianMatch - for now, this is for local use and is unnecessary @@ -151,10 +121,31 @@ namespace ktx { } } - std::unique_ptr KTX::create(const Storage& src) { - auto srcCopy = std::make_unique(src); + Images KTX::parseImages(const Header& header, size_t srcSize, const Byte* srcBytes) { + Images images; + auto currentPtr = srcBytes; + auto numMips = header.getNumberOfLevels(); - return create(srcCopy); + // Keep identifying new mip as long as we can at list query the next imageSize + while ((currentPtr - srcBytes) + sizeof(uint32_t) <= (srcSize)) { + + // Grab the imageSize coming up + size_t imageSize = *reinterpret_cast(currentPtr); + currentPtr += sizeof(uint32_t); + + // If enough data ahead then capture the pointer + if ((currentPtr - srcBytes) + imageSize <= (srcSize)) { + auto padding = Header::evalPadding(imageSize); + + images.emplace_back(Image(imageSize, padding, currentPtr)); + + currentPtr += imageSize + padding; + } else { + break; + } + } + + return images; } std::unique_ptr KTX::create(std::unique_ptr& src) { @@ -162,25 +153,19 @@ namespace ktx { return nullptr; } - try { - if (!checkHeaderFromStorage(*src)) { - - } - - std::unique_ptr result(new KTX()); - result->resetStorage(src.release()); - - // read metadata - result->_keyValues = getKeyValues(result->getHeader()->bytesOfKeyValueData, result->getKeyValueData()); - - // populate image table - result->_images = getImagesTable(*result->getHeader(), result->getTexelsDataSize(), result->getTexelsData()); - - return result; - } - catch (ReaderException& e) { - qWarning(e.what()); + if (!checkHeaderFromStorage(src->size(), src->data())) { return nullptr; } + + std::unique_ptr result(new KTX()); + result->resetStorage(src.release()); + + // read metadata + // result->_keyValues = getKeyValues(result->getHeader()->bytesOfKeyValueData, result->getKeyValueData()); + + // populate image table + result->_images = parseImages(*result->getHeader(), result->getTexelsDataSize(), result->getTexelsData()); + + return result; } } \ No newline at end of file From b4745657e0378cf3fdb66b4c5c7bf1401a1aba19 Mon Sep 17 00:00:00 2001 From: sam Date: Fri, 17 Feb 2017 01:15:27 -0800 Subject: [PATCH 030/302] progressing on io with ktx --- libraries/gpu/src/gpu/Texture.cpp | 24 ++--- libraries/gpu/src/gpu/Texture.h | 17 ++-- libraries/gpu/src/gpu/Texture_ktx.cpp | 84 ++++++++++++++-- libraries/ktx/src/ktx/KTX.h | 19 +++- libraries/model/src/model/TextureMap.cpp | 120 +++++++++++++---------- 5 files changed, 185 insertions(+), 79 deletions(-) diff --git a/libraries/gpu/src/gpu/Texture.cpp b/libraries/gpu/src/gpu/Texture.cpp index 21c32cc55e..664a2273a6 100755 --- a/libraries/gpu/src/gpu/Texture.cpp +++ b/libraries/gpu/src/gpu/Texture.cpp @@ -263,27 +263,27 @@ Texture* Texture::createExternal(const ExternalRecycler& recycler, const Sampler } Texture* Texture::createRenderBuffer(const Element& texelFormat, uint16 width, uint16 height, const Sampler& sampler) { - return create(TextureUsageType::RENDERBUFFER, TEX_2D, texelFormat, width, height, 1, 1, 1, sampler); + return create(TextureUsageType::RENDERBUFFER, TEX_2D, texelFormat, width, height, 1, 1, 0, sampler); } Texture* Texture::create1D(const Element& texelFormat, uint16 width, const Sampler& sampler) { - return create(TextureUsageType::RESOURCE, TEX_1D, texelFormat, width, 1, 1, 1, 1, sampler); + return create(TextureUsageType::RESOURCE, TEX_1D, texelFormat, width, 1, 1, 1, 0, sampler); } Texture* Texture::create2D(const Element& texelFormat, uint16 width, uint16 height, const Sampler& sampler) { - return create(TextureUsageType::RESOURCE, TEX_2D, texelFormat, width, height, 1, 1, 1, sampler); + return create(TextureUsageType::RESOURCE, TEX_2D, texelFormat, width, height, 1, 1, 0, sampler); } Texture* Texture::createStrict(const Element& texelFormat, uint16 width, uint16 height, const Sampler& sampler) { - return create(TextureUsageType::STRICT_RESOURCE, TEX_2D, texelFormat, width, height, 1, 1, 1, sampler); + return create(TextureUsageType::STRICT_RESOURCE, TEX_2D, texelFormat, width, height, 1, 1, 0, sampler); } Texture* Texture::create3D(const Element& texelFormat, uint16 width, uint16 height, uint16 depth, const Sampler& sampler) { - return create(TextureUsageType::RESOURCE, TEX_3D, texelFormat, width, height, depth, 1, 1, sampler); + return create(TextureUsageType::RESOURCE, TEX_3D, texelFormat, width, height, depth, 1, 0, sampler); } Texture* Texture::createCube(const Element& texelFormat, uint16 width, const Sampler& sampler) { - return create(TextureUsageType::RESOURCE, TEX_CUBE, texelFormat, width, width, 1, 1, 1, sampler); + return create(TextureUsageType::RESOURCE, TEX_CUBE, texelFormat, width, width, 1, 1, 0, sampler); } Texture* Texture::create(TextureUsageType usageType, Type type, const Element& texelFormat, uint16 width, uint16 height, uint16 depth, uint16 numSamples, uint16 numSlices, const Sampler& sampler) @@ -326,7 +326,7 @@ Texture::~Texture() { } Texture::Size Texture::resize(Type type, const Element& texelFormat, uint16 width, uint16 height, uint16 depth, uint16 numSamples, uint16 numSlices) { - if (width && height && depth && numSamples && numSlices) { + if (width && height && depth && numSamples) { bool changed = false; if ( _type != type) { @@ -387,20 +387,20 @@ Texture::Size Texture::resize(Type type, const Element& texelFormat, uint16 widt } Texture::Size Texture::resize1D(uint16 width, uint16 numSamples) { - return resize(TEX_1D, getTexelFormat(), width, 1, 1, numSamples, 1); + return resize(TEX_1D, getTexelFormat(), width, 1, 1, numSamples, 0); } Texture::Size Texture::resize2D(uint16 width, uint16 height, uint16 numSamples) { - return resize(TEX_2D, getTexelFormat(), width, height, 1, numSamples, 1); + return resize(TEX_2D, getTexelFormat(), width, height, 1, numSamples, 0); } Texture::Size Texture::resize3D(uint16 width, uint16 height, uint16 depth, uint16 numSamples) { - return resize(TEX_3D, getTexelFormat(), width, height, depth, numSamples, 1); + return resize(TEX_3D, getTexelFormat(), width, height, depth, numSamples, 0); } Texture::Size Texture::resizeCube(uint16 width, uint16 numSamples) { - return resize(TEX_CUBE, getTexelFormat(), width, 1, 1, numSamples, 1); + return resize(TEX_CUBE, getTexelFormat(), width, 1, 1, numSamples, 0); } Texture::Size Texture::reformat(const Element& texelFormat) { - return resize(_type, texelFormat, getWidth(), getHeight(), getDepth(), getNumSamples(), getNumSlices()); + return resize(_type, texelFormat, getWidth(), getHeight(), getDepth(), getNumSamples(), _numSlices); } bool Texture::isColorRenderTarget() const { diff --git a/libraries/gpu/src/gpu/Texture.h b/libraries/gpu/src/gpu/Texture.h index acf063e365..9c8ebd627c 100755 --- a/libraries/gpu/src/gpu/Texture.h +++ b/libraries/gpu/src/gpu/Texture.h @@ -359,7 +359,12 @@ public: uint32 getNumTexels() const { return _width * _height * _depth * getNumFaces(); } - uint16 getNumSlices() const { return _numSlices; } + // The texture is an array if the _numSlices is not 0. + // otherwise, if _numSLices is 0, then the texture is NOT an array + // The number of slices returned is 1 at the minimum (if not an array) or the actual _numSlices. + bool isArray() const { return _numSlices > 0; } + uint16 getNumSlices() const { return (isArray() ? _numSlices : 1); } + uint16 getNumSamples() const { return _numSamples; } @@ -511,12 +516,12 @@ protected: uint32 _size = 0; Element _texelFormat; - uint16 _width = 1; - uint16 _height = 1; - uint16 _depth = 1; + uint16 _width { 1 }; + uint16 _height { 1 }; + uint16 _depth { 1 }; - uint16 _numSamples = 1; - uint16 _numSlices = 1; + uint16 _numSamples { 1 }; + uint16 _numSlices { 0 }; // if _numSlices is 0, the texture is not an "Array", the getNumSlices reported is 1 uint16 _maxMip { 0 }; uint16 _minMip { 0 }; diff --git a/libraries/gpu/src/gpu/Texture_ktx.cpp b/libraries/gpu/src/gpu/Texture_ktx.cpp index 623a3493da..c36c0f14d8 100644 --- a/libraries/gpu/src/gpu/Texture_ktx.cpp +++ b/libraries/gpu/src/gpu/Texture_ktx.cpp @@ -16,12 +16,57 @@ using namespace gpu; ktx::KTXUniquePointer Texture::serialize(const Texture& texture) { - ktx::Header header; + + // From texture format to ktx format description + auto texelFormat = texture.getTexelFormat(); + if ( !( (texelFormat == Format::COLOR_RGBA_32) + || (texelFormat == Format::COLOR_SRGBA_32) + )) + return nullptr; + header.setUncompressed(ktx::GLType::UNSIGNED_BYTE, 4, ktx::GLFormat::BGRA, ktx::GLInternalFormat_Uncompressed::RGBA8, ktx::GLBaseInternalFormat::RGBA); - header.pixelWidth = texture.getWidth(); - header.pixelHeight = texture.getHeight(); - header.numberOfMipmapLevels = texture.mipLevels(); + + // Set Dimensions + switch (texture.getType()) { + case TEX_1D: { + if (texture.isArray()) { + header.set1DArray(texture.getWidth(), texture.getNumSlices()); + } else { + header.set1D(texture.getWidth()); + } + break; + } + case TEX_2D: { + if (texture.isArray()) { + header.set2DArray(texture.getWidth(), texture.getHeight(), texture.getNumSlices()); + } else { + header.set2D(texture.getWidth(), texture.getHeight()); + } + break; + } + case TEX_3D: { + if (texture.isArray()) { + header.set3DArray(texture.getWidth(), texture.getHeight(), texture.getDepth(), texture.getNumSlices()); + } else { + header.set3D(texture.getWidth(), texture.getHeight(), texture.getDepth()); + } + break; + } + case TEX_CUBE: { + if (texture.isArray()) { + header.setCubeArray(texture.getWidth(), texture.getHeight(), texture.getNumSlices()); + } else { + header.setCube(texture.getWidth(), texture.getHeight()); + } + break; + } + default: + return nullptr; + } + + // Number level of mips coming + header.numberOfMipmapLevels = texture.maxMip(); ktx::Images images; for (uint32_t level = 0; level < header.numberOfMipmapLevels; level++) { @@ -34,17 +79,42 @@ ktx::KTXUniquePointer Texture::serialize(const Texture& texture) { auto ktxBuffer = ktx::KTX::create(header, images); return ktxBuffer; } + Texture* Texture::unserialize(const ktx::KTXUniquePointer& srcData) { if (!srcData) { return nullptr; } - const auto& header = *srcData->getHeader(); Format mipFormat = Format::COLOR_SBGRA_32; - Format pixelFormat = Format::COLOR_SRGBA_32; + Format texelFormat = Format::COLOR_SRGBA_32; - auto tex = Texture::create2D(pixelFormat, header.getPixelWidth(), header.getPixelHeight()); + // Find Texture Type based on dimensions + Type type = TEX_1D; + if (header.pixelWidth == 0) { + return nullptr; + } else if (header.pixelHeight == 0) { + type = TEX_1D; + } else if (header.pixelDepth == 0) { + if (header.numberOfFaces == ktx::NUM_CUBEMAPFACES) { + type = TEX_CUBE; + } else { + type = TEX_2D; + } + } else { + type = TEX_3D; + } + + auto tex = Texture::create( type, + texelFormat, + header.getPixelWidth(), + header.getPixelHeight(), + header.getPixelDepth(), + 1, // num Samples + header.getNumberOfSlices(), + Sampler()); + + // Assing the mips availables uint16_t level = 0; for (auto& image : srcData->_images) { tex->assignStoredMip(level, mipFormat, image._imageSize, image._bytes); diff --git a/libraries/ktx/src/ktx/KTX.h b/libraries/ktx/src/ktx/KTX.h index 3a4c19971a..8e79d90dc0 100644 --- a/libraries/ktx/src/ktx/KTX.h +++ b/libraries/ktx/src/ktx/KTX.h @@ -353,7 +353,7 @@ namespace ktx { uint32_t glInternalFormat; uint32_t glBaseInternalFormat; - uint32_t pixelWidth { 0 }; + uint32_t pixelWidth { 1 }; uint32_t pixelHeight { 0 }; uint32_t pixelDepth { 0 }; uint32_t numberOfArrayElements { 0 }; @@ -393,6 +393,23 @@ namespace ktx { glInternalFormat = (uint32_t) internalFormat; glBaseInternalFormat = (uint32_t) baseInternalFormat; } + + void setDimensions(uint32_t width, uint32_t height = 0, uint32_t depth = 0, uint32_t numSlices = 0, uint32_t numFaces = 1) { + pixelWidth = (width > 0 ? width : 1); + pixelHeight = height; + pixelDepth = depth; + numberOfArrayElements = numSlices; + numberOfFaces = ((numFaces == 1) || (numFaces == NUM_CUBEMAPFACES) ? numFaces : 1); + } + void set1D(uint32_t width) { setDimensions(width); } + void set1DArray(uint32_t width, uint32_t numSlices) { setDimensions(width, 0, 0, (numSlices > 0 ? numSlices : 1)); } + void set2D(uint32_t width, uint32_t height) { setDimensions(width, height); } + void set2DArray(uint32_t width, uint32_t height, uint32_t numSlices) { setDimensions(width, height, 0, (numSlices > 0 ? numSlices : 1)); } + void set3D(uint32_t width, uint32_t height, uint32_t depth) { setDimensions(width, height, depth); } + void set3DArray(uint32_t width, uint32_t height, uint32_t depth, uint32_t numSlices) { setDimensions(width, height, depth, (numSlices > 0 ? numSlices : 1)); } + void setCube(uint32_t width, uint32_t height) { setDimensions(width, height, 0, 0, NUM_CUBEMAPFACES); } + void setCubeArray(uint32_t width, uint32_t height, uint32_t numSlices) { setDimensions(width, height, 0, (numSlices > 0 ? numSlices : 1), NUM_CUBEMAPFACES); } + }; // Key Values diff --git a/libraries/model/src/model/TextureMap.cpp b/libraries/model/src/model/TextureMap.cpp index 4f8a43412e..03605bd6ef 100755 --- a/libraries/model/src/model/TextureMap.cpp +++ b/libraries/model/src/model/TextureMap.cpp @@ -85,6 +85,61 @@ QImage processSourceImage(const QImage& srcImage, bool cubemap) { return srcImage; } +gpu::Texture* cacheTexture(const std::string& name, gpu::Texture* srcTexture, bool write = true, bool read = true) { + if (!srcTexture) { + return nullptr; + } + gpu::Texture* returnedTexture = srcTexture; + + auto theKTX = Texture::serialize(*srcTexture); + if (theKTX) { + // Prepare cache directory + QString path("hifi_ktx/"); + QFileInfo originalFileInfo(path); + QString docsLocation = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation); + path = docsLocation + "/" + path; + QFileInfo info(path); + if (!info.absoluteDir().exists()) { + QString originalRelativePath = originalFileInfo.path(); + QDir(docsLocation).mkpath(originalRelativePath); + } + std::string filename(path.toStdString()); + filename += name; + filename += ".ktx"; + + if (write) { + FILE* file = fopen (filename.c_str(),"wb"); + if (file != nullptr) { + fwrite(theKTX->_storage->data(), 1, theKTX->_storage->size(), file); + fclose (file); + } + } + + if (read) { + FILE* file = fopen (filename.c_str(),"rb"); + if (file != nullptr) { + // obtain file size: + fseek (file , 0 , SEEK_END); + auto size = ftell(file); + rewind(file); + + std::unique_ptr storage(new ktx::Storage(size)); + fread(storage->_bytes, 1, storage->_size, file); + fclose (file); + + //then create a new texture out of the ktx + auto theNewTexure = Texture::unserialize(ktx::KTX::create(storage)); + + if (theNewTexure) { + returnedTexture = theNewTexure; + delete srcTexture; + } + } + } + } + return returnedTexture; +} + void TextureMap::setTextureSource(TextureSourcePointer& textureSource) { _textureSource = textureSource; } @@ -273,57 +328,10 @@ gpu::Texture* TextureUsage::process2DTextureColorFromImage(const QImage& srcImag if (generateMips) { ::generateMips(theTexture, image, formatMip, false); } - auto theKTX = Texture::serialize(*theTexture); - if (theKTX) { - // save that! - QString path("hifi_ktx/"); - QFileInfo originalFileInfo(path); - QString docsLocation = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation); - path = docsLocation + "/" + path; - QFileInfo info(path); - if (!info.absoluteDir().exists()) { - QString originalRelativePath = originalFileInfo.path(); - QDir(docsLocation).mkpath(originalRelativePath); - } - std::string filename(path.toStdString()); - filename += std::to_string((size_t) theTexture); - filename += ".ktx"; - - { - FILE* file = fopen (filename.c_str(),"wb"); - if (file != nullptr) { - fwrite(theKTX->_storage->data(), 1, theKTX->_storage->size(), file); - fclose (file); - } - } - - { - FILE* file = fopen (filename.c_str(),"rb"); - if (file != nullptr) { - // obtain file size: - fseek (file , 0 , SEEK_END); - auto size = ftell(file); - rewind(file); - - std::unique_ptr storage(new ktx::Storage(size)); - fread(storage->_bytes, 1, storage->_size, file); - fclose (file); - - //then create a new texture out of the ktx - auto theNewTexure = Texture::unserialize(ktx::KTX::create(storage)); - - if (theNewTexure) { - auto srcTexture = theTexture; - theTexture = theNewTexure; - delete srcTexture; - } - } - } - } + theTexture = cacheTexture(std::to_string((size_t) theTexture), theTexture); } - return theTexture; } @@ -367,6 +375,8 @@ gpu::Texture* TextureUsage::createNormalTextureFromNormalImage(const QImage& src theTexture->setSource(srcImageName); theTexture->assignStoredMip(0, formatMip, image.byteCount(), image.constBits()); generateMips(theTexture, image, formatMip, true); + + theTexture = cacheTexture(std::to_string((size_t) theTexture), theTexture); } return theTexture; @@ -453,6 +463,8 @@ gpu::Texture* TextureUsage::createNormalTextureFromBumpImage(const QImage& srcIm theTexture->setSource(srcImageName); theTexture->assignStoredMip(0, formatMip, image.byteCount(), image.constBits()); generateMips(theTexture, image, formatMip, true); + + theTexture = cacheTexture(std::to_string((size_t) theTexture), theTexture); } return theTexture; @@ -486,8 +498,8 @@ gpu::Texture* TextureUsage::createRoughnessTextureFromImage(const QImage& srcIma theTexture->setSource(srcImageName); theTexture->assignStoredMip(0, formatMip, image.byteCount(), image.constBits()); generateMips(theTexture, image, formatMip, true); - - // FIXME queue for transfer to GPU and block on completion + + theTexture = cacheTexture(std::to_string((size_t) theTexture), theTexture); } return theTexture; @@ -525,8 +537,8 @@ gpu::Texture* TextureUsage::createRoughnessTextureFromGlossImage(const QImage& s theTexture->setSource(srcImageName); theTexture->assignStoredMip(0, formatMip, image.byteCount(), image.constBits()); generateMips(theTexture, image, formatMip, true); - - // FIXME queue for transfer to GPU and block on completion + + theTexture = cacheTexture(std::to_string((size_t) theTexture), theTexture); } return theTexture; @@ -562,7 +574,7 @@ gpu::Texture* TextureUsage::createMetallicTextureFromImage(const QImage& srcImag theTexture->assignStoredMip(0, formatMip, image.byteCount(), image.constBits()); generateMips(theTexture, image, formatMip, true); - // FIXME queue for transfer to GPU and block on completion + theTexture = cacheTexture(std::to_string((size_t) theTexture), theTexture); } return theTexture; @@ -893,6 +905,8 @@ gpu::Texture* TextureUsage::processCubeTextureColorFromImage(const QImage& srcIm PROFILE_RANGE(resource_parse, "generateIrradiance"); theTexture->generateIrradiance(); } + + theTexture = cacheTexture(std::to_string((size_t) theTexture), theTexture); } } From d1b91cb436d1e602452aa358477108bef61ab86a Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Fri, 17 Feb 2017 09:18:51 -0800 Subject: [PATCH 031/302] Adding abstracted storage for in-memory or file backed data --- libraries/shared/src/shared/Storage.cpp | 86 +++++++++++++++++++++++++ libraries/shared/src/shared/Storage.h | 65 +++++++++++++++++++ tests/shared/src/StorageTests.cpp | 75 +++++++++++++++++++++ tests/shared/src/StorageTests.h | 32 +++++++++ 4 files changed, 258 insertions(+) create mode 100644 libraries/shared/src/shared/Storage.cpp create mode 100644 libraries/shared/src/shared/Storage.h create mode 100644 tests/shared/src/StorageTests.cpp create mode 100644 tests/shared/src/StorageTests.h diff --git a/libraries/shared/src/shared/Storage.cpp b/libraries/shared/src/shared/Storage.cpp new file mode 100644 index 0000000000..fe17d678c7 --- /dev/null +++ b/libraries/shared/src/shared/Storage.cpp @@ -0,0 +1,86 @@ +// +// Created by Bradley Austin Davis on 2016/02/17 +// Copyright 2013-2017 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 "Storage.h" + +#include + +using namespace storage; + +MemoryStoragePointer Storage::toMemoryStorage() const { + return std::make_unique(size(), data()); +} + +FileStoragePointer Storage::toFileStorage(const QString& filename) const { + return FileStorage::create(filename, size(), data()); +} + +MemoryStorage::MemoryStorage(size_t size, const uint8_t* data) { + _data.resize(size); + memcpy(_data.data(), data, size); +} + +const uint8_t* MemoryStorage::data() const { + return _data.data(); +} + +size_t MemoryStorage::size() const { + return _data.size(); +} + +FileStoragePointer FileStorage::create(const QString& filename, size_t size, const uint8_t* data) { + QFile file(filename); + if (!file.open(QFile::ReadWrite | QIODevice::Truncate)) { + throw std::runtime_error("Unable to open file for writing"); + } + if (!file.resize(size)) { + throw std::runtime_error("Unable to resize file"); + } + { + auto mapped = file.map(0, size); + if (!mapped) { + throw std::runtime_error("Unable to map file"); + } + memcpy(mapped, data, size); + if (!file.unmap(mapped)) { + throw std::runtime_error("Unable to unmap file"); + } + } + file.close(); + return std::make_unique(filename); +} + +FileStorage::FileStorage(const QString& filename) : _file(filename) { + if (!_file.open(QFile::ReadOnly)) { + throw std::runtime_error("Unable to open file"); + } + _mapped = _file.map(0, _file.size()); + if (!_mapped) { + throw std::runtime_error("Unable to map file"); + } +} + +FileStorage::~FileStorage() { + if (_mapped) { + if (!_file.unmap(_mapped)) { + throw std::runtime_error("Unable to unmap file"); + } + } + if (_file.isOpen()) { + _file.close(); + } +} + + +const uint8_t* FileStorage::data() const { + return _mapped; +} + +size_t FileStorage::size() const { + return _file.size(); +} diff --git a/libraries/shared/src/shared/Storage.h b/libraries/shared/src/shared/Storage.h new file mode 100644 index 0000000000..3198172b06 --- /dev/null +++ b/libraries/shared/src/shared/Storage.h @@ -0,0 +1,65 @@ +// +// Created by Bradley Austin Davis on 2016/02/17 +// Copyright 2013-2017 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 +// + +#pragma once +#ifndef hifi_Storage_h +#define hifi_Storage_h + +#include +#include +#include +#include +#include + +namespace storage { + class Storage; + using StoragePointer = std::unique_ptr; + class MemoryStorage; + using MemoryStoragePointer = std::unique_ptr; + class FileStorage; + using FileStoragePointer = std::unique_ptr; + + class Storage { + public: + virtual ~Storage() {} + virtual const uint8_t* data() const = 0; + virtual size_t size() const = 0; + + FileStoragePointer toFileStorage(const QString& filename) const; + MemoryStoragePointer toMemoryStorage() const; + }; + + class MemoryStorage : public Storage { + public: + MemoryStorage(size_t size, const uint8_t* data); + const uint8_t* data() const override; + size_t size() const override; + private: + std::vector _data; + }; + + class FileStorage : public Storage { + public: + static FileStoragePointer create(const QString& filename, size_t size, const uint8_t* data); + FileStorage(const QString& filename); + ~FileStorage(); + // Prevent copying + FileStorage(const FileStorage& other) = delete; + FileStorage& operator=(const FileStorage& other) = delete; + + const uint8_t* data() const override; + size_t size() const override; + private: + QFile _file; + uint8_t* _mapped { nullptr }; + }; + + +} + +#endif // hifi_Storage_h diff --git a/tests/shared/src/StorageTests.cpp b/tests/shared/src/StorageTests.cpp new file mode 100644 index 0000000000..fa538f6911 --- /dev/null +++ b/tests/shared/src/StorageTests.cpp @@ -0,0 +1,75 @@ +// +// Created by Bradley Austin Davis on 2016/02/17 +// Copyright 2013-2017 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 "StorageTests.h" + +QTEST_MAIN(StorageTests) + +using namespace storage; + +StorageTests::StorageTests() { + for (size_t i = 0; i < _testData.size(); ++i) { + _testData[i] = (uint8_t)rand(); + } + _testFile = QDir::tempPath() + "/" + QUuid::createUuid().toString(); +} + +StorageTests::~StorageTests() { + QFileInfo fileInfo(_testFile); + if (fileInfo.exists()) { + QFile(_testFile).remove(); + } +} + + +void StorageTests::testConversion() { + { + QFileInfo fileInfo(_testFile); + QCOMPARE(fileInfo.exists(), false); + } + StoragePointer storagePointer = std::make_unique(_testData.size(), _testData.data()); + QCOMPARE(storagePointer->size(), (quint64)_testData.size()); + QCOMPARE(memcmp(_testData.data(), storagePointer->data(), _testData.size()), 0); + // Convert to a file + storagePointer = storagePointer->toFileStorage(_testFile); + { + QFileInfo fileInfo(_testFile); + QCOMPARE(fileInfo.exists(), true); + QCOMPARE(fileInfo.size(), (qint64)_testData.size()); + } + QCOMPARE(storagePointer->size(), (quint64)_testData.size()); + QCOMPARE(memcmp(_testData.data(), storagePointer->data(), _testData.size()), 0); + + // Convert to memory + storagePointer = storagePointer->toMemoryStorage(); + QCOMPARE(storagePointer->size(), (quint64)_testData.size()); + QCOMPARE(memcmp(_testData.data(), storagePointer->data(), _testData.size()), 0); + { + // ensure the file is unaffected + QFileInfo fileInfo(_testFile); + QCOMPARE(fileInfo.exists(), true); + QCOMPARE(fileInfo.size(), (qint64)_testData.size()); + } + + // truncate the data as a new memory object + auto newSize = _testData.size() / 2; + storagePointer = std::make_unique(newSize, storagePointer->data()); + QCOMPARE(storagePointer->size(), (quint64)newSize); + QCOMPARE(memcmp(_testData.data(), storagePointer->data(), newSize), 0); + + // Convert back to file + storagePointer = storagePointer->toFileStorage(_testFile); + QCOMPARE(storagePointer->size(), (quint64)newSize); + QCOMPARE(memcmp(_testData.data(), storagePointer->data(), newSize), 0); + { + // ensure the file is truncated + QFileInfo fileInfo(_testFile); + QCOMPARE(fileInfo.exists(), true); + QCOMPARE(fileInfo.size(), (qint64)newSize); + } +} diff --git a/tests/shared/src/StorageTests.h b/tests/shared/src/StorageTests.h new file mode 100644 index 0000000000..6a2c153223 --- /dev/null +++ b/tests/shared/src/StorageTests.h @@ -0,0 +1,32 @@ +// +// Created by Bradley Austin Davis on 2016/02/17 +// Copyright 2013-2017 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_StorageTests_h +#define hifi_StorageTests_h + +#include + +#include +#include + +class StorageTests : public QObject { + Q_OBJECT + +public: + StorageTests(); + ~StorageTests(); + +private slots: + void testConversion(); + +private: + std::array _testData; + QString _testFile; +}; + +#endif // hifi_StorageTests_h From 2d7ba45667ff00369cfcb429a88d93e216ace21b Mon Sep 17 00:00:00 2001 From: samcake Date: Fri, 17 Feb 2017 12:01:33 -0800 Subject: [PATCH 032/302] FIx creationg issue , need a usageType at creation --- libraries/gpu/src/gpu/Texture.cpp | 25 ------------------------ libraries/gpu/src/gpu/Texture.h | 2 +- libraries/gpu/src/gpu/Texture_ktx.cpp | 5 +++-- libraries/model/src/model/TextureMap.cpp | 2 +- 4 files changed, 5 insertions(+), 29 deletions(-) diff --git a/libraries/gpu/src/gpu/Texture.cpp b/libraries/gpu/src/gpu/Texture.cpp index 664a2273a6..b2cb41e9bb 100755 --- a/libraries/gpu/src/gpu/Texture.cpp +++ b/libraries/gpu/src/gpu/Texture.cpp @@ -1006,28 +1006,3 @@ Texture::ExternalUpdates Texture::getUpdates() const { } return result; } - -#include - -ktx::KTXUniquePointer Texture::serialize(const Texture& texture) { - - ktx::Header header; - header.setUncompressed(ktx::GLType::UNSIGNED_BYTE, 4, ktx::GLFormat::BGRA, ktx::GLInternalFormat_Uncompressed::RGBA8, ktx::GLBaseInternalFormat::RGBA); - header.pixelWidth = texture.getWidth(); - header.pixelHeight = texture.getHeight(); - header.numberOfMipmapLevels = texture.mipLevels(); - - ktx::Images images; - for (int level = 0; level < header.numberOfMipmapLevels; level++) { - auto mip = texture.accessStoredMipFace(level); - if (mip) { - images.emplace_back(ktx::Image(mip->getSize(), 0, mip->readData())); - } - } - - auto ktxBuffer = ktx::KTX::create(header, ktx::KeyValues(), images); - return ktxBuffer; -} -TexturePointer Texture::unserialize(const ktx::KTXUniquePointer& srcData) { - return nullptr; -} diff --git a/libraries/gpu/src/gpu/Texture.h b/libraries/gpu/src/gpu/Texture.h index 9c8ebd627c..cc351de787 100755 --- a/libraries/gpu/src/gpu/Texture.h +++ b/libraries/gpu/src/gpu/Texture.h @@ -493,7 +493,7 @@ public: ExternalUpdates getUpdates() const; static ktx::KTXUniquePointer serialize(const Texture& texture); - static Texture* unserialize(const ktx::KTXUniquePointer& srcData); + static Texture* unserialize(TextureUsageType usageType, const ktx::KTXUniquePointer& srcData); protected: const TextureUsageType _usageType; diff --git a/libraries/gpu/src/gpu/Texture_ktx.cpp b/libraries/gpu/src/gpu/Texture_ktx.cpp index c36c0f14d8..d63bffb74f 100644 --- a/libraries/gpu/src/gpu/Texture_ktx.cpp +++ b/libraries/gpu/src/gpu/Texture_ktx.cpp @@ -80,7 +80,7 @@ ktx::KTXUniquePointer Texture::serialize(const Texture& texture) { return ktxBuffer; } -Texture* Texture::unserialize(const ktx::KTXUniquePointer& srcData) { +Texture* Texture::unserialize(TextureUsageType usageType, const ktx::KTXUniquePointer& srcData) { if (!srcData) { return nullptr; } @@ -105,7 +105,8 @@ Texture* Texture::unserialize(const ktx::KTXUniquePointer& srcData) { type = TEX_3D; } - auto tex = Texture::create( type, + auto tex = Texture::create( usageType, + type, texelFormat, header.getPixelWidth(), header.getPixelHeight(), diff --git a/libraries/model/src/model/TextureMap.cpp b/libraries/model/src/model/TextureMap.cpp index 03605bd6ef..e4e358c581 100755 --- a/libraries/model/src/model/TextureMap.cpp +++ b/libraries/model/src/model/TextureMap.cpp @@ -128,7 +128,7 @@ gpu::Texture* cacheTexture(const std::string& name, gpu::Texture* srcTexture, bo fclose (file); //then create a new texture out of the ktx - auto theNewTexure = Texture::unserialize(ktx::KTX::create(storage)); + auto theNewTexure = Texture::unserialize(gpu::TextureUsageType::RESOURCE, ktx::KTX::create(storage)); if (theNewTexure) { returnedTexture = theNewTexure; From e6c6cd2825bd476958917d72bfd79f62b801b5e1 Mon Sep 17 00:00:00 2001 From: samcake Date: Fri, 17 Feb 2017 12:24:04 -0800 Subject: [PATCH 033/302] cleaner unserialization --- libraries/model/src/model/TextureMap.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/model/src/model/TextureMap.cpp b/libraries/model/src/model/TextureMap.cpp index e4e358c581..3f10f66256 100755 --- a/libraries/model/src/model/TextureMap.cpp +++ b/libraries/model/src/model/TextureMap.cpp @@ -128,7 +128,7 @@ gpu::Texture* cacheTexture(const std::string& name, gpu::Texture* srcTexture, bo fclose (file); //then create a new texture out of the ktx - auto theNewTexure = Texture::unserialize(gpu::TextureUsageType::RESOURCE, ktx::KTX::create(storage)); + auto theNewTexure = Texture::unserialize(srcTexture->getUsageType(), ktx::KTX::create(storage)); if (theNewTexure) { returnedTexture = theNewTexure; From 285222d4907ebd45efe1a2f17ff9ce8f60d9250f Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Fri, 17 Feb 2017 12:49:55 -0800 Subject: [PATCH 034/302] Add view storage --- libraries/shared/src/shared/Storage.cpp | 29 +++++++++---------------- libraries/shared/src/shared/Storage.h | 21 +++++++++++++----- 2 files changed, 26 insertions(+), 24 deletions(-) diff --git a/libraries/shared/src/shared/Storage.cpp b/libraries/shared/src/shared/Storage.cpp index fe17d678c7..d3f1f3c5d3 100644 --- a/libraries/shared/src/shared/Storage.cpp +++ b/libraries/shared/src/shared/Storage.cpp @@ -12,8 +12,16 @@ using namespace storage; +ViewStoragePointer Storage::createView(size_t viewSize, size_t offset) const { + auto selfSize = size(); + if ((viewSize + offset) > selfSize) { + throw std::runtime_error("Unable to map file"); + } + return ViewStoragePointer(new ViewStorage(viewSize, data() + offset)); +} + MemoryStoragePointer Storage::toMemoryStorage() const { - return std::make_unique(size(), data()); + return MemoryStoragePointer(new MemoryStorage(size(), data())); } FileStoragePointer Storage::toFileStorage(const QString& filename) const { @@ -25,14 +33,6 @@ MemoryStorage::MemoryStorage(size_t size, const uint8_t* data) { memcpy(_data.data(), data, size); } -const uint8_t* MemoryStorage::data() const { - return _data.data(); -} - -size_t MemoryStorage::size() const { - return _data.size(); -} - FileStoragePointer FileStorage::create(const QString& filename, size_t size, const uint8_t* data) { QFile file(filename); if (!file.open(QFile::ReadWrite | QIODevice::Truncate)) { @@ -52,7 +52,7 @@ FileStoragePointer FileStorage::create(const QString& filename, size_t size, con } } file.close(); - return std::make_unique(filename); + return FileStoragePointer(new FileStorage(filename)); } FileStorage::FileStorage(const QString& filename) : _file(filename) { @@ -75,12 +75,3 @@ FileStorage::~FileStorage() { _file.close(); } } - - -const uint8_t* FileStorage::data() const { - return _mapped; -} - -size_t FileStorage::size() const { - return _file.size(); -} diff --git a/libraries/shared/src/shared/Storage.h b/libraries/shared/src/shared/Storage.h index 3198172b06..8096b631ed 100644 --- a/libraries/shared/src/shared/Storage.h +++ b/libraries/shared/src/shared/Storage.h @@ -23,13 +23,15 @@ namespace storage { using MemoryStoragePointer = std::unique_ptr; class FileStorage; using FileStoragePointer = std::unique_ptr; + class ViewStorage; + using ViewStoragePointer = std::unique_ptr; class Storage { public: virtual ~Storage() {} virtual const uint8_t* data() const = 0; virtual size_t size() const = 0; - + ViewStoragePointer createView(size_t size, size_t offset = 0) const; FileStoragePointer toFileStorage(const QString& filename) const; MemoryStoragePointer toMemoryStorage() const; }; @@ -37,8 +39,8 @@ namespace storage { class MemoryStorage : public Storage { public: MemoryStorage(size_t size, const uint8_t* data); - const uint8_t* data() const override; - size_t size() const override; + const uint8_t* data() const override { return _data.data(); } + size_t size() const override { return _data.size(); } private: std::vector _data; }; @@ -52,13 +54,22 @@ namespace storage { FileStorage(const FileStorage& other) = delete; FileStorage& operator=(const FileStorage& other) = delete; - const uint8_t* data() const override; - size_t size() const override; + const uint8_t* data() const override { return _mapped; } + size_t size() const override { return _file.size(); } private: QFile _file; uint8_t* _mapped { nullptr }; }; + class ViewStorage : public Storage { + public: + ViewStorage(size_t size, const uint8_t* data) : _size(size), _data(data) {} + const uint8_t* data() const override { return _data; } + size_t size() const override { return _size; } + private: + const size_t _size; + const uint8_t* _data; + }; } From eafe0a04d5666b5dd5be01c936be482889c785ec Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Fri, 17 Feb 2017 13:00:38 -0800 Subject: [PATCH 035/302] Fix transfer buffering --- libraries/gpu-gl/src/gpu/gl45/GL45Backend.h | 9 +-- .../gpu/gl45/GL45BackendVariableTexture.cpp | 55 +++++++++++-------- 2 files changed, 34 insertions(+), 30 deletions(-) diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h b/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h index 4f299d417f..29e5a59ec5 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h +++ b/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h @@ -108,16 +108,10 @@ public: using VoidLambdaQueue = std::queue; using ThreadPointer = std::shared_ptr; const GL45VariableAllocationTexture& _parent; - const uint16_t _sourceMip; - const uint16_t _targetMip; - const uint8_t _face; - const uint32_t _lines; - const uint32_t _lineOffset; // Holds the contents to transfer to the GPU in CPU memory std::vector _buffer; // Indicates if a transfer from backing storage to interal storage has started bool _bufferingStarted { false }; - bool _transferOnly { false }; bool _bufferingCompleted { false }; VoidLambda _transferLambda; VoidLambda _bufferingLambda; @@ -128,6 +122,7 @@ public: static void bufferLoop(); public: + TransferJob(const TransferJob& other) = delete; TransferJob(const GL45VariableAllocationTexture& parent, std::function transferLambda); TransferJob(const GL45VariableAllocationTexture& parent, uint16_t sourceMip, uint16_t targetMip, uint8_t face, uint32_t lines = 0, uint32_t lineOffset = 0); bool tryTransfer(); @@ -139,7 +134,7 @@ public: void transfer(); }; - using TransferQueue = std::queue; + using TransferQueue = std::queue>; static MemoryPressureState _memoryPressureState; protected: static std::atomic _memoryPressureStateStale; diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp b/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp index e26a5c262f..8c93ed6a65 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp @@ -78,11 +78,18 @@ void TransferJob::stopTransferLoop() { } TransferJob::TransferJob(const GL45VariableAllocationTexture& parent, uint16_t sourceMip, uint16_t targetMip, uint8_t face, uint32_t lines, uint32_t lineOffset) - : _parent(parent), _sourceMip(sourceMip), _targetMip(targetMip), _face(face), _lines(lines), _lineOffset(lineOffset) { + : _parent(parent) { + + auto transferDimensions = _parent._gpuObject.evalMipDimensions(sourceMip); + GLenum format; + GLenum type; + auto mipData = _parent._gpuObject.accessStoredMipFace(sourceMip, face); + GLTexelFormat texelFormat = GLTexelFormat::evalGLTexelFormat(_parent._gpuObject.getTexelFormat(), mipData->getFormat()); + format = texelFormat.format; + type = texelFormat.type; if (0 == lines) { - _bufferingLambda = [this] { - auto mipData = _parent._gpuObject.accessStoredMipFace(_sourceMip, _face); + _bufferingLambda = [=] { auto size = mipData->getSize(); _buffer.resize(size); memcpy(&_buffer[0], mipData->readData(), size); @@ -90,38 +97,39 @@ TransferJob::TransferJob(const GL45VariableAllocationTexture& parent, uint16_t s }; } else { - _bufferingLambda = [this] { - auto mipData = _parent._gpuObject.accessStoredMipFace(_sourceMip, _face); - auto dimensions = _parent._gpuObject.evalMipDimensions(_sourceMip); + transferDimensions.y = lines; + _bufferingLambda = [=] { + auto dimensions = _parent._gpuObject.evalMipDimensions(sourceMip); auto mipSize = mipData->getSize(); auto bytesPerLine = (uint32_t)mipSize / dimensions.y; - auto transferSize = bytesPerLine * _lines; - auto sourceOffset = bytesPerLine * _lineOffset; + auto transferSize = bytesPerLine * lines; + auto sourceOffset = bytesPerLine * lineOffset; _buffer.resize(transferSize); memcpy(&_buffer[0], mipData->readData() + sourceOffset, transferSize); _bufferingCompleted = true; }; } - _transferLambda = [this] { - auto mipData = _parent._gpuObject.accessStoredMipFace(_sourceMip, _face); - auto dimensions = _parent._gpuObject.evalMipDimensions(_sourceMip); - GLTexelFormat texelFormat = GLTexelFormat::evalGLTexelFormat(_parent._gpuObject.getTexelFormat(), mipData->getFormat()); - _parent.copyMipFaceLinesFromTexture(_targetMip, _face, dimensions, _lineOffset, texelFormat.format, texelFormat.type, &_buffer[0]); + _transferLambda = [=] { + _parent.copyMipFaceLinesFromTexture(targetMip, face, transferDimensions, lineOffset, format, type, _buffer.data()); _buffer.swap(std::vector()); }; } TransferJob::TransferJob(const GL45VariableAllocationTexture& parent, std::function transferLambda) - : _parent(parent), _sourceMip(0), _targetMip(0), _face(0), _lines(0), _lineOffset(0), _bufferingCompleted(true), _transferLambda(transferLambda) { - if (!_bufferThread) { - _bufferThread = std::make_shared([] { - TransferJob::bufferLoop(); - }); - } + : _parent(parent), _bufferingCompleted(true), _transferLambda(transferLambda) { } bool TransferJob::tryTransfer() { + // Disable threaded texture transfer for now +#if 1 + if (!_bufferingCompleted) { + _bufferingLambda(); + _bufferingCompleted = true; + } + _transferLambda(); + return true; +#else // Are we ready to transfer if (_bufferingCompleted) { _transferLambda(); @@ -130,6 +138,7 @@ bool TransferJob::tryTransfer() { startBuffering(); return false; +#endif } void TransferJob::startBuffering() { @@ -391,7 +400,7 @@ void GL45VariableAllocationTexture::executeNextTransfer(const TexturePointer& cu if (!_pendingTransfers.empty()) { // Keeping hold of a strong pointer during the transfer ensures that the transfer thread cannot try to access a destroyed texture _currentTransferTexture = currentTexture; - if (_pendingTransfers.front().tryTransfer()) { + if (_pendingTransfers.front()->tryTransfer()) { _pendingTransfers.pop(); _currentTransferTexture.reset(); } @@ -542,7 +551,7 @@ void GL45ResourceTexture::populateTransferQueue() { // If the mip is less than the max transfer size, then just do it in one transfer if (glm::all(glm::lessThanEqual(mipDimensions, MAX_TRANSFER_DIMENSIONS))) { // Can the mip be transferred in one go - _pendingTransfers.emplace(*this, sourceMip, targetMip, face); + _pendingTransfers.emplace(new TransferJob(*this, sourceMip, targetMip, face)); continue; } @@ -556,13 +565,13 @@ void GL45ResourceTexture::populateTransferQueue() { uint32_t lineOffset = 0; while (lineOffset < lines) { uint32_t linesToCopy = std::min(lines - lineOffset, linesPerTransfer); - _pendingTransfers.emplace(TransferJob(*this, sourceMip, targetMip, face, linesToCopy, lineOffset)); + _pendingTransfers.emplace(new TransferJob(*this, sourceMip, targetMip, face, linesToCopy, lineOffset)); lineOffset += linesToCopy; } } // queue up the sampler and populated mip change for after the transfer has completed - _pendingTransfers.emplace(TransferJob(*this, [=] { + _pendingTransfers.emplace(new TransferJob(*this, [=] { _populatedMip = sourceMip; syncSampler(); })); From 61ce66a03956c8a68a73ce11f99bdea6b6af8667 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Fri, 17 Feb 2017 13:01:46 -0800 Subject: [PATCH 036/302] Switching texture backing to opaque storage type --- libraries/gpu/src/gpu/Texture.cpp | 47 +++++---------------------- libraries/gpu/src/gpu/Texture.h | 27 +++++++-------- libraries/gpu/src/gpu/Texture_ktx.cpp | 2 +- 3 files changed, 20 insertions(+), 56 deletions(-) diff --git a/libraries/gpu/src/gpu/Texture.cpp b/libraries/gpu/src/gpu/Texture.cpp index b2cb41e9bb..833647bbda 100755 --- a/libraries/gpu/src/gpu/Texture.cpp +++ b/libraries/gpu/src/gpu/Texture.cpp @@ -122,36 +122,12 @@ uint8 Texture::NUM_FACES_PER_TYPE[NUM_TYPES] = { 1, 1, 1, 6 }; Texture::Pixels::Pixels(const Element& format, Size size, const Byte* bytes) : _format(format), - _sysmem(size, bytes), - _isGPULoaded(false) { - Texture::updateTextureCPUMemoryUsage(0, _sysmem.getSize()); + _storage(new storage::MemoryStorage(size, bytes)) { + Texture::updateTextureCPUMemoryUsage(0, _storage->size()); } Texture::Pixels::~Pixels() { - Texture::updateTextureCPUMemoryUsage(_sysmem.getSize(), 0); -} - -Texture::Size Texture::Pixels::resize(Size pSize) { - auto prevSize = _sysmem.getSize(); - auto newSize = _sysmem.resize(pSize); - Texture::updateTextureCPUMemoryUsage(prevSize, newSize); - return newSize; -} - -Texture::Size Texture::Pixels::setData(const Element& format, Size size, const Byte* bytes ) { - _format = format; - auto prevSize = _sysmem.getSize(); - auto newSize = _sysmem.setData(size, bytes); - Texture::updateTextureCPUMemoryUsage(prevSize, newSize); - _isGPULoaded = false; - return newSize; -} - -void Texture::Pixels::notifyGPULoaded() { - _isGPULoaded = true; - auto prevSize = _sysmem.getSize(); - auto newSize = _sysmem.resize(0); - Texture::updateTextureCPUMemoryUsage(prevSize, newSize); + Texture::updateTextureCPUMemoryUsage(_storage->size(), 0); } void Texture::Storage::assignTexture(Texture* texture) { @@ -183,14 +159,6 @@ const Texture::PixelsPointer Texture::Storage::getMipFace(uint16 level, uint8 fa return PixelsPointer(); } -void Texture::Storage::notifyMipFaceGPULoaded(uint16 level, uint8 face) const { - PixelsPointer mipFace = getMipFace(level, face); - // Free the mips - if (mipFace) { - mipFace->notifyGPULoaded(); - } -} - bool Texture::Storage::isMipAvailable(uint16 level, uint8 face) const { PixelsPointer mipFace = getMipFace(level, face); return (mipFace && mipFace->getSize()); @@ -229,7 +197,8 @@ bool Texture::Storage::assignMipData(uint16 level, const Element& format, Size s auto faceBytes = bytes; Size allocated = 0; for (auto& face : mip) { - allocated += face->setData(format, sizePerFace, faceBytes); + face.reset(new Pixels(format, size, bytes)); + allocated += size; faceBytes += sizePerFace; } @@ -242,11 +211,11 @@ bool Texture::Storage::assignMipData(uint16 level, const Element& format, Size s bool Texture::Storage::assignMipFaceData(uint16 level, const Element& format, Size size, const Byte* bytes, uint8 face) { allocateMip(level); - auto mip = _mips[level]; + auto& mip = _mips[level]; Size allocated = 0; if (face < mip.size()) { - auto mipFace = mip[face]; - allocated += mipFace->setData(format, size, bytes); + mip[face].reset(new Pixels(format, size, bytes)); + allocated += size; bumpStamp(); } diff --git a/libraries/gpu/src/gpu/Texture.h b/libraries/gpu/src/gpu/Texture.h index cc351de787..0a5afe78c3 100755 --- a/libraries/gpu/src/gpu/Texture.h +++ b/libraries/gpu/src/gpu/Texture.h @@ -17,6 +17,8 @@ #include #include +#include + #include "Forward.h" #include "Resource.h" @@ -224,26 +226,26 @@ public: bool operator!=(const Usage& usage) { return (_flags != usage._flags); } }; + class Pixels { public: + using StoragePointer = storage::StoragePointer; + Pixels() {} Pixels(const Pixels& pixels) = default; Pixels(const Element& format, Size size, const Byte* bytes); + Pixels(const Element& format, StoragePointer& storage) : _format(format), _storage(storage.release()) {} ~Pixels(); - const Byte* readData() const { return _sysmem.readData(); } - Size getSize() const { return _sysmem.getSize(); } - Size resize(Size pSize); - Size setData(const Element& format, Size size, const Byte* bytes ); + const Byte* readData() const { return _storage->data(); } + Size getSize() const { return _storage->size(); } const Element& getFormat() const { return _format; } - - void notifyGPULoaded(); - + + protected: Element _format; - Sysmem _sysmem; - bool _isGPULoaded; + StoragePointer _storage; friend class Texture; }; @@ -296,10 +298,6 @@ public: const Texture* getTexture() const { return _texture; } friend class Texture; - - // THis should be only called by the Texture from the Backend to notify the storage that the specified mip face pixels - // have been uploaded to the GPU memory. IT is possible for the storage to free the system memory then - virtual void notifyMipFaceGPULoaded(uint16 level, uint8 face) const; }; @@ -481,9 +479,6 @@ public: const Sampler& getSampler() const { return _sampler; } Stamp getSamplerStamp() const { return _samplerStamp; } - // Only callable by the Backend - void notifyMipFaceGPULoaded(uint16 level, uint8 face = 0) const { return _storage->notifyMipFaceGPULoaded(level, face); } - void setExternalTexture(uint32 externalId, void* externalFence); void setExternalRecycler(const ExternalRecycler& recycler); ExternalRecycler getExternalRecycler() const; diff --git a/libraries/gpu/src/gpu/Texture_ktx.cpp b/libraries/gpu/src/gpu/Texture_ktx.cpp index d63bffb74f..404aca77a4 100644 --- a/libraries/gpu/src/gpu/Texture_ktx.cpp +++ b/libraries/gpu/src/gpu/Texture_ktx.cpp @@ -72,7 +72,7 @@ ktx::KTXUniquePointer Texture::serialize(const Texture& texture) { for (uint32_t level = 0; level < header.numberOfMipmapLevels; level++) { auto mip = texture.accessStoredMipFace(level); if (mip) { - images.emplace_back(ktx::Image(mip->getSize(), 0, mip->readData())); + images.emplace_back(ktx::Image((uint32_t)mip->getSize(), 0, mip->readData())); } } From 6c03927b97173bc667f8f9f558496ab28c54b61c Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Fri, 17 Feb 2017 13:53:27 -0800 Subject: [PATCH 037/302] Additional fix to new storage --- libraries/gpu/src/gpu/Texture.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/libraries/gpu/src/gpu/Texture.cpp b/libraries/gpu/src/gpu/Texture.cpp index 833647bbda..281d768182 100755 --- a/libraries/gpu/src/gpu/Texture.cpp +++ b/libraries/gpu/src/gpu/Texture.cpp @@ -127,7 +127,9 @@ Texture::Pixels::Pixels(const Element& format, Size size, const Byte* bytes) : } Texture::Pixels::~Pixels() { - Texture::updateTextureCPUMemoryUsage(_storage->size(), 0); + if (_storage) { + Texture::updateTextureCPUMemoryUsage(_storage->size(), 0); + } } void Texture::Storage::assignTexture(Texture* texture) { From 94960a7a80881b7699d7708ff4c0a3397b3092d4 Mon Sep 17 00:00:00 2001 From: samcake Date: Fri, 17 Feb 2017 16:05:11 -0800 Subject: [PATCH 038/302] Small bug when accessing inexisting storage from the Pixels --- libraries/gpu/src/gpu/Texture.cpp | 4 +++- libraries/model/src/model/TextureMap.cpp | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/libraries/gpu/src/gpu/Texture.cpp b/libraries/gpu/src/gpu/Texture.cpp index 833647bbda..281d768182 100755 --- a/libraries/gpu/src/gpu/Texture.cpp +++ b/libraries/gpu/src/gpu/Texture.cpp @@ -127,7 +127,9 @@ Texture::Pixels::Pixels(const Element& format, Size size, const Byte* bytes) : } Texture::Pixels::~Pixels() { - Texture::updateTextureCPUMemoryUsage(_storage->size(), 0); + if (_storage) { + Texture::updateTextureCPUMemoryUsage(_storage->size(), 0); + } } void Texture::Storage::assignTexture(Texture* texture) { diff --git a/libraries/model/src/model/TextureMap.cpp b/libraries/model/src/model/TextureMap.cpp index 3f10f66256..417fd6992e 100755 --- a/libraries/model/src/model/TextureMap.cpp +++ b/libraries/model/src/model/TextureMap.cpp @@ -85,7 +85,7 @@ QImage processSourceImage(const QImage& srcImage, bool cubemap) { return srcImage; } -gpu::Texture* cacheTexture(const std::string& name, gpu::Texture* srcTexture, bool write = true, bool read = true) { +gpu::Texture* cacheTexture(const std::string& name, gpu::Texture* srcTexture, bool write = true, bool read = false) { if (!srcTexture) { return nullptr; } From 27e8750b9bf3b5b8039fb3700c50c285891173e1 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Fri, 17 Feb 2017 17:17:56 -0800 Subject: [PATCH 039/302] Adding new storage backing for gpu::Texture, moving mip format into Storage abstraction --- .../display-plugins/OpenGLDisplayPlugin.cpp | 3 +- .../display-plugins/hmd/HmdDisplayPlugin.cpp | 3 +- .../src/gpu/gl41/GL41BackendTexture.cpp | 3 +- .../src/gpu/gl45/GL45BackendTexture.cpp | 2 +- .../gpu/gl45/GL45BackendVariableTexture.cpp | 2 +- libraries/gpu/src/gpu/Texture.cpp | 119 ++++++++---------- libraries/gpu/src/gpu/Texture.h | 100 +++++++++------ libraries/gpu/src/gpu/Texture_ktx.cpp | 3 +- .../src/model-networking/TextureCache.cpp | 15 ++- libraries/model/src/model/TextureMap.cpp | 48 +++---- libraries/render-utils/src/text/Font.cpp | 3 +- libraries/shared/src/shared/Storage.cpp | 9 +- libraries/shared/src/shared/Storage.h | 20 +-- tests/render-perf/CMakeLists.txt | 2 +- 14 files changed, 178 insertions(+), 154 deletions(-) diff --git a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp index 1bfe4f3dcc..dca5ac4b77 100644 --- a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp @@ -362,7 +362,8 @@ void OpenGLDisplayPlugin::customizeContext() { cursorData.texture->setSource("cursor texture"); auto usage = gpu::Texture::Usage::Builder().withColor().withAlpha(); cursorData.texture->setUsage(usage.build()); - cursorData.texture->assignStoredMip(0, gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA), image.byteCount(), image.constBits()); + cursorData.texture->setStoredMipFormat(gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA)); + cursorData.texture->assignStoredMip(0, image.byteCount(), image.constBits()); cursorData.texture->autoGenerateMips(-1); } } diff --git a/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.cpp index 24f4e429ef..c55d985a62 100644 --- a/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.cpp @@ -302,7 +302,8 @@ void HmdDisplayPlugin::internalPresent() { gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR))); _previewTexture->setSource("HMD Preview Texture"); _previewTexture->setUsage(gpu::Texture::Usage::Builder().withColor().build()); - _previewTexture->assignStoredMip(0, gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA), image.byteCount(), image.constBits()); + _previewTexture->setStoredMipFormat(gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA)); + _previewTexture->assignStoredMip(0, image.byteCount(), image.constBits()); _previewTexture->autoGenerateMips(-1); } diff --git a/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp b/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp index efbc6903b1..80649c4d64 100644 --- a/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp @@ -71,7 +71,7 @@ GL41Texture::GL41Texture(const std::weak_ptr& backend, const Texture& : GLTexture(backend, texture, allocate()), _storageStamp { texture.getStamp() }, _size(texture.evalTotalSize()) { incrementTextureGPUCount(); withPreservedTexture([&] { - GLTexelFormat texelFormat = GLTexelFormat::evalGLTexelFormat(_gpuObject.getTexelFormat()); + GLTexelFormat texelFormat = GLTexelFormat::evalGLTexelFormat(_gpuObject.getTexelFormat(), _gpuObject.getStoredMipFormat()); const Sampler& sampler = _gpuObject.getSampler(); auto numMips = _gpuObject.evalNumMips(); for (uint16_t mipLevel = 0; mipLevel < numMips; ++mipLevel) { @@ -83,7 +83,6 @@ GL41Texture::GL41Texture(const std::weak_ptr& backend, const Texture& if (_gpuObject.isStoredMipFaceAvailable(mipLevel, face)) { auto mip = _gpuObject.accessStoredMipFace(mipLevel, face); mipData = mip->readData(); - texelFormat = GLTexelFormat::evalGLTexelFormat(_gpuObject.getTexelFormat(), mip->getFormat()); } glTexImage2D(target, mipLevel, texelFormat.internalFormat, dimensions.x, dimensions.y, 0, texelFormat.format, texelFormat.type, mipData); (void)CHECK_GL_ERROR(); diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp b/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp index 6dd1d6aea3..95837c16d9 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp @@ -142,7 +142,7 @@ void GL45Texture::copyMipFaceFromTexture(uint16_t sourceMip, uint16_t targetMip, } auto size = _gpuObject.evalMipDimensions(sourceMip); auto mipData = _gpuObject.accessStoredMipFace(sourceMip, face); - GLTexelFormat texelFormat = GLTexelFormat::evalGLTexelFormat(_gpuObject.getTexelFormat(), mipData->getFormat()); + GLTexelFormat texelFormat = GLTexelFormat::evalGLTexelFormat(_gpuObject.getTexelFormat(), _gpuObject.getStoredMipFormat()); copyMipFaceLinesFromTexture(targetMip, face, size, 0, texelFormat.format, texelFormat.type, mipData->readData()); } diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp b/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp index 8c93ed6a65..0e2053ad06 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp @@ -84,7 +84,7 @@ TransferJob::TransferJob(const GL45VariableAllocationTexture& parent, uint16_t s GLenum format; GLenum type; auto mipData = _parent._gpuObject.accessStoredMipFace(sourceMip, face); - GLTexelFormat texelFormat = GLTexelFormat::evalGLTexelFormat(_parent._gpuObject.getTexelFormat(), mipData->getFormat()); + GLTexelFormat texelFormat = GLTexelFormat::evalGLTexelFormat(_parent._gpuObject.getTexelFormat(), _parent._gpuObject.getStoredMipFormat()); format = texelFormat.format; type = texelFormat.type; diff --git a/libraries/gpu/src/gpu/Texture.cpp b/libraries/gpu/src/gpu/Texture.cpp index 281d768182..6afab476de 100755 --- a/libraries/gpu/src/gpu/Texture.cpp +++ b/libraries/gpu/src/gpu/Texture.cpp @@ -120,18 +120,6 @@ void Texture::setAllowedGPUMemoryUsage(Size size) { uint8 Texture::NUM_FACES_PER_TYPE[NUM_TYPES] = { 1, 1, 1, 6 }; -Texture::Pixels::Pixels(const Element& format, Size size, const Byte* bytes) : - _format(format), - _storage(new storage::MemoryStorage(size, bytes)) { - Texture::updateTextureCPUMemoryUsage(0, _storage->size()); -} - -Texture::Pixels::~Pixels() { - if (_storage) { - Texture::updateTextureCPUMemoryUsage(_storage->size(), 0); - } -} - void Texture::Storage::assignTexture(Texture* texture) { _texture = texture; if (_texture) { @@ -139,21 +127,12 @@ void Texture::Storage::assignTexture(Texture* texture) { } } -void Texture::Storage::reset() { +void Texture::MemoryStorage::reset() { _mips.clear(); bumpStamp(); } -Texture::PixelsPointer Texture::Storage::editMipFace(uint16 level, uint8 face) { - if (level < _mips.size()) { - assert(face < _mips[level].size()); - bumpStamp(); - return _mips[level][face]; - } - return PixelsPointer(); -} - -const Texture::PixelsPointer Texture::Storage::getMipFace(uint16 level, uint8 face) const { +const Texture::PixelsPointer Texture::MemoryStorage::getMipFace(uint16 level, uint8 face) const { if (level < _mips.size()) { assert(face < _mips[level].size()); return _mips[level][face]; @@ -161,12 +140,12 @@ const Texture::PixelsPointer Texture::Storage::getMipFace(uint16 level, uint8 fa return PixelsPointer(); } -bool Texture::Storage::isMipAvailable(uint16 level, uint8 face) const { +bool Texture::MemoryStorage::isMipAvailable(uint16 level, uint8 face) const { PixelsPointer mipFace = getMipFace(level, face); return (mipFace && mipFace->getSize()); } -bool Texture::Storage::allocateMip(uint16 level) { +bool Texture::MemoryStorage::allocateMip(uint16 level) { bool changed = false; if (level >= _mips.size()) { _mips.resize(level+1, std::vector(Texture::NUM_FACES_PER_TYPE[getType()])); @@ -176,7 +155,6 @@ bool Texture::Storage::allocateMip(uint16 level) { auto& mip = _mips[level]; for (auto& face : mip) { if (!face) { - face = std::make_shared(); changed = true; } } @@ -186,7 +164,7 @@ bool Texture::Storage::allocateMip(uint16 level) { return changed; } -bool Texture::Storage::assignMipData(uint16 level, const Element& format, Size size, const Byte* bytes) { +void Texture::MemoryStorage::assignMipData(uint16 level, const storage::StoragePointer& storagePointer) { allocateMip(level); auto& mip = _mips[level]; @@ -195,33 +173,24 @@ bool Texture::Storage::assignMipData(uint16 level, const Element& format, Size s // The bytes assigned here are supposed to contain all the faces bytes of the mip. // For tex1D, 2D, 3D there is only one face // For Cube, we expect the 6 faces in the order X+, X-, Y+, Y-, Z+, Z- - auto sizePerFace = size / mip.size(); - auto faceBytes = bytes; - Size allocated = 0; + auto sizePerFace = storagePointer->size() / mip.size(); + size_t offset = 0; for (auto& face : mip) { - face.reset(new Pixels(format, size, bytes)); - allocated += size; - faceBytes += sizePerFace; + face = storagePointer->createView(sizePerFace, offset); + offset += sizePerFace; } bumpStamp(); - - return allocated == size; } -bool Texture::Storage::assignMipFaceData(uint16 level, const Element& format, Size size, const Byte* bytes, uint8 face) { - +void Texture::MemoryStorage::assignMipFaceData(uint16 level, uint8 face, const storage::StoragePointer& storagePointer) { allocateMip(level); auto& mip = _mips[level]; - Size allocated = 0; if (face < mip.size()) { - mip[face].reset(new Pixels(format, size, bytes)); - allocated += size; + mip[face] = storagePointer; bumpStamp(); } - - return allocated == size; } Texture* Texture::createExternal(const ExternalRecycler& recycler, const Sampler& sampler) { @@ -260,7 +229,7 @@ Texture* Texture::createCube(const Element& texelFormat, uint16 width, const Sam Texture* Texture::create(TextureUsageType usageType, Type type, const Element& texelFormat, uint16 width, uint16 height, uint16 depth, uint16 numSamples, uint16 numSlices, const Sampler& sampler) { Texture* tex = new Texture(usageType); - tex->_storage.reset(new Storage()); + tex->_storage.reset(new MemoryStorage()); tex->_type = type; tex->_storage->assignTexture(tex); tex->_maxMip = 0; @@ -402,69 +371,83 @@ uint16 Texture::evalNumMips() const { return evalNumMips({ _width, _height, _depth }); } -bool Texture::assignStoredMip(uint16 level, const Element& format, Size size, const Byte* bytes) { +void Texture::setStoredMipFormat(const Element& format) { + _storage->setFormat(format); +} + +const Element& Texture::getStoredMipFormat() const { + return _storage->getFormat(); +} + +void Texture::assignStoredMip(uint16 level, Size size, const Byte* bytes) { + storage::StoragePointer storage { new storage::MemoryStorage(size, bytes) }; + assignStoredMip(level, storage); +} + +void Texture::assignStoredMipFace(uint16 level, uint8 face, Size size, const Byte* bytes) { + storage::StoragePointer storage { new storage::MemoryStorage(size, bytes) }; + assignStoredMipFace(level, face, storage); +} + +void Texture::assignStoredMip(uint16 level, storage::StoragePointer& storage) { // Check that level accessed make sense if (level != 0) { if (_autoGenerateMips) { - return false; + return; } if (level >= evalNumMips()) { - return false; + return; } } // THen check that the mem texture passed make sense with its format - Size expectedSize = evalStoredMipSize(level, format); - if (size == expectedSize) { - _storage->assignMipData(level, format, size, bytes); + Size expectedSize = evalStoredMipSize(level, getStoredMipFormat()); + auto size = storage->size(); + auto bytes = storage->data(); + if (storage->size() == expectedSize) { + _storage->assignMipData(level, storage); _maxMip = std::max(_maxMip, level); _stamp++; - return true; } else if (size > expectedSize) { // NOTE: We are facing this case sometime because apparently QImage (from where we get the bits) is generating images // and alligning the line of pixels to 32 bits. // We should probably consider something a bit more smart to get the correct result but for now (UI elements) // it seems to work... - _storage->assignMipData(level, format, size, bytes); + _storage->assignMipData(level, storage); _maxMip = std::max(_maxMip, level); _stamp++; - return true; } - - return false; } - -bool Texture::assignStoredMipFace(uint16 level, const Element& format, Size size, const Byte* bytes, uint8 face) { +void Texture::assignStoredMipFace(uint16 level, uint8 face, storage::StoragePointer& storage) { // Check that level accessed make sense if (level != 0) { if (_autoGenerateMips) { - return false; + return; } if (level >= evalNumMips()) { - return false; + return; } } // THen check that the mem texture passed make sense with its format - Size expectedSize = evalStoredMipFaceSize(level, format); + Size expectedSize = evalStoredMipFaceSize(level, getStoredMipFormat()); + auto size = storage->size(); + auto bytes = storage->data(); if (size == expectedSize) { - _storage->assignMipFaceData(level, format, size, bytes, face); + _storage->assignMipFaceData(level, face, storage); _stamp++; - return true; } else if (size > expectedSize) { // NOTE: We are facing this case sometime because apparently QImage (from where we get the bits) is generating images // and alligning the line of pixels to 32 bits. // We should probably consider something a bit more smart to get the correct result but for now (UI elements) // it seems to work... - _storage->assignMipFaceData(level, format, size, bytes, face); + _storage->assignMipFaceData(level, face, storage); _stamp++; - return true; } - - return false; } + uint16 Texture::autoGenerateMips(uint16 maxMip) { bool changed = false; if (!_autoGenerateMips) { @@ -498,7 +481,7 @@ uint16 Texture::getStoredMipHeight(uint16 level) const { if (mip && mip->getSize()) { return evalMipHeight(level); } - return 0; + return 0; } uint16 Texture::getStoredMipDepth(uint16 level) const { @@ -770,8 +753,8 @@ bool sphericalHarmonicsFromTexture(const gpu::Texture& cubeTexture, std::vector< for(int face=0; face < gpu::Texture::NUM_CUBE_FACES; face++) { PROFILE_RANGE(render_gpu, "ProcessFace"); - auto numComponents = cubeTexture.accessStoredMipFace(0,face)->getFormat().getScalarCount(); - auto data = cubeTexture.accessStoredMipFace(0,face)->readData(); + auto numComponents = cubeTexture.getStoredMipFormat().getScalarCount(); + auto data = cubeTexture.accessStoredMipFace(0,face)->data(); if (data == nullptr) { continue; } diff --git a/libraries/gpu/src/gpu/Texture.h b/libraries/gpu/src/gpu/Texture.h index 0a5afe78c3..84241442c5 100755 --- a/libraries/gpu/src/gpu/Texture.h +++ b/libraries/gpu/src/gpu/Texture.h @@ -226,30 +226,7 @@ public: bool operator!=(const Usage& usage) { return (_flags != usage._flags); } }; - - class Pixels { - public: - using StoragePointer = storage::StoragePointer; - - Pixels() {} - Pixels(const Pixels& pixels) = default; - Pixels(const Element& format, Size size, const Byte* bytes); - Pixels(const Element& format, StoragePointer& storage) : _format(format), _storage(storage.release()) {} - ~Pixels(); - - const Byte* readData() const { return _storage->data(); } - Size getSize() const { return _storage->size(); } - - const Element& getFormat() const { return _format; } - - - protected: - Element _format; - StoragePointer _storage; - - friend class Texture; - }; - typedef std::shared_ptr< Pixels > PixelsPointer; + using PixelsPointer = storage::StoragePointer; enum Type { TEX_1D = 0, @@ -272,35 +249,68 @@ public: NUM_CUBE_FACES, // Not a valid vace index }; + class Storage { public: Storage() {} virtual ~Storage() {} - virtual void reset(); - virtual PixelsPointer editMipFace(uint16 level, uint8 face = 0); - virtual const PixelsPointer getMipFace(uint16 level, uint8 face = 0) const; - virtual bool allocateMip(uint16 level); - virtual bool assignMipData(uint16 level, const Element& format, Size size, const Byte* bytes); - virtual bool assignMipFaceData(uint16 level, const Element& format, Size size, const Byte* bytes, uint8 face); - virtual bool isMipAvailable(uint16 level, uint8 face = 0) const; + virtual void reset() = 0; + virtual const PixelsPointer getMipFace(uint16 level, uint8 face = 0) const = 0; + virtual void assignMipData(uint16 level, const storage::StoragePointer& storage) = 0; + virtual void assignMipFaceData(uint16 level, uint8 face, const storage::StoragePointer& storage) = 0; + virtual bool isMipAvailable(uint16 level, uint8 face = 0) const = 0; Texture::Type getType() const { return _type; } - + Stamp getStamp() const { return _stamp; } Stamp bumpStamp() { return ++_stamp; } - protected: - Stamp _stamp = 0; - Texture* _texture = nullptr; // Points to the parent texture (not owned) - Texture::Type _type = Texture::TEX_2D; // The type of texture is needed to know the number of faces to expect - std::vector> _mips; // an array of mips, each mip is an array of faces + void setFormat(const Element& format) { _format = format; } + const Element& getFormat() const { return _format; } + + private: + Stamp _stamp { 0 }; + Element _format; + Texture::Type _type { Texture::TEX_2D }; // The type of texture is needed to know the number of faces to expect + Texture* _texture { nullptr }; // Points to the parent texture (not owned) virtual void assignTexture(Texture* tex); // Texture storage is pointing to ONE corrresponding Texture. const Texture* getTexture() const { return _texture; } - friend class Texture; }; - + class MemoryStorage : public Storage { + public: + void reset() override; + const PixelsPointer getMipFace(uint16 level, uint8 face = 0) const override; + void assignMipData(uint16 level, const storage::StoragePointer& storage) override; + void assignMipFaceData(uint16 level, uint8 face, const storage::StoragePointer& storage) override; + bool isMipAvailable(uint16 level, uint8 face = 0) const override; + + protected: + bool allocateMip(uint16 level); + std::vector> _mips; // an array of mips, each mip is an array of faces + }; + + class KtxStorage : public Storage { + public: + KtxStorage(ktx::KTXUniquePointer& ktxData); + const PixelsPointer getMipFace(uint16 level, uint8 face = 0) const override; + bool isMipAvailable(uint16 level, uint8 face = 0) const override; + + void assignMipData(uint16 level, const storage::StoragePointer& storage) override { + throw std::runtime_error("Invalid call"); + } + + void assignMipFaceData(uint16 level, uint8 face, const storage::StoragePointer& storage) override { + throw std::runtime_error("Invalid call"); + } + void reset() override { } + + protected: + ktx::KTXUniquePointer _ktxData; + friend class Texture; + }; + static Texture* create1D(const Element& texelFormat, uint16 width, const Sampler& sampler = Sampler()); static Texture* create2D(const Element& texelFormat, uint16 width, uint16 height, const Sampler& sampler = Sampler()); static Texture* create3D(const Element& texelFormat, uint16 width, uint16 height, uint16 depth, const Sampler& sampler = Sampler()); @@ -444,13 +454,21 @@ public: // Managing Storage and mips + // Mip storage format is constant across all mips + void setStoredMipFormat(const Element& format); + const Element& getStoredMipFormat() const; + // Manually allocate the mips down until the specified maxMip // this is just allocating the sysmem version of it // in case autoGen is on, this doesn't allocate // Explicitely assign mip data for a certain level // If Bytes is NULL then simply allocate the space so mip sysmem can be accessed - bool assignStoredMip(uint16 level, const Element& format, Size size, const Byte* bytes); - bool assignStoredMipFace(uint16 level, const Element& format, Size size, const Byte* bytes, uint8 face); + + void assignStoredMip(uint16 level, Size size, const Byte* bytes); + void assignStoredMipFace(uint16 level, uint8 face, Size size, const Byte* bytes); + + void assignStoredMip(uint16 level, storage::StoragePointer& storage); + void assignStoredMipFace(uint16 level, uint8 face, storage::StoragePointer& storage); // Access the the sub mips bool isStoredMipFaceAvailable(uint16 level, uint8 face = 0) const { return _storage->isMipAvailable(level, face); } diff --git a/libraries/gpu/src/gpu/Texture_ktx.cpp b/libraries/gpu/src/gpu/Texture_ktx.cpp index 404aca77a4..13d4c2a464 100644 --- a/libraries/gpu/src/gpu/Texture_ktx.cpp +++ b/libraries/gpu/src/gpu/Texture_ktx.cpp @@ -116,9 +116,10 @@ Texture* Texture::unserialize(TextureUsageType usageType, const ktx::KTXUniquePo Sampler()); // Assing the mips availables + tex->setStoredMipFormat(mipFormat); uint16_t level = 0; for (auto& image : srcData->_images) { - tex->assignStoredMip(level, mipFormat, image._imageSize, image._bytes); + tex->assignStoredMip(level, image._imageSize, image._bytes); level++; } diff --git a/libraries/model-networking/src/model-networking/TextureCache.cpp b/libraries/model-networking/src/model-networking/TextureCache.cpp index 6a84fc960f..1f21e9e78d 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.cpp +++ b/libraries/model-networking/src/model-networking/TextureCache.cpp @@ -108,7 +108,8 @@ const gpu::TexturePointer& TextureCache::getPermutationNormalTexture() { } _permutationNormalTexture = gpu::TexturePointer(gpu::Texture::create2D(gpu::Element(gpu::VEC3, gpu::NUINT8, gpu::RGB), 256, 2)); - _permutationNormalTexture->assignStoredMip(0, _blueTexture->getTexelFormat(), sizeof(data), data); + _permutationNormalTexture->setStoredMipFormat(_permutationNormalTexture->getTexelFormat()); + _permutationNormalTexture->assignStoredMip(0, sizeof(data), data); } return _permutationNormalTexture; } @@ -122,7 +123,8 @@ const gpu::TexturePointer& TextureCache::getWhiteTexture() { if (!_whiteTexture) { _whiteTexture = gpu::TexturePointer(gpu::Texture::createStrict(gpu::Element::COLOR_RGBA_32, 1, 1)); _whiteTexture->setSource("TextureCache::_whiteTexture"); - _whiteTexture->assignStoredMip(0, _whiteTexture->getTexelFormat(), sizeof(OPAQUE_WHITE), OPAQUE_WHITE); + _whiteTexture->setStoredMipFormat(_whiteTexture->getTexelFormat()); + _whiteTexture->assignStoredMip(0, sizeof(OPAQUE_WHITE), OPAQUE_WHITE); } return _whiteTexture; } @@ -131,7 +133,8 @@ const gpu::TexturePointer& TextureCache::getGrayTexture() { if (!_grayTexture) { _grayTexture = gpu::TexturePointer(gpu::Texture::createStrict(gpu::Element::COLOR_RGBA_32, 1, 1)); _grayTexture->setSource("TextureCache::_grayTexture"); - _grayTexture->assignStoredMip(0, _grayTexture->getTexelFormat(), sizeof(OPAQUE_GRAY), OPAQUE_GRAY); + _grayTexture->setStoredMipFormat(_grayTexture->getTexelFormat()); + _grayTexture->assignStoredMip(0, sizeof(OPAQUE_GRAY), OPAQUE_GRAY); } return _grayTexture; } @@ -140,7 +143,8 @@ const gpu::TexturePointer& TextureCache::getBlueTexture() { if (!_blueTexture) { _blueTexture = gpu::TexturePointer(gpu::Texture::createStrict(gpu::Element::COLOR_RGBA_32, 1, 1)); _blueTexture->setSource("TextureCache::_blueTexture"); - _blueTexture->assignStoredMip(0, _blueTexture->getTexelFormat(), sizeof(OPAQUE_BLUE), OPAQUE_BLUE); + _blueTexture->setStoredMipFormat(_blueTexture->getTexelFormat()); + _blueTexture->assignStoredMip(0, sizeof(OPAQUE_BLUE), OPAQUE_BLUE); } return _blueTexture; } @@ -149,7 +153,8 @@ const gpu::TexturePointer& TextureCache::getBlackTexture() { if (!_blackTexture) { _blackTexture = gpu::TexturePointer(gpu::Texture::createStrict(gpu::Element::COLOR_RGBA_32, 1, 1)); _blackTexture->setSource("TextureCache::_blackTexture"); - _blackTexture->assignStoredMip(0, _blackTexture->getTexelFormat(), sizeof(OPAQUE_BLACK), OPAQUE_BLACK); + _blackTexture->setStoredMipFormat(_blackTexture->getTexelFormat()); + _blackTexture->assignStoredMip(0, sizeof(OPAQUE_BLACK), OPAQUE_BLACK); } return _blackTexture; } diff --git a/libraries/model/src/model/TextureMap.cpp b/libraries/model/src/model/TextureMap.cpp index 3f10f66256..0a1eb705eb 100755 --- a/libraries/model/src/model/TextureMap.cpp +++ b/libraries/model/src/model/TextureMap.cpp @@ -96,7 +96,7 @@ gpu::Texture* cacheTexture(const std::string& name, gpu::Texture* srcTexture, bo // Prepare cache directory QString path("hifi_ktx/"); QFileInfo originalFileInfo(path); - QString docsLocation = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation); + QString docsLocation = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation); path = docsLocation + "/" + path; QFileInfo info(path); if (!info.absoluteDir().exists()) { @@ -262,7 +262,7 @@ const QImage& image, bool isLinear, bool doCompress) { #define CPU_MIPMAPS 1 -void generateMips(gpu::Texture* texture, QImage& image, gpu::Element formatMip, bool fastResize) { +void generateMips(gpu::Texture* texture, QImage& image, bool fastResize) { #if CPU_MIPMAPS PROFILE_RANGE(resource_parse, "generateMips"); auto numMips = texture->evalNumMips(); @@ -270,10 +270,10 @@ void generateMips(gpu::Texture* texture, QImage& image, gpu::Element formatMip, QSize mipSize(texture->evalMipWidth(level), texture->evalMipHeight(level)); if (fastResize) { image = image.scaled(mipSize); - texture->assignStoredMip(level, formatMip, image.byteCount(), image.constBits()); + texture->assignStoredMip(level, image.byteCount(), image.constBits()); } else { QImage mipImage = image.scaled(mipSize, Qt::IgnoreAspectRatio, Qt::SmoothTransformation); - texture->assignStoredMip(level, formatMip, mipImage.byteCount(), mipImage.constBits()); + texture->assignStoredMip(level, mipImage.byteCount(), mipImage.constBits()); } } #else @@ -281,14 +281,14 @@ void generateMips(gpu::Texture* texture, QImage& image, gpu::Element formatMip, #endif } -void generateFaceMips(gpu::Texture* texture, QImage& image, gpu::Element formatMip, uint8 face) { +void generateFaceMips(gpu::Texture* texture, QImage& image, uint8 face) { #if CPU_MIPMAPS PROFILE_RANGE(resource_parse, "generateFaceMips"); auto numMips = texture->evalNumMips(); for (uint16 level = 1; level < numMips; ++level) { QSize mipSize(texture->evalMipWidth(level), texture->evalMipHeight(level)); QImage mipImage = image.scaled(mipSize, Qt::IgnoreAspectRatio, Qt::SmoothTransformation); - texture->assignStoredMipFace(level, formatMip, mipImage.byteCount(), mipImage.constBits(), face); + texture->assignStoredMipFace(level, face, mipImage.byteCount(), mipImage.constBits()); } #else texture->autoGenerateMips(-1); @@ -322,11 +322,11 @@ gpu::Texture* TextureUsage::process2DTextureColorFromImage(const QImage& srcImag } } theTexture->setUsage(usage.build()); - - theTexture->assignStoredMip(0, formatMip, image.byteCount(), image.constBits()); + theTexture->setStoredMipFormat(formatMip); + theTexture->assignStoredMip(0, image.byteCount(), image.constBits()); if (generateMips) { - ::generateMips(theTexture, image, formatMip, false); + ::generateMips(theTexture, image, false); } theTexture = cacheTexture(std::to_string((size_t) theTexture), theTexture); @@ -373,8 +373,9 @@ gpu::Texture* TextureUsage::createNormalTextureFromNormalImage(const QImage& src theTexture = (gpu::Texture::create2D(formatGPU, image.width(), image.height(), gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR))); theTexture->setSource(srcImageName); - theTexture->assignStoredMip(0, formatMip, image.byteCount(), image.constBits()); - generateMips(theTexture, image, formatMip, true); + theTexture->setStoredMipFormat(formatMip); + theTexture->assignStoredMip(0, image.byteCount(), image.constBits()); + generateMips(theTexture, image, true); theTexture = cacheTexture(std::to_string((size_t) theTexture), theTexture); } @@ -461,8 +462,9 @@ gpu::Texture* TextureUsage::createNormalTextureFromBumpImage(const QImage& srcIm theTexture = (gpu::Texture::create2D(formatGPU, image.width(), image.height(), gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR))); theTexture->setSource(srcImageName); - theTexture->assignStoredMip(0, formatMip, image.byteCount(), image.constBits()); - generateMips(theTexture, image, formatMip, true); + theTexture->setStoredMipFormat(formatMip); + theTexture->assignStoredMip(0, image.byteCount(), image.constBits()); + generateMips(theTexture, image, true); theTexture = cacheTexture(std::to_string((size_t) theTexture), theTexture); } @@ -496,8 +498,9 @@ gpu::Texture* TextureUsage::createRoughnessTextureFromImage(const QImage& srcIma theTexture = (gpu::Texture::create2D(formatGPU, image.width(), image.height(), gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR))); theTexture->setSource(srcImageName); - theTexture->assignStoredMip(0, formatMip, image.byteCount(), image.constBits()); - generateMips(theTexture, image, formatMip, true); + theTexture->setStoredMipFormat(formatMip); + theTexture->assignStoredMip(0, image.byteCount(), image.constBits()); + generateMips(theTexture, image, true); theTexture = cacheTexture(std::to_string((size_t) theTexture), theTexture); } @@ -535,8 +538,9 @@ gpu::Texture* TextureUsage::createRoughnessTextureFromGlossImage(const QImage& s theTexture = (gpu::Texture::create2D(formatGPU, image.width(), image.height(), gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR))); theTexture->setSource(srcImageName); - theTexture->assignStoredMip(0, formatMip, image.byteCount(), image.constBits()); - generateMips(theTexture, image, formatMip, true); + theTexture->setStoredMipFormat(formatMip); + theTexture->assignStoredMip(0, image.byteCount(), image.constBits()); + generateMips(theTexture, image, true); theTexture = cacheTexture(std::to_string((size_t) theTexture), theTexture); } @@ -571,8 +575,9 @@ gpu::Texture* TextureUsage::createMetallicTextureFromImage(const QImage& srcImag theTexture = (gpu::Texture::create2D(formatGPU, image.width(), image.height(), gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR))); theTexture->setSource(srcImageName); - theTexture->assignStoredMip(0, formatMip, image.byteCount(), image.constBits()); - generateMips(theTexture, image, formatMip, true); + theTexture->setStoredMipFormat(formatMip); + theTexture->assignStoredMip(0, image.byteCount(), image.constBits()); + generateMips(theTexture, image, true); theTexture = cacheTexture(std::to_string((size_t) theTexture), theTexture); } @@ -886,11 +891,12 @@ gpu::Texture* TextureUsage::processCubeTextureColorFromImage(const QImage& srcIm if (faces.size() == gpu::Texture::NUM_FACES_PER_TYPE[gpu::Texture::TEX_CUBE]) { theTexture = gpu::Texture::createCube(formatGPU, faces[0].width(), gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR, gpu::Sampler::WRAP_CLAMP)); theTexture->setSource(srcImageName); + theTexture->setStoredMipFormat(formatMip); int f = 0; for (auto& face : faces) { - theTexture->assignStoredMipFace(0, formatMip, face.byteCount(), face.constBits(), f); + theTexture->assignStoredMipFace(0, f, face.byteCount(), face.constBits()); if (generateMips) { - generateFaceMips(theTexture, face, formatMip, f); + generateFaceMips(theTexture, face, f); } f++; } diff --git a/libraries/render-utils/src/text/Font.cpp b/libraries/render-utils/src/text/Font.cpp index 4f4ee12622..c405f6d6ae 100644 --- a/libraries/render-utils/src/text/Font.cpp +++ b/libraries/render-utils/src/text/Font.cpp @@ -209,7 +209,8 @@ void Font::read(QIODevice& in) { } _texture = gpu::TexturePointer(gpu::Texture::create2D(formatGPU, image.width(), image.height(), gpu::Sampler(gpu::Sampler::FILTER_MIN_POINT_MAG_LINEAR))); - _texture->assignStoredMip(0, formatMip, image.byteCount(), image.constBits()); + _texture->setStoredMipFormat(formatMip); + _texture->assignStoredMip(0, image.byteCount(), image.constBits()); } void Font::setupGPU() { diff --git a/libraries/shared/src/shared/Storage.cpp b/libraries/shared/src/shared/Storage.cpp index d3f1f3c5d3..dacb840de7 100644 --- a/libraries/shared/src/shared/Storage.cpp +++ b/libraries/shared/src/shared/Storage.cpp @@ -14,10 +14,13 @@ using namespace storage; ViewStoragePointer Storage::createView(size_t viewSize, size_t offset) const { auto selfSize = size(); - if ((viewSize + offset) > selfSize) { - throw std::runtime_error("Unable to map file"); + if (0 == viewSize) { + viewSize = selfSize; } - return ViewStoragePointer(new ViewStorage(viewSize, data() + offset)); + if ((viewSize + offset) > selfSize) { + throw std::runtime_error("Invalid mapping range"); + } + return ViewStoragePointer(new ViewStorage(shared_from_this(), viewSize, data() + offset)); } MemoryStoragePointer Storage::toMemoryStorage() const { diff --git a/libraries/shared/src/shared/Storage.h b/libraries/shared/src/shared/Storage.h index 8096b631ed..4dbb2a03a5 100644 --- a/libraries/shared/src/shared/Storage.h +++ b/libraries/shared/src/shared/Storage.h @@ -18,22 +18,27 @@ namespace storage { class Storage; - using StoragePointer = std::unique_ptr; + using StoragePointer = std::shared_ptr; class MemoryStorage; - using MemoryStoragePointer = std::unique_ptr; + using MemoryStoragePointer = std::shared_ptr; class FileStorage; - using FileStoragePointer = std::unique_ptr; + using FileStoragePointer = std::shared_ptr; class ViewStorage; - using ViewStoragePointer = std::unique_ptr; + using ViewStoragePointer = std::shared_ptr; - class Storage { + class Storage : public std::enable_shared_from_this { public: virtual ~Storage() {} virtual const uint8_t* data() const = 0; virtual size_t size() const = 0; - ViewStoragePointer createView(size_t size, size_t offset = 0) const; + + ViewStoragePointer createView(size_t size = 0, size_t offset = 0) const; FileStoragePointer toFileStorage(const QString& filename) const; MemoryStoragePointer toMemoryStorage() const; + + // Aliases to prevent having to re-write a ton of code + inline size_t getSize() const { return size(); } + inline const uint8_t* readData() const { return data(); } }; class MemoryStorage : public Storage { @@ -63,10 +68,11 @@ namespace storage { class ViewStorage : public Storage { public: - ViewStorage(size_t size, const uint8_t* data) : _size(size), _data(data) {} + ViewStorage(const storage::StoragePointer& owner, size_t size, const uint8_t* data) : _owner(owner), _size(size), _data(data) {} const uint8_t* data() const override { return _data; } size_t size() const override { return _size; } private: + const storage::StoragePointer _owner; const size_t _size; const uint8_t* _data; }; diff --git a/tests/render-perf/CMakeLists.txt b/tests/render-perf/CMakeLists.txt index d4f90fdace..96cede9c43 100644 --- a/tests/render-perf/CMakeLists.txt +++ b/tests/render-perf/CMakeLists.txt @@ -10,7 +10,7 @@ setup_hifi_project(Quick Gui OpenGL) set_target_properties(${TARGET_NAME} PROPERTIES FOLDER "Tests/manual-tests/") # link in the shared libraries -link_hifi_libraries(shared octree gl gpu gpu-gl render model model-networking networking render-utils fbx entities entities-renderer animation audio avatars script-engine physics) +link_hifi_libraries(shared octree ktx gl gpu gpu-gl render model model-networking networking render-utils fbx entities entities-renderer animation audio avatars script-engine physics) package_libraries_for_deployment() From b502b1e200f49a8dede4005504dd7b6e13aec9e7 Mon Sep 17 00:00:00 2001 From: sam Date: Mon, 20 Feb 2017 19:40:39 -0800 Subject: [PATCH 040/302] assigning the names of source to the texture --- libraries/model/src/model/TextureMap.cpp | 31 ++++++++++++++++-------- 1 file changed, 21 insertions(+), 10 deletions(-) diff --git a/libraries/model/src/model/TextureMap.cpp b/libraries/model/src/model/TextureMap.cpp index 3f10f66256..5eb1b3982a 100755 --- a/libraries/model/src/model/TextureMap.cpp +++ b/libraries/model/src/model/TextureMap.cpp @@ -85,7 +85,7 @@ QImage processSourceImage(const QImage& srcImage, bool cubemap) { return srcImage; } -gpu::Texture* cacheTexture(const std::string& name, gpu::Texture* srcTexture, bool write = true, bool read = true) { +gpu::Texture* cacheTexture(const std::string& name, gpu::Texture* srcTexture, bool write = true, bool read = false) { if (!srcTexture) { return nullptr; } @@ -103,8 +103,13 @@ gpu::Texture* cacheTexture(const std::string& name, gpu::Texture* srcTexture, bo QString originalRelativePath = originalFileInfo.path(); QDir(docsLocation).mkpath(originalRelativePath); } + + std::string cleanedName = name; + + cleanedName = cleanedName.substr(cleanedName.find_last_of('//') + 1); + std::string filename(path.toStdString()); - filename += name; + filename += cleanedName; filename += ".ktx"; if (write) { @@ -328,8 +333,8 @@ gpu::Texture* TextureUsage::process2DTextureColorFromImage(const QImage& srcImag if (generateMips) { ::generateMips(theTexture, image, formatMip, false); } - - theTexture = cacheTexture(std::to_string((size_t) theTexture), theTexture); + theTexture->setSource(srcImageName); + theTexture = cacheTexture(theTexture->source(), theTexture); } return theTexture; @@ -376,7 +381,8 @@ gpu::Texture* TextureUsage::createNormalTextureFromNormalImage(const QImage& src theTexture->assignStoredMip(0, formatMip, image.byteCount(), image.constBits()); generateMips(theTexture, image, formatMip, true); - theTexture = cacheTexture(std::to_string((size_t) theTexture), theTexture); + theTexture->setSource(srcImageName); + theTexture = cacheTexture(theTexture->source(), theTexture); } return theTexture; @@ -464,7 +470,8 @@ gpu::Texture* TextureUsage::createNormalTextureFromBumpImage(const QImage& srcIm theTexture->assignStoredMip(0, formatMip, image.byteCount(), image.constBits()); generateMips(theTexture, image, formatMip, true); - theTexture = cacheTexture(std::to_string((size_t) theTexture), theTexture); + theTexture->setSource(srcImageName); + theTexture = cacheTexture(theTexture->source(), theTexture); } return theTexture; @@ -499,7 +506,8 @@ gpu::Texture* TextureUsage::createRoughnessTextureFromImage(const QImage& srcIma theTexture->assignStoredMip(0, formatMip, image.byteCount(), image.constBits()); generateMips(theTexture, image, formatMip, true); - theTexture = cacheTexture(std::to_string((size_t) theTexture), theTexture); + theTexture->setSource(srcImageName); + theTexture = cacheTexture(theTexture->source(), theTexture); } return theTexture; @@ -538,7 +546,8 @@ gpu::Texture* TextureUsage::createRoughnessTextureFromGlossImage(const QImage& s theTexture->assignStoredMip(0, formatMip, image.byteCount(), image.constBits()); generateMips(theTexture, image, formatMip, true); - theTexture = cacheTexture(std::to_string((size_t) theTexture), theTexture); + theTexture->setSource(srcImageName); + theTexture = cacheTexture(theTexture->source(), theTexture); } return theTexture; @@ -574,7 +583,8 @@ gpu::Texture* TextureUsage::createMetallicTextureFromImage(const QImage& srcImag theTexture->assignStoredMip(0, formatMip, image.byteCount(), image.constBits()); generateMips(theTexture, image, formatMip, true); - theTexture = cacheTexture(std::to_string((size_t) theTexture), theTexture); + theTexture->setSource(srcImageName); + theTexture = cacheTexture(theTexture->source(), theTexture); } return theTexture; @@ -906,7 +916,8 @@ gpu::Texture* TextureUsage::processCubeTextureColorFromImage(const QImage& srcIm theTexture->generateIrradiance(); } - theTexture = cacheTexture(std::to_string((size_t) theTexture), theTexture); + theTexture->setSource(srcImageName); + theTexture = cacheTexture(theTexture->source(), theTexture); } } From e5c2f6f651f17f1797ef82b4be2613431bf2b39d Mon Sep 17 00:00:00 2001 From: sam Date: Tue, 21 Feb 2017 01:28:13 -0800 Subject: [PATCH 041/302] enabled the usage and cube map with KTX correctly --- libraries/gpu/src/gpu/Texture.cpp | 2 ++ libraries/gpu/src/gpu/Texture.h | 2 +- libraries/gpu/src/gpu/Texture_ktx.cpp | 23 +++++++++++++++++++---- libraries/ktx/src/ktx/KTX.cpp | 5 +++-- libraries/ktx/src/ktx/KTX.h | 22 ++++++++++++++++++++-- libraries/ktx/src/ktx/Reader.cpp | 17 ++++++++++++++--- libraries/ktx/src/ktx/Writer.cpp | 21 ++++++++++++++++----- libraries/model/src/model/TextureMap.cpp | 15 ++++++++++----- 8 files changed, 85 insertions(+), 22 deletions(-) diff --git a/libraries/gpu/src/gpu/Texture.cpp b/libraries/gpu/src/gpu/Texture.cpp index f65242c578..ba44bd6341 100755 --- a/libraries/gpu/src/gpu/Texture.cpp +++ b/libraries/gpu/src/gpu/Texture.cpp @@ -450,6 +450,7 @@ bool Texture::assignStoredMipFace(uint16 level, const Element& format, Size size Size expectedSize = evalStoredMipFaceSize(level, format); if (size == expectedSize) { _storage->assignMipFaceData(level, format, size, bytes, face); + _maxMip = std::max(_maxMip, level); _stamp++; return true; } else if (size > expectedSize) { @@ -458,6 +459,7 @@ bool Texture::assignStoredMipFace(uint16 level, const Element& format, Size size // We should probably consider something a bit more smart to get the correct result but for now (UI elements) // it seems to work... _storage->assignMipFaceData(level, format, size, bytes, face); + _maxMip = std::max(_maxMip, level); _stamp++; return true; } diff --git a/libraries/gpu/src/gpu/Texture.h b/libraries/gpu/src/gpu/Texture.h index 0a5afe78c3..b8537b07c2 100755 --- a/libraries/gpu/src/gpu/Texture.h +++ b/libraries/gpu/src/gpu/Texture.h @@ -488,7 +488,7 @@ public: ExternalUpdates getUpdates() const; static ktx::KTXUniquePointer serialize(const Texture& texture); - static Texture* unserialize(TextureUsageType usageType, const ktx::KTXUniquePointer& srcData); + static Texture* unserialize(Usage usage, TextureUsageType usageType, const ktx::KTXUniquePointer& srcData, const Sampler& sampler = Sampler()); protected: const TextureUsageType _usageType; diff --git a/libraries/gpu/src/gpu/Texture_ktx.cpp b/libraries/gpu/src/gpu/Texture_ktx.cpp index 404aca77a4..508467e79f 100644 --- a/libraries/gpu/src/gpu/Texture_ktx.cpp +++ b/libraries/gpu/src/gpu/Texture_ktx.cpp @@ -28,6 +28,7 @@ ktx::KTXUniquePointer Texture::serialize(const Texture& texture) { header.setUncompressed(ktx::GLType::UNSIGNED_BYTE, 4, ktx::GLFormat::BGRA, ktx::GLInternalFormat_Uncompressed::RGBA8, ktx::GLBaseInternalFormat::RGBA); // Set Dimensions + uint32_t numFaces = 1; switch (texture.getType()) { case TEX_1D: { if (texture.isArray()) { @@ -59,6 +60,7 @@ ktx::KTXUniquePointer Texture::serialize(const Texture& texture) { } else { header.setCube(texture.getWidth(), texture.getHeight()); } + numFaces = 6; break; } default: @@ -72,7 +74,16 @@ ktx::KTXUniquePointer Texture::serialize(const Texture& texture) { for (uint32_t level = 0; level < header.numberOfMipmapLevels; level++) { auto mip = texture.accessStoredMipFace(level); if (mip) { - images.emplace_back(ktx::Image((uint32_t)mip->getSize(), 0, mip->readData())); + if (numFaces == 1) { + images.emplace_back(ktx::Image((uint32_t)mip->getSize(), 0, mip->readData())); + } else { + ktx::Image::FaceBytes cubeFaces(6); + cubeFaces[0] = mip->readData(); + for (int face = 1; face < 6; face++) { + cubeFaces[face] = texture.accessStoredMipFace(level, face)->readData(); + } + images.emplace_back(ktx::Image((uint32_t)mip->getSize(), 0, cubeFaces)); + } } } @@ -80,7 +91,7 @@ ktx::KTXUniquePointer Texture::serialize(const Texture& texture) { return ktxBuffer; } -Texture* Texture::unserialize(TextureUsageType usageType, const ktx::KTXUniquePointer& srcData) { +Texture* Texture::unserialize(Usage usage, TextureUsageType usageType, const ktx::KTXUniquePointer& srcData, const Sampler& sampler) { if (!srcData) { return nullptr; } @@ -113,12 +124,16 @@ Texture* Texture::unserialize(TextureUsageType usageType, const ktx::KTXUniquePo header.getPixelDepth(), 1, // num Samples header.getNumberOfSlices(), - Sampler()); + sampler); + + tex->setUsage(usage); // Assing the mips availables uint16_t level = 0; for (auto& image : srcData->_images) { - tex->assignStoredMip(level, mipFormat, image._imageSize, image._bytes); + for (uint32_t face = 0; face < image._numFaces; face++) { + tex->assignStoredMipFace(level, mipFormat, image._faceSize, image._faceBytes[face], face); + } level++; } diff --git a/libraries/ktx/src/ktx/KTX.cpp b/libraries/ktx/src/ktx/KTX.cpp index f8f315d784..8635fbb684 100644 --- a/libraries/ktx/src/ktx/KTX.cpp +++ b/libraries/ktx/src/ktx/KTX.cpp @@ -16,8 +16,9 @@ using namespace ktx; uint32_t Header::evalPadding(size_t byteSize) { - auto padding = byteSize % PACKING_SIZE; - return (uint32_t) (padding ? PACKING_SIZE - padding : 0); + //auto padding = byteSize % PACKING_SIZE; + // return (uint32_t) (padding ? PACKING_SIZE - padding : 0); + return (uint32_t) (3 - (byteSize + 3) % PACKING_SIZE);// padding ? PACKING_SIZE - padding : 0); } diff --git a/libraries/ktx/src/ktx/KTX.h b/libraries/ktx/src/ktx/KTX.h index 8e79d90dc0..52c65c9cd4 100644 --- a/libraries/ktx/src/ktx/KTX.h +++ b/libraries/ktx/src/ktx/KTX.h @@ -418,14 +418,32 @@ namespace ktx { struct Image { + using FaceBytes = std::vector; + + uint32_t _numFaces{ 1 }; uint32_t _imageSize; + uint32_t _faceSize; uint32_t _padding; - const Byte* _bytes; + FaceBytes _faceBytes; + Image(uint32_t imageSize, uint32_t padding, const Byte* bytes) : + _numFaces(1), _imageSize(imageSize), _padding(padding), - _bytes(bytes) {} + _faceSize(imageSize), + _faceBytes(1, bytes) {} + + Image(uint32_t pageSize, uint32_t padding, const FaceBytes& cubeFaceBytes) : + _numFaces(NUM_CUBEMAPFACES), + _imageSize(pageSize * NUM_CUBEMAPFACES), + _padding(padding), + _faceSize(pageSize) + { + if (cubeFaceBytes.size() == NUM_CUBEMAPFACES) { + _faceBytes = cubeFaceBytes; + } + } }; using Images = std::vector; diff --git a/libraries/ktx/src/ktx/Reader.cpp b/libraries/ktx/src/ktx/Reader.cpp index 00c0c4e19a..dffe6cf828 100644 --- a/libraries/ktx/src/ktx/Reader.cpp +++ b/libraries/ktx/src/ktx/Reader.cpp @@ -125,6 +125,7 @@ namespace ktx { Images images; auto currentPtr = srcBytes; auto numMips = header.getNumberOfLevels(); + auto numFaces = header.numberOfFaces; // Keep identifying new mip as long as we can at list query the next imageSize while ((currentPtr - srcBytes) + sizeof(uint32_t) <= (srcSize)) { @@ -137,9 +138,19 @@ namespace ktx { if ((currentPtr - srcBytes) + imageSize <= (srcSize)) { auto padding = Header::evalPadding(imageSize); - images.emplace_back(Image(imageSize, padding, currentPtr)); - - currentPtr += imageSize + padding; + if (numFaces == 6) { + size_t faceSize = imageSize / 6; + Image::FaceBytes faces(6); + for (uint32_t face = 0; face < 6; face++) { + faces[face] = currentPtr; + currentPtr += faceSize; + } + images.emplace_back(Image(faceSize, padding, faces)); + currentPtr += padding; + } else { + images.emplace_back(Image(imageSize, padding, currentPtr)); + currentPtr += imageSize + padding; + } } else { break; } diff --git a/libraries/ktx/src/ktx/Writer.cpp b/libraries/ktx/src/ktx/Writer.cpp index 75836ad5b5..edd9a9ec97 100644 --- a/libraries/ktx/src/ktx/Writer.cpp +++ b/libraries/ktx/src/ktx/Writer.cpp @@ -125,14 +125,25 @@ namespace ktx { // If enough data ahead then capture the copy source pointer if (currentDataSize + imageSize <= (allocatedImagesDataSize)) { - - auto copied = memcpy(currentPtr, srcImages[l]._bytes, imageSize); - auto padding = Header::evalPadding(imageSize); - destImages.emplace_back(Image(imageSize, padding, currentPtr)); + // Single face vs cubes + if (srcImages[l]._numFaces == 1) { + auto copied = memcpy(currentPtr, srcImages[l]._faceBytes[0], imageSize); + destImages.emplace_back(Image(imageSize, padding, currentPtr)); + currentPtr += imageSize; + } else { + Image::FaceBytes faceBytes(6); + auto faceSize = srcImages[l]._faceSize; + for (int face = 0; face < 6; face++) { + auto copied = memcpy(currentPtr, srcImages[l]._faceBytes[face], faceSize); + faceBytes[face] = currentPtr; + currentPtr += faceSize; + } + destImages.emplace_back(Image(faceSize, padding, faceBytes)); + } - currentPtr += imageSize + padding; + currentPtr += padding; currentDataSize += imageSize + padding; } } diff --git a/libraries/model/src/model/TextureMap.cpp b/libraries/model/src/model/TextureMap.cpp index 5eb1b3982a..a95ed78be3 100755 --- a/libraries/model/src/model/TextureMap.cpp +++ b/libraries/model/src/model/TextureMap.cpp @@ -85,7 +85,7 @@ QImage processSourceImage(const QImage& srcImage, bool cubemap) { return srcImage; } -gpu::Texture* cacheTexture(const std::string& name, gpu::Texture* srcTexture, bool write = true, bool read = false) { +gpu::Texture* cacheTexture(const std::string& name, gpu::Texture* srcTexture, bool write = true, bool read = true) { if (!srcTexture) { return nullptr; } @@ -113,10 +113,15 @@ gpu::Texture* cacheTexture(const std::string& name, gpu::Texture* srcTexture, bo filename += ".ktx"; if (write) { - FILE* file = fopen (filename.c_str(),"wb"); + /* FILE *file = fopen(name.c_str(), "r"); if (file != nullptr) { - fwrite(theKTX->_storage->data(), 1, theKTX->_storage->size(), file); - fclose (file); + fclose(file); + } else*/ { + FILE *file = fopen (filename.c_str(),"wb"); + if (file != nullptr) { + fwrite(theKTX->_storage->data(), 1, theKTX->_storage->size(), file); + fclose (file); + } } } @@ -133,7 +138,7 @@ gpu::Texture* cacheTexture(const std::string& name, gpu::Texture* srcTexture, bo fclose (file); //then create a new texture out of the ktx - auto theNewTexure = Texture::unserialize(srcTexture->getUsageType(), ktx::KTX::create(storage)); + auto theNewTexure = Texture::unserialize(srcTexture->getUsage(), srcTexture->getUsageType(), ktx::KTX::create(storage), srcTexture->getSampler()); if (theNewTexure) { returnedTexture = theNewTexure; From 5e307f247ef4b449d1c6c59cc281fa25efa2fda7 Mon Sep 17 00:00:00 2001 From: samcake Date: Tue, 21 Feb 2017 10:46:49 -0800 Subject: [PATCH 042/302] Add support for the R8 textures --- libraries/gpu-gl/src/gpu/gl/GLTexelFormat.cpp | 3 ++ libraries/gpu/src/gpu/Format.cpp | 2 + libraries/gpu/src/gpu/Format.h | 2 + libraries/gpu/src/gpu/Texture_ktx.cpp | 37 +++++++++++++++---- libraries/ktx/src/ktx/KTX.h | 8 ++++ libraries/model/src/model/TextureMap.cpp | 12 +++--- 6 files changed, 51 insertions(+), 13 deletions(-) diff --git a/libraries/gpu-gl/src/gpu/gl/GLTexelFormat.cpp b/libraries/gpu-gl/src/gpu/gl/GLTexelFormat.cpp index bd945cbaaa..fa639aab11 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLTexelFormat.cpp +++ b/libraries/gpu-gl/src/gpu/gl/GLTexelFormat.cpp @@ -17,6 +17,7 @@ GLenum GLTexelFormat::evalGLTexelFormatInternal(const gpu::Element& dstFormat) { switch (dstFormat.getDimension()) { case gpu::SCALAR: { switch (dstFormat.getSemantic()) { + case gpu::RED: case gpu::RGB: case gpu::RGBA: case gpu::SRGB: @@ -262,6 +263,7 @@ GLTexelFormat GLTexelFormat::evalGLTexelFormat(const Element& dstFormat, const E texel.type = ELEMENT_TYPE_TO_GL[dstFormat.getType()]; switch (dstFormat.getSemantic()) { + case gpu::RED: case gpu::RGB: case gpu::RGBA: texel.internalFormat = GL_R8; @@ -403,6 +405,7 @@ GLTexelFormat GLTexelFormat::evalGLTexelFormat(const Element& dstFormat, const E texel.internalFormat = GL_COMPRESSED_RED_RGTC1; break; } + case gpu::RED: case gpu::RGB: case gpu::RGBA: case gpu::SRGB: diff --git a/libraries/gpu/src/gpu/Format.cpp b/libraries/gpu/src/gpu/Format.cpp index 0f5e85c907..5b61a3a5a4 100644 --- a/libraries/gpu/src/gpu/Format.cpp +++ b/libraries/gpu/src/gpu/Format.cpp @@ -10,6 +10,8 @@ using namespace gpu; +const Element Element::COLOR_R_8 { SCALAR, NUINT8, RED }; + const Element Element::COLOR_RGBA_32{ VEC4, NUINT8, RGBA }; const Element Element::COLOR_SRGBA_32{ VEC4, NUINT8, SRGBA }; diff --git a/libraries/gpu/src/gpu/Format.h b/libraries/gpu/src/gpu/Format.h index c7dc2eed8d..4610597a56 100644 --- a/libraries/gpu/src/gpu/Format.h +++ b/libraries/gpu/src/gpu/Format.h @@ -133,6 +133,7 @@ static const int SCALAR_COUNT[NUM_DIMENSIONS] = { enum Semantic { RAW = 0, // used as RAW memory + RED, RGB, RGBA, BGRA, @@ -227,6 +228,7 @@ public: return getRaw() != right.getRaw(); } + static const Element COLOR_R_8; static const Element COLOR_RGBA_32; static const Element COLOR_SRGBA_32; static const Element COLOR_BGRA_32; diff --git a/libraries/gpu/src/gpu/Texture_ktx.cpp b/libraries/gpu/src/gpu/Texture_ktx.cpp index 8b47c7409e..f1d91d39ab 100644 --- a/libraries/gpu/src/gpu/Texture_ktx.cpp +++ b/libraries/gpu/src/gpu/Texture_ktx.cpp @@ -20,13 +20,18 @@ ktx::KTXUniquePointer Texture::serialize(const Texture& texture) { // From texture format to ktx format description auto texelFormat = texture.getTexelFormat(); - if ( !( (texelFormat == Format::COLOR_RGBA_32) - || (texelFormat == Format::COLOR_SRGBA_32) - )) - return nullptr; - - header.setUncompressed(ktx::GLType::UNSIGNED_BYTE, 4, ktx::GLFormat::BGRA, ktx::GLInternalFormat_Uncompressed::RGBA8, ktx::GLBaseInternalFormat::RGBA); + auto mipFormat = texture.getStoredMipFormat(); + if (texelFormat == Format::COLOR_RGBA_32 && mipFormat == Format::COLOR_BGRA_32) { + header.setUncompressed(ktx::GLType::UNSIGNED_BYTE, 4, ktx::GLFormat::BGRA, ktx::GLInternalFormat_Uncompressed::RGBA8, ktx::GLBaseInternalFormat::RGBA); + } else if (texelFormat == Format::COLOR_SRGBA_32 && mipFormat == Format::COLOR_BGRA_32) { + header.setUncompressed(ktx::GLType::UNSIGNED_BYTE, 4, ktx::GLFormat::BGRA, ktx::GLInternalFormat_Uncompressed::SRGB8_ALPHA8, ktx::GLBaseInternalFormat::RGBA); + } else if (texelFormat == Format::COLOR_R_8 && mipFormat == Format::COLOR_R_8) { + header.setUncompressed(ktx::GLType::UNSIGNED_BYTE, 1, ktx::GLFormat::RED, ktx::GLInternalFormat_Uncompressed::R8, ktx::GLBaseInternalFormat::RED); + } else { + return nullptr; + } + // Set Dimensions uint32_t numFaces = 1; switch (texture.getType()) { @@ -97,9 +102,27 @@ Texture* Texture::unserialize(Usage usage, TextureUsageType usageType, const ktx } const auto& header = *srcData->getHeader(); - Format mipFormat = Format::COLOR_SBGRA_32; + Format mipFormat = Format::COLOR_BGRA_32; Format texelFormat = Format::COLOR_SRGBA_32; + if (header.getGLFormat() == ktx::GLFormat::BGRA && header.getGLType() == ktx::GLType::UNSIGNED_BYTE && header.getTypeSize() == 4) { + mipFormat = Format::COLOR_BGRA_32; + if (header.getGLInternaFormat_Uncompressed() == ktx::GLInternalFormat_Uncompressed::RGBA8) { + texelFormat = Format::COLOR_RGBA_32; + } else if (header.getGLInternaFormat_Uncompressed() == ktx::GLInternalFormat_Uncompressed::SRGB8_ALPHA8) { + texelFormat = Format::COLOR_SRGBA_32; + } else { + return nullptr; + } + } else if (header.getGLFormat() == ktx::GLFormat::RED && header.getGLType() == ktx::GLType::UNSIGNED_BYTE && header.getTypeSize() == 1) { + mipFormat = Format::COLOR_R_8; + if (header.getGLInternaFormat_Uncompressed() == ktx::GLInternalFormat_Uncompressed::R8) { + texelFormat = Format::COLOR_R_8; + } else { + return nullptr; + } + } + // Find Texture Type based on dimensions Type type = TEX_1D; if (header.pixelWidth == 0) { diff --git a/libraries/ktx/src/ktx/KTX.h b/libraries/ktx/src/ktx/KTX.h index 52c65c9cd4..128d92ccc8 100644 --- a/libraries/ktx/src/ktx/KTX.h +++ b/libraries/ktx/src/ktx/KTX.h @@ -394,6 +394,14 @@ namespace ktx { glBaseInternalFormat = (uint32_t) baseInternalFormat; } + GLType getGLType() const { return (GLType)glType; } + uint32_t getTypeSize() const { return glTypeSize; } + GLFormat getGLFormat() const { return (GLFormat)glFormat; } + GLInternalFormat_Uncompressed getGLInternaFormat_Uncompressed() const { return (GLInternalFormat_Uncompressed)glInternalFormat; } + GLInternalFormat_Compressed getGLInternaFormat_Compressed() const { return (GLInternalFormat_Compressed)glInternalFormat; } + GLBaseInternalFormat getGLBaseInternalFormat() const { return (GLBaseInternalFormat)glBaseInternalFormat; } + + void setDimensions(uint32_t width, uint32_t height = 0, uint32_t depth = 0, uint32_t numSlices = 0, uint32_t numFaces = 1) { pixelWidth = (width > 0 ? width : 1); pixelHeight = height; diff --git a/libraries/model/src/model/TextureMap.cpp b/libraries/model/src/model/TextureMap.cpp index 41d1ba5e12..760cd98898 100755 --- a/libraries/model/src/model/TextureMap.cpp +++ b/libraries/model/src/model/TextureMap.cpp @@ -504,9 +504,9 @@ gpu::Texture* TextureUsage::createRoughnessTextureFromImage(const QImage& srcIma #ifdef COMPRESS_TEXTURES gpu::Element formatGPU = gpu::Element(gpu::SCALAR, gpu::NUINT8, gpu::COMPRESSED_R); #else - gpu::Element formatGPU = gpu::Element(gpu::SCALAR, gpu::NUINT8, gpu::RGB); + gpu::Element formatGPU = gpu::Element::COLOR_R_8; #endif - gpu::Element formatMip = gpu::Element(gpu::SCALAR, gpu::NUINT8, gpu::RGB); + gpu::Element formatMip = gpu::Element::COLOR_R_8; theTexture = (gpu::Texture::create2D(formatGPU, image.width(), image.height(), gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR))); theTexture->setSource(srcImageName); @@ -545,9 +545,9 @@ gpu::Texture* TextureUsage::createRoughnessTextureFromGlossImage(const QImage& s #ifdef COMPRESS_TEXTURES gpu::Element formatGPU = gpu::Element(gpu::SCALAR, gpu::NUINT8, gpu::COMPRESSED_R); #else - gpu::Element formatGPU = gpu::Element(gpu::SCALAR, gpu::NUINT8, gpu::RGB); + gpu::Element formatGPU = gpu::Element::COLOR_R_8; #endif - gpu::Element formatMip = gpu::Element(gpu::SCALAR, gpu::NUINT8, gpu::RGB); + gpu::Element formatMip = gpu::Element::COLOR_R_8; theTexture = (gpu::Texture::create2D(formatGPU, image.width(), image.height(), gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR))); theTexture->setSource(srcImageName); @@ -583,9 +583,9 @@ gpu::Texture* TextureUsage::createMetallicTextureFromImage(const QImage& srcImag #ifdef COMPRESS_TEXTURES gpu::Element formatGPU = gpu::Element(gpu::SCALAR, gpu::NUINT8, gpu::COMPRESSED_R); #else - gpu::Element formatGPU = gpu::Element(gpu::SCALAR, gpu::NUINT8, gpu::RGB); + gpu::Element formatGPU = gpu::Element::COLOR_R_8; #endif - gpu::Element formatMip = gpu::Element(gpu::SCALAR, gpu::NUINT8, gpu::RGB); + gpu::Element formatMip = gpu::Element::COLOR_R_8; theTexture = (gpu::Texture::create2D(formatGPU, image.width(), image.height(), gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR))); theTexture->setSource(srcImageName); From 666be6912bd38bfe4bb0f244d619c78c2f100b27 Mon Sep 17 00:00:00 2001 From: Zach Pomerantz Date: Tue, 21 Feb 2017 14:46:25 -0500 Subject: [PATCH 043/302] fix KTX gcc errors --- libraries/ktx/src/ktx/KTX.h | 1 + libraries/ktx/src/ktx/Reader.cpp | 6 ++---- libraries/ktx/src/ktx/Writer.cpp | 4 +--- 3 files changed, 4 insertions(+), 7 deletions(-) diff --git a/libraries/ktx/src/ktx/KTX.h b/libraries/ktx/src/ktx/KTX.h index 128d92ccc8..94529d6e68 100644 --- a/libraries/ktx/src/ktx/KTX.h +++ b/libraries/ktx/src/ktx/KTX.h @@ -16,6 +16,7 @@ #include #include #include +#include #include /* KTX Spec: diff --git a/libraries/ktx/src/ktx/Reader.cpp b/libraries/ktx/src/ktx/Reader.cpp index dffe6cf828..584bf219c4 100644 --- a/libraries/ktx/src/ktx/Reader.cpp +++ b/libraries/ktx/src/ktx/Reader.cpp @@ -17,9 +17,7 @@ namespace ktx { class ReaderException: public std::exception { public: ReaderException(const std::string& explanation) : _explanation("KTX deserialization error: " + explanation) {} - const char* what() const override { - return _explanation.c_str(); - } + const char* what() const noexcept override { return _explanation.c_str(); } private: const std::string _explanation; }; @@ -179,4 +177,4 @@ namespace ktx { return result; } -} \ No newline at end of file +} diff --git a/libraries/ktx/src/ktx/Writer.cpp b/libraries/ktx/src/ktx/Writer.cpp index edd9a9ec97..7cc6f9972d 100644 --- a/libraries/ktx/src/ktx/Writer.cpp +++ b/libraries/ktx/src/ktx/Writer.cpp @@ -16,9 +16,7 @@ namespace ktx { class WriterException : public std::exception { public: WriterException(const std::string& explanation) : _explanation("KTX serialization error: " + explanation) {} - const char* what() const override { - return _explanation.c_str(); - } + const char* what() const noexcept override { return _explanation.c_str(); } private: const std::string _explanation; }; From be4a9dcd5a3e78152a087c512218af8cf0cb5575 Mon Sep 17 00:00:00 2001 From: Zach Pomerantz Date: Tue, 21 Feb 2017 14:50:12 -0500 Subject: [PATCH 044/302] clear buffer without rref --- libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp b/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp index 0e2053ad06..92cc945a86 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp @@ -112,7 +112,8 @@ TransferJob::TransferJob(const GL45VariableAllocationTexture& parent, uint16_t s _transferLambda = [=] { _parent.copyMipFaceLinesFromTexture(targetMip, face, transferDimensions, lineOffset, format, type, _buffer.data()); - _buffer.swap(std::vector()); + std::vector emptyVector; + _buffer.swap(emptyVector); }; } From 748d7c0ce45ff90b42e644982e50f0f1d0074922 Mon Sep 17 00:00:00 2001 From: Zach Pomerantz Date: Wed, 22 Feb 2017 15:06:01 -0500 Subject: [PATCH 045/302] macro NOEXCEPT for ktx on msvc --- libraries/ktx/src/ktx/Reader.cpp | 8 +++++++- libraries/ktx/src/ktx/Writer.cpp | 7 ++++++- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/libraries/ktx/src/ktx/Reader.cpp b/libraries/ktx/src/ktx/Reader.cpp index 584bf219c4..e977f9ab4f 100644 --- a/libraries/ktx/src/ktx/Reader.cpp +++ b/libraries/ktx/src/ktx/Reader.cpp @@ -13,11 +13,17 @@ #include #include +#ifndef _MSC_VER +#define NOEXCEPT noexcept +#else +#define NOEXCEPT +#endif + namespace ktx { class ReaderException: public std::exception { public: ReaderException(const std::string& explanation) : _explanation("KTX deserialization error: " + explanation) {} - const char* what() const noexcept override { return _explanation.c_str(); } + const char* what() const NOEXCEPT override { return _explanation.c_str(); } private: const std::string _explanation; }; diff --git a/libraries/ktx/src/ktx/Writer.cpp b/libraries/ktx/src/ktx/Writer.cpp index 7cc6f9972d..1839624038 100644 --- a/libraries/ktx/src/ktx/Writer.cpp +++ b/libraries/ktx/src/ktx/Writer.cpp @@ -10,13 +10,18 @@ // #include "KTX.h" +#ifndef _MSC_VER +#define NOEXCEPT noexcept +#else +#define NOEXCEPT +#endif namespace ktx { class WriterException : public std::exception { public: WriterException(const std::string& explanation) : _explanation("KTX serialization error: " + explanation) {} - const char* what() const noexcept override { return _explanation.c_str(); } + const char* what() const NOEXCEPT override { return _explanation.c_str(); } private: const std::string _explanation; }; From 3f67f05b02fdf6cf447471fe5c75c7db782a88bd Mon Sep 17 00:00:00 2001 From: samcake Date: Wed, 22 Feb 2017 13:06:34 -0800 Subject: [PATCH 046/302] Remove the noexcept --- libraries/ktx/src/ktx/Reader.cpp | 2 +- libraries/ktx/src/ktx/Writer.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/ktx/src/ktx/Reader.cpp b/libraries/ktx/src/ktx/Reader.cpp index 584bf219c4..a00bfd90e3 100644 --- a/libraries/ktx/src/ktx/Reader.cpp +++ b/libraries/ktx/src/ktx/Reader.cpp @@ -17,7 +17,7 @@ namespace ktx { class ReaderException: public std::exception { public: ReaderException(const std::string& explanation) : _explanation("KTX deserialization error: " + explanation) {} - const char* what() const noexcept override { return _explanation.c_str(); } + const char* what() const override { return _explanation.c_str(); } private: const std::string _explanation; }; diff --git a/libraries/ktx/src/ktx/Writer.cpp b/libraries/ktx/src/ktx/Writer.cpp index 7cc6f9972d..63f8fb3988 100644 --- a/libraries/ktx/src/ktx/Writer.cpp +++ b/libraries/ktx/src/ktx/Writer.cpp @@ -16,7 +16,7 @@ namespace ktx { class WriterException : public std::exception { public: WriterException(const std::string& explanation) : _explanation("KTX serialization error: " + explanation) {} - const char* what() const noexcept override { return _explanation.c_str(); } + const char* what() const override { return _explanation.c_str(); } private: const std::string _explanation; }; From c6b72dfe3c8c76534c3e79641633c56e5f423faf Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Wed, 22 Feb 2017 12:04:29 -0800 Subject: [PATCH 047/302] Make memory storage mutable --- libraries/shared/src/shared/Storage.cpp | 4 +++- libraries/shared/src/shared/Storage.h | 3 ++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/libraries/shared/src/shared/Storage.cpp b/libraries/shared/src/shared/Storage.cpp index dacb840de7..3b83676bc8 100644 --- a/libraries/shared/src/shared/Storage.cpp +++ b/libraries/shared/src/shared/Storage.cpp @@ -33,7 +33,9 @@ FileStoragePointer Storage::toFileStorage(const QString& filename) const { MemoryStorage::MemoryStorage(size_t size, const uint8_t* data) { _data.resize(size); - memcpy(_data.data(), data, size); + if (data) { + memcpy(_data.data(), data, size); + } } FileStoragePointer FileStorage::create(const QString& filename, size_t size, const uint8_t* data) { diff --git a/libraries/shared/src/shared/Storage.h b/libraries/shared/src/shared/Storage.h index 4dbb2a03a5..b79b9d6080 100644 --- a/libraries/shared/src/shared/Storage.h +++ b/libraries/shared/src/shared/Storage.h @@ -43,8 +43,9 @@ namespace storage { class MemoryStorage : public Storage { public: - MemoryStorage(size_t size, const uint8_t* data); + MemoryStorage(size_t size, const uint8_t* data = nullptr); const uint8_t* data() const override { return _data.data(); } + uint8_t* data() { return _data.data(); } size_t size() const override { return _data.size(); } private: std::vector _data; From a2269f488f99175cb603a9ff8afbcc6d24f4ab0c Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Wed, 22 Feb 2017 12:07:06 -0800 Subject: [PATCH 048/302] Add some testing / validation for KTX --- libraries/gpu/src/gpu/Texture_ktx.cpp | 21 +++++ libraries/ktx/src/ktx/KTX.cpp | 20 ++--- libraries/ktx/src/ktx/KTX.h | 55 ++---------- libraries/ktx/src/ktx/Writer.cpp | 43 ++-------- scripts/developer/tests/.gitignore | 1 + tests/ktx/CMakeLists.txt | 15 ++++ tests/ktx/src/main.cpp | 117 ++++++++++++++++++++++++++ 7 files changed, 176 insertions(+), 96 deletions(-) create mode 100644 scripts/developer/tests/.gitignore create mode 100644 tests/ktx/CMakeLists.txt create mode 100644 tests/ktx/src/main.cpp diff --git a/libraries/gpu/src/gpu/Texture_ktx.cpp b/libraries/gpu/src/gpu/Texture_ktx.cpp index f1d91d39ab..47e7bde333 100644 --- a/libraries/gpu/src/gpu/Texture_ktx.cpp +++ b/libraries/gpu/src/gpu/Texture_ktx.cpp @@ -93,6 +93,27 @@ ktx::KTXUniquePointer Texture::serialize(const Texture& texture) { } auto ktxBuffer = ktx::KTX::create(header, images); + + assert(0 == memcmp(&header, ktxBuffer->getHeader(), sizeof(ktx::Header))); + assert(ktxBuffer->_images.size() == images.size()); + auto start = ktxBuffer->_storage->data(); + for (size_t i = 0; i < images.size(); ++i) { + auto expected = images[i]; + auto actual = ktxBuffer->_images[i]; + assert(expected._padding == actual._padding); + assert(expected._numFaces == actual._numFaces); + assert(expected._imageSize == actual._imageSize); + assert(expected._faceSize == actual._faceSize); + assert(actual._faceBytes.size() == actual._numFaces); + for (uint32_t face = 0; face < expected._numFaces; ++face) { + auto expectedFace = expected._faceBytes[face]; + auto actualFace = actual._faceBytes[face]; + auto offset = actualFace - start; + assert(offset % 4 == 0); + assert(expectedFace != actualFace); + assert(0 == memcmp(expectedFace, actualFace, expected._faceSize)); + } + } return ktxBuffer; } diff --git a/libraries/ktx/src/ktx/KTX.cpp b/libraries/ktx/src/ktx/KTX.cpp index 8635fbb684..ea7653e1ed 100644 --- a/libraries/ktx/src/ktx/KTX.cpp +++ b/libraries/ktx/src/ktx/KTX.cpp @@ -86,11 +86,10 @@ void KTX::resetStorage(Storage* storage) { } const Header* KTX::getHeader() const { - if (_storage) { - return reinterpret_cast (_storage->_bytes); - } else { + if (!_storage) { return nullptr; - } + } + return reinterpret_cast(_storage->data()); } @@ -105,7 +104,7 @@ size_t KTX::getKeyValueDataSize() const { size_t KTX::getTexelsDataSize() const { if (_storage) { //return _storage->size() - (sizeof(Header) + getKeyValueDataSize()); - return (_storage->_bytes + _storage->_size) - getTexelsData(); + return (_storage->data() + _storage->size()) - getTexelsData(); } else { return 0; } @@ -113,7 +112,7 @@ size_t KTX::getTexelsDataSize() const { const Byte* KTX::getKeyValueData() const { if (_storage) { - return (_storage->_bytes + sizeof(Header)); + return (_storage->data() + sizeof(Header)); } else { return nullptr; } @@ -121,16 +120,9 @@ const Byte* KTX::getKeyValueData() const { const Byte* KTX::getTexelsData() const { if (_storage) { - return (_storage->_bytes + sizeof(Header) + getKeyValueDataSize()); + return (_storage->data() + sizeof(Header) + getKeyValueDataSize()); } else { return nullptr; } } -Byte* KTX::getTexelsData() { - if (_storage) { - return (_storage->_bytes + sizeof(Header) + getKeyValueDataSize()); - } else { - return nullptr; - } -} diff --git a/libraries/ktx/src/ktx/KTX.h b/libraries/ktx/src/ktx/KTX.h index 94529d6e68..c1521a52b9 100644 --- a/libraries/ktx/src/ktx/KTX.h +++ b/libraries/ktx/src/ktx/KTX.h @@ -19,6 +19,8 @@ #include #include +#include + /* KTX Spec: Byte[12] identifier @@ -291,46 +293,8 @@ namespace ktx { NUM_CUBEMAPFACES = 6, }; - - // Chunk of data - struct Storage { - size_t _size {0}; - Byte* _bytes {nullptr}; - - Byte* data() { - return _bytes; - } - const Byte* data() const { - return _bytes; - } - size_t size() const { return _size; } - - ~Storage() { if (_bytes) { delete _bytes; } } - - Storage() {} - Storage(size_t size) : - _size(size) - { - if (_size) { _bytes = new Byte[_size]; } - } - - Storage(size_t size, Byte* bytes) : - _size(size) - { - if (_size && _bytes) { _bytes = bytes; } - } - Storage(size_t size, const Byte* bytes) : - Storage(size) - { - if (_size && _bytes && bytes) { - memcpy(_bytes, bytes, size); - } - } - Storage(const Storage& src) : - Storage(src.size(), src.data()) - {} - - }; + using Storage = storage::Storage; + using StoragePointer = std::unique_ptr; // Header struct Header { @@ -382,7 +346,8 @@ namespace ktx { void setUncompressed(GLType type, uint32_t typeSize, GLFormat format, GLInternalFormat_Uncompressed internalFormat, GLBaseInternalFormat baseInternalFormat) { glType = (uint32_t) type; - glTypeSize = 0; + // FIXME this should correspond to the size of glType + glTypeSize = 1; glFormat = (uint32_t) format; glInternalFormat = (uint32_t) internalFormat; glBaseInternalFormat = (uint32_t) baseInternalFormat; @@ -456,13 +421,9 @@ namespace ktx { }; using Images = std::vector; - class KTX { void resetStorage(Storage* src); - void resetHeader(const Header& header); - void resetImages(const Images& images); - KTX(); public: @@ -498,12 +459,12 @@ namespace ktx { const Header* getHeader() const; const Byte* getKeyValueData() const; const Byte* getTexelsData() const; - Byte* getTexelsData(); + const StoragePointer& getStorage() const { return _storage; } size_t getKeyValueDataSize() const; size_t getTexelsDataSize() const; - std::unique_ptr _storage; + StoragePointer _storage; KeyValues _keyValues; Images _images; }; diff --git a/libraries/ktx/src/ktx/Writer.cpp b/libraries/ktx/src/ktx/Writer.cpp index 1839624038..7fcf4c60d2 100644 --- a/libraries/ktx/src/ktx/Writer.cpp +++ b/libraries/ktx/src/ktx/Writer.cpp @@ -26,40 +26,15 @@ namespace ktx { const std::string _explanation; }; - - void KTX::resetHeader(const Header& header) { - if (!_storage) { - return; - } - memcpy(_storage->_bytes, &header, sizeof(Header)); - } - void KTX::resetImages(const Images& srcImages) { - auto imagesDataPtr = getTexelsData(); - if (!imagesDataPtr) { - return; - } - auto allocatedImagesDataSize = getTexelsDataSize(); - - // Just copy in our storage - _images = writeImages(imagesDataPtr, allocatedImagesDataSize, srcImages); - } - std::unique_ptr KTX::create(const Header& header, const Images& images, const KeyValues& keyValues) { - auto storageSize = evalStorageSize(header, images, keyValues); - - std::unique_ptr result(new KTX()); - - result->resetStorage(new Storage(storageSize)); - - result->resetHeader(header); - - // read metadata - result->_keyValues = keyValues; - - // populate image table - result->resetImages(images); - - return result; + std::unique_ptr storagePointer; + { + auto storageSize = ktx::KTX::evalStorageSize(header, images); + auto memoryStorage = new storage::MemoryStorage(storageSize); + ktx::KTX::write(memoryStorage->data(), memoryStorage->size(), header, images); + storagePointer.reset(memoryStorage); + } + return create(storagePointer); } size_t KTX::evalStorageSize(const Header& header, const Images& images, const KeyValues& keyValues) { @@ -117,8 +92,6 @@ namespace ktx { size_t currentDataSize = 0; auto currentPtr = imagesDataPtr; - - for (uint32_t l = 0; l < srcImages.size(); l++) { if (currentDataSize + sizeof(uint32_t) < allocatedImagesDataSize) { size_t imageSize = srcImages[l]._imageSize; diff --git a/scripts/developer/tests/.gitignore b/scripts/developer/tests/.gitignore new file mode 100644 index 0000000000..7cacbf042c --- /dev/null +++ b/scripts/developer/tests/.gitignore @@ -0,0 +1 @@ +cube_texture.ktx \ No newline at end of file diff --git a/tests/ktx/CMakeLists.txt b/tests/ktx/CMakeLists.txt new file mode 100644 index 0000000000..d72379efd6 --- /dev/null +++ b/tests/ktx/CMakeLists.txt @@ -0,0 +1,15 @@ + +set(TARGET_NAME ktx-test) + +if (WIN32) + SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /ignore:4049 /ignore:4217") +endif() + +# This is not a testcase -- just set it up as a regular hifi project +setup_hifi_project(Quick Gui OpenGL) +set_target_properties(${TARGET_NAME} PROPERTIES FOLDER "Tests/manual-tests/") + +# link in the shared libraries +link_hifi_libraries(shared octree ktx gl gpu gpu-gl render model model-networking networking render-utils fbx entities entities-renderer animation audio avatars script-engine physics) + +package_libraries_for_deployment() diff --git a/tests/ktx/src/main.cpp b/tests/ktx/src/main.cpp new file mode 100644 index 0000000000..a3c3b99960 --- /dev/null +++ b/tests/ktx/src/main.cpp @@ -0,0 +1,117 @@ +// +// Created by Bradley Austin Davis on 2016/07/01 +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + + +QSharedPointer logger; + +gpu::Texture* cacheTexture(const std::string& name, gpu::Texture* srcTexture, bool write = true, bool read = true); + + +void messageHandler(QtMsgType type, const QMessageLogContext& context, const QString& message) { + QString logMessage = LogHandler::getInstance().printMessage((LogMsgType)type, context, message); + + if (!logMessage.isEmpty()) { +#ifdef Q_OS_WIN + OutputDebugStringA(logMessage.toLocal8Bit().constData()); + OutputDebugStringA("\n"); +#endif + logger->addMessage(qPrintable(logMessage + "\n")); + } +} + +const char * LOG_FILTER_RULES = R"V0G0N( +hifi.gpu=true +)V0G0N"; + +QString getRootPath() { + static std::once_flag once; + static QString result; + std::call_once(once, [&] { + QFileInfo file(__FILE__); + QDir parent = file.absolutePath(); + result = QDir::cleanPath(parent.currentPath() + "/../../.."); + }); + return result; +} + +const QString TEST_IMAGE = getRootPath() + "/scripts/developer/tests/cube_texture.png"; +const QString TEST_IMAGE_KTX = getRootPath() + "/scripts/developer/tests/cube_texture.ktx"; + +int main(int argc, char** argv) { + QApplication app(argc, argv); + QCoreApplication::setApplicationName("KTX"); + QCoreApplication::setOrganizationName("High Fidelity"); + QCoreApplication::setOrganizationDomain("highfidelity.com"); + logger.reset(new FileLogger()); + + DependencyManager::set(); + qInstallMessageHandler(messageHandler); + QLoggingCategory::setFilterRules(LOG_FILTER_RULES); + + QImage image(TEST_IMAGE); + gpu::Texture* testTexture = model::TextureUsage::process2DTextureColorFromImage(image, TEST_IMAGE.toStdString(), true, false, true); + + auto ktxPtr = gpu::Texture::serialize(*testTexture); + const auto& ktxStorage = ktxPtr->getStorage(); + auto header = ktxPtr->getHeader(); + assert(sizeof(ktx::Header) == 12 + (sizeof(uint32_t) * 13)); + QFile outFile(TEST_IMAGE_KTX); + if (!outFile.open(QFile::Truncate | QFile::ReadWrite)) { + throw std::runtime_error("Unable to open file"); + } + //auto ktxSize = sizeof(ktx::Header); // ktxStorage->size() + auto ktxSize = ktxStorage->size(); + outFile.resize(ktxSize); + auto dest = outFile.map(0, ktxSize); + memcpy(dest, ktxStorage->data(), ktxSize); + outFile.unmap(dest); + outFile.close(); +// gpu::Texture* ktxTexture = cacheTexture(TEST_IMAGE.toStdString(), testTexture, true, true); + return 0; +} + +#include "main.moc" + From 61ba2dd11e303045cea9b29fca2ad542d5e17209 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Wed, 22 Feb 2017 13:24:54 -0800 Subject: [PATCH 049/302] Disable texture cache processing for now --- libraries/model/src/model/TextureMap.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/libraries/model/src/model/TextureMap.cpp b/libraries/model/src/model/TextureMap.cpp index 760cd98898..dcc685ca22 100755 --- a/libraries/model/src/model/TextureMap.cpp +++ b/libraries/model/src/model/TextureMap.cpp @@ -91,6 +91,7 @@ gpu::Texture* cacheTexture(const std::string& name, gpu::Texture* srcTexture, bo } gpu::Texture* returnedTexture = srcTexture; +#if 0 auto theKTX = Texture::serialize(*srcTexture); if (theKTX) { // Prepare cache directory @@ -147,6 +148,7 @@ gpu::Texture* cacheTexture(const std::string& name, gpu::Texture* srcTexture, bo } } } +#endif return returnedTexture; } From 9ca4926c1d40520bd1a59f414ade94000973092c Mon Sep 17 00:00:00 2001 From: samcake Date: Wed, 22 Feb 2017 13:43:23 -0800 Subject: [PATCH 050/302] Fixing the bad assignment for the srgb textures --- libraries/gpu/src/gpu/Texture_ktx.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libraries/gpu/src/gpu/Texture_ktx.cpp b/libraries/gpu/src/gpu/Texture_ktx.cpp index f1d91d39ab..e2da959a1a 100644 --- a/libraries/gpu/src/gpu/Texture_ktx.cpp +++ b/libraries/gpu/src/gpu/Texture_ktx.cpp @@ -106,10 +106,11 @@ Texture* Texture::unserialize(Usage usage, TextureUsageType usageType, const ktx Format texelFormat = Format::COLOR_SRGBA_32; if (header.getGLFormat() == ktx::GLFormat::BGRA && header.getGLType() == ktx::GLType::UNSIGNED_BYTE && header.getTypeSize() == 4) { - mipFormat = Format::COLOR_BGRA_32; if (header.getGLInternaFormat_Uncompressed() == ktx::GLInternalFormat_Uncompressed::RGBA8) { + mipFormat = Format::COLOR_BGRA_32; texelFormat = Format::COLOR_RGBA_32; } else if (header.getGLInternaFormat_Uncompressed() == ktx::GLInternalFormat_Uncompressed::SRGB8_ALPHA8) { + mipFormat = Format::COLOR_BGRA_32; texelFormat = Format::COLOR_SRGBA_32; } else { return nullptr; From c2831a513bde3aeda2eb6350fa9bd43c1980a3ae Mon Sep 17 00:00:00 2001 From: samcake Date: Wed, 22 Feb 2017 14:41:43 -0800 Subject: [PATCH 051/302] Adjusting the representation of the format in the KTX for sRGB, unfortunately not working with PicoPixel --- libraries/gpu/src/gpu/Texture_ktx.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/gpu/src/gpu/Texture_ktx.cpp b/libraries/gpu/src/gpu/Texture_ktx.cpp index e2da959a1a..d5e9122a4f 100644 --- a/libraries/gpu/src/gpu/Texture_ktx.cpp +++ b/libraries/gpu/src/gpu/Texture_ktx.cpp @@ -24,7 +24,7 @@ ktx::KTXUniquePointer Texture::serialize(const Texture& texture) { if (texelFormat == Format::COLOR_RGBA_32 && mipFormat == Format::COLOR_BGRA_32) { header.setUncompressed(ktx::GLType::UNSIGNED_BYTE, 4, ktx::GLFormat::BGRA, ktx::GLInternalFormat_Uncompressed::RGBA8, ktx::GLBaseInternalFormat::RGBA); - } else if (texelFormat == Format::COLOR_SRGBA_32 && mipFormat == Format::COLOR_BGRA_32) { + } else if (texelFormat == Format::COLOR_SRGBA_32 && mipFormat == Format::COLOR_SBGRA_32) { header.setUncompressed(ktx::GLType::UNSIGNED_BYTE, 4, ktx::GLFormat::BGRA, ktx::GLInternalFormat_Uncompressed::SRGB8_ALPHA8, ktx::GLBaseInternalFormat::RGBA); } else if (texelFormat == Format::COLOR_R_8 && mipFormat == Format::COLOR_R_8) { header.setUncompressed(ktx::GLType::UNSIGNED_BYTE, 1, ktx::GLFormat::RED, ktx::GLInternalFormat_Uncompressed::R8, ktx::GLBaseInternalFormat::RED); @@ -110,7 +110,7 @@ Texture* Texture::unserialize(Usage usage, TextureUsageType usageType, const ktx mipFormat = Format::COLOR_BGRA_32; texelFormat = Format::COLOR_RGBA_32; } else if (header.getGLInternaFormat_Uncompressed() == ktx::GLInternalFormat_Uncompressed::SRGB8_ALPHA8) { - mipFormat = Format::COLOR_BGRA_32; + mipFormat = Format::COLOR_SBGRA_32; texelFormat = Format::COLOR_SRGBA_32; } else { return nullptr; From 05efac9ddfc3262a6fe0c2ab562f4ab5b55235b4 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Wed, 22 Feb 2017 15:42:51 -0800 Subject: [PATCH 052/302] Add compile time toggle for threaded buffering --- libraries/gpu-gl/src/gpu/gl45/GL45Backend.h | 9 ++++++- .../gpu/gl45/GL45BackendVariableTexture.cpp | 26 ++++++++++++------- 2 files changed, 25 insertions(+), 10 deletions(-) diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h b/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h index 29e5a59ec5..28425433c4 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h +++ b/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h @@ -115,22 +115,29 @@ public: bool _bufferingCompleted { false }; VoidLambda _transferLambda; VoidLambda _bufferingLambda; - static ThreadPointer _bufferThread; +#if THREADED_TEXTURE_BUFFERING static Mutex _mutex; static VoidLambdaQueue _bufferLambdaQueue; + static ThreadPointer _bufferThread; static std::atomic _shutdownBufferingThread; static void bufferLoop(); +#endif public: TransferJob(const TransferJob& other) = delete; TransferJob(const GL45VariableAllocationTexture& parent, std::function transferLambda); TransferJob(const GL45VariableAllocationTexture& parent, uint16_t sourceMip, uint16_t targetMip, uint8_t face, uint32_t lines = 0, uint32_t lineOffset = 0); bool tryTransfer(); + +#if THREADED_TEXTURE_BUFFERING static void startTransferLoop(); static void stopTransferLoop(); +#endif private: +#if THREADED_TEXTURE_BUFFERING void startBuffering(); +#endif void transfer(); }; diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp b/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp index 92cc945a86..62f1a3c248 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp @@ -52,6 +52,7 @@ using TransferJob = GL45VariableAllocationTexture::TransferJob; static const uvec3 MAX_TRANSFER_DIMENSIONS { 1024, 1024, 1 }; static const size_t MAX_TRANSFER_SIZE = MAX_TRANSFER_DIMENSIONS.x * MAX_TRANSFER_DIMENSIONS.y * 4; +#if THREADED_TEXTURE_BUFFERING std::shared_ptr TransferJob::_bufferThread { nullptr }; std::atomic TransferJob::_shutdownBufferingThread { false }; Mutex TransferJob::_mutex; @@ -76,6 +77,7 @@ void TransferJob::stopTransferLoop() { _bufferThread.reset(); _shutdownBufferingThread = false; } +#endif TransferJob::TransferJob(const GL45VariableAllocationTexture& parent, uint16_t sourceMip, uint16_t targetMip, uint8_t face, uint32_t lines, uint32_t lineOffset) : _parent(parent) { @@ -123,14 +125,7 @@ TransferJob::TransferJob(const GL45VariableAllocationTexture& parent, std::funct bool TransferJob::tryTransfer() { // Disable threaded texture transfer for now -#if 1 - if (!_bufferingCompleted) { - _bufferingLambda(); - _bufferingCompleted = true; - } - _transferLambda(); - return true; -#else +#if THREADED_TEXTURE_BUFFERING // Are we ready to transfer if (_bufferingCompleted) { _transferLambda(); @@ -139,9 +134,18 @@ bool TransferJob::tryTransfer() { startBuffering(); return false; +#else + if (!_bufferingCompleted) { + _bufferingLambda(); + _bufferingCompleted = true; + } + _transferLambda(); + return true; #endif } +#if THREADED_TEXTURE_BUFFERING + void TransferJob::startBuffering() { if (_bufferingStarted) { return; @@ -172,6 +176,7 @@ void TransferJob::bufferLoop() { } } } +#endif void GL45VariableAllocationTexture::addMemoryManagedTexture(const TexturePointer& texturePointer) { @@ -318,6 +323,7 @@ void GL45VariableAllocationTexture::updateMemoryPressure() { } if (newState != _memoryPressureState) { +#if THREADED_TEXTURE_BUFFERING if (MemoryPressureState::Transfer == _memoryPressureState) { TransferJob::stopTransferLoop(); } @@ -325,7 +331,9 @@ void GL45VariableAllocationTexture::updateMemoryPressure() { if (MemoryPressureState::Transfer == _memoryPressureState) { TransferJob::startTransferLoop(); } - +#else + _memoryPressureState = newState; +#endif // Clear the existing queue _transferQueue = WorkQueue(); _promoteQueue = WorkQueue(); From 62422690d05e037a7ea7680db3563755a68bfad1 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Wed, 22 Feb 2017 15:44:06 -0800 Subject: [PATCH 053/302] More KTX testing, functionality --- libraries/gpu/src/gpu/Texture.cpp | 33 +++++++++++++--- libraries/gpu/src/gpu/Texture.h | 12 ++++-- libraries/ktx/src/ktx/KTX.cpp | 12 ++++++ libraries/ktx/src/ktx/KTX.h | 1 + tests/ktx/src/main.cpp | 65 ++++++++++++++++++++++++------- 5 files changed, 98 insertions(+), 25 deletions(-) diff --git a/libraries/gpu/src/gpu/Texture.cpp b/libraries/gpu/src/gpu/Texture.cpp index aa81b34914..4b2156ec79 100755 --- a/libraries/gpu/src/gpu/Texture.cpp +++ b/libraries/gpu/src/gpu/Texture.cpp @@ -19,6 +19,7 @@ #include #include +#include #include #include "GPULogging.h" @@ -120,19 +121,24 @@ void Texture::setAllowedGPUMemoryUsage(Size size) { uint8 Texture::NUM_FACES_PER_TYPE[NUM_TYPES] = { 1, 1, 1, 6 }; -void Texture::Storage::assignTexture(Texture* texture) { +using Storage = Texture::Storage; +using PixelsPointer = Texture::PixelsPointer; +using MemoryStorage = Texture::MemoryStorage; +using KtxStorage = Texture::KtxStorage; + +void Storage::assignTexture(Texture* texture) { _texture = texture; if (_texture) { _type = _texture->getType(); } } -void Texture::MemoryStorage::reset() { +void MemoryStorage::reset() { _mips.clear(); bumpStamp(); } -const Texture::PixelsPointer Texture::MemoryStorage::getMipFace(uint16 level, uint8 face) const { +PixelsPointer MemoryStorage::getMipFace(uint16 level, uint8 face) const { if (level < _mips.size()) { assert(face < _mips[level].size()); return _mips[level][face]; @@ -140,12 +146,12 @@ const Texture::PixelsPointer Texture::MemoryStorage::getMipFace(uint16 level, ui return PixelsPointer(); } -bool Texture::MemoryStorage::isMipAvailable(uint16 level, uint8 face) const { +bool MemoryStorage::isMipAvailable(uint16 level, uint8 face) const { PixelsPointer mipFace = getMipFace(level, face); return (mipFace && mipFace->getSize()); } -bool Texture::MemoryStorage::allocateMip(uint16 level) { +bool MemoryStorage::allocateMip(uint16 level) { bool changed = false; if (level >= _mips.size()) { _mips.resize(level+1, std::vector(Texture::NUM_FACES_PER_TYPE[getType()])); @@ -164,7 +170,7 @@ bool Texture::MemoryStorage::allocateMip(uint16 level) { return changed; } -void Texture::MemoryStorage::assignMipData(uint16 level, const storage::StoragePointer& storagePointer) { +void MemoryStorage::assignMipData(uint16 level, const storage::StoragePointer& storagePointer) { allocateMip(level); auto& mip = _mips[level]; @@ -193,6 +199,13 @@ void Texture::MemoryStorage::assignMipFaceData(uint16 level, uint8 face, const s } } +KtxStorage::KtxStorage(ktx::KTXUniquePointer& ktxData) : _ktxData(ktxData.release()) { +} + +PixelsPointer KtxStorage::getMipFace(uint16 level, uint8 face) const { + return _ktxData->getMipFaceTexelsData(level, face); +} + Texture* Texture::createExternal(const ExternalRecycler& recycler, const Sampler& sampler) { Texture* tex = new Texture(TextureUsageType::EXTERNAL); tex->_type = TEX_2D; @@ -963,3 +976,11 @@ Texture::ExternalUpdates Texture::getUpdates() const { return result; } +void Texture::setStorage(std::unique_ptr& newStorage) { + _storage.swap(newStorage); +} + +void Texture::setKtxBacking(ktx::KTXUniquePointer& ktxBacking) { + auto newBacking = std::unique_ptr(new KtxStorage(ktxBacking)); + setStorage(newBacking); +} \ No newline at end of file diff --git a/libraries/gpu/src/gpu/Texture.h b/libraries/gpu/src/gpu/Texture.h index a8fd9e3b0d..637b098504 100755 --- a/libraries/gpu/src/gpu/Texture.h +++ b/libraries/gpu/src/gpu/Texture.h @@ -256,7 +256,7 @@ public: virtual ~Storage() {} virtual void reset() = 0; - virtual const PixelsPointer getMipFace(uint16 level, uint8 face = 0) const = 0; + virtual PixelsPointer getMipFace(uint16 level, uint8 face = 0) const = 0; virtual void assignMipData(uint16 level, const storage::StoragePointer& storage) = 0; virtual void assignMipFaceData(uint16 level, uint8 face, const storage::StoragePointer& storage) = 0; virtual bool isMipAvailable(uint16 level, uint8 face = 0) const = 0; @@ -281,7 +281,7 @@ public: class MemoryStorage : public Storage { public: void reset() override; - const PixelsPointer getMipFace(uint16 level, uint8 face = 0) const override; + PixelsPointer getMipFace(uint16 level, uint8 face = 0) const override; void assignMipData(uint16 level, const storage::StoragePointer& storage) override; void assignMipFaceData(uint16 level, uint8 face, const storage::StoragePointer& storage) override; bool isMipAvailable(uint16 level, uint8 face = 0) const override; @@ -294,8 +294,9 @@ public: class KtxStorage : public Storage { public: KtxStorage(ktx::KTXUniquePointer& ktxData); - const PixelsPointer getMipFace(uint16 level, uint8 face = 0) const override; - bool isMipAvailable(uint16 level, uint8 face = 0) const override; + PixelsPointer getMipFace(uint16 level, uint8 face = 0) const override; + // By convention, all mip levels and faces MUST be populated when using KTX backing + bool isMipAvailable(uint16 level, uint8 face = 0) const override { return true; } void assignMipData(uint16 level, const storage::StoragePointer& storage) override { throw std::runtime_error("Invalid call"); @@ -474,6 +475,9 @@ public: bool isStoredMipFaceAvailable(uint16 level, uint8 face = 0) const { return _storage->isMipAvailable(level, face); } const PixelsPointer accessStoredMipFace(uint16 level, uint8 face = 0) const { return _storage->getMipFace(level, face); } + void setStorage(std::unique_ptr& newStorage); + void setKtxBacking(ktx::KTXUniquePointer& newBacking); + // access sizes for the stored mips uint16 getStoredMipWidth(uint16 level) const; uint16 getStoredMipHeight(uint16 level) const; diff --git a/libraries/ktx/src/ktx/KTX.cpp b/libraries/ktx/src/ktx/KTX.cpp index ea7653e1ed..e30bde64d6 100644 --- a/libraries/ktx/src/ktx/KTX.cpp +++ b/libraries/ktx/src/ktx/KTX.cpp @@ -126,3 +126,15 @@ const Byte* KTX::getTexelsData() const { } } +storage::StoragePointer KTX::getMipFaceTexelsData(uint16_t mip, uint8_t face) const { + storage::StoragePointer result; + if (mip < _images.size()) { + const auto& faces = _images[mip]; + if (face < faces._numFaces) { + auto faceOffset = faces._faceBytes[face] - _storage->data(); + auto faceSize = faces._faceSize; + result = _storage->createView(faceSize, faceOffset); + } + } + return result; +} diff --git a/libraries/ktx/src/ktx/KTX.h b/libraries/ktx/src/ktx/KTX.h index c1521a52b9..783f7e428c 100644 --- a/libraries/ktx/src/ktx/KTX.h +++ b/libraries/ktx/src/ktx/KTX.h @@ -459,6 +459,7 @@ namespace ktx { const Header* getHeader() const; const Byte* getKeyValueData() const; const Byte* getTexelsData() const; + storage::StoragePointer getMipFaceTexelsData(uint16_t mip = 0, uint8_t face = 0) const; const StoragePointer& getStorage() const { return _storage; } size_t getKeyValueDataSize() const; diff --git a/tests/ktx/src/main.cpp b/tests/ktx/src/main.cpp index a3c3b99960..2dbf2f60d7 100644 --- a/tests/ktx/src/main.cpp +++ b/tests/ktx/src/main.cpp @@ -87,6 +87,8 @@ int main(int argc, char** argv) { QCoreApplication::setOrganizationDomain("highfidelity.com"); logger.reset(new FileLogger()); + Q_ASSERT(sizeof(ktx::Header) == 12 + (sizeof(uint32_t) * 13)); + DependencyManager::set(); qInstallMessageHandler(messageHandler); QLoggingCategory::setFilterRules(LOG_FILTER_RULES); @@ -94,22 +96,55 @@ int main(int argc, char** argv) { QImage image(TEST_IMAGE); gpu::Texture* testTexture = model::TextureUsage::process2DTextureColorFromImage(image, TEST_IMAGE.toStdString(), true, false, true); - auto ktxPtr = gpu::Texture::serialize(*testTexture); - const auto& ktxStorage = ktxPtr->getStorage(); - auto header = ktxPtr->getHeader(); - assert(sizeof(ktx::Header) == 12 + (sizeof(uint32_t) * 13)); - QFile outFile(TEST_IMAGE_KTX); - if (!outFile.open(QFile::Truncate | QFile::ReadWrite)) { - throw std::runtime_error("Unable to open file"); + auto ktxMemory = gpu::Texture::serialize(*testTexture); + { + const auto& ktxStorage = ktxMemory->getStorage(); + auto header = ktxMemory->getHeader(); + QFile outFile(TEST_IMAGE_KTX); + if (!outFile.open(QFile::Truncate | QFile::ReadWrite)) { + throw std::runtime_error("Unable to open file"); + } + //auto ktxSize = sizeof(ktx::Header); // ktxStorage->size() + auto ktxSize = ktxStorage->size(); + outFile.resize(ktxSize); + auto dest = outFile.map(0, ktxSize); + memcpy(dest, ktxStorage->data(), ktxSize); + outFile.unmap(dest); + outFile.close(); } - //auto ktxSize = sizeof(ktx::Header); // ktxStorage->size() - auto ktxSize = ktxStorage->size(); - outFile.resize(ktxSize); - auto dest = outFile.map(0, ktxSize); - memcpy(dest, ktxStorage->data(), ktxSize); - outFile.unmap(dest); - outFile.close(); -// gpu::Texture* ktxTexture = cacheTexture(TEST_IMAGE.toStdString(), testTexture, true, true); + + auto ktxFile = ktx::KTX::create(std::unique_ptr(new storage::FileStorage(TEST_IMAGE_KTX))); + { + const auto& memStorage = ktxMemory->getStorage(); + const auto& fileStorage = ktxFile->getStorage(); + Q_ASSERT(memStorage->size() == fileStorage->size()); + Q_ASSERT(memStorage->data() != fileStorage->data()); + Q_ASSERT(0 == memcmp(memStorage->data(), fileStorage->data(), memStorage->size())); + Q_ASSERT(ktxFile->_images.size() == ktxMemory->_images.size()); + auto imageCount = ktxFile->_images.size(); + auto startMemory = ktxMemory->_storage->data(); + auto startFile = ktxFile->_storage->data(); + for (size_t i = 0; i < imageCount; ++i) { + auto memImages = ktxMemory->_images[i]; + auto fileImages = ktxFile->_images[i]; + Q_ASSERT(memImages._padding == fileImages._padding); + Q_ASSERT(memImages._numFaces == fileImages._numFaces); + Q_ASSERT(memImages._imageSize == fileImages._imageSize); + Q_ASSERT(memImages._faceSize == fileImages._faceSize); + Q_ASSERT(memImages._faceBytes.size() == memImages._numFaces); + Q_ASSERT(fileImages._faceBytes.size() == fileImages._numFaces); + auto faceCount = fileImages._numFaces; + for (uint32_t face = 0; face < faceCount; ++face) { + auto memFace = memImages._faceBytes[face]; + auto memOffset = memFace - startMemory; + auto fileFace = fileImages._faceBytes[face]; + auto fileOffset = fileFace - startFile; + Q_ASSERT(memOffset % 4 == 0); + Q_ASSERT(memOffset == fileOffset); + } + } + } + testTexture->setKtxBacking(ktxFile); return 0; } From 81cee57bb1bfe5a77be4edf371843fd50009eac7 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Wed, 22 Feb 2017 15:44:27 -0800 Subject: [PATCH 054/302] Add gamma-aware scaling test image --- scripts/developer/tests/scaling.png | Bin 0 -> 3172 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 scripts/developer/tests/scaling.png diff --git a/scripts/developer/tests/scaling.png b/scripts/developer/tests/scaling.png new file mode 100644 index 0000000000000000000000000000000000000000..1e6a7df45d8440cc52d3b45501b909419fed2f1b GIT binary patch literal 3172 zcmd5;c~p~E7XQ63A%rEG0zw6aOe>MPK~IBJisaL=qAd_A)CCaGwo@pwR8gZC_|%SD z7o4J$st8ugBC@Cz1PuvBYq45TM5xH3-~b|o9YRPlCwSUFX3m*2?O*fPeed4;yT5ne zyYHTRFu>o3XKrr}fVXnRvQ+>DfPl*ZaO3bV9|M+iS1wx;Bqh%uyiNeFQgFJcjPsAZ zQ+zK$=}JukxPSm)@JBWj{=Z@I&oh>M-7es>42Jw255u%;9b7_Ar+anY4|u#h;LQ2T zXWDU%Msp|eqeX1oR5;Ed z%2}=L#||eS>yaPipfV=$G*|spnb+iuN)}3+#M!v_fuM66=g`gr46y9i{f9JUb}sR=GCq% zR(P3YRJ{JO!mW$0%9Jz@eYk?q7U{t?rcyCMhi z6{Z`TtYfCa`{>n@WsR%lQ;+N=tEm?r;P=t9QTaV#{6N zh1Pmom+C}u<3gMv&9tVn=P0E-_pG{G3+9@Qtto#hLS%otuAK(%ys9nwp}|Zmc#fx{ z@4e;VgALMJ*0VC@D40W;6Y8yVt(VamH@OrP?uF{bEryGZ>yr$b7q$3XH8k5~RT|1?GJ0JWv*9}?nbf>SGfY8XT`;`MnhRsYzrz*~= z*A&XdI*PFZ)w71p)L&UCq1xqkU@kKXfq;M)EJ!w0C{PvwOE%?t%W$GO* zQv5VVq%*X-k;SH=>(8gm)6xC*3s9bQ+sx>5RT7 zd4@9yS6et(OIvWcdDMMG9_j>>&9K$pDm%-Ru&KU$#B5$#Qtn?aEa-mH*Htd;G0_(7mw3bheDNh9_>{ z^Qy-bHmNZfzG!*8rbotKzSn=mCMS0TmwUqEoo9E0*ZsPG0VuUUW9$J8a9P8m5Z~r{ znNSEYOmn9J|Bl24<@*GEGdIz|qp>9b*Z}=!1OfPyUG2Y#)@lD4`*&c4Ogob4Bu+Xq z_pKif4jc5(+ssD9x+{L8cDah|fwsBUq1F8w&M`yF?qNW+?YVekiy}GmSVryVJ^eS| zh;`M+4z^hYRd^Q_9@K?wTb$A{7Xri|9nTYI6X~FIpsJ+#E>F2LwJt$rXE|;LH{VE| z;Xo7yWI3rZm-dT5(ROTHRc9U&zROcKs$;mhfnn zo9B75R?*7_8w>h>xgX>%Gl`By(!f8X>z@^CVZb)o~%L;_f%|NAB(nv;`jxp6AniR}RWt zOD#%0&=$Nt`J&_#-1=(EQqWKi8Lkb8!WuZfDcvVI0&)GgVqLa^bHM_2#0)@K3 zdjq1dUpco+wDwFy&qUnnvkH^scz73k? z*0^wSGR<<^RCVlhggmC)f!R|PKFR46xws_6p1H3d{JG8pRmZZE`8aUG%TL(o%%i$p34%Ee%*y;?&!bVsIIHVCU`&x67@V=oz80bPkRU#~ z>gCI6fJ#&z?)_yH7DU1RsqTCaah zxRduvIgME=l>hJbq$|g`^ed--g)kOKgQTUv^iBC-opi?Po#V+ zm|^$~;%#`!C&53@XE@4QRL7AQRrS7~vVf;t!Q#)z7t-jQjVzM8iRz8TkG3?+?X(#m z<7SA&gQxS2ubgGVr}+3Khj9P?+aBJ>0aLF{GTn+ZpK29FWaXm}S4>*R@TAF$j? zP`PuvJC1>5odjZPH}|b$E>yXWsgLIvw%Rj3?!mY;P72a{OGk~S zzi8qDb@f4?vTADBHjg5jCSF2k=JgQ|XpW2w2Br6wKArRmOB~8C&>lM*hdIYVKp2Y| z6JeT!ai>eA(2mL#^M^8v9P}5>P@G3x7M0vnt4+sq z^pA=!`D%4M<-@eFMq`Z_C+h!Q7-w(pixK<}i+|%%4w63`O{v~IOI3Pm{_;5hu<~vH KWra&4_WTP0c9{JD literal 0 HcmV?d00001 From 055db531b1b1dad9413bce4bfc318aa3ce69e720 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Wed, 22 Feb 2017 15:59:05 -0800 Subject: [PATCH 055/302] Update 'caching' of textures to KTX file backing --- libraries/model/src/model/TextureMap.cpp | 82 ++++++++++-------------- 1 file changed, 35 insertions(+), 47 deletions(-) diff --git a/libraries/model/src/model/TextureMap.cpp b/libraries/model/src/model/TextureMap.cpp index dcc685ca22..81d768e91d 100755 --- a/libraries/model/src/model/TextureMap.cpp +++ b/libraries/model/src/model/TextureMap.cpp @@ -89,66 +89,54 @@ gpu::Texture* cacheTexture(const std::string& name, gpu::Texture* srcTexture, bo if (!srcTexture) { return nullptr; } - gpu::Texture* returnedTexture = srcTexture; -#if 0 - auto theKTX = Texture::serialize(*srcTexture); - if (theKTX) { + static QString ktxCacheFolder; + static std::once_flag once; + std::call_once(once, [&] { // Prepare cache directory - QString path("hifi_ktx/"); - QFileInfo originalFileInfo(path); + static const QString HIFI_KTX_FOLDER("hifi_ktx"); QString docsLocation = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation); - path = docsLocation + "/" + path; - QFileInfo info(path); - if (!info.absoluteDir().exists()) { - QString originalRelativePath = originalFileInfo.path(); - QDir(docsLocation).mkpath(originalRelativePath); + ktxCacheFolder = docsLocation + "/" + HIFI_KTX_FOLDER; + QFileInfo info(ktxCacheFolder); + if (!info.exists()) { + QDir(docsLocation).mkpath(HIFI_KTX_FOLDER); } + }); - std::string cleanedName = name; - cleanedName = cleanedName.substr(cleanedName.find_last_of('//') + 1); + std::string cleanedName = name; + cleanedName = cleanedName.substr(cleanedName.find_last_of('//') + 1); + std::string cacheFilename(ktxCacheFolder.toStdString()); + cacheFilename += cleanedName; + cacheFilename += ".ktx"; - std::string filename(path.toStdString()); - filename += cleanedName; - filename += ".ktx"; - - if (write) { - /* FILE *file = fopen(name.c_str(), "r"); - if (file != nullptr) { - fclose(file); - } else*/ { - FILE *file = fopen (filename.c_str(),"wb"); - if (file != nullptr) { - fwrite(theKTX->_storage->data(), 1, theKTX->_storage->size(), file); - fclose (file); + gpu::Texture* returnedTexture = srcTexture; + { + if (write && !QFileInfo(cacheFilename.c_str()).exists()) { + auto ktxMemory = gpu::Texture::serialize(*srcTexture); + if (ktxMemory) { + const auto& ktxStorage = ktxMemory->getStorage(); + auto header = ktxMemory->getHeader(); + QFile outFile(cacheFilename.c_str()); + if (!outFile.open(QFile::Truncate | QFile::ReadWrite)) { + throw std::runtime_error("Unable to open file"); } + //auto ktxSize = sizeof(ktx::Header); // ktxStorage->size() + auto ktxSize = ktxStorage->size(); + outFile.resize(ktxSize); + auto dest = outFile.map(0, ktxSize); + memcpy(dest, ktxStorage->data(), ktxSize); + outFile.unmap(dest); + outFile.close(); } } - if (read) { - FILE* file = fopen (filename.c_str(),"rb"); - if (file != nullptr) { - // obtain file size: - fseek (file , 0 , SEEK_END); - auto size = ftell(file); - rewind(file); - - std::unique_ptr storage(new ktx::Storage(size)); - fread(storage->_bytes, 1, storage->_size, file); - fclose (file); - - //then create a new texture out of the ktx - auto theNewTexure = Texture::unserialize(srcTexture->getUsage(), srcTexture->getUsageType(), ktx::KTX::create(storage), srcTexture->getSampler()); - - if (theNewTexure) { - returnedTexture = theNewTexure; - delete srcTexture; - } - } + if (read && QFileInfo(cacheFilename.c_str()).exists()) { + auto ktxFile = ktx::KTX::create(std::unique_ptr(new storage::FileStorage(cacheFilename.c_str()))); + returnedTexture->setKtxBacking(ktxFile); } } -#endif + return returnedTexture; } From ad40e2d7d95a57385a517559b5de4442acf3aed6 Mon Sep 17 00:00:00 2001 From: samcake Date: Wed, 22 Feb 2017 17:16:37 -0800 Subject: [PATCH 056/302] Trying to address the ktx serialization problem with normal, still have a bug --- libraries/gpu/src/gpu/Texture_ktx.cpp | 14 ++++++++++++++ libraries/ktx/src/ktx/KTX.cpp | 4 ---- libraries/ktx/src/ktx/KTX.h | 1 - libraries/ktx/src/ktx/Reader.cpp | 4 ++-- libraries/ktx/src/ktx/Writer.cpp | 4 ++-- libraries/model/src/model/TextureMap.cpp | 15 ++++++++------- libraries/render-utils/src/MaterialTextures.slh | 2 +- 7 files changed, 27 insertions(+), 17 deletions(-) diff --git a/libraries/gpu/src/gpu/Texture_ktx.cpp b/libraries/gpu/src/gpu/Texture_ktx.cpp index d5e9122a4f..7512f9ddb7 100644 --- a/libraries/gpu/src/gpu/Texture_ktx.cpp +++ b/libraries/gpu/src/gpu/Texture_ktx.cpp @@ -24,8 +24,12 @@ ktx::KTXUniquePointer Texture::serialize(const Texture& texture) { if (texelFormat == Format::COLOR_RGBA_32 && mipFormat == Format::COLOR_BGRA_32) { header.setUncompressed(ktx::GLType::UNSIGNED_BYTE, 4, ktx::GLFormat::BGRA, ktx::GLInternalFormat_Uncompressed::RGBA8, ktx::GLBaseInternalFormat::RGBA); + } else if (texelFormat == Format::COLOR_RGBA_32 && mipFormat == Format::COLOR_RGBA_32) { + header.setUncompressed(ktx::GLType::UNSIGNED_BYTE, 4, ktx::GLFormat::RGBA, ktx::GLInternalFormat_Uncompressed::RGBA8, ktx::GLBaseInternalFormat::RGBA); } else if (texelFormat == Format::COLOR_SRGBA_32 && mipFormat == Format::COLOR_SBGRA_32) { header.setUncompressed(ktx::GLType::UNSIGNED_BYTE, 4, ktx::GLFormat::BGRA, ktx::GLInternalFormat_Uncompressed::SRGB8_ALPHA8, ktx::GLBaseInternalFormat::RGBA); + } else if (texelFormat == Format::COLOR_SRGBA_32 && mipFormat == Format::COLOR_SRGBA_32) { + header.setUncompressed(ktx::GLType::UNSIGNED_BYTE, 4, ktx::GLFormat::RGBA, ktx::GLInternalFormat_Uncompressed::SRGB8_ALPHA8, ktx::GLBaseInternalFormat::RGBA); } else if (texelFormat == Format::COLOR_R_8 && mipFormat == Format::COLOR_R_8) { header.setUncompressed(ktx::GLType::UNSIGNED_BYTE, 1, ktx::GLFormat::RED, ktx::GLInternalFormat_Uncompressed::R8, ktx::GLBaseInternalFormat::RED); } else { @@ -115,6 +119,16 @@ Texture* Texture::unserialize(Usage usage, TextureUsageType usageType, const ktx } else { return nullptr; } + } else if (header.getGLFormat() == ktx::GLFormat::RGBA && header.getGLType() == ktx::GLType::UNSIGNED_BYTE && header.getTypeSize() == 4) { + if (header.getGLInternaFormat_Uncompressed() == ktx::GLInternalFormat_Uncompressed::RGBA8) { + mipFormat = Format::COLOR_RGBA_32; + texelFormat = Format::COLOR_RGBA_32; + } else if (header.getGLInternaFormat_Uncompressed() == ktx::GLInternalFormat_Uncompressed::SRGB8_ALPHA8) { + mipFormat = Format::COLOR_SRGBA_32; + texelFormat = Format::COLOR_SRGBA_32; + } else { + return nullptr; + } } else if (header.getGLFormat() == ktx::GLFormat::RED && header.getGLType() == ktx::GLType::UNSIGNED_BYTE && header.getTypeSize() == 1) { mipFormat = Format::COLOR_R_8; if (header.getGLInternaFormat_Uncompressed() == ktx::GLInternalFormat_Uncompressed::R8) { diff --git a/libraries/ktx/src/ktx/KTX.cpp b/libraries/ktx/src/ktx/KTX.cpp index 8635fbb684..7613747e5c 100644 --- a/libraries/ktx/src/ktx/KTX.cpp +++ b/libraries/ktx/src/ktx/KTX.cpp @@ -34,10 +34,6 @@ uint32_t Header::evalMaxDimension() const { return std::max(getPixelWidth(), std::max(getPixelHeight(), getPixelDepth())); } -uint32_t Header::evalMaxLevel() const { - return 1 + log2(evalMaxDimension()); -} - uint32_t Header::evalPixelWidth(uint32_t level) const { return std::max(getPixelWidth() >> level, 1U); } diff --git a/libraries/ktx/src/ktx/KTX.h b/libraries/ktx/src/ktx/KTX.h index 94529d6e68..7dc1963090 100644 --- a/libraries/ktx/src/ktx/KTX.h +++ b/libraries/ktx/src/ktx/KTX.h @@ -370,7 +370,6 @@ namespace ktx { uint32_t getNumberOfLevels() const { return (numberOfMipmapLevels ? numberOfMipmapLevels : 1); } uint32_t evalMaxDimension() const; - uint32_t evalMaxLevel() const; uint32_t evalPixelWidth(uint32_t level) const; uint32_t evalPixelHeight(uint32_t level) const; uint32_t evalPixelDepth(uint32_t level) const; diff --git a/libraries/ktx/src/ktx/Reader.cpp b/libraries/ktx/src/ktx/Reader.cpp index e977f9ab4f..e9e0f2760c 100644 --- a/libraries/ktx/src/ktx/Reader.cpp +++ b/libraries/ktx/src/ktx/Reader.cpp @@ -149,10 +149,10 @@ namespace ktx { faces[face] = currentPtr; currentPtr += faceSize; } - images.emplace_back(Image(faceSize, padding, faces)); + images.emplace_back(Image((uint32_t) faceSize, padding, faces)); currentPtr += padding; } else { - images.emplace_back(Image(imageSize, padding, currentPtr)); + images.emplace_back(Image((uint32_t) imageSize, padding, currentPtr)); currentPtr += imageSize + padding; } } else { diff --git a/libraries/ktx/src/ktx/Writer.cpp b/libraries/ktx/src/ktx/Writer.cpp index 1839624038..89b1d975a2 100644 --- a/libraries/ktx/src/ktx/Writer.cpp +++ b/libraries/ktx/src/ktx/Writer.cpp @@ -122,7 +122,7 @@ namespace ktx { for (uint32_t l = 0; l < srcImages.size(); l++) { if (currentDataSize + sizeof(uint32_t) < allocatedImagesDataSize) { size_t imageSize = srcImages[l]._imageSize; - *(reinterpret_cast (currentPtr)) = imageSize; + *(reinterpret_cast (currentPtr)) = (uint32_t) imageSize; currentPtr += sizeof(uint32_t); currentDataSize += sizeof(uint32_t); @@ -133,7 +133,7 @@ namespace ktx { // Single face vs cubes if (srcImages[l]._numFaces == 1) { auto copied = memcpy(currentPtr, srcImages[l]._faceBytes[0], imageSize); - destImages.emplace_back(Image(imageSize, padding, currentPtr)); + destImages.emplace_back(Image((uint32_t) imageSize, padding, currentPtr)); currentPtr += imageSize; } else { Image::FaceBytes faceBytes(6); diff --git a/libraries/model/src/model/TextureMap.cpp b/libraries/model/src/model/TextureMap.cpp index 760cd98898..f3d6918453 100755 --- a/libraries/model/src/model/TextureMap.cpp +++ b/libraries/model/src/model/TextureMap.cpp @@ -106,7 +106,7 @@ gpu::Texture* cacheTexture(const std::string& name, gpu::Texture* srcTexture, bo std::string cleanedName = name; - cleanedName = cleanedName.substr(cleanedName.find_last_of('//') + 1); + cleanedName = cleanedName.substr(cleanedName.find_last_of((char) '//') + 1); std::string filename(path.toStdString()); filename += cleanedName; @@ -378,8 +378,8 @@ gpu::Texture* TextureUsage::createNormalTextureFromNormalImage(const QImage& src gpu::Texture* theTexture = nullptr; if ((image.width() > 0) && (image.height() > 0)) { - gpu::Element formatGPU = gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA); - gpu::Element formatMip = gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA); + gpu::Element formatMip = gpu::Element::COLOR_RGBA_32; + gpu::Element formatGPU = gpu::Element::COLOR_RGBA_32; theTexture = (gpu::Texture::create2D(formatGPU, image.width(), image.height(), gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR))); theTexture->setSource(srcImageName); @@ -388,7 +388,7 @@ gpu::Texture* TextureUsage::createNormalTextureFromNormalImage(const QImage& src generateMips(theTexture, image, true); theTexture->setSource(srcImageName); - theTexture = cacheTexture(theTexture->source(), theTexture); + theTexture = cacheTexture(theTexture->source(), theTexture, true, false); } return theTexture; @@ -468,8 +468,9 @@ gpu::Texture* TextureUsage::createNormalTextureFromBumpImage(const QImage& srcIm gpu::Texture* theTexture = nullptr; if ((image.width() > 0) && (image.height() > 0)) { - gpu::Element formatGPU = gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA); - gpu::Element formatMip = gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA); + + gpu::Element formatMip = gpu::Element::COLOR_RGBA_32; + gpu::Element formatGPU = gpu::Element::COLOR_RGBA_32; theTexture = (gpu::Texture::create2D(formatGPU, image.width(), image.height(), gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR))); theTexture->setSource(srcImageName); @@ -478,7 +479,7 @@ gpu::Texture* TextureUsage::createNormalTextureFromBumpImage(const QImage& srcIm generateMips(theTexture, image, true); theTexture->setSource(srcImageName); - theTexture = cacheTexture(theTexture->source(), theTexture); + theTexture = cacheTexture(theTexture->source(), theTexture, true, true); } return theTexture; diff --git a/libraries/render-utils/src/MaterialTextures.slh b/libraries/render-utils/src/MaterialTextures.slh index 6d2ad23c21..7b73896cc5 100644 --- a/libraries/render-utils/src/MaterialTextures.slh +++ b/libraries/render-utils/src/MaterialTextures.slh @@ -64,7 +64,7 @@ float fetchRoughnessMap(vec2 uv) { uniform sampler2D normalMap; vec3 fetchNormalMap(vec2 uv) { // unpack normal, swizzle to get into hifi tangent space with Y axis pointing out - return normalize(texture(normalMap, uv).xzy -vec3(0.5, 0.5, 0.5)); + return normalize(texture(normalMap, uv).rbg -vec3(0.5, 0.5, 0.5)); } <@endif@> From e8835b34f446d0b55eee2686566d67d002ce61d8 Mon Sep 17 00:00:00 2001 From: samcake Date: Wed, 22 Feb 2017 19:05:20 -0800 Subject: [PATCH 057/302] fix issues while testing the ktxStorage --- libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp | 8 ++++++-- libraries/ktx/src/ktx/KTX.h | 2 +- libraries/model/src/model/TextureMap.cpp | 1 + libraries/shared/src/shared/Storage.cpp | 3 ++- 4 files changed, 10 insertions(+), 4 deletions(-) diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp b/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp index 95837c16d9..f6c40259ea 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp @@ -142,8 +142,12 @@ void GL45Texture::copyMipFaceFromTexture(uint16_t sourceMip, uint16_t targetMip, } auto size = _gpuObject.evalMipDimensions(sourceMip); auto mipData = _gpuObject.accessStoredMipFace(sourceMip, face); - GLTexelFormat texelFormat = GLTexelFormat::evalGLTexelFormat(_gpuObject.getTexelFormat(), _gpuObject.getStoredMipFormat()); - copyMipFaceLinesFromTexture(targetMip, face, size, 0, texelFormat.format, texelFormat.type, mipData->readData()); + if (mipData) { + GLTexelFormat texelFormat = GLTexelFormat::evalGLTexelFormat(_gpuObject.getTexelFormat(), _gpuObject.getStoredMipFormat()); + copyMipFaceLinesFromTexture(targetMip, face, size, 0, texelFormat.format, texelFormat.type, mipData->readData()); + } else { + qCDebug(gpugllogging) << "Missing mipData level=" << sourceMip << " face=" << (int)face << " for texture " << _gpuObject.source().c_str(); + } } void GL45Texture::syncSampler() const { diff --git a/libraries/ktx/src/ktx/KTX.h b/libraries/ktx/src/ktx/KTX.h index 0ea8d4896d..0e0bb3831e 100644 --- a/libraries/ktx/src/ktx/KTX.h +++ b/libraries/ktx/src/ktx/KTX.h @@ -294,7 +294,7 @@ namespace ktx { }; using Storage = storage::Storage; - using StoragePointer = std::unique_ptr; + using StoragePointer = std::shared_ptr; // Header struct Header { diff --git a/libraries/model/src/model/TextureMap.cpp b/libraries/model/src/model/TextureMap.cpp index f0e8074aaa..43c8a8fbc2 100755 --- a/libraries/model/src/model/TextureMap.cpp +++ b/libraries/model/src/model/TextureMap.cpp @@ -106,6 +106,7 @@ gpu::Texture* cacheTexture(const std::string& name, gpu::Texture* srcTexture, bo std::string cleanedName = name; cleanedName = cleanedName.substr(cleanedName.find_last_of((char) '//') + 1); std::string cacheFilename(ktxCacheFolder.toStdString()); + cacheFilename += "/"; cacheFilename += cleanedName; cacheFilename += ".ktx"; diff --git a/libraries/shared/src/shared/Storage.cpp b/libraries/shared/src/shared/Storage.cpp index 3b83676bc8..7075d9c6f7 100644 --- a/libraries/shared/src/shared/Storage.cpp +++ b/libraries/shared/src/shared/Storage.cpp @@ -57,7 +57,8 @@ FileStoragePointer FileStorage::create(const QString& filename, size_t size, con } } file.close(); - return FileStoragePointer(new FileStorage(filename)); + //return FileStoragePointer(new FileStorage(filename)); + return std::make_shared(filename); } FileStorage::FileStorage(const QString& filename) : _file(filename) { From bbded6aa517e79876ba4069ae9f421bbf168105f Mon Sep 17 00:00:00 2001 From: samcake Date: Wed, 22 Feb 2017 19:07:01 -0800 Subject: [PATCH 058/302] fix issues while testing the ktxStorage --- libraries/model/src/model/TextureMap.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/model/src/model/TextureMap.cpp b/libraries/model/src/model/TextureMap.cpp index 43c8a8fbc2..d2cdeb0a0b 100755 --- a/libraries/model/src/model/TextureMap.cpp +++ b/libraries/model/src/model/TextureMap.cpp @@ -85,7 +85,7 @@ QImage processSourceImage(const QImage& srcImage, bool cubemap) { return srcImage; } -gpu::Texture* cacheTexture(const std::string& name, gpu::Texture* srcTexture, bool write = true, bool read = true) { +gpu::Texture* cacheTexture(const std::string& name, gpu::Texture* srcTexture, bool write = true, bool read = false) { // FIXME: set read to false for a working state if (!srcTexture) { return nullptr; } @@ -469,7 +469,7 @@ gpu::Texture* TextureUsage::createNormalTextureFromBumpImage(const QImage& srcIm generateMips(theTexture, image, true); theTexture->setSource(srcImageName); - theTexture = cacheTexture(theTexture->source(), theTexture, true, true); + theTexture = cacheTexture(theTexture->source(), theTexture, true, false); } return theTexture; From fbb0a24c4f45a42db917a1491f849eccb33d145b Mon Sep 17 00:00:00 2001 From: sam Date: Thu, 23 Feb 2017 02:08:46 -0800 Subject: [PATCH 059/302] FIxing the bug preventing to deserialize normals, clean up the ktx usage of the storage::Storage class and cleaning up somewhat the Qt pixel formats used --- .../src/gpu/gl45/GL45BackendTexture.cpp | 2 +- libraries/gpu/src/gpu/Format.cpp | 1 + libraries/gpu/src/gpu/Format.h | 2 + libraries/ktx/src/ktx/KTX.cpp | 4 +- libraries/ktx/src/ktx/KTX.h | 7 ++-- libraries/ktx/src/ktx/Reader.cpp | 4 +- libraries/ktx/src/ktx/Writer.cpp | 2 +- .../src/model-networking/TextureCache.cpp | 4 ++ libraries/model/src/model/TextureMap.cpp | 41 +++++++++++++++---- tests/ktx/src/main.cpp | 2 +- 10 files changed, 50 insertions(+), 19 deletions(-) diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp b/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp index f6c40259ea..d5ad0204bf 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp @@ -146,7 +146,7 @@ void GL45Texture::copyMipFaceFromTexture(uint16_t sourceMip, uint16_t targetMip, GLTexelFormat texelFormat = GLTexelFormat::evalGLTexelFormat(_gpuObject.getTexelFormat(), _gpuObject.getStoredMipFormat()); copyMipFaceLinesFromTexture(targetMip, face, size, 0, texelFormat.format, texelFormat.type, mipData->readData()); } else { - qCDebug(gpugllogging) << "Missing mipData level=" << sourceMip << " face=" << (int)face << " for texture " << _gpuObject.source().c_str(); + qCDebug(gpugllogging) << "Missing mipData level=" << sourceMip << " face=" << (int)face << " for texture " << _gpuObject.source().c_str(); } } diff --git a/libraries/gpu/src/gpu/Format.cpp b/libraries/gpu/src/gpu/Format.cpp index 5b61a3a5a4..de202911e3 100644 --- a/libraries/gpu/src/gpu/Format.cpp +++ b/libraries/gpu/src/gpu/Format.cpp @@ -11,6 +11,7 @@ using namespace gpu; const Element Element::COLOR_R_8 { SCALAR, NUINT8, RED }; +const Element Element::COLOR_SR_8 { SCALAR, NUINT8, SRED }; const Element Element::COLOR_RGBA_32{ VEC4, NUINT8, RGBA }; const Element Element::COLOR_SRGBA_32{ VEC4, NUINT8, SRGBA }; diff --git a/libraries/gpu/src/gpu/Format.h b/libraries/gpu/src/gpu/Format.h index 4610597a56..493a2de3c2 100644 --- a/libraries/gpu/src/gpu/Format.h +++ b/libraries/gpu/src/gpu/Format.h @@ -150,6 +150,7 @@ enum Semantic { STENCIL, // Stencil only buffer DEPTH_STENCIL, // Depth Stencil buffer + SRED, SRGB, SRGBA, SBGRA, @@ -229,6 +230,7 @@ public: } static const Element COLOR_R_8; + static const Element COLOR_SR_8; static const Element COLOR_RGBA_32; static const Element COLOR_SRGBA_32; static const Element COLOR_BGRA_32; diff --git a/libraries/ktx/src/ktx/KTX.cpp b/libraries/ktx/src/ktx/KTX.cpp index b03f855783..cc9c1069b1 100644 --- a/libraries/ktx/src/ktx/KTX.cpp +++ b/libraries/ktx/src/ktx/KTX.cpp @@ -77,8 +77,8 @@ KTX::KTX() { KTX::~KTX() { } -void KTX::resetStorage(Storage* storage) { - _storage.reset(storage); +void KTX::resetStorage(StoragePointer& storage) { + _storage = storage; } const Header* KTX::getHeader() const { diff --git a/libraries/ktx/src/ktx/KTX.h b/libraries/ktx/src/ktx/KTX.h index 0e0bb3831e..7aef33704e 100644 --- a/libraries/ktx/src/ktx/KTX.h +++ b/libraries/ktx/src/ktx/KTX.h @@ -345,8 +345,7 @@ namespace ktx { void setUncompressed(GLType type, uint32_t typeSize, GLFormat format, GLInternalFormat_Uncompressed internalFormat, GLBaseInternalFormat baseInternalFormat) { glType = (uint32_t) type; - // FIXME this should correspond to the size of glType - glTypeSize = 1; + glTypeSize = typeSize; glFormat = (uint32_t) format; glInternalFormat = (uint32_t) internalFormat; glBaseInternalFormat = (uint32_t) baseInternalFormat; @@ -421,7 +420,7 @@ namespace ktx { using Images = std::vector; class KTX { - void resetStorage(Storage* src); + void resetStorage(StoragePointer& src); KTX(); public: @@ -449,7 +448,7 @@ namespace ktx { static Images writeImages(Byte* destBytes, size_t destByteSize, const Images& images); // Parse a block of memory and create a KTX object from it - static std::unique_ptr create(std::unique_ptr& src); + static std::unique_ptr create(StoragePointer& src); static bool checkHeaderFromStorage(size_t srcSize, const Byte* srcBytes); static Images parseImages(const Header& header, size_t srcSize, const Byte* srcBytes); diff --git a/libraries/ktx/src/ktx/Reader.cpp b/libraries/ktx/src/ktx/Reader.cpp index e9e0f2760c..d74b45c01c 100644 --- a/libraries/ktx/src/ktx/Reader.cpp +++ b/libraries/ktx/src/ktx/Reader.cpp @@ -163,7 +163,7 @@ namespace ktx { return images; } - std::unique_ptr KTX::create(std::unique_ptr& src) { + std::unique_ptr KTX::create(StoragePointer& src) { if (!src) { return nullptr; } @@ -173,7 +173,7 @@ namespace ktx { } std::unique_ptr result(new KTX()); - result->resetStorage(src.release()); + result->resetStorage(src); // read metadata // result->_keyValues = getKeyValues(result->getHeader()->bytesOfKeyValueData, result->getKeyValueData()); diff --git a/libraries/ktx/src/ktx/Writer.cpp b/libraries/ktx/src/ktx/Writer.cpp index 747e1e7bac..901571f804 100644 --- a/libraries/ktx/src/ktx/Writer.cpp +++ b/libraries/ktx/src/ktx/Writer.cpp @@ -27,7 +27,7 @@ namespace ktx { }; std::unique_ptr KTX::create(const Header& header, const Images& images, const KeyValues& keyValues) { - std::unique_ptr storagePointer; + StoragePointer storagePointer; { auto storageSize = ktx::KTX::evalStorageSize(header, images); auto memoryStorage = new storage::MemoryStorage(storageSize); diff --git a/libraries/model-networking/src/model-networking/TextureCache.cpp b/libraries/model-networking/src/model-networking/TextureCache.cpp index 1f21e9e78d..f4bb3707e8 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.cpp +++ b/libraries/model-networking/src/model-networking/TextureCache.cpp @@ -232,6 +232,10 @@ NetworkTexture::TextureLoaderFunc getTextureLoaderForType(NetworkTexture::Type t return model::TextureUsage::createMetallicTextureFromImage; break; } + case Type::OCCLUSION_TEXTURE: { + return model::TextureUsage::create2DTextureFromImage; + break; + } case Type::STRICT_TEXTURE: { return model::TextureUsage::createStrict2DTextureFromImage; break; diff --git a/libraries/model/src/model/TextureMap.cpp b/libraries/model/src/model/TextureMap.cpp index d2cdeb0a0b..c2bca9d1be 100755 --- a/libraries/model/src/model/TextureMap.cpp +++ b/libraries/model/src/model/TextureMap.cpp @@ -85,7 +85,7 @@ QImage processSourceImage(const QImage& srcImage, bool cubemap) { return srcImage; } -gpu::Texture* cacheTexture(const std::string& name, gpu::Texture* srcTexture, bool write = true, bool read = false) { // FIXME: set read to false for a working state +gpu::Texture* cacheTexture(const std::string& name, gpu::Texture* srcTexture, bool write = true, bool read = true) { if (!srcTexture) { return nullptr; } @@ -132,8 +132,32 @@ gpu::Texture* cacheTexture(const std::string& name, gpu::Texture* srcTexture, bo } if (read && QFileInfo(cacheFilename.c_str()).exists()) { - auto ktxFile = ktx::KTX::create(std::unique_ptr(new storage::FileStorage(cacheFilename.c_str()))); - returnedTexture->setKtxBacking(ktxFile); + { + FILE* file = fopen(cacheFilename.c_str(), "rb"); + if (file != nullptr) { + // obtain file size: + fseek (file , 0 , SEEK_END); + auto size = ftell(file); + rewind(file); + + auto storage = std::make_shared(size); + fread(storage->data(), 1, storage->size(), file); + fclose (file); + + //then create a new texture out of the ktx + auto theNewTexure = Texture::unserialize(srcTexture->getUsage(), srcTexture->getUsageType(), + ktx::KTX::create(std::static_pointer_cast(storage)), srcTexture->getSampler()); + + if (theNewTexure) { + returnedTexture = theNewTexure; + delete srcTexture; + } + } + } + + // auto ktxFile = ktx::KTX::create(std::unique_ptr(new storage::FileStorage(cacheFilename.c_str()))); + // returnedTexture->setKtxBacking(ktxFile); + } } @@ -360,15 +384,16 @@ gpu::Texture* TextureUsage::createNormalTextureFromNormalImage(const QImage& src PROFILE_RANGE(resource_parse, "createNormalTextureFromNormalImage"); QImage image = processSourceImage(srcImage, false); - // Make sure the normal map source image is RGBA32 - if (image.format() != QImage::Format_RGBA8888) { - image = image.convertToFormat(QImage::Format_RGBA8888); + // Make sure the normal map source image is ARGB32 + if (image.format() != QImage::Format_ARGB32) { + image = image.convertToFormat(QImage::Format_ARGB32); } + gpu::Texture* theTexture = nullptr; if ((image.width() > 0) && (image.height() > 0)) { - gpu::Element formatMip = gpu::Element::COLOR_RGBA_32; + gpu::Element formatMip = gpu::Element::COLOR_BGRA_32; gpu::Element formatGPU = gpu::Element::COLOR_RGBA_32; theTexture = (gpu::Texture::create2D(formatGPU, image.width(), image.height(), gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR))); @@ -378,7 +403,7 @@ gpu::Texture* TextureUsage::createNormalTextureFromNormalImage(const QImage& src generateMips(theTexture, image, true); theTexture->setSource(srcImageName); - theTexture = cacheTexture(theTexture->source(), theTexture, true, false); + theTexture = cacheTexture(theTexture->source(), theTexture, true, true); } return theTexture; diff --git a/tests/ktx/src/main.cpp b/tests/ktx/src/main.cpp index 2dbf2f60d7..34280cb263 100644 --- a/tests/ktx/src/main.cpp +++ b/tests/ktx/src/main.cpp @@ -113,7 +113,7 @@ int main(int argc, char** argv) { outFile.close(); } - auto ktxFile = ktx::KTX::create(std::unique_ptr(new storage::FileStorage(TEST_IMAGE_KTX))); + auto ktxFile = ktx::KTX::create(std::shared_ptr(new storage::FileStorage(TEST_IMAGE_KTX))); { const auto& memStorage = ktxMemory->getStorage(); const auto& fileStorage = ktxFile->getStorage(); From d7cb479dbe21547c56eb5e4007ce496cb097c299 Mon Sep 17 00:00:00 2001 From: sam Date: Thu, 23 Feb 2017 02:17:18 -0800 Subject: [PATCH 060/302] clean the clean ups --- .../src/model-networking/TextureCache.cpp | 4 ---- libraries/model/src/model/TextureMap.cpp | 10 ++++++---- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/libraries/model-networking/src/model-networking/TextureCache.cpp b/libraries/model-networking/src/model-networking/TextureCache.cpp index f4bb3707e8..1f21e9e78d 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.cpp +++ b/libraries/model-networking/src/model-networking/TextureCache.cpp @@ -232,10 +232,6 @@ NetworkTexture::TextureLoaderFunc getTextureLoaderForType(NetworkTexture::Type t return model::TextureUsage::createMetallicTextureFromImage; break; } - case Type::OCCLUSION_TEXTURE: { - return model::TextureUsage::create2DTextureFromImage; - break; - } case Type::STRICT_TEXTURE: { return model::TextureUsage::createStrict2DTextureFromImage; break; diff --git a/libraries/model/src/model/TextureMap.cpp b/libraries/model/src/model/TextureMap.cpp index c2bca9d1be..a3bd5fc7c1 100755 --- a/libraries/model/src/model/TextureMap.cpp +++ b/libraries/model/src/model/TextureMap.cpp @@ -132,6 +132,8 @@ gpu::Texture* cacheTexture(const std::string& name, gpu::Texture* srcTexture, bo } if (read && QFileInfo(cacheFilename.c_str()).exists()) { +#define DEBUG_KTX_LOADING 1 +#ifdef DEBUG_KTX_LOADING { FILE* file = fopen(cacheFilename.c_str(), "rb"); if (file != nullptr) { @@ -154,10 +156,10 @@ gpu::Texture* cacheTexture(const std::string& name, gpu::Texture* srcTexture, bo } } } - - // auto ktxFile = ktx::KTX::create(std::unique_ptr(new storage::FileStorage(cacheFilename.c_str()))); - // returnedTexture->setKtxBacking(ktxFile); - +#else + auto ktxFile = ktx::KTX::create(std::unique_ptr(new storage::FileStorage(cacheFilename.c_str()))); + returnedTexture->setKtxBacking(ktxFile); +#endif } } From f32817beacad647222aa4a89e943b18194b2ba40 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Thu, 23 Feb 2017 10:19:20 -0800 Subject: [PATCH 061/302] Fix number of mips calculation --- libraries/gpu/src/gpu/Texture_ktx.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/gpu/src/gpu/Texture_ktx.cpp b/libraries/gpu/src/gpu/Texture_ktx.cpp index 83d8e20b1c..d18440bbc6 100644 --- a/libraries/gpu/src/gpu/Texture_ktx.cpp +++ b/libraries/gpu/src/gpu/Texture_ktx.cpp @@ -77,7 +77,7 @@ ktx::KTXUniquePointer Texture::serialize(const Texture& texture) { } // Number level of mips coming - header.numberOfMipmapLevels = texture.maxMip(); + header.numberOfMipmapLevels = texture.maxMip() + 1; ktx::Images images; for (uint32_t level = 0; level < header.numberOfMipmapLevels; level++) { From a168bbf2dda4d3b628b43c3830cfa4cce8f78b7a Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Thu, 23 Feb 2017 10:26:59 -0800 Subject: [PATCH 062/302] Fix ktx file location, unique->shared pointer --- libraries/model/src/model/TextureMap.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/libraries/model/src/model/TextureMap.cpp b/libraries/model/src/model/TextureMap.cpp index a3bd5fc7c1..dd0ae402d5 100755 --- a/libraries/model/src/model/TextureMap.cpp +++ b/libraries/model/src/model/TextureMap.cpp @@ -96,7 +96,7 @@ gpu::Texture* cacheTexture(const std::string& name, gpu::Texture* srcTexture, bo // Prepare cache directory static const QString HIFI_KTX_FOLDER("hifi_ktx"); QString docsLocation = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation); - ktxCacheFolder = docsLocation + "/" + HIFI_KTX_FOLDER; + ktxCacheFolder = docsLocation + "/" + HIFI_KTX_FOLDER + "/"; QFileInfo info(ktxCacheFolder); if (!info.exists()) { QDir(docsLocation).mkpath(HIFI_KTX_FOLDER); @@ -132,8 +132,8 @@ gpu::Texture* cacheTexture(const std::string& name, gpu::Texture* srcTexture, bo } if (read && QFileInfo(cacheFilename.c_str()).exists()) { -#define DEBUG_KTX_LOADING 1 -#ifdef DEBUG_KTX_LOADING +#define DEBUG_KTX_LOADING 0 +#if DEBUG_KTX_LOADING { FILE* file = fopen(cacheFilename.c_str(), "rb"); if (file != nullptr) { @@ -157,12 +157,12 @@ gpu::Texture* cacheTexture(const std::string& name, gpu::Texture* srcTexture, bo } } #else - auto ktxFile = ktx::KTX::create(std::unique_ptr(new storage::FileStorage(cacheFilename.c_str()))); + ktx::StoragePointer storage = std::make_shared(cacheFilename.c_str()); + auto ktxFile = ktx::KTX::create(storage); returnedTexture->setKtxBacking(ktxFile); #endif } } - return returnedTexture; } From 48c7d17140f10605019ddadf960c12332d21a9f1 Mon Sep 17 00:00:00 2001 From: samcake Date: Thu, 23 Feb 2017 12:43:52 -0800 Subject: [PATCH 063/302] make sure to assign the proper mipFOrmat to the KTXStorage, and fix the glTypeSize again --- libraries/gpu/src/gpu/Texture.cpp | 12 --- libraries/gpu/src/gpu/Texture.h | 4 + libraries/gpu/src/gpu/Texture_ktx.cpp | 125 ++++++++++++++++++-------- 3 files changed, 90 insertions(+), 51 deletions(-) diff --git a/libraries/gpu/src/gpu/Texture.cpp b/libraries/gpu/src/gpu/Texture.cpp index 4b2156ec79..44e35aa387 100755 --- a/libraries/gpu/src/gpu/Texture.cpp +++ b/libraries/gpu/src/gpu/Texture.cpp @@ -124,7 +124,6 @@ uint8 Texture::NUM_FACES_PER_TYPE[NUM_TYPES] = { 1, 1, 1, 6 }; using Storage = Texture::Storage; using PixelsPointer = Texture::PixelsPointer; using MemoryStorage = Texture::MemoryStorage; -using KtxStorage = Texture::KtxStorage; void Storage::assignTexture(Texture* texture) { _texture = texture; @@ -199,13 +198,6 @@ void Texture::MemoryStorage::assignMipFaceData(uint16 level, uint8 face, const s } } -KtxStorage::KtxStorage(ktx::KTXUniquePointer& ktxData) : _ktxData(ktxData.release()) { -} - -PixelsPointer KtxStorage::getMipFace(uint16 level, uint8 face) const { - return _ktxData->getMipFaceTexelsData(level, face); -} - Texture* Texture::createExternal(const ExternalRecycler& recycler, const Sampler& sampler) { Texture* tex = new Texture(TextureUsageType::EXTERNAL); tex->_type = TEX_2D; @@ -980,7 +972,3 @@ void Texture::setStorage(std::unique_ptr& newStorage) { _storage.swap(newStorage); } -void Texture::setKtxBacking(ktx::KTXUniquePointer& ktxBacking) { - auto newBacking = std::unique_ptr(new KtxStorage(ktxBacking)); - setStorage(newBacking); -} \ No newline at end of file diff --git a/libraries/gpu/src/gpu/Texture.h b/libraries/gpu/src/gpu/Texture.h index 637b098504..b0054472fe 100755 --- a/libraries/gpu/src/gpu/Texture.h +++ b/libraries/gpu/src/gpu/Texture.h @@ -25,6 +25,7 @@ namespace ktx { class KTX; using KTXUniquePointer = std::unique_ptr; + struct Header; } namespace gpu { @@ -509,8 +510,11 @@ public: ExternalUpdates getUpdates() const; + // Textures can be serialized directly to ktx data file, here is how static ktx::KTXUniquePointer serialize(const Texture& texture); static Texture* unserialize(Usage usage, TextureUsageType usageType, const ktx::KTXUniquePointer& srcData, const Sampler& sampler = Sampler()); + static bool evalKTXFormat(const Element& mipFormat, const Element& texelFormat, ktx::Header& header); + static bool evalTextureFormat(const ktx::Header& header, Element& mipFormat, Element& texelFormat); protected: const TextureUsageType _usageType; diff --git a/libraries/gpu/src/gpu/Texture_ktx.cpp b/libraries/gpu/src/gpu/Texture_ktx.cpp index d18440bbc6..3b56b1e8c5 100644 --- a/libraries/gpu/src/gpu/Texture_ktx.cpp +++ b/libraries/gpu/src/gpu/Texture_ktx.cpp @@ -15,6 +15,36 @@ #include using namespace gpu; +using PixelsPointer = Texture::PixelsPointer; +using KtxStorage = Texture::KtxStorage; + +KtxStorage::KtxStorage(ktx::KTXUniquePointer& ktxData) { + + // if the source ktx is valid let's config this KtxStorage correctly + if (ktxData && ktxData->getHeader()) { + + // now that we know the ktx, let's get the header info to configure this Texture::Storage: + Format mipFormat = Format::COLOR_BGRA_32; + Format texelFormat = Format::COLOR_SRGBA_32; + if (Texture::evalTextureFormat(*ktxData->getHeader(), mipFormat, texelFormat)) { + _format = mipFormat; + } + + + } + + _ktxData.reset(ktxData.release()); +} + +PixelsPointer KtxStorage::getMipFace(uint16 level, uint8 face) const { + return _ktxData->getMipFaceTexelsData(level, face); +} + +void Texture::setKtxBacking(ktx::KTXUniquePointer& ktxBacking) { + auto newBacking = std::unique_ptr(new KtxStorage(ktxBacking)); + setStorage(newBacking); +} + ktx::KTXUniquePointer Texture::serialize(const Texture& texture) { ktx::Header header; @@ -22,17 +52,7 @@ ktx::KTXUniquePointer Texture::serialize(const Texture& texture) { auto texelFormat = texture.getTexelFormat(); auto mipFormat = texture.getStoredMipFormat(); - if (texelFormat == Format::COLOR_RGBA_32 && mipFormat == Format::COLOR_BGRA_32) { - header.setUncompressed(ktx::GLType::UNSIGNED_BYTE, 4, ktx::GLFormat::BGRA, ktx::GLInternalFormat_Uncompressed::RGBA8, ktx::GLBaseInternalFormat::RGBA); - } else if (texelFormat == Format::COLOR_RGBA_32 && mipFormat == Format::COLOR_RGBA_32) { - header.setUncompressed(ktx::GLType::UNSIGNED_BYTE, 4, ktx::GLFormat::RGBA, ktx::GLInternalFormat_Uncompressed::RGBA8, ktx::GLBaseInternalFormat::RGBA); - } else if (texelFormat == Format::COLOR_SRGBA_32 && mipFormat == Format::COLOR_SBGRA_32) { - header.setUncompressed(ktx::GLType::UNSIGNED_BYTE, 4, ktx::GLFormat::BGRA, ktx::GLInternalFormat_Uncompressed::SRGB8_ALPHA8, ktx::GLBaseInternalFormat::RGBA); - } else if (texelFormat == Format::COLOR_SRGBA_32 && mipFormat == Format::COLOR_SRGBA_32) { - header.setUncompressed(ktx::GLType::UNSIGNED_BYTE, 4, ktx::GLFormat::RGBA, ktx::GLInternalFormat_Uncompressed::SRGB8_ALPHA8, ktx::GLBaseInternalFormat::RGBA); - } else if (texelFormat == Format::COLOR_R_8 && mipFormat == Format::COLOR_R_8) { - header.setUncompressed(ktx::GLType::UNSIGNED_BYTE, 1, ktx::GLFormat::RED, ktx::GLInternalFormat_Uncompressed::R8, ktx::GLBaseInternalFormat::RED); - } else { + if (!Texture::evalKTXFormat(mipFormat, texelFormat, header)) { return nullptr; } @@ -130,33 +150,8 @@ Texture* Texture::unserialize(Usage usage, TextureUsageType usageType, const ktx Format mipFormat = Format::COLOR_BGRA_32; Format texelFormat = Format::COLOR_SRGBA_32; - if (header.getGLFormat() == ktx::GLFormat::BGRA && header.getGLType() == ktx::GLType::UNSIGNED_BYTE && header.getTypeSize() == 4) { - if (header.getGLInternaFormat_Uncompressed() == ktx::GLInternalFormat_Uncompressed::RGBA8) { - mipFormat = Format::COLOR_BGRA_32; - texelFormat = Format::COLOR_RGBA_32; - } else if (header.getGLInternaFormat_Uncompressed() == ktx::GLInternalFormat_Uncompressed::SRGB8_ALPHA8) { - mipFormat = Format::COLOR_SBGRA_32; - texelFormat = Format::COLOR_SRGBA_32; - } else { - return nullptr; - } - } else if (header.getGLFormat() == ktx::GLFormat::RGBA && header.getGLType() == ktx::GLType::UNSIGNED_BYTE && header.getTypeSize() == 4) { - if (header.getGLInternaFormat_Uncompressed() == ktx::GLInternalFormat_Uncompressed::RGBA8) { - mipFormat = Format::COLOR_RGBA_32; - texelFormat = Format::COLOR_RGBA_32; - } else if (header.getGLInternaFormat_Uncompressed() == ktx::GLInternalFormat_Uncompressed::SRGB8_ALPHA8) { - mipFormat = Format::COLOR_SRGBA_32; - texelFormat = Format::COLOR_SRGBA_32; - } else { - return nullptr; - } - } else if (header.getGLFormat() == ktx::GLFormat::RED && header.getGLType() == ktx::GLType::UNSIGNED_BYTE && header.getTypeSize() == 1) { - mipFormat = Format::COLOR_R_8; - if (header.getGLInternaFormat_Uncompressed() == ktx::GLInternalFormat_Uncompressed::R8) { - texelFormat = Format::COLOR_R_8; - } else { - return nullptr; - } + if (!Texture::evalTextureFormat(header, mipFormat, texelFormat)) { + return nullptr; } // Find Texture Type based on dimensions @@ -198,4 +193,56 @@ Texture* Texture::unserialize(Usage usage, TextureUsageType usageType, const ktx } return tex; -} \ No newline at end of file +} + +bool Texture::evalKTXFormat(const Element& mipFormat, const Element& texelFormat, ktx::Header& header) { + if (texelFormat == Format::COLOR_RGBA_32 && mipFormat == Format::COLOR_BGRA_32) { + header.setUncompressed(ktx::GLType::UNSIGNED_BYTE, 1, ktx::GLFormat::BGRA, ktx::GLInternalFormat_Uncompressed::RGBA8, ktx::GLBaseInternalFormat::RGBA); + } else if (texelFormat == Format::COLOR_RGBA_32 && mipFormat == Format::COLOR_RGBA_32) { + header.setUncompressed(ktx::GLType::UNSIGNED_BYTE, 1, ktx::GLFormat::RGBA, ktx::GLInternalFormat_Uncompressed::RGBA8, ktx::GLBaseInternalFormat::RGBA); + } else if (texelFormat == Format::COLOR_SRGBA_32 && mipFormat == Format::COLOR_SBGRA_32) { + header.setUncompressed(ktx::GLType::UNSIGNED_BYTE, 1, ktx::GLFormat::BGRA, ktx::GLInternalFormat_Uncompressed::SRGB8_ALPHA8, ktx::GLBaseInternalFormat::RGBA); + } else if (texelFormat == Format::COLOR_SRGBA_32 && mipFormat == Format::COLOR_SRGBA_32) { + header.setUncompressed(ktx::GLType::UNSIGNED_BYTE, 1, ktx::GLFormat::RGBA, ktx::GLInternalFormat_Uncompressed::SRGB8_ALPHA8, ktx::GLBaseInternalFormat::RGBA); + } else if (texelFormat == Format::COLOR_R_8 && mipFormat == Format::COLOR_R_8) { + header.setUncompressed(ktx::GLType::UNSIGNED_BYTE, 1, ktx::GLFormat::RED, ktx::GLInternalFormat_Uncompressed::R8, ktx::GLBaseInternalFormat::RED); + } else { + return false; + } + + return true; +} + +bool Texture::evalTextureFormat(const ktx::Header& header, Element& mipFormat, Element& texelFormat) { + if (header.getGLFormat() == ktx::GLFormat::BGRA && header.getGLType() == ktx::GLType::UNSIGNED_BYTE && header.getTypeSize() == 1) { + if (header.getGLInternaFormat_Uncompressed() == ktx::GLInternalFormat_Uncompressed::RGBA8) { + mipFormat = Format::COLOR_BGRA_32; + texelFormat = Format::COLOR_RGBA_32; + } else if (header.getGLInternaFormat_Uncompressed() == ktx::GLInternalFormat_Uncompressed::SRGB8_ALPHA8) { + mipFormat = Format::COLOR_SBGRA_32; + texelFormat = Format::COLOR_SRGBA_32; + } else { + return false; + } + } else if (header.getGLFormat() == ktx::GLFormat::RGBA && header.getGLType() == ktx::GLType::UNSIGNED_BYTE && header.getTypeSize() == 1) { + if (header.getGLInternaFormat_Uncompressed() == ktx::GLInternalFormat_Uncompressed::RGBA8) { + mipFormat = Format::COLOR_RGBA_32; + texelFormat = Format::COLOR_RGBA_32; + } else if (header.getGLInternaFormat_Uncompressed() == ktx::GLInternalFormat_Uncompressed::SRGB8_ALPHA8) { + mipFormat = Format::COLOR_SRGBA_32; + texelFormat = Format::COLOR_SRGBA_32; + } else { + return false; + } + } else if (header.getGLFormat() == ktx::GLFormat::RED && header.getGLType() == ktx::GLType::UNSIGNED_BYTE && header.getTypeSize() == 1) { + mipFormat = Format::COLOR_R_8; + if (header.getGLInternaFormat_Uncompressed() == ktx::GLInternalFormat_Uncompressed::R8) { + texelFormat = Format::COLOR_R_8; + } else { + return false; + } + } else { + return false; + } + return true; +} From cd8f3e1b0187a524411af0f01742da050f0a28f7 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Thu, 23 Feb 2017 13:04:04 -0800 Subject: [PATCH 064/302] Prevent name collisions in KTX from causing crashes --- libraries/gpu/src/gpu/Texture_ktx.cpp | 3 +++ libraries/model/src/model/TextureMap.cpp | 3 +-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/libraries/gpu/src/gpu/Texture_ktx.cpp b/libraries/gpu/src/gpu/Texture_ktx.cpp index 3b56b1e8c5..c7da499e98 100644 --- a/libraries/gpu/src/gpu/Texture_ktx.cpp +++ b/libraries/gpu/src/gpu/Texture_ktx.cpp @@ -117,6 +117,9 @@ ktx::KTXUniquePointer Texture::serialize(const Texture& texture) { } auto ktxBuffer = ktx::KTX::create(header, images); + auto expectedMipCount = texture.evalNumMips(); + assert(expectedMipCount == ktxBuffer->_images.size()); + assert(expectedMipCount == header.numberOfMipmapLevels); assert(0 == memcmp(&header, ktxBuffer->getHeader(), sizeof(ktx::Header))); assert(ktxBuffer->_images.size() == images.size()); diff --git a/libraries/model/src/model/TextureMap.cpp b/libraries/model/src/model/TextureMap.cpp index dd0ae402d5..7b2e363eff 100755 --- a/libraries/model/src/model/TextureMap.cpp +++ b/libraries/model/src/model/TextureMap.cpp @@ -103,8 +103,7 @@ gpu::Texture* cacheTexture(const std::string& name, gpu::Texture* srcTexture, bo } }); - std::string cleanedName = name; - cleanedName = cleanedName.substr(cleanedName.find_last_of((char) '//') + 1); + std::string cleanedName = QUrl::toPercentEncoding(name.c_str()).toStdString(); std::string cacheFilename(ktxCacheFolder.toStdString()); cacheFilename += "/"; cacheFilename += cleanedName; From b8e1340a0d587972a88e15d4976bbc13173ecaa9 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Thu, 23 Feb 2017 16:08:29 -0800 Subject: [PATCH 065/302] Enable threaded texture buffering --- libraries/gpu-gl/src/gpu/gl45/GL45Backend.h | 1 + 1 file changed, 1 insertion(+) diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h b/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h index 28425433c4..cfc4dc39da 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h +++ b/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h @@ -17,6 +17,7 @@ #include #define INCREMENTAL_TRANSFER 0 +#define THREADED_TEXTURE_BUFFERING 1 namespace gpu { namespace gl45 { From 594fe9b1bef52f1be053e8fd7b8ae6176455bd78 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Thu, 23 Feb 2017 22:01:53 -0800 Subject: [PATCH 066/302] Use hashes of URLs for filenames, due to length constraints --- libraries/model/src/model/TextureMap.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/libraries/model/src/model/TextureMap.cpp b/libraries/model/src/model/TextureMap.cpp index 7b2e363eff..ce602b7b9b 100755 --- a/libraries/model/src/model/TextureMap.cpp +++ b/libraries/model/src/model/TextureMap.cpp @@ -18,7 +18,7 @@ #include #include #include - +#include #include #include "ModelLogging.h" @@ -103,7 +103,8 @@ gpu::Texture* cacheTexture(const std::string& name, gpu::Texture* srcTexture, bo } }); - std::string cleanedName = QUrl::toPercentEncoding(name.c_str()).toStdString(); + + std::string cleanedName = QCryptographicHash::hash(QUrl::toPercentEncoding(name.c_str()), QCryptographicHash::Sha1).toHex().toStdString(); std::string cacheFilename(ktxCacheFolder.toStdString()); cacheFilename += "/"; cacheFilename += cleanedName; From 3d2e6713eea32c6553113bfe1422c4b6ed2bb89a Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Fri, 24 Feb 2017 12:04:17 -0800 Subject: [PATCH 067/302] Ensure complete population of allocated mip levels --- libraries/gpu-gl/src/gpu/gl45/GL45Backend.h | 2 +- .../src/gpu/gl45/GL45BackendVariableTexture.cpp | 11 ++++++++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h b/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h index cfc4dc39da..a320008dc5 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h +++ b/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h @@ -168,7 +168,7 @@ public: //bool canPromoteNoAllocate() const { return _allocatedMip < _populatedMip; } bool canPromote() const { return _allocatedMip > 0; } bool canDemote() const { return _allocatedMip < _maxAllocatedMip; } - bool hasPendingTransfers() const { return !_pendingTransfers.empty(); } + bool hasPendingTransfers() const { return _populatedMip > _allocatedMip; } void executeNextTransfer(const TexturePointer& currentTexture); uint32 size() const override { return _size; } virtual void populateTransferQueue() = 0; diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp b/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp index 62f1a3c248..f21530ff9e 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp @@ -338,6 +338,7 @@ void GL45VariableAllocationTexture::updateMemoryPressure() { _transferQueue = WorkQueue(); _promoteQueue = WorkQueue(); _demoteQueue = WorkQueue(); + // Populate the existing textures into the queue for (const auto& texture : strongTextures) { addToWorkQueue(texture); @@ -387,7 +388,7 @@ void GL45VariableAllocationTexture::processWorkQueues() { } if (workQueue.empty()) { - _memoryPressureState = MemoryPressureState::Idle; + _memoryPressureStateStale = true; } } @@ -406,6 +407,14 @@ GL45VariableAllocationTexture::~GL45VariableAllocationTexture() { } void GL45VariableAllocationTexture::executeNextTransfer(const TexturePointer& currentTexture) { + if (_populatedMip <= _allocatedMip) { + return; + } + + if (_pendingTransfers.empty()) { + populateTransferQueue(); + } + if (!_pendingTransfers.empty()) { // Keeping hold of a strong pointer during the transfer ensures that the transfer thread cannot try to access a destroyed texture _currentTransferTexture = currentTexture; From cd8bba47cf3821c8986763c22ed08a077f6cd44a Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Fri, 24 Feb 2017 12:39:38 -0800 Subject: [PATCH 068/302] Add pending texture transfer size to tracked stats --- interface/resources/qml/Stats.qml | 3 +++ interface/src/ui/Stats.cpp | 1 + interface/src/ui/Stats.h | 2 ++ libraries/gpu-gl/src/gpu/gl45/GL45Backend.h | 2 ++ .../gpu/gl45/GL45BackendVariableTexture.cpp | 27 ++++++++++++------- libraries/gpu/src/gpu/Context.cpp | 17 ++++++++++++ libraries/gpu/src/gpu/Context.h | 4 +++ libraries/gpu/src/gpu/Texture.cpp | 4 +++ libraries/gpu/src/gpu/Texture.h | 1 + 9 files changed, 51 insertions(+), 10 deletions(-) diff --git a/interface/resources/qml/Stats.qml b/interface/resources/qml/Stats.qml index 5e0eac37bd..25c2e3903e 100644 --- a/interface/resources/qml/Stats.qml +++ b/interface/resources/qml/Stats.qml @@ -252,6 +252,9 @@ Item { StatText { text: " Decimated: " + root.decimatedTextureCount; } + StatText { + text: " Pending Transfer: " + root.texturePendingTransfers + " MB"; + } StatText { text: " Resource Memory: " + root.gpuTextureMemory + " MB"; } diff --git a/interface/src/ui/Stats.cpp b/interface/src/ui/Stats.cpp index 49602833e6..9ba5170856 100644 --- a/interface/src/ui/Stats.cpp +++ b/interface/src/ui/Stats.cpp @@ -321,6 +321,7 @@ void Stats::updateStats(bool force) { STAT_UPDATE(glContextSwapchainMemory, (int)BYTES_TO_MB(gl::Context::getSwapchainMemoryUsage())); STAT_UPDATE(qmlTextureMemory, (int)BYTES_TO_MB(OffscreenQmlSurface::getUsedTextureMemory())); + STAT_UPDATE(texturePendingTransfers, (int)BYTES_TO_MB(gpu::Texture::getTextureTransferPendingSize())); STAT_UPDATE(gpuTextureMemory, (int)BYTES_TO_MB(gpu::Texture::getTextureGPUMemoryUsage())); STAT_UPDATE(gpuTextureVirtualMemory, (int)BYTES_TO_MB(gpu::Texture::getTextureGPUVirtualMemoryUsage())); STAT_UPDATE(gpuTextureFramebufferMemory, (int)BYTES_TO_MB(gpu::Texture::getTextureGPUFramebufferMemoryUsage())); diff --git a/interface/src/ui/Stats.h b/interface/src/ui/Stats.h index 569e737117..8e01f39e07 100644 --- a/interface/src/ui/Stats.h +++ b/interface/src/ui/Stats.h @@ -105,6 +105,7 @@ class Stats : public QQuickItem { STATS_PROPERTY(int, gpuTexturesSparse, 0) STATS_PROPERTY(int, glContextSwapchainMemory, 0) STATS_PROPERTY(int, qmlTextureMemory, 0) + STATS_PROPERTY(int, texturePendingTransfers, 0) STATS_PROPERTY(int, gpuTextureMemory, 0) STATS_PROPERTY(int, gpuTextureVirtualMemory, 0) STATS_PROPERTY(int, gpuTextureFramebufferMemory, 0) @@ -210,6 +211,7 @@ signals: void timingStatsChanged(); void glContextSwapchainMemoryChanged(); void qmlTextureMemoryChanged(); + void texturePendingTransfersChanged(); void gpuBuffersChanged(); void gpuBufferMemoryChanged(); void gpuTexturesChanged(); diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h b/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h index a320008dc5..cfdcc356a6 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h +++ b/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h @@ -128,6 +128,7 @@ public: TransferJob(const TransferJob& other) = delete; TransferJob(const GL45VariableAllocationTexture& parent, std::function transferLambda); TransferJob(const GL45VariableAllocationTexture& parent, uint16_t sourceMip, uint16_t targetMip, uint8_t face, uint32_t lines = 0, uint32_t lineOffset = 0); + ~TransferJob(); bool tryTransfer(); #if THREADED_TEXTURE_BUFFERING @@ -136,6 +137,7 @@ public: #endif private: + size_t _transferSize { 0 }; #if THREADED_TEXTURE_BUFFERING void startBuffering(); #endif diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp b/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp index f21530ff9e..92251bd381 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp @@ -91,27 +91,29 @@ TransferJob::TransferJob(const GL45VariableAllocationTexture& parent, uint16_t s type = texelFormat.type; if (0 == lines) { + _transferSize = mipData->getSize(); _bufferingLambda = [=] { - auto size = mipData->getSize(); - _buffer.resize(size); - memcpy(&_buffer[0], mipData->readData(), size); + _buffer.resize(_transferSize); + memcpy(&_buffer[0], mipData->readData(), _transferSize); _bufferingCompleted = true; }; } else { transferDimensions.y = lines; + auto dimensions = _parent._gpuObject.evalMipDimensions(sourceMip); + auto mipSize = mipData->getSize(); + auto bytesPerLine = (uint32_t)mipSize / dimensions.y; + _transferSize = bytesPerLine * lines; + auto sourceOffset = bytesPerLine * lineOffset; _bufferingLambda = [=] { - auto dimensions = _parent._gpuObject.evalMipDimensions(sourceMip); - auto mipSize = mipData->getSize(); - auto bytesPerLine = (uint32_t)mipSize / dimensions.y; - auto transferSize = bytesPerLine * lines; - auto sourceOffset = bytesPerLine * lineOffset; - _buffer.resize(transferSize); - memcpy(&_buffer[0], mipData->readData() + sourceOffset, transferSize); + _buffer.resize(_transferSize); + memcpy(&_buffer[0], mipData->readData() + sourceOffset, _transferSize); _bufferingCompleted = true; }; } + Backend::updateTextureTransferPendingSize(0, _transferSize); + _transferLambda = [=] { _parent.copyMipFaceLinesFromTexture(targetMip, face, transferDimensions, lineOffset, format, type, _buffer.data()); std::vector emptyVector; @@ -123,6 +125,11 @@ TransferJob::TransferJob(const GL45VariableAllocationTexture& parent, std::funct : _parent(parent), _bufferingCompleted(true), _transferLambda(transferLambda) { } +TransferJob::~TransferJob() { + Backend::updateTextureTransferPendingSize(_transferSize, 0); +} + + bool TransferJob::tryTransfer() { // Disable threaded texture transfer for now #if THREADED_TEXTURE_BUFFERING diff --git a/libraries/gpu/src/gpu/Context.cpp b/libraries/gpu/src/gpu/Context.cpp index 78b472bdae..cc570f696f 100644 --- a/libraries/gpu/src/gpu/Context.cpp +++ b/libraries/gpu/src/gpu/Context.cpp @@ -241,6 +241,7 @@ std::atomic Context::_bufferGPUMemoryUsage { 0 }; std::atomic Context::_textureGPUCount{ 0 }; std::atomic Context::_textureGPUSparseCount { 0 }; +std::atomic Context::_textureTransferPendingSize { 0 }; std::atomic Context::_textureGPUMemoryUsage { 0 }; std::atomic Context::_textureGPUVirtualMemoryUsage { 0 }; std::atomic Context::_textureGPUFramebufferMemoryUsage { 0 }; @@ -317,6 +318,17 @@ void Context::decrementTextureGPUSparseCount() { --_textureGPUSparseCount; } +void Context::updateTextureTransferPendingSize(Size prevObjectSize, Size newObjectSize) { + if (prevObjectSize == newObjectSize) { + return; + } + if (newObjectSize > prevObjectSize) { + _textureTransferPendingSize.fetch_add(newObjectSize - prevObjectSize); + } else { + _textureTransferPendingSize.fetch_sub(prevObjectSize - newObjectSize); + } +} + void Context::updateTextureGPUMemoryUsage(Size prevObjectSize, Size newObjectSize) { if (prevObjectSize == newObjectSize) { return; @@ -390,6 +402,10 @@ uint32_t Context::getTextureGPUSparseCount() { return _textureGPUSparseCount.load(); } +Context::Size Context::getTextureTransferPendingSize() { + return _textureTransferPendingSize.load(); +} + Context::Size Context::getTextureGPUMemoryUsage() { return _textureGPUMemoryUsage.load(); } @@ -419,6 +435,7 @@ void Backend::incrementTextureGPUCount() { Context::incrementTextureGPUCount(); void Backend::decrementTextureGPUCount() { Context::decrementTextureGPUCount(); } void Backend::incrementTextureGPUSparseCount() { Context::incrementTextureGPUSparseCount(); } void Backend::decrementTextureGPUSparseCount() { Context::decrementTextureGPUSparseCount(); } +void Backend::updateTextureTransferPendingSize(Resource::Size prevObjectSize, Resource::Size newObjectSize) { Context::updateTextureTransferPendingSize(prevObjectSize, newObjectSize); } void Backend::updateTextureGPUMemoryUsage(Resource::Size prevObjectSize, Resource::Size newObjectSize) { Context::updateTextureGPUMemoryUsage(prevObjectSize, newObjectSize); } void Backend::updateTextureGPUVirtualMemoryUsage(Resource::Size prevObjectSize, Resource::Size newObjectSize) { Context::updateTextureGPUVirtualMemoryUsage(prevObjectSize, newObjectSize); } void Backend::updateTextureGPUFramebufferMemoryUsage(Resource::Size prevObjectSize, Resource::Size newObjectSize) { Context::updateTextureGPUFramebufferMemoryUsage(prevObjectSize, newObjectSize); } diff --git a/libraries/gpu/src/gpu/Context.h b/libraries/gpu/src/gpu/Context.h index 01c841992d..102c754cd7 100644 --- a/libraries/gpu/src/gpu/Context.h +++ b/libraries/gpu/src/gpu/Context.h @@ -101,6 +101,7 @@ public: static void decrementTextureGPUCount(); static void incrementTextureGPUSparseCount(); static void decrementTextureGPUSparseCount(); + static void updateTextureTransferPendingSize(Resource::Size prevObjectSize, Resource::Size newObjectSize); static void updateTextureGPUMemoryUsage(Resource::Size prevObjectSize, Resource::Size newObjectSize); static void updateTextureGPUSparseMemoryUsage(Resource::Size prevObjectSize, Resource::Size newObjectSize); static void updateTextureGPUVirtualMemoryUsage(Resource::Size prevObjectSize, Resource::Size newObjectSize); @@ -220,6 +221,7 @@ public: static uint32_t getTextureGPUSparseCount(); static Size getFreeGPUMemory(); static Size getUsedGPUMemory(); + static Size getTextureTransferPendingSize(); static Size getTextureGPUMemoryUsage(); static Size getTextureGPUVirtualMemoryUsage(); static Size getTextureGPUFramebufferMemoryUsage(); @@ -263,6 +265,7 @@ protected: static void decrementTextureGPUCount(); static void incrementTextureGPUSparseCount(); static void decrementTextureGPUSparseCount(); + static void updateTextureTransferPendingSize(Size prevObjectSize, Size newObjectSize); static void updateTextureGPUMemoryUsage(Size prevObjectSize, Size newObjectSize); static void updateTextureGPUSparseMemoryUsage(Size prevObjectSize, Size newObjectSize); static void updateTextureGPUVirtualMemoryUsage(Size prevObjectSize, Size newObjectSize); @@ -279,6 +282,7 @@ protected: static std::atomic _textureGPUCount; static std::atomic _textureGPUSparseCount; + static std::atomic _textureTransferPendingSize; static std::atomic _textureGPUMemoryUsage; static std::atomic _textureGPUSparseMemoryUsage; static std::atomic _textureGPUVirtualMemoryUsage; diff --git a/libraries/gpu/src/gpu/Texture.cpp b/libraries/gpu/src/gpu/Texture.cpp index 44e35aa387..6b9087333c 100755 --- a/libraries/gpu/src/gpu/Texture.cpp +++ b/libraries/gpu/src/gpu/Texture.cpp @@ -89,6 +89,10 @@ uint32_t Texture::getTextureGPUSparseCount() { return Context::getTextureGPUSparseCount(); } +Texture::Size Texture::getTextureTransferPendingSize() { + return Context::getTextureTransferPendingSize(); +} + Texture::Size Texture::getTextureGPUMemoryUsage() { return Context::getTextureGPUMemoryUsage(); } diff --git a/libraries/gpu/src/gpu/Texture.h b/libraries/gpu/src/gpu/Texture.h index b0054472fe..d840687af2 100755 --- a/libraries/gpu/src/gpu/Texture.h +++ b/libraries/gpu/src/gpu/Texture.h @@ -166,6 +166,7 @@ public: static Size getTextureCPUMemoryUsage(); static uint32_t getTextureGPUCount(); static uint32_t getTextureGPUSparseCount(); + static Size getTextureTransferPendingSize(); static Size getTextureGPUMemoryUsage(); static Size getTextureGPUVirtualMemoryUsage(); static Size getTextureGPUFramebufferMemoryUsage(); From 8969d7df1d9cd61718e45043b2c172b7d4684b43 Mon Sep 17 00:00:00 2001 From: Zach Pomerantz Date: Thu, 23 Feb 2017 16:26:52 -0500 Subject: [PATCH 069/302] FileCache --- libraries/networking/src/FileCache.cpp | 246 +++++++++++++++++++++++++ libraries/networking/src/FileCache.h | 149 +++++++++++++++ 2 files changed, 395 insertions(+) create mode 100644 libraries/networking/src/FileCache.cpp create mode 100644 libraries/networking/src/FileCache.h diff --git a/libraries/networking/src/FileCache.cpp b/libraries/networking/src/FileCache.cpp new file mode 100644 index 0000000000..a717546de4 --- /dev/null +++ b/libraries/networking/src/FileCache.cpp @@ -0,0 +1,246 @@ +// +// FileCache.cpp +// libraries/model-networking/src +// +// Created by Zach Pomerantz on 2/21/2017. +// Copyright 2017 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 "FileCache.h" + +#include +#include +#include + +#include + +#include + +Q_LOGGING_CATEGORY(file_cache, "hifi.file_cache") + +static const std::string MANIFEST_NAME = "manifest"; + +void FileCache::setUnusedFileCacheSize(size_t unusedFilesMaxSize) { + _unusedFilesMaxSize = std::min(unusedFilesMaxSize, MAX_UNUSED_MAX_SIZE); + reserve(0); + emit dirty(); +} + +void FileCache::setOfflineFileCacheSize(size_t offlineFilesMaxSize) { + _offlineFilesMaxSize = std::min(offlineFilesMaxSize, MAX_UNUSED_MAX_SIZE); +} + +FileCache::FileCache(const std::string& dirname, const std::string& ext, QObject* parent) : + QObject(parent), + _dir(createDir(dirname)), + _ext(ext) {} + +FileCache::~FileCache() { + clear(); +} + +void fileDeleter(File* file) { + file->deleter(); +} + +FilePointer FileCache::writeFile(const Key& key, const char* data, size_t length, void* extra) { + std::string filepath = getFilepath(key); + + Lock lock(_filesMutex); + + // if file already exists, return it + FilePointer file = getFile(key); + if (file) { + qCWarning(file_cache) << "Attempted to overwrite" << filepath.c_str(); + return file; + } + + // write the new file + FILE* saveFile = fopen(filepath.c_str(), "wb"); + if (saveFile != nullptr && fwrite(data, length, 1, saveFile) && fclose(saveFile) == 0) { + file.reset(createFile(key, filepath, length, extra), &fileDeleter); + fclose(saveFile); + file->_cache = this; + _files[key] = file; + _numTotalFiles += 1; + _totalFilesSize += length; + + emit dirty(); + } else { + qCWarning(file_cache, "Failed to write %s (%s)", filepath.c_str(), strerror(errno)); + errno = 0; + } + + return file; +} + +FilePointer FileCache::getFile(const Key& key) { + FilePointer file; + + Lock lock(_filesMutex); + + // check if file already exists + const auto it = _files.find(key); + if (it != _files.cend()) { + file = it->second.lock(); + if (file) { + // if it exists, it is active - remove it from the cache + removeUnusedFile(file); + emit dirty(); + } else { + // if not, remove the weak_ptr + _files.erase(it); + } + } + + return file; +} + +File* FileCache::createFile(const Key& key, const std::string& filepath, size_t length, void* extra) { + return new File(key, filepath, length); +} + +std::string FileCache::createDir(const std::string& dirname) { + QString dirpath = ServerPathUtils::getDataFilePath(dirname.c_str()); + QDir dir(dirpath); + + if (dir.exists()) { + std::unordered_set persistedEntries; + if (dir.exists(MANIFEST_NAME.c_str())) { + std::ifstream manifest; + manifest.open(dir.absoluteFilePath(MANIFEST_NAME.c_str()).toStdString()); + while (manifest.good()) { + std::string entry; + manifest >> entry; + persistedEntries.insert(entry); + + // ZZMP: rm + for (const auto& entry : persistedEntries) + qDebug() << "ZZMP" << entry.c_str(); + qDebug() << "ZZMP" << "---"; + } + } + + foreach(QString filename, dir.entryList()) { + if (persistedEntries.find(filename.toStdString()) == persistedEntries.cend()) { + dir.remove(filename); + } + } + } else { + dir.mkpath(dirpath); + } + + return dirpath.toStdString(); +} + +std::string FileCache::getFilepath(const Key& key) { + return _dir + key + '.' + _ext; +} + +void FileCache::addUnusedFile(const FilePointer file) { + { + Lock lock(_filesMutex); + _files[file->getKey()] = file; + } + + reserve(file->getLength()); + file->_LRUKey = ++_lastLRUKey; + + { + Lock lock(_unusedFilesMutex); + _unusedFiles.insert({ file->_LRUKey, file }); + _numUnusedFiles += 1; + _unusedFilesSize += file->getLength(); + } + + emit dirty(); +} + +void FileCache::removeUnusedFile(const FilePointer file) { + Lock lock(_unusedFilesMutex); + const auto it = _unusedFiles.find(file->_LRUKey); + if (it != _unusedFiles.cend()) { + _unusedFiles.erase(it); + _numUnusedFiles -= 1; + _unusedFilesSize -= file->getLength(); + } +} + +void FileCache::reserve(size_t length) { + Lock unusedLock(_unusedFilesMutex); + while (!_unusedFiles.empty() && + _unusedFilesSize + length > _unusedFilesMaxSize) { + auto it = _unusedFiles.begin(); + auto file = it->second; + auto length = file->getLength(); + + unusedLock.unlock(); + { + file->_cache = nullptr; + Lock lock(_filesMutex); + _files.erase(file->getKey()); + } + unusedLock.lock(); + + _unusedFiles.erase(it); + _numTotalFiles -= 1; + _numUnusedFiles -= 1; + _totalFilesSize -= length; + _unusedFilesSize -= length; + + unusedLock.unlock(); + evictedFile(file); + unusedLock.lock(); + } +} + +void FileCache::clear() { + std::string manifestPath= _dir + MANIFEST_NAME; + FILE* manifest = fopen(manifestPath.c_str(), "wb"); + + Lock lock(_unusedFilesMutex); + for (const auto& val : _unusedFiles) { + const FilePointer& file = val.second; + file->_cache = nullptr; + + if (_unusedFilesSize > _offlineFilesMaxSize) { + _unusedFilesSize -= file->getLength(); + } else { + std::string key = file->getKey() + '.' + _ext + '\n'; + if (manifest != nullptr && !fwrite(key.c_str(), key.length(), 1, manifest)) { + manifest = nullptr; // to prevent future writes + } + file->_shouldPersist = true; + } + } + + if (manifest == nullptr || fclose(manifest) != 0) { + for (const auto& val : _unusedFiles) { + val.second->_shouldPersist = false; + } + + qCWarning(file_cache, "Failed to write %s (%s)", manifestPath.c_str(), strerror(errno)); + errno = 0; + } + + _unusedFiles.clear(); +} + +void File::deleter() { + if (_cache) { + FilePointer self(this, &fileDeleter); + _cache->addUnusedFile(self); + } else { + deleteLater(); + } +} + +File::~File() { + QFile file(getFilepath().c_str()); + if (file.exists() && !_shouldPersist) { + file.remove(); + } +} diff --git a/libraries/networking/src/FileCache.h b/libraries/networking/src/FileCache.h new file mode 100644 index 0000000000..f068f6e7d5 --- /dev/null +++ b/libraries/networking/src/FileCache.h @@ -0,0 +1,149 @@ +// +// FileCache.h +// libraries/networking/src +// +// Created by Zach Pomerantz on 2/21/2017. +// Copyright 2017 High Fidelity, Inc. // // Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_FileCache_h +#define hifi_FileCache_h + +#include +#include +#include +#include +#include +#include + +#include +#include + +Q_DECLARE_LOGGING_CATEGORY(file_cache) + +class File; +using FilePointer = std::shared_ptr; + +class FileCache : public QObject { + Q_OBJECT + Q_PROPERTY(size_t numTotal READ getNumTotalFiles NOTIFY dirty) + Q_PROPERTY(size_t numCached READ getNumCachedFiles NOTIFY dirty) + Q_PROPERTY(size_t sizeTotal READ getSizeTotalFiles NOTIFY dirty) + Q_PROPERTY(size_t sizeCached READ getSizeCachedFiles NOTIFY dirty) + + static const size_t BYTES_PER_MEGABYTES = 1024 * 1024; + static const size_t BYTES_PER_GIGABYTES = 1024 * BYTES_PER_MEGABYTES; + static const size_t DEFAULT_UNUSED_MAX_SIZE = 5 * BYTES_PER_GIGABYTES; // 5GB + static const size_t MAX_UNUSED_MAX_SIZE = 100 * BYTES_PER_GIGABYTES; // 100GB + static const size_t DEFAULT_OFFLINE_MAX_SIZE = 2 * BYTES_PER_GIGABYTES; // 2GB + +public: + size_t getNumTotalFiles() const { return _numTotalFiles; } + size_t getNumCachedFiles() const { return _numUnusedFiles; } + size_t getSizeTotalFiles() const { return _totalFilesSize; } + size_t getSizeCachedFiles() const { return _unusedFilesSize; } + + void setUnusedFileCacheSize(size_t unusedFilesMaxSize); + size_t getUnusedFileCacheSize() const { return _unusedFilesSize; } + + void setOfflineFileCacheSize(size_t offlineFilesMaxSize); + + // initialize FileCache with a directory name (not a path, ex.: "temp_jpgs") and an ext (ex.: "jpg") + FileCache(const std::string& dirname, const std::string& ext, QObject* parent = nullptr); + // precondition: there should be no references to Files when FileCache is destroyed + virtual ~FileCache(); + + // derived classes are left to implement hashing of the files on their own + using Key = std::string; + + // derived classes should implement a setter/getter, for example, for a FileCache backing a network cache: + // + // DerivedFilePointer writeFile(const DerivedData& data) { + // return writeFile(data->key, data->data, data->length, &data); + // } + // + // DerivedFilePointer getFile(const QUrl& url) { + // // assuming storage/removal of url->hash in createFile/evictedFile overrides + // auto key = lookup_hash_for(url); + // return getFile(key); + // } + +signals: + void dirty(); + +protected: + FilePointer writeFile(const Key& key, const char* data, size_t length, void* extra); + FilePointer getFile(const Key& key); + + virtual File* createFile(const Key& key, const std::string& filepath, size_t length, void* extra); + virtual void evictedFile(const FilePointer& file) = 0; + +private: + using Mutex = std::recursive_mutex; + using Lock = std::unique_lock; + + friend class File; + + std::string createDir(const std::string& dirname); + std::string getFilepath(const Key& key); + + void addUnusedFile(const FilePointer file); + void removeUnusedFile(const FilePointer file); + void reserve(size_t length); + void clear(); + + std::atomic _numTotalFiles { 0 }; + std::atomic _numUnusedFiles { 0 }; + std::atomic _totalFilesSize { 0 }; + std::atomic _unusedFilesSize { 0 }; + + std::string _dir; + std::string _ext; + + std::unordered_map> _files; + Mutex _filesMutex; + + std::map _unusedFiles; + Mutex _unusedFilesMutex; + size_t _unusedFilesMaxSize { DEFAULT_UNUSED_MAX_SIZE }; + int _lastLRUKey { 0 }; + + size_t _offlineFilesMaxSize { DEFAULT_OFFLINE_MAX_SIZE }; +}; + +class File : public QObject { + Q_OBJECT + +public: + using Key = FileCache::Key; + + std::string getFilepath() const { return _filepath; } + Key getKey() const { return _key; } + size_t getLength() const { return _length; } + + // overrides should call File::deleter to maintain caching behavior + virtual void deleter(); + +protected: + // when constructed, the file has already been created/written + File(const Key& key, const std::string& filepath, size_t length) : + _filepath(filepath), _key(key), _length(length) {} + // the destructor should handle unlinking of the actual filepath + virtual ~File(); + + const std::string _filepath; + +private: + friend class FileCache; + + const Key _key; + const size_t _length; + + FileCache* _cache; + int _LRUKey { 0 }; + + bool _shouldPersist { false }; +}; + +#endif // hifi_FileCache_h From cfe14518a1ba08884a8c1399b3ac78051cab9815 Mon Sep 17 00:00:00 2001 From: Zach Pomerantz Date: Thu, 23 Feb 2017 16:27:08 -0500 Subject: [PATCH 070/302] KTXCache --- .../src/model-networking/KTXCache.cpp | 49 +++++++++++++ .../src/model-networking/KTXCache.h | 69 +++++++++++++++++++ 2 files changed, 118 insertions(+) create mode 100644 libraries/model-networking/src/model-networking/KTXCache.cpp create mode 100644 libraries/model-networking/src/model-networking/KTXCache.h diff --git a/libraries/model-networking/src/model-networking/KTXCache.cpp b/libraries/model-networking/src/model-networking/KTXCache.cpp new file mode 100644 index 0000000000..1ab32698b6 --- /dev/null +++ b/libraries/model-networking/src/model-networking/KTXCache.cpp @@ -0,0 +1,49 @@ +// +// KTXCache.cpp +// libraries/model-networking/src +// +// Created by Zach Pomerantz on 2/22/2017. +// Copyright 2017 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 "KTXCache.h" + +#include + +KTXFilePointer KTXCache::writeFile(Data data) { + return std::static_pointer_cast(FileCache::writeFile(data.key, data.data, data.length, (void*)&data)); +} + +KTXFilePointer KTXCache::getFile(const QUrl& url) { + Key key; + { + Lock lock(_urlMutex); + const auto it = _urlMap.find(url); + if (it != _urlMap.cend()) { + key = it->second; + } + } + + KTXFilePointer file; + if (!key.empty()) { + file = std::static_pointer_cast(FileCache::getFile(key)); + } + + return file; +} + +File* KTXCache::createFile(const Key& key, const std::string& filepath, size_t length, void* extra) { + const QUrl& url = reinterpret_cast(extra)->url; + Lock lock(_urlMutex); + _urlMap[url] = key; + return new KTXFile(key, filepath, length, url); +} + +void KTXCache::evictedFile(const FilePointer& file) { + const QUrl url = std::static_pointer_cast(file)->getUrl(); + Lock lock(_urlMutex); + _urlMap.erase(url); +} diff --git a/libraries/model-networking/src/model-networking/KTXCache.h b/libraries/model-networking/src/model-networking/KTXCache.h new file mode 100644 index 0000000000..5b9cb04061 --- /dev/null +++ b/libraries/model-networking/src/model-networking/KTXCache.h @@ -0,0 +1,69 @@ +// +// KTXCache.h +// libraries/model-networking/src +// +// Created by Zach Pomerantz 2/22/2017. +// Copyright 2017 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_KTXCache_h +#define hifi_KTXCache_h + +#include + +#include + +class KTXFile; +using KTXFilePointer = std::shared_ptr; + +class KTXCache : public FileCache { + Q_OBJECT + +public: + KTXCache(const std::string& dir, const std::string& ext) : FileCache(dir, ext) {} + + struct Data { + Data(const QUrl& url, const Key& key, const char* data, size_t length) : + url(url), key(key), data(data), length(length) {} + const QUrl url; + const Key key; + const char* data; + size_t length; + }; + + KTXFilePointer writeFile(Data data); + KTXFilePointer getFile(const QUrl& url); + +protected: + File* createFile(const Key& key, const std::string& filepath, size_t length, void* extra) override final; + void evictedFile(const FilePointer& file) override final; + +private: + using Mutex = std::mutex; + using Lock = std::lock_guard; + struct QUrlHasher { std::size_t operator()(QUrl const& url) const { return qHash(url); } }; + + std::unordered_map _urlMap; + Mutex _urlMutex; +}; + +class KTXFile : public File { + Q_OBJECT + +public: + QUrl getUrl() const { return _url; } + +protected: + KTXFile(const Key& key, const std::string& filepath, size_t length, const QUrl& url) : + File(key, filepath, length), _url(url) {} + +private: + friend class KTXCache; + + const QUrl _url; +}; + +#endif // hifi_KTXCache_h From c044daf7b22953211197e7cef6d30102b47f0e0f Mon Sep 17 00:00:00 2001 From: Zach Pomerantz Date: Thu, 23 Feb 2017 16:27:28 -0500 Subject: [PATCH 071/302] TextureCache --- libraries/model-networking/CMakeLists.txt | 2 +- .../src/model-networking/TextureCache.cpp | 383 +++++++++++------- .../src/model-networking/TextureCache.h | 16 +- libraries/model/src/model/TextureMap.cpp | 88 ---- 4 files changed, 247 insertions(+), 242 deletions(-) diff --git a/libraries/model-networking/CMakeLists.txt b/libraries/model-networking/CMakeLists.txt index ed8cd7b5f9..00aa17ff57 100644 --- a/libraries/model-networking/CMakeLists.txt +++ b/libraries/model-networking/CMakeLists.txt @@ -1,4 +1,4 @@ set(TARGET_NAME model-networking) setup_hifi_library() -link_hifi_libraries(shared networking model fbx) +link_hifi_libraries(shared networking model fbx ktx) diff --git a/libraries/model-networking/src/model-networking/TextureCache.cpp b/libraries/model-networking/src/model-networking/TextureCache.cpp index 1f21e9e78d..4224cf076c 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.cpp +++ b/libraries/model-networking/src/model-networking/TextureCache.cpp @@ -18,27 +18,39 @@ #include #include #include + +#if DEBUG_DUMP_TEXTURE_LOADS #include #include +#endif #include #include #include +#include + #include #include #include #include +#include #include "ModelNetworkingLogging.h" #include #include Q_LOGGING_CATEGORY(trace_resource_parse_image, "trace.resource.parse.image") +Q_LOGGING_CATEGORY(trace_resource_parse_ktx, "trace.resource.parse.ktx") +Q_LOGGING_CATEGORY(trace_resource_cache_ktx, "trace.resource.cache.ktx") -TextureCache::TextureCache() { +const std::string TextureCache::KTX_DIRNAME { "ktx_cache" }; +const std::string TextureCache::KTX_EXT { "ktx" }; + +TextureCache::TextureCache() : + _ktxCache(KTX_DIRNAME, KTX_EXT) { setUnusedResourceCacheSize(0); setObjectName("TextureCache"); @@ -61,7 +73,7 @@ TextureCache::~TextureCache() { // this list taken from Ken Perlin's Improved Noise reference implementation (orig. in Java) at // http://mrl.nyu.edu/~perlin/noise/ -const int permutation[256] = +const int permutation[256] = { 151, 160, 137, 91, 90, 15, 131, 13, 201, 95, 96, 53, 194, 233, 7, 225, 140, 36, 103, 30, 69, 142, 8, 99, 37, 240, 21, 10, 23, 190, 6, 148, @@ -241,7 +253,7 @@ NetworkTexture::TextureLoaderFunc getTextureLoaderForType(NetworkTexture::Type t return NetworkTexture::TextureLoaderFunc(); break; } - + case Type::DEFAULT_TEXTURE: default: { return model::TextureUsage::create2DTextureFromImage; @@ -259,12 +271,32 @@ gpu::TexturePointer TextureCache::getImageTexture(const QString& path, Type type QSharedPointer TextureCache::createResource(const QUrl& url, const QSharedPointer& fallback, const void* extra) { + KTXFilePointer file = _ktxCache.getFile(url); const TextureExtra* textureExtra = static_cast(extra); auto type = textureExtra ? textureExtra->type : Type::DEFAULT_TEXTURE; - auto content = textureExtra ? textureExtra->content : QByteArray(); - auto maxNumPixels = textureExtra ? textureExtra->maxNumPixels : ABSOLUTE_MAX_TEXTURE_NUM_PIXELS; - return QSharedPointer(new NetworkTexture(url, type, content, maxNumPixels), - &Resource::deleter); + + NetworkTexture* texture; + if (file) { + texture = new NetworkTexture(url, type, file); + } else { + auto content = textureExtra ? textureExtra->content : QByteArray(); + auto maxNumPixels = textureExtra ? textureExtra->maxNumPixels : ABSOLUTE_MAX_TEXTURE_NUM_PIXELS; + texture = new NetworkTexture(url, type, content, maxNumPixels); + } + + return QSharedPointer(texture, &Resource::deleter); +} + +NetworkTexture::NetworkTexture(const QUrl& url, Type type, const KTXFilePointer& file) : + Resource(url), + _type(type), + _file(file) { + _textureSource = std::make_shared(); + + if (file) { + _startedLoading = true; + QMetaObject::invokeMethod(this, "loadFile", Qt::QueuedConnection); + } } NetworkTexture::NetworkTexture(const QUrl& url, Type type, const QByteArray& content, int maxNumPixels) : @@ -278,7 +310,6 @@ NetworkTexture::NetworkTexture(const QUrl& url, Type type, const QByteArray& con _loaded = true; } - std::string theName = url.toString().toStdString(); // if we have content, load it after we have our self pointer if (!content.isEmpty()) { _startedLoading = true; @@ -299,149 +330,6 @@ NetworkTexture::TextureLoaderFunc NetworkTexture::getTextureLoader() const { return getTextureLoaderForType(_type); } - -class ImageReader : public QRunnable { -public: - - ImageReader(const QWeakPointer& resource, const QByteArray& data, - const QUrl& url = QUrl(), int maxNumPixels = ABSOLUTE_MAX_TEXTURE_NUM_PIXELS); - - virtual void run() override; - -private: - static void listSupportedImageFormats(); - - QWeakPointer _resource; - QUrl _url; - QByteArray _content; - int _maxNumPixels; -}; - -void NetworkTexture::downloadFinished(const QByteArray& data) { - // send the reader off to the thread pool - QThreadPool::globalInstance()->start(new ImageReader(_self, data, _url)); -} - -void NetworkTexture::loadContent(const QByteArray& content) { - QThreadPool::globalInstance()->start(new ImageReader(_self, content, _url, _maxNumPixels)); -} - -ImageReader::ImageReader(const QWeakPointer& resource, const QByteArray& data, - const QUrl& url, int maxNumPixels) : - _resource(resource), - _url(url), - _content(data), - _maxNumPixels(maxNumPixels) -{ -#if DEBUG_DUMP_TEXTURE_LOADS - static auto start = usecTimestampNow() / USECS_PER_MSEC; - auto now = usecTimestampNow() / USECS_PER_MSEC - start; - QString urlStr = _url.toString(); - auto dot = urlStr.lastIndexOf("."); - QString outFileName = QString(QCryptographicHash::hash(urlStr.toLocal8Bit(), QCryptographicHash::Md5).toHex()) + urlStr.right(urlStr.length() - dot); - QFile loadRecord("h:/textures/loads.txt"); - loadRecord.open(QFile::Text | QFile::Append | QFile::ReadWrite); - loadRecord.write(QString("%1 %2\n").arg(now).arg(outFileName).toLocal8Bit()); - outFileName = "h:/textures/" + outFileName; - QFileInfo outInfo(outFileName); - if (!outInfo.exists()) { - QFile outFile(outFileName); - outFile.open(QFile::WriteOnly | QFile::Truncate); - outFile.write(data); - outFile.close(); - } -#endif - DependencyManager::get()->incrementStat("PendingProcessing"); -} - -void ImageReader::listSupportedImageFormats() { - static std::once_flag once; - std::call_once(once, []{ - auto supportedFormats = QImageReader::supportedImageFormats(); - qCDebug(modelnetworking) << "List of supported Image formats:" << supportedFormats.join(", "); - }); -} - -void ImageReader::run() { - DependencyManager::get()->decrementStat("PendingProcessing"); - - CounterStat counter("Processing"); - - PROFILE_RANGE_EX(resource_parse_image, __FUNCTION__, 0xffff0000, 0, { { "url", _url.toString() } }); - auto originalPriority = QThread::currentThread()->priority(); - if (originalPriority == QThread::InheritPriority) { - originalPriority = QThread::NormalPriority; - } - QThread::currentThread()->setPriority(QThread::LowPriority); - Finally restorePriority([originalPriority]{ - QThread::currentThread()->setPriority(originalPriority); - }); - - if (!_resource.data()) { - qCWarning(modelnetworking) << "Abandoning load of" << _url << "; could not get strong ref"; - return; - } - listSupportedImageFormats(); - - // Help the QImage loader by extracting the image file format from the url filename ext. - // Some tga are not created properly without it. - auto filename = _url.fileName().toStdString(); - auto filenameExtension = filename.substr(filename.find_last_of('.') + 1); - QImage image = QImage::fromData(_content, filenameExtension.c_str()); - - // Note that QImage.format is the pixel format which is different from the "format" of the image file... - auto imageFormat = image.format(); - int imageWidth = image.width(); - int imageHeight = image.height(); - - if (imageWidth == 0 || imageHeight == 0 || imageFormat == QImage::Format_Invalid) { - if (filenameExtension.empty()) { - qCDebug(modelnetworking) << "QImage failed to create from content, no file extension:" << _url; - } else { - qCDebug(modelnetworking) << "QImage failed to create from content" << _url; - } - return; - } - - if (imageWidth * imageHeight > _maxNumPixels) { - float scaleFactor = sqrtf(_maxNumPixels / (float)(imageWidth * imageHeight)); - int originalWidth = imageWidth; - int originalHeight = imageHeight; - imageWidth = (int)(scaleFactor * (float)imageWidth + 0.5f); - imageHeight = (int)(scaleFactor * (float)imageHeight + 0.5f); - QImage newImage = image.scaled(QSize(imageWidth, imageHeight), Qt::IgnoreAspectRatio, Qt::SmoothTransformation); - image.swap(newImage); - qCDebug(modelnetworking) << "Downscale image" << _url - << "from" << originalWidth << "x" << originalHeight - << "to" << imageWidth << "x" << imageHeight; - } - - gpu::TexturePointer texture = nullptr; - { - // Double-check the resource still exists between long operations. - auto resource = _resource.toStrongRef(); - if (!resource) { - qCWarning(modelnetworking) << "Abandoning load of" << _url << "; could not get strong ref"; - return; - } - - auto url = _url.toString().toStdString(); - - PROFILE_RANGE_EX(resource_parse_image, __FUNCTION__, 0xffffff00, 0); - texture.reset(resource.dynamicCast()->getTextureLoader()(image, url)); - } - - // Ensure the resource has not been deleted - auto resource = _resource.toStrongRef(); - if (!resource) { - qCWarning(modelnetworking) << "Abandoning load of" << _url << "; could not get strong ref"; - } else { - QMetaObject::invokeMethod(resource.data(), "setImage", - Q_ARG(gpu::TexturePointer, texture), - Q_ARG(int, imageWidth), Q_ARG(int, imageHeight)); - } -} - void NetworkTexture::setImage(gpu::TexturePointer texture, int originalWidth, int originalHeight) { _originalWidth = originalWidth; @@ -464,3 +352,196 @@ void NetworkTexture::setImage(gpu::TexturePointer texture, int originalWidth, emit networkTextureCreated(qWeakPointerCast (_self)); } + +class Reader : public QRunnable { +public: + Reader(const QWeakPointer& resource, const QUrl& url) : _resource(resource), _url(url) { + DependencyManager::get()->incrementStat("PendingProcessing"); + } + void run() override final { + DependencyManager::get()->decrementStat("PendingProcessing"); + CounterStat counter("Processing"); + + // Run this with low priority, then restore thread priority + auto originalPriority = QThread::currentThread()->priority(); + if (originalPriority == QThread::InheritPriority) { + originalPriority = QThread::NormalPriority; + } + QThread::currentThread()->setPriority(QThread::LowPriority); + Finally restorePriority([originalPriority]{ + QThread::currentThread()->setPriority(originalPriority); + }); + + if (!_resource.lock()) { // to ensure the resource is still needed + qCDebug(modelnetworking) << _url << "loading stopped; resource out of scope"; + return; + } + + read(); + } + virtual void read() = 0; + +protected: + QWeakPointer _resource; + QUrl _url; +}; + +class FileReader : public Reader { +public: + FileReader(const QWeakPointer& resource, const QUrl& url) : Reader(resource, url) {} + void read() override final; +}; + +class ImageReader : public Reader { +public: + ImageReader(const QWeakPointer& resource, const QUrl& url, + const QByteArray& data, int maxNumPixels = ABSOLUTE_MAX_TEXTURE_NUM_PIXELS); + void read() override final; + +private: + static void listSupportedImageFormats(); + + QByteArray _content; + int _maxNumPixels; +}; + +void NetworkTexture::downloadFinished(const QByteArray& data) { + // send the reader off to the thread pool + QThreadPool::globalInstance()->start(new ImageReader(_self, _url, data)); +} + +void NetworkTexture::loadFile() { + QThreadPool::globalInstance()->start(new FileReader(_self, _url)); +} + +void NetworkTexture::loadContent(const QByteArray& content) { + QThreadPool::globalInstance()->start(new ImageReader(_self, _url, content, _maxNumPixels)); +} + +ImageReader::ImageReader(const QWeakPointer& resource, const QUrl& url, + const QByteArray& data, int maxNumPixels) : + Reader(resource, url), _content(data), _maxNumPixels(maxNumPixels) { + listSupportedImageFormats(); +#if DEBUG_DUMP_TEXTURE_LOADS + static auto start = usecTimestampNow() / USECS_PER_MSEC; + auto now = usecTimestampNow() / USECS_PER_MSEC - start; + QString urlStr = _url.toString(); + auto dot = urlStr.lastIndexOf("."); + QString outFileName = QString(QCryptographicHash::hash(urlStr.toLocal8Bit(), QCryptographicHash::Md5).toHex()) + urlStr.right(urlStr.length() - dot); + QFile loadRecord("h:/textures/loads.txt"); + loadRecord.open(QFile::Text | QFile::Append | QFile::ReadWrite); + loadRecord.write(QString("%1 %2\n").arg(now).arg(outFileName).toLocal8Bit()); + outFileName = "h:/textures/" + outFileName; + QFileInfo outInfo(outFileName); + if (!outInfo.exists()) { + QFile outFile(outFileName); + outFile.open(QFile::WriteOnly | QFile::Truncate); + outFile.write(data); + outFile.close(); + } +#endif +} + +void ImageReader::listSupportedImageFormats() { + static std::once_flag once; + std::call_once(once, []{ + auto supportedFormats = QImageReader::supportedImageFormats(); + qCDebug(modelnetworking) << "List of supported Image formats:" << supportedFormats.join(", "); + }); +} + +void FileReader::read() { + PROFILE_RANGE_EX(resource_parse_ktx, __FUNCTION__, 0xffff0000, 0); + + // TODO: + // auto ktx = ktx::KTX::create(); + // auto texture = gpu::Texture::unserialize(getUsage(), getUsageType(), ktx, getSampler()); + // FIXME: do I need to set the file as a backing file here? +} + +void ImageReader::read() { + PROFILE_RANGE_EX(resource_parse_image, __FUNCTION__, 0xffff0000, 0, { { "url", _url.toString() } }); + + // Help the QImage loader by extracting the image file format from the url filename ext. + // Some tga are not created properly without it. + auto filename = _url.fileName().toStdString(); + auto filenameExtension = filename.substr(filename.find_last_of('.') + 1); + QImage image = QImage::fromData(_content, filenameExtension.c_str()); + int imageWidth = image.width(); + int imageHeight = image.height(); + + // Validate that the image loaded + if (imageWidth == 0 || imageHeight == 0 || image.format() == QImage::Format_Invalid) { + QString reason(filenameExtension.empty() ? "" : "(no file extension)"); + qCWarning(modelnetworking) << "Failed to load" << _url << reason; + return; + } + + // Validate the image is less than _maxNumPixels, and downscale if necessary + if (imageWidth * imageHeight > _maxNumPixels) { + float scaleFactor = sqrtf(_maxNumPixels / (float)(imageWidth * imageHeight)); + int originalWidth = imageWidth; + int originalHeight = imageHeight; + imageWidth = (int)(scaleFactor * (float)imageWidth + 0.5f); + imageHeight = (int)(scaleFactor * (float)imageHeight + 0.5f); + QImage newImage = image.scaled(QSize(imageWidth, imageHeight), Qt::IgnoreAspectRatio, Qt::SmoothTransformation); + image.swap(newImage); + qCDebug(modelnetworking).nospace() << "Downscaled " << _url << " (" << + QSize(originalWidth, originalHeight) << " to " << + QSize(imageWidth, imageHeight) << ")"; + } + + // Load the image into a gpu::Texture + gpu::TexturePointer texture = nullptr; + { + auto resource = _resource.lock(); // to ensure the resource is still needed + if (!resource) { + qCDebug(modelnetworking) << _url << "loading stopped; resource out of scope"; + return; + } + + auto url = _url.toString().toStdString(); + + PROFILE_RANGE_EX(resource_parse_image, __FUNCTION__, 0xffff0000, 0); + texture.reset(resource.dynamicCast()->getTextureLoader()(image, url)); + texture->setSource(url); + } + + // Hash the source image to use as a filename for on-disk caching + std::string hash; + { + QCryptographicHash hasher(QCryptographicHash::Md5); + hasher.addData((const char*)image.bits(), image.byteCount() * sizeof(char)); + hash = hasher.result().toHex().toStdString(); + } + + { + auto resource = _resource.lock(); // to ensure the resource is still needed + if (!resource) { + qCDebug(modelnetworking) << _url << "loading stopped; resource out of scope"; + return; + } + + PROFILE_RANGE_EX(resource_cache_ktx, __FUNCTION__, 0xffffff00, 0); + auto ktx = gpu::Texture::serialize(*texture); + const char* data = reinterpret_cast(ktx->_storage->data()); + size_t length = ktx->_storage->size(); + KTXFilePointer file; + auto& ktxCache = DependencyManager::get()->_ktxCache; + if (!ktx || !(file = ktxCache.writeFile({ _url, hash, data, length }))) { + qCWarning(modelnetworking) << _url << "file cache failed"; + } else { + resource.dynamicCast()->_file = file; + // FIXME: do I need to set the file as a backing file here? + } + } + + auto resource = _resource.toStrongRef(); // to ensure the resource is still needed + if (resource) { + QMetaObject::invokeMethod(resource.data(), "setImage", + Q_ARG(gpu::TexturePointer, texture), + Q_ARG(int, imageWidth), Q_ARG(int, imageHeight)); + } else { + qCDebug(modelnetworking) << _url << "loading stopped; resource out of scope"; + } +} diff --git a/libraries/model-networking/src/model-networking/TextureCache.h b/libraries/model-networking/src/model-networking/TextureCache.h index 749b5a2ebb..f05ab6b220 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.h +++ b/libraries/model-networking/src/model-networking/TextureCache.h @@ -23,6 +23,8 @@ #include #include +#include "KTXCache.h" + const int ABSOLUTE_MAX_TEXTURE_NUM_PIXELS = 8192 * 8192; namespace gpu { @@ -63,6 +65,7 @@ public: typedef gpu::Texture* TextureLoader(const QImage& image, const std::string& srcImageName); using TextureLoaderFunc = std::function; + NetworkTexture(const QUrl& url, Type type, const KTXFilePointer& file); NetworkTexture(const QUrl& url, Type type, const QByteArray& content, int maxNumPixels); NetworkTexture(const QUrl& url, const TextureLoaderFunc& textureLoader, const QByteArray& content); @@ -80,17 +83,20 @@ signals: void networkTextureCreated(const QWeakPointer& self); protected: - virtual bool isCacheable() const override { return _loaded; } virtual void downloadFinished(const QByteArray& data) override; Q_INVOKABLE void loadContent(const QByteArray& content); + Q_INVOKABLE void loadFile(); Q_INVOKABLE void setImage(gpu::TexturePointer texture, int originalWidth, int originalHeight); private: + friend class ImageReader; + Type _type; TextureLoaderFunc _textureLoader { [](const QImage&, const std::string&){ return nullptr; } }; + KTXFilePointer _file; int _originalWidth { 0 }; int _originalHeight { 0 }; int _width { 0 }; @@ -143,9 +149,15 @@ protected: const void* extra) override; private: + friend class ImageReader; + friend class DilatableNetworkTexture; + TextureCache(); virtual ~TextureCache(); - friend class DilatableNetworkTexture; + + static const std::string KTX_DIRNAME; + static const std::string KTX_EXT; + KTXCache _ktxCache; gpu::TexturePointer _permutationNormalTexture; gpu::TexturePointer _whiteTexture; diff --git a/libraries/model/src/model/TextureMap.cpp b/libraries/model/src/model/TextureMap.cpp index ce602b7b9b..e8d6d4cd6f 100755 --- a/libraries/model/src/model/TextureMap.cpp +++ b/libraries/model/src/model/TextureMap.cpp @@ -85,87 +85,6 @@ QImage processSourceImage(const QImage& srcImage, bool cubemap) { return srcImage; } -gpu::Texture* cacheTexture(const std::string& name, gpu::Texture* srcTexture, bool write = true, bool read = true) { - if (!srcTexture) { - return nullptr; - } - - static QString ktxCacheFolder; - static std::once_flag once; - std::call_once(once, [&] { - // Prepare cache directory - static const QString HIFI_KTX_FOLDER("hifi_ktx"); - QString docsLocation = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation); - ktxCacheFolder = docsLocation + "/" + HIFI_KTX_FOLDER + "/"; - QFileInfo info(ktxCacheFolder); - if (!info.exists()) { - QDir(docsLocation).mkpath(HIFI_KTX_FOLDER); - } - }); - - - std::string cleanedName = QCryptographicHash::hash(QUrl::toPercentEncoding(name.c_str()), QCryptographicHash::Sha1).toHex().toStdString(); - std::string cacheFilename(ktxCacheFolder.toStdString()); - cacheFilename += "/"; - cacheFilename += cleanedName; - cacheFilename += ".ktx"; - - gpu::Texture* returnedTexture = srcTexture; - { - if (write && !QFileInfo(cacheFilename.c_str()).exists()) { - auto ktxMemory = gpu::Texture::serialize(*srcTexture); - if (ktxMemory) { - const auto& ktxStorage = ktxMemory->getStorage(); - auto header = ktxMemory->getHeader(); - QFile outFile(cacheFilename.c_str()); - if (!outFile.open(QFile::Truncate | QFile::ReadWrite)) { - throw std::runtime_error("Unable to open file"); - } - //auto ktxSize = sizeof(ktx::Header); // ktxStorage->size() - auto ktxSize = ktxStorage->size(); - outFile.resize(ktxSize); - auto dest = outFile.map(0, ktxSize); - memcpy(dest, ktxStorage->data(), ktxSize); - outFile.unmap(dest); - outFile.close(); - } - } - - if (read && QFileInfo(cacheFilename.c_str()).exists()) { -#define DEBUG_KTX_LOADING 0 -#if DEBUG_KTX_LOADING - { - FILE* file = fopen(cacheFilename.c_str(), "rb"); - if (file != nullptr) { - // obtain file size: - fseek (file , 0 , SEEK_END); - auto size = ftell(file); - rewind(file); - - auto storage = std::make_shared(size); - fread(storage->data(), 1, storage->size(), file); - fclose (file); - - //then create a new texture out of the ktx - auto theNewTexure = Texture::unserialize(srcTexture->getUsage(), srcTexture->getUsageType(), - ktx::KTX::create(std::static_pointer_cast(storage)), srcTexture->getSampler()); - - if (theNewTexure) { - returnedTexture = theNewTexure; - delete srcTexture; - } - } - } -#else - ktx::StoragePointer storage = std::make_shared(cacheFilename.c_str()); - auto ktxFile = ktx::KTX::create(storage); - returnedTexture->setKtxBacking(ktxFile); -#endif - } - } - return returnedTexture; -} - void TextureMap::setTextureSource(TextureSourcePointer& textureSource) { _textureSource = textureSource; } @@ -355,7 +274,6 @@ gpu::Texture* TextureUsage::process2DTextureColorFromImage(const QImage& srcImag ::generateMips(theTexture, image, false); } theTexture->setSource(srcImageName); - theTexture = cacheTexture(theTexture->source(), theTexture); } return theTexture; @@ -405,7 +323,6 @@ gpu::Texture* TextureUsage::createNormalTextureFromNormalImage(const QImage& src generateMips(theTexture, image, true); theTexture->setSource(srcImageName); - theTexture = cacheTexture(theTexture->source(), theTexture, true, true); } return theTexture; @@ -496,7 +413,6 @@ gpu::Texture* TextureUsage::createNormalTextureFromBumpImage(const QImage& srcIm generateMips(theTexture, image, true); theTexture->setSource(srcImageName); - theTexture = cacheTexture(theTexture->source(), theTexture, true, false); } return theTexture; @@ -533,7 +449,6 @@ gpu::Texture* TextureUsage::createRoughnessTextureFromImage(const QImage& srcIma generateMips(theTexture, image, true); theTexture->setSource(srcImageName); - theTexture = cacheTexture(theTexture->source(), theTexture); } return theTexture; @@ -574,7 +489,6 @@ gpu::Texture* TextureUsage::createRoughnessTextureFromGlossImage(const QImage& s generateMips(theTexture, image, true); theTexture->setSource(srcImageName); - theTexture = cacheTexture(theTexture->source(), theTexture); } return theTexture; @@ -612,7 +526,6 @@ gpu::Texture* TextureUsage::createMetallicTextureFromImage(const QImage& srcImag generateMips(theTexture, image, true); theTexture->setSource(srcImageName); - theTexture = cacheTexture(theTexture->source(), theTexture); } return theTexture; @@ -946,7 +859,6 @@ gpu::Texture* TextureUsage::processCubeTextureColorFromImage(const QImage& srcIm } theTexture->setSource(srcImageName); - theTexture = cacheTexture(theTexture->source(), theTexture); } } From b03f51dd26c740bbd086994a2422ad6ed2a66548 Mon Sep 17 00:00:00 2001 From: Zach Pomerantz Date: Tue, 28 Feb 2017 16:08:54 -0500 Subject: [PATCH 072/302] fix double fclose --- libraries/networking/src/FileCache.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/libraries/networking/src/FileCache.cpp b/libraries/networking/src/FileCache.cpp index a717546de4..20b78c281a 100644 --- a/libraries/networking/src/FileCache.cpp +++ b/libraries/networking/src/FileCache.cpp @@ -62,7 +62,6 @@ FilePointer FileCache::writeFile(const Key& key, const char* data, size_t length FILE* saveFile = fopen(filepath.c_str(), "wb"); if (saveFile != nullptr && fwrite(data, length, 1, saveFile) && fclose(saveFile) == 0) { file.reset(createFile(key, filepath, length, extra), &fileDeleter); - fclose(saveFile); file->_cache = this; _files[key] = file; _numTotalFiles += 1; From de23a11dee7a01525cbee3a0e8ec41d967e428be Mon Sep 17 00:00:00 2001 From: Zach Pomerantz Date: Tue, 28 Feb 2017 17:25:56 -0500 Subject: [PATCH 073/302] wip --- libraries/networking/src/FileCache.cpp | 69 ++++++++++++++++---------- 1 file changed, 43 insertions(+), 26 deletions(-) diff --git a/libraries/networking/src/FileCache.cpp b/libraries/networking/src/FileCache.cpp index 20b78c281a..e4c715d020 100644 --- a/libraries/networking/src/FileCache.cpp +++ b/libraries/networking/src/FileCache.cpp @@ -54,13 +54,14 @@ FilePointer FileCache::writeFile(const Key& key, const char* data, size_t length // if file already exists, return it FilePointer file = getFile(key); if (file) { - qCWarning(file_cache) << "Attempted to overwrite" << filepath.c_str(); + qCWarning(file_cache, "Attempted to overwrite %", key.c_str()); return file; } // write the new file FILE* saveFile = fopen(filepath.c_str(), "wb"); if (saveFile != nullptr && fwrite(data, length, 1, saveFile) && fclose(saveFile) == 0) { + qCInfo(file_cache, "Wrote %s", key.c_str()); file.reset(createFile(key, filepath, length, extra), &fileDeleter); file->_cache = this; _files[key] = file; @@ -69,7 +70,7 @@ FilePointer FileCache::writeFile(const Key& key, const char* data, size_t length emit dirty(); } else { - qCWarning(file_cache, "Failed to write %s (%s)", filepath.c_str(), strerror(errno)); + qCWarning(file_cache, "Failed to write %s (%s)", key.c_str(), strerror(errno)); errno = 0; } @@ -88,6 +89,7 @@ FilePointer FileCache::getFile(const Key& key) { if (file) { // if it exists, it is active - remove it from the cache removeUnusedFile(file); + qCInfo(file_cache, "Found %s", key.c_str()); emit dirty(); } else { // if not, remove the weak_ptr @@ -115,28 +117,30 @@ std::string FileCache::createDir(const std::string& dirname) { std::string entry; manifest >> entry; persistedEntries.insert(entry); - - // ZZMP: rm - for (const auto& entry : persistedEntries) - qDebug() << "ZZMP" << entry.c_str(); - qDebug() << "ZZMP" << "---"; + qCInfo(file_cache, "Manifest contents: %s", entry.c_str()); } + } else { + qCWarning(file_cache, "Missing manifest"); } + foreach(QString filename, dir.entryList()) { if (persistedEntries.find(filename.toStdString()) == persistedEntries.cend()) { dir.remove(filename); + qCInfo(file_cache) << "Cleaned" << filename; } } + qCDebug(file_cache, "Initiated %s", dirpath.data()); } else { dir.mkpath(dirpath); + qCDebug(file_cache, "Created %s", dirpath.data()); } return dirpath.toStdString(); } std::string FileCache::getFilepath(const Key& key) { - return _dir + key + '.' + _ext; + return _dir + '/' + key + '.' + _ext; } void FileCache::addUnusedFile(const FilePointer file) { @@ -197,34 +201,46 @@ void FileCache::reserve(size_t length) { } void FileCache::clear() { - std::string manifestPath= _dir + MANIFEST_NAME; - FILE* manifest = fopen(manifestPath.c_str(), "wb"); + try { + std::string manifestPath= _dir + '/' + MANIFEST_NAME; + std::ofstream manifest(manifestPath); - Lock lock(_unusedFilesMutex); - for (const auto& val : _unusedFiles) { - const FilePointer& file = val.second; - file->_cache = nullptr; + bool firstEntry = true; - if (_unusedFilesSize > _offlineFilesMaxSize) { - _unusedFilesSize -= file->getLength(); - } else { - std::string key = file->getKey() + '.' + _ext + '\n'; - if (manifest != nullptr && !fwrite(key.c_str(), key.length(), 1, manifest)) { - manifest = nullptr; // to prevent future writes + { + Lock lock(_unusedFilesMutex); + for (const auto& val : _unusedFiles) { + const FilePointer& file = val.second; + file->_cache = nullptr; + + if (_unusedFilesSize > _offlineFilesMaxSize) { + _unusedFilesSize -= file->getLength(); + } else { + if (!firstEntry) { + manifest << '\n'; + } + firstEntry = false; + manifest << file->getKey(); + + file->_shouldPersist = true; + qCInfo(file_cache, "Persisting %s", file->getKey().c_str()); + } } - file->_shouldPersist = true; } - } - if (manifest == nullptr || fclose(manifest) != 0) { + { + Lock lock(_filesMutex); + for (const auto& val : _files) { + const FilePointer& file = val.second + } + } catch (std::exception& e) { + qCWarning(file_cache, "Failed to write manifest (%s)", e.what()); for (const auto& val : _unusedFiles) { val.second->_shouldPersist = false; } - - qCWarning(file_cache, "Failed to write %s (%s)", manifestPath.c_str(), strerror(errno)); - errno = 0; } + Lock lock(_unusedFilesMutex); _unusedFiles.clear(); } @@ -240,6 +256,7 @@ void File::deleter() { File::~File() { QFile file(getFilepath().c_str()); if (file.exists() && !_shouldPersist) { + qCInfo(file_cache, "Unlinked %s", getFilepath().c_str()); file.remove(); } } From a6a0fd3851a46b3e2f9b6c48d6c75a29928ce7ed Mon Sep 17 00:00:00 2001 From: Zach Pomerantz Date: Wed, 1 Mar 2017 11:32:11 -0500 Subject: [PATCH 074/302] fix FileCache persistence --- .../src/model-networking/KTXCache.cpp | 1 + libraries/networking/src/FileCache.cpp | 92 ++++++++++--------- libraries/networking/src/FileCache.h | 3 +- 3 files changed, 52 insertions(+), 44 deletions(-) diff --git a/libraries/model-networking/src/model-networking/KTXCache.cpp b/libraries/model-networking/src/model-networking/KTXCache.cpp index 1ab32698b6..74926d12a1 100644 --- a/libraries/model-networking/src/model-networking/KTXCache.cpp +++ b/libraries/model-networking/src/model-networking/KTXCache.cpp @@ -39,6 +39,7 @@ File* KTXCache::createFile(const Key& key, const std::string& filepath, size_t l const QUrl& url = reinterpret_cast(extra)->url; Lock lock(_urlMutex); _urlMap[url] = key; + qCInfo(file_cache) << "Wrote KTX" << key.c_str() << url; return new KTXFile(key, filepath, length, url); } diff --git a/libraries/networking/src/FileCache.cpp b/libraries/networking/src/FileCache.cpp index e4c715d020..61712b383b 100644 --- a/libraries/networking/src/FileCache.cpp +++ b/libraries/networking/src/FileCache.cpp @@ -35,8 +35,9 @@ void FileCache::setOfflineFileCacheSize(size_t offlineFilesMaxSize) { FileCache::FileCache(const std::string& dirname, const std::string& ext, QObject* parent) : QObject(parent), - _dir(createDir(dirname)), - _ext(ext) {} + _ext(ext), + _dirname(dirname), + _dir(createDir(_dirname)) {} FileCache::~FileCache() { clear(); @@ -54,14 +55,13 @@ FilePointer FileCache::writeFile(const Key& key, const char* data, size_t length // if file already exists, return it FilePointer file = getFile(key); if (file) { - qCWarning(file_cache, "Attempted to overwrite %", key.c_str()); + qCWarning(file_cache, "[%s] Attempted to overwrite %s", _dirname.c_str(), key.c_str()); return file; } // write the new file FILE* saveFile = fopen(filepath.c_str(), "wb"); if (saveFile != nullptr && fwrite(data, length, 1, saveFile) && fclose(saveFile) == 0) { - qCInfo(file_cache, "Wrote %s", key.c_str()); file.reset(createFile(key, filepath, length, extra), &fileDeleter); file->_cache = this; _files[key] = file; @@ -70,7 +70,7 @@ FilePointer FileCache::writeFile(const Key& key, const char* data, size_t length emit dirty(); } else { - qCWarning(file_cache, "Failed to write %s (%s)", key.c_str(), strerror(errno)); + qCWarning(file_cache, "[%s] Failed to write %s (%s)", _dirname.c_str(), key.c_str(), strerror(errno)); errno = 0; } @@ -89,7 +89,7 @@ FilePointer FileCache::getFile(const Key& key) { if (file) { // if it exists, it is active - remove it from the cache removeUnusedFile(file); - qCInfo(file_cache, "Found %s", key.c_str()); + qCInfo(file_cache, "[%s] Found %s", _dirname.c_str(), key.c_str()); emit dirty(); } else { // if not, remove the weak_ptr @@ -101,6 +101,7 @@ FilePointer FileCache::getFile(const Key& key) { } File* FileCache::createFile(const Key& key, const std::string& filepath, size_t length, void* extra) { + qCInfo(file_cache, "Wrote %s", key.c_str()); return new File(key, filepath, length); } @@ -116,24 +117,26 @@ std::string FileCache::createDir(const std::string& dirname) { while (manifest.good()) { std::string entry; manifest >> entry; - persistedEntries.insert(entry); - qCInfo(file_cache, "Manifest contents: %s", entry.c_str()); + if (!entry.empty()) { + qCInfo(file_cache, "[%s] Manifest contains %s", _dirname.c_str(), entry.c_str()); + persistedEntries.insert(entry + '.' + _ext); + } } } else { - qCWarning(file_cache, "Missing manifest"); + qCWarning(file_cache, "[%s] Missing manifest", _dirname.c_str()); } - foreach(QString filename, dir.entryList()) { + foreach(QString filename, dir.entryList(QDir::Filters(QDir::NoDotAndDotDot | QDir::Files))) { if (persistedEntries.find(filename.toStdString()) == persistedEntries.cend()) { dir.remove(filename); - qCInfo(file_cache) << "Cleaned" << filename; + qCInfo(file_cache, "[%s] Cleaned %s", _dirname.c_str(), filename.toStdString().c_str()); } } - qCDebug(file_cache, "Initiated %s", dirpath.data()); + qCDebug(file_cache) << "Initiated" << dirpath; } else { dir.mkpath(dirpath); - qCDebug(file_cache, "Created %s", dirpath.data()); + qCDebug(file_cache) << "Created" << dirpath; } return dirpath.toStdString(); @@ -201,43 +204,46 @@ void FileCache::reserve(size_t length) { } void FileCache::clear() { + auto forAllFiles = [&](std::function functor) { + Lock unusedFilesLock(_unusedFilesMutex); + for (const auto& pair : _unusedFiles) { + functor(pair.second); + } + // clear files so they are not reiterated from _files + _unusedFiles.clear(); + unusedFilesLock.unlock(); + + Lock filesLock(_filesMutex); + for (const auto& pair : _files) { + FilePointer file; + if ((file = pair.second.lock())) { + functor(file); + } + } + }; + try { std::string manifestPath= _dir + '/' + MANIFEST_NAME; std::ofstream manifest(manifestPath); - bool firstEntry = true; + forAllFiles([&](const FilePointer& file) { + file->_cache = nullptr; - { - Lock lock(_unusedFilesMutex); - for (const auto& val : _unusedFiles) { - const FilePointer& file = val.second; - file->_cache = nullptr; - - if (_unusedFilesSize > _offlineFilesMaxSize) { - _unusedFilesSize -= file->getLength(); - } else { - if (!firstEntry) { - manifest << '\n'; - } - firstEntry = false; - manifest << file->getKey(); - - file->_shouldPersist = true; - qCInfo(file_cache, "Persisting %s", file->getKey().c_str()); - } + if (_totalFilesSize > _offlineFilesMaxSize) { + _totalFilesSize -= file->getLength(); + } else { + manifest << file->getKey() << '\n'; + file->_shouldPersist = true; + qCInfo(file_cache, "[%s] Persisting %s", _dirname.c_str(), file->getKey().c_str()); } - } - - { - Lock lock(_filesMutex); - for (const auto& val : _files) { - const FilePointer& file = val.second - } + }); } catch (std::exception& e) { - qCWarning(file_cache, "Failed to write manifest (%s)", e.what()); - for (const auto& val : _unusedFiles) { - val.second->_shouldPersist = false; - } + qCWarning(file_cache, "[%s] Failed to write manifest (%s)", _dirname.c_str(), e.what()); + + forAllFiles([](const FilePointer& file) { + file->_cache = nullptr; + file->_shouldPersist = false; + }); } Lock lock(_unusedFilesMutex); diff --git a/libraries/networking/src/FileCache.h b/libraries/networking/src/FileCache.h index f068f6e7d5..09e1760ae5 100644 --- a/libraries/networking/src/FileCache.h +++ b/libraries/networking/src/FileCache.h @@ -98,8 +98,9 @@ private: std::atomic _totalFilesSize { 0 }; std::atomic _unusedFilesSize { 0 }; - std::string _dir; std::string _ext; + std::string _dirname; + std::string _dir; std::unordered_map> _files; Mutex _filesMutex; From e8319f967dd7c376dbf0418aa5c1aefdd3d07664 Mon Sep 17 00:00:00 2001 From: Zach Pomerantz Date: Wed, 1 Mar 2017 17:28:51 -0500 Subject: [PATCH 075/302] add loading from KTXCache --- .../src/model-networking/KTXCache.cpp | 7 +++- .../src/model-networking/KTXCache.h | 5 +++ .../src/model-networking/TextureCache.cpp | 39 ++++++++++++++----- .../src/model-networking/TextureCache.h | 1 + 4 files changed, 42 insertions(+), 10 deletions(-) diff --git a/libraries/model-networking/src/model-networking/KTXCache.cpp b/libraries/model-networking/src/model-networking/KTXCache.cpp index 74926d12a1..3e288c6b27 100644 --- a/libraries/model-networking/src/model-networking/KTXCache.cpp +++ b/libraries/model-networking/src/model-networking/KTXCache.cpp @@ -11,7 +11,7 @@ #include "KTXCache.h" -#include +#include KTXFilePointer KTXCache::writeFile(Data data) { return std::static_pointer_cast(FileCache::writeFile(data.key, data.data, data.length, (void*)&data)); @@ -48,3 +48,8 @@ void KTXCache::evictedFile(const FilePointer& file) { Lock lock(_urlMutex); _urlMap.erase(url); } + +std::unique_ptr KTXFile::getKTX() const { + ktx::StoragePointer storage = std::make_shared(getFilepath().c_str()); + return ktx::KTX::create(storage); +} diff --git a/libraries/model-networking/src/model-networking/KTXCache.h b/libraries/model-networking/src/model-networking/KTXCache.h index 5b9cb04061..835c28e6db 100644 --- a/libraries/model-networking/src/model-networking/KTXCache.h +++ b/libraries/model-networking/src/model-networking/KTXCache.h @@ -16,6 +16,10 @@ #include +namespace ktx { + class KTX; +} + class KTXFile; using KTXFilePointer = std::shared_ptr; @@ -55,6 +59,7 @@ class KTXFile : public File { public: QUrl getUrl() const { return _url; } + std::unique_ptr getKTX() const; protected: KTXFile(const Key& key, const std::string& filepath, size_t length, const QUrl& url) : diff --git a/libraries/model-networking/src/model-networking/TextureCache.cpp b/libraries/model-networking/src/model-networking/TextureCache.cpp index 4224cf076c..aef0a6d56f 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.cpp +++ b/libraries/model-networking/src/model-networking/TextureCache.cpp @@ -451,12 +451,32 @@ void ImageReader::listSupportedImageFormats() { } void FileReader::read() { - PROFILE_RANGE_EX(resource_parse_ktx, __FUNCTION__, 0xffff0000, 0); + gpu::TexturePointer texture; + { + auto resource = _resource.lock(); // to ensure the resource is still needed + if (!resource) { + qCDebug(modelnetworking) << _url << "loading stopped; resource out of scope"; + return; + } + + PROFILE_RANGE_EX(resource_parse_ktx, __FUNCTION__, 0xffff0000, 0); + auto ktx = resource.staticCast()->_file->getKTX(); + gpu::Texture::Usage usage; + gpu::TextureUsageType usageType(gpu::TextureUsageType::RESOURCE); + gpu::Sampler sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR); + texture.reset(gpu::Texture::unserialize(usage, usageType, ktx, sampler)); + texture->setKtxBacking(ktx); + } + + auto resource = _resource.lock(); // to ensure the resource is still needed + if (resource) { + QMetaObject::invokeMethod(resource.data(), "setImage", + Q_ARG(gpu::TexturePointer, texture), + Q_ARG(int, texture->getWidth()), Q_ARG(int, texture->getHeight())); + } else { + qCDebug(modelnetworking) << _url << "loading stopped; resource out of scope"; + } - // TODO: - // auto ktx = ktx::KTX::create(); - // auto texture = gpu::Texture::unserialize(getUsage(), getUsageType(), ktx, getSampler()); - // FIXME: do I need to set the file as a backing file here? } void ImageReader::read() { @@ -503,7 +523,7 @@ void ImageReader::read() { auto url = _url.toString().toStdString(); PROFILE_RANGE_EX(resource_parse_image, __FUNCTION__, 0xffff0000, 0); - texture.reset(resource.dynamicCast()->getTextureLoader()(image, url)); + texture.reset(resource.staticCast()->getTextureLoader()(image, url)); texture->setSource(url); } @@ -531,12 +551,13 @@ void ImageReader::read() { if (!ktx || !(file = ktxCache.writeFile({ _url, hash, data, length }))) { qCWarning(modelnetworking) << _url << "file cache failed"; } else { - resource.dynamicCast()->_file = file; - // FIXME: do I need to set the file as a backing file here? + resource.staticCast()->_file = file; + auto ktx = file->getKTX(); + texture->setKtxBacking(ktx); } } - auto resource = _resource.toStrongRef(); // to ensure the resource is still needed + auto resource = _resource.lock(); // to ensure the resource is still needed if (resource) { QMetaObject::invokeMethod(resource.data(), "setImage", Q_ARG(gpu::TexturePointer, texture), diff --git a/libraries/model-networking/src/model-networking/TextureCache.h b/libraries/model-networking/src/model-networking/TextureCache.h index f05ab6b220..bc3baa6423 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.h +++ b/libraries/model-networking/src/model-networking/TextureCache.h @@ -92,6 +92,7 @@ protected: Q_INVOKABLE void setImage(gpu::TexturePointer texture, int originalWidth, int originalHeight); private: + friend class FileReader; friend class ImageReader; Type _type; From c71e614dd59e9ece4492cffc77621d05d520781a Mon Sep 17 00:00:00 2001 From: Zach Pomerantz Date: Wed, 1 Mar 2017 20:18:31 -0500 Subject: [PATCH 076/302] add loading from persisted files --- .../src/model-networking/KTXCache.cpp | 11 ++ .../src/model-networking/KTXCache.h | 5 +- .../src/model-networking/TextureCache.cpp | 3 +- libraries/networking/src/FileCache.cpp | 112 ++++++++++-------- libraries/networking/src/FileCache.h | 16 ++- 5 files changed, 95 insertions(+), 52 deletions(-) diff --git a/libraries/model-networking/src/model-networking/KTXCache.cpp b/libraries/model-networking/src/model-networking/KTXCache.cpp index 3e288c6b27..036e520af3 100644 --- a/libraries/model-networking/src/model-networking/KTXCache.cpp +++ b/libraries/model-networking/src/model-networking/KTXCache.cpp @@ -43,12 +43,23 @@ File* KTXCache::createFile(const Key& key, const std::string& filepath, size_t l return new KTXFile(key, filepath, length, url); } +File* KTXCache::loadFile(const Key& key, const std::string& filepath, size_t length, const std::string& metadata) { + const QUrl url = QString(metadata.c_str()); + _urlMap[url] = key; + qCInfo(file_cache) << "Loaded KTX" << key.c_str() << url; + return new KTXFile(key, filepath, length, url); +} + void KTXCache::evictedFile(const FilePointer& file) { const QUrl url = std::static_pointer_cast(file)->getUrl(); Lock lock(_urlMutex); _urlMap.erase(url); } +std::string KTXFile::getMetadata() const { + return _url.toString().toStdString(); +} + std::unique_ptr KTXFile::getKTX() const { ktx::StoragePointer storage = std::make_shared(getFilepath().c_str()); return ktx::KTX::create(storage); diff --git a/libraries/model-networking/src/model-networking/KTXCache.h b/libraries/model-networking/src/model-networking/KTXCache.h index 835c28e6db..7fe3ed872b 100644 --- a/libraries/model-networking/src/model-networking/KTXCache.h +++ b/libraries/model-networking/src/model-networking/KTXCache.h @@ -27,7 +27,7 @@ class KTXCache : public FileCache { Q_OBJECT public: - KTXCache(const std::string& dir, const std::string& ext) : FileCache(dir, ext) {} + KTXCache(const std::string& dir, const std::string& ext) : FileCache(dir, ext) { initialize(); } struct Data { Data(const QUrl& url, const Key& key, const char* data, size_t length) : @@ -43,6 +43,7 @@ public: protected: File* createFile(const Key& key, const std::string& filepath, size_t length, void* extra) override final; + File* loadFile(const Key& key, const std::string& filepath, size_t length, const std::string& metadata) override final; void evictedFile(const FilePointer& file) override final; private: @@ -65,6 +66,8 @@ protected: KTXFile(const Key& key, const std::string& filepath, size_t length, const QUrl& url) : File(key, filepath, length), _url(url) {} + std::string getMetadata() const override final; + private: friend class KTXCache; diff --git a/libraries/model-networking/src/model-networking/TextureCache.cpp b/libraries/model-networking/src/model-networking/TextureCache.cpp index aef0a6d56f..56325614ff 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.cpp +++ b/libraries/model-networking/src/model-networking/TextureCache.cpp @@ -451,6 +451,8 @@ void ImageReader::listSupportedImageFormats() { } void FileReader::read() { + PROFILE_RANGE_EX(resource_parse_ktx, __FUNCTION__, 0xffff0000, 0, { { "url", _url.toString() } }); + gpu::TexturePointer texture; { auto resource = _resource.lock(); // to ensure the resource is still needed @@ -459,7 +461,6 @@ void FileReader::read() { return; } - PROFILE_RANGE_EX(resource_parse_ktx, __FUNCTION__, 0xffff0000, 0); auto ktx = resource.staticCast()->_file->getKTX(); gpu::Texture::Usage usage; gpu::TextureUsageType usageType(gpu::TextureUsageType::RESOURCE); diff --git a/libraries/networking/src/FileCache.cpp b/libraries/networking/src/FileCache.cpp index 61712b383b..40a2509b7c 100644 --- a/libraries/networking/src/FileCache.cpp +++ b/libraries/networking/src/FileCache.cpp @@ -12,6 +12,7 @@ #include "FileCache.h" #include +#include #include #include @@ -37,7 +38,7 @@ FileCache::FileCache(const std::string& dirname, const std::string& ext, QObject QObject(parent), _ext(ext), _dirname(dirname), - _dir(createDir(_dirname)) {} + _dirpath(ServerPathUtils::getDataFilePath(dirname.c_str()).toStdString()) {} FileCache::~FileCache() { clear(); @@ -47,7 +48,63 @@ void fileDeleter(File* file) { file->deleter(); } +void FileCache::initialize() { + QDir dir(_dirpath.c_str()); + + if (dir.exists()) { + std::unordered_map> persistedEntries; + if (dir.exists(MANIFEST_NAME.c_str())) { + std::ifstream manifest; + manifest.open(dir.absoluteFilePath(MANIFEST_NAME.c_str()).toStdString()); + while (manifest.good()) { + std::string key, metadata; + std::getline(manifest, key, '\t'); + std::getline(manifest, metadata, '\n'); + if (!key.empty()) { + qCInfo(file_cache, "[%s] Manifest contains %s (%s)", _dirname.c_str(), key.c_str(), metadata.c_str()); + auto filename = key + '.' + _ext; + persistedEntries[filename] = { key, metadata }; + } + } + } else { + qCWarning(file_cache, "[%s] Missing manifest", _dirname.c_str()); + } + + std::unordered_map entries; + + foreach(QString filename, dir.entryList(QDir::Filters(QDir::NoDotAndDotDot | QDir::Files))) { + const auto& it = persistedEntries.find(filename.toStdString()); + if (it == persistedEntries.cend()) { + // unlink extra files + dir.remove(filename); + qCInfo(file_cache, "[%s] Cleaned %s", _dirname.c_str(), filename.toStdString().c_str()); + } else { + // load existing files + const Key& key = it->second.first; + const std::string& metadata = it->second.second; + const std::string filepath = dir.filePath(filename).toStdString(); + const size_t length = std::ifstream(filepath, std::ios::binary | std::ios::ate).tellg(); + + FilePointer file(loadFile(key, filepath, length, metadata), &fileDeleter); + file->_cache = this; + _files[key] = file; + _numTotalFiles += 1; + _totalFilesSize += length; + } + } + + qCDebug(file_cache, "[%s] Initialized %s", _dirname.c_str(), _dirpath.c_str()); + } else { + dir.mkpath(_dirpath.c_str()); + qCDebug(file_cache, "[%s] Created %s", _dirname.c_str(), _dirpath.c_str()); + } + + _initialized = true; +} + FilePointer FileCache::writeFile(const Key& key, const char* data, size_t length, void* extra) { + assert(_initialized); + std::string filepath = getFilepath(key); Lock lock(_filesMutex); @@ -78,6 +135,8 @@ FilePointer FileCache::writeFile(const Key& key, const char* data, size_t length } FilePointer FileCache::getFile(const Key& key) { + assert(_initialized); + FilePointer file; Lock lock(_filesMutex); @@ -100,50 +159,8 @@ FilePointer FileCache::getFile(const Key& key) { return file; } -File* FileCache::createFile(const Key& key, const std::string& filepath, size_t length, void* extra) { - qCInfo(file_cache, "Wrote %s", key.c_str()); - return new File(key, filepath, length); -} - -std::string FileCache::createDir(const std::string& dirname) { - QString dirpath = ServerPathUtils::getDataFilePath(dirname.c_str()); - QDir dir(dirpath); - - if (dir.exists()) { - std::unordered_set persistedEntries; - if (dir.exists(MANIFEST_NAME.c_str())) { - std::ifstream manifest; - manifest.open(dir.absoluteFilePath(MANIFEST_NAME.c_str()).toStdString()); - while (manifest.good()) { - std::string entry; - manifest >> entry; - if (!entry.empty()) { - qCInfo(file_cache, "[%s] Manifest contains %s", _dirname.c_str(), entry.c_str()); - persistedEntries.insert(entry + '.' + _ext); - } - } - } else { - qCWarning(file_cache, "[%s] Missing manifest", _dirname.c_str()); - } - - - foreach(QString filename, dir.entryList(QDir::Filters(QDir::NoDotAndDotDot | QDir::Files))) { - if (persistedEntries.find(filename.toStdString()) == persistedEntries.cend()) { - dir.remove(filename); - qCInfo(file_cache, "[%s] Cleaned %s", _dirname.c_str(), filename.toStdString().c_str()); - } - } - qCDebug(file_cache) << "Initiated" << dirpath; - } else { - dir.mkpath(dirpath); - qCDebug(file_cache) << "Created" << dirpath; - } - - return dirpath.toStdString(); -} - std::string FileCache::getFilepath(const Key& key) { - return _dir + '/' + key + '.' + _ext; + return _dirpath + '/' + key + '.' + _ext; } void FileCache::addUnusedFile(const FilePointer file) { @@ -223,7 +240,7 @@ void FileCache::clear() { }; try { - std::string manifestPath= _dir + '/' + MANIFEST_NAME; + std::string manifestPath= _dirpath + '/' + MANIFEST_NAME; std::ofstream manifest(manifestPath); forAllFiles([&](const FilePointer& file) { @@ -232,9 +249,10 @@ void FileCache::clear() { if (_totalFilesSize > _offlineFilesMaxSize) { _totalFilesSize -= file->getLength(); } else { - manifest << file->getKey() << '\n'; + manifest << file->getKey() << '\t' << file->getMetadata() << '\n'; file->_shouldPersist = true; - qCInfo(file_cache, "[%s] Persisting %s", _dirname.c_str(), file->getKey().c_str()); + qCInfo(file_cache, "[%s] Persisting %s (%s)", + _dirname.c_str(), file->getKey().c_str(), file->getMetadata().c_str()); } }); } catch (std::exception& e) { diff --git a/libraries/networking/src/FileCache.h b/libraries/networking/src/FileCache.h index 09e1760ae5..c5d8ce3fc2 100644 --- a/libraries/networking/src/FileCache.h +++ b/libraries/networking/src/FileCache.h @@ -73,10 +73,17 @@ signals: void dirty(); protected: + /// must be called after construction to create the cache on the fs and restore persisted files + void initialize(); + FilePointer writeFile(const Key& key, const char* data, size_t length, void* extra); FilePointer getFile(const Key& key); - virtual File* createFile(const Key& key, const std::string& filepath, size_t length, void* extra); + /// create a file (ex.: create a class derived from File and store it in a secondary map with extra->url) + virtual File* createFile(const Key& key, const std::string& filepath, size_t length, void* extra) = 0; + /// load a file + virtual File* loadFile(const Key& key, const std::string& filepath, size_t length, const std::string& metadata) = 0; + /// take action when a file is evicted from the cache (ex.: evict it from a secondary map) virtual void evictedFile(const FilePointer& file) = 0; private: @@ -85,7 +92,6 @@ private: friend class File; - std::string createDir(const std::string& dirname); std::string getFilepath(const Key& key); void addUnusedFile(const FilePointer file); @@ -100,7 +106,8 @@ private: std::string _ext; std::string _dirname; - std::string _dir; + std::string _dirpath; + bool _initialized { false }; std::unordered_map> _files; Mutex _filesMutex; @@ -133,6 +140,9 @@ protected: // the destructor should handle unlinking of the actual filepath virtual ~File(); + /// get metadata to store with a file between instances (ex.: return the url of a hash) + virtual std::string getMetadata() const = 0; + const std::string _filepath; private: From febeeeca3ac28e4d4e9d9be953016c84da039476 Mon Sep 17 00:00:00 2001 From: Zach Pomerantz Date: Wed, 1 Mar 2017 20:58:00 -0500 Subject: [PATCH 077/302] namespace cache::FileCache and use unique_ptr --- .../src/model-networking/KTXCache.cpp | 20 ++++++++++++------- .../src/model-networking/KTXCache.h | 12 ++++++----- libraries/networking/src/FileCache.cpp | 6 ++++-- libraries/networking/src/FileCache.h | 12 +++++++---- 4 files changed, 32 insertions(+), 18 deletions(-) diff --git a/libraries/model-networking/src/model-networking/KTXCache.cpp b/libraries/model-networking/src/model-networking/KTXCache.cpp index 036e520af3..d0380c7635 100644 --- a/libraries/model-networking/src/model-networking/KTXCache.cpp +++ b/libraries/model-networking/src/model-networking/KTXCache.cpp @@ -13,6 +13,9 @@ #include +using File = cache::File; +using FilePointer = cache::FilePointer; + KTXFilePointer KTXCache::writeFile(Data data) { return std::static_pointer_cast(FileCache::writeFile(data.key, data.data, data.length, (void*)&data)); } @@ -35,19 +38,22 @@ KTXFilePointer KTXCache::getFile(const QUrl& url) { return file; } -File* KTXCache::createFile(const Key& key, const std::string& filepath, size_t length, void* extra) { - const QUrl& url = reinterpret_cast(extra)->url; +std::unique_ptr KTXCache::createKTXFile(const Key& key, const std::string& filepath, size_t length, const QUrl& url) { Lock lock(_urlMutex); _urlMap[url] = key; - qCInfo(file_cache) << "Wrote KTX" << key.c_str() << url; - return new KTXFile(key, filepath, length, url); + return std::unique_ptr(new KTXFile(key, filepath, length, url)); } -File* KTXCache::loadFile(const Key& key, const std::string& filepath, size_t length, const std::string& metadata) { +std::unique_ptr KTXCache::createFile(const Key& key, const std::string& filepath, size_t length, void* extra) { + const QUrl& url = reinterpret_cast(extra)->url; + qCInfo(file_cache) << "Wrote KTX" << key.c_str() << url; + return createKTXFile(key, filepath, length, url); +} + +std::unique_ptr KTXCache::loadFile(const Key& key, const std::string& filepath, size_t length, const std::string& metadata) { const QUrl url = QString(metadata.c_str()); - _urlMap[url] = key; qCInfo(file_cache) << "Loaded KTX" << key.c_str() << url; - return new KTXFile(key, filepath, length, url); + return createKTXFile(key, filepath, length, url); } void KTXCache::evictedFile(const FilePointer& file) { diff --git a/libraries/model-networking/src/model-networking/KTXCache.h b/libraries/model-networking/src/model-networking/KTXCache.h index 7fe3ed872b..84dda48ee2 100644 --- a/libraries/model-networking/src/model-networking/KTXCache.h +++ b/libraries/model-networking/src/model-networking/KTXCache.h @@ -23,7 +23,7 @@ namespace ktx { class KTXFile; using KTXFilePointer = std::shared_ptr; -class KTXCache : public FileCache { +class KTXCache : public cache::FileCache { Q_OBJECT public: @@ -42,11 +42,13 @@ public: KTXFilePointer getFile(const QUrl& url); protected: - File* createFile(const Key& key, const std::string& filepath, size_t length, void* extra) override final; - File* loadFile(const Key& key, const std::string& filepath, size_t length, const std::string& metadata) override final; - void evictedFile(const FilePointer& file) override final; + std::unique_ptr createFile(const Key& key, const std::string& filepath, size_t length, void* extra) override final; + std::unique_ptr loadFile(const Key& key, const std::string& filepath, size_t length, const std::string& metadata) override final; + void evictedFile(const cache::FilePointer& file) override final; private: + std::unique_ptr createKTXFile(const Key& key, const std::string& filepath, size_t length, const QUrl& url); + using Mutex = std::mutex; using Lock = std::lock_guard; struct QUrlHasher { std::size_t operator()(QUrl const& url) const { return qHash(url); } }; @@ -55,7 +57,7 @@ private: Mutex _urlMutex; }; -class KTXFile : public File { +class KTXFile : public cache::File { Q_OBJECT public: diff --git a/libraries/networking/src/FileCache.cpp b/libraries/networking/src/FileCache.cpp index 40a2509b7c..034e24c8cd 100644 --- a/libraries/networking/src/FileCache.cpp +++ b/libraries/networking/src/FileCache.cpp @@ -22,6 +22,8 @@ Q_LOGGING_CATEGORY(file_cache, "hifi.file_cache") +using namespace cache; + static const std::string MANIFEST_NAME = "manifest"; void FileCache::setUnusedFileCacheSize(size_t unusedFilesMaxSize) { @@ -85,7 +87,7 @@ void FileCache::initialize() { const std::string filepath = dir.filePath(filename).toStdString(); const size_t length = std::ifstream(filepath, std::ios::binary | std::ios::ate).tellg(); - FilePointer file(loadFile(key, filepath, length, metadata), &fileDeleter); + FilePointer file(loadFile(key, filepath, length, metadata).release(), &fileDeleter); file->_cache = this; _files[key] = file; _numTotalFiles += 1; @@ -119,7 +121,7 @@ FilePointer FileCache::writeFile(const Key& key, const char* data, size_t length // write the new file FILE* saveFile = fopen(filepath.c_str(), "wb"); if (saveFile != nullptr && fwrite(data, length, 1, saveFile) && fclose(saveFile) == 0) { - file.reset(createFile(key, filepath, length, extra), &fileDeleter); + file.reset(createFile(key, filepath, length, extra).release(), &fileDeleter); file->_cache = this; _files[key] = file; _numTotalFiles += 1; diff --git a/libraries/networking/src/FileCache.h b/libraries/networking/src/FileCache.h index c5d8ce3fc2..7e751d56be 100644 --- a/libraries/networking/src/FileCache.h +++ b/libraries/networking/src/FileCache.h @@ -22,6 +22,8 @@ Q_DECLARE_LOGGING_CATEGORY(file_cache) +namespace cache { + class File; using FilePointer = std::shared_ptr; @@ -80,9 +82,9 @@ protected: FilePointer getFile(const Key& key); /// create a file (ex.: create a class derived from File and store it in a secondary map with extra->url) - virtual File* createFile(const Key& key, const std::string& filepath, size_t length, void* extra) = 0; + virtual std::unique_ptr createFile(const Key& key, const std::string& filepath, size_t length, void* extra) = 0; /// load a file - virtual File* loadFile(const Key& key, const std::string& filepath, size_t length, const std::string& metadata) = 0; + virtual std::unique_ptr loadFile(const Key& key, const std::string& filepath, size_t length, const std::string& metadata) = 0; /// take action when a file is evicted from the cache (ex.: evict it from a secondary map) virtual void evictedFile(const FilePointer& file) = 0; @@ -130,6 +132,8 @@ public: Key getKey() const { return _key; } size_t getLength() const { return _length; } + // the destructor should handle unlinking of the actual filepath + virtual ~File(); // overrides should call File::deleter to maintain caching behavior virtual void deleter(); @@ -137,8 +141,6 @@ protected: // when constructed, the file has already been created/written File(const Key& key, const std::string& filepath, size_t length) : _filepath(filepath), _key(key), _length(length) {} - // the destructor should handle unlinking of the actual filepath - virtual ~File(); /// get metadata to store with a file between instances (ex.: return the url of a hash) virtual std::string getMetadata() const = 0; @@ -157,4 +159,6 @@ private: bool _shouldPersist { false }; }; +} + #endif // hifi_FileCache_h From cca1f2fb257fb921bdb168d565e0b0a6873618ce Mon Sep 17 00:00:00 2001 From: Zach Pomerantz Date: Thu, 2 Mar 2017 11:28:49 -0500 Subject: [PATCH 078/302] include in FileCache --- libraries/networking/src/FileCache.h | 1 + 1 file changed, 1 insertion(+) diff --git a/libraries/networking/src/FileCache.h b/libraries/networking/src/FileCache.h index 7e751d56be..b19f2d10cd 100644 --- a/libraries/networking/src/FileCache.h +++ b/libraries/networking/src/FileCache.h @@ -10,6 +10,7 @@ #ifndef hifi_FileCache_h #define hifi_FileCache_h +#include #include #include #include From 6a7ee4321ba5e17904aaf88c9c650ef773f7857c Mon Sep 17 00:00:00 2001 From: Zach Pomerantz Date: Thu, 2 Mar 2017 13:30:51 -0500 Subject: [PATCH 079/302] suppress logs for file_cache --- libraries/networking/src/FileCache.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/libraries/networking/src/FileCache.cpp b/libraries/networking/src/FileCache.cpp index 034e24c8cd..c94d0e1b8c 100644 --- a/libraries/networking/src/FileCache.cpp +++ b/libraries/networking/src/FileCache.cpp @@ -20,7 +20,7 @@ #include -Q_LOGGING_CATEGORY(file_cache, "hifi.file_cache") +Q_LOGGING_CATEGORY(file_cache, "hifi.file_cache", QtWarningMsg) using namespace cache; @@ -63,7 +63,7 @@ void FileCache::initialize() { std::getline(manifest, key, '\t'); std::getline(manifest, metadata, '\n'); if (!key.empty()) { - qCInfo(file_cache, "[%s] Manifest contains %s (%s)", _dirname.c_str(), key.c_str(), metadata.c_str()); + qCDebug(file_cache, "[%s] Manifest contains %s (%s)", _dirname.c_str(), key.c_str(), metadata.c_str()); auto filename = key + '.' + _ext; persistedEntries[filename] = { key, metadata }; } @@ -79,7 +79,7 @@ void FileCache::initialize() { if (it == persistedEntries.cend()) { // unlink extra files dir.remove(filename); - qCInfo(file_cache, "[%s] Cleaned %s", _dirname.c_str(), filename.toStdString().c_str()); + qCDebug(file_cache, "[%s] Cleaned %s", _dirname.c_str(), filename.toStdString().c_str()); } else { // load existing files const Key& key = it->second.first; @@ -150,7 +150,7 @@ FilePointer FileCache::getFile(const Key& key) { if (file) { // if it exists, it is active - remove it from the cache removeUnusedFile(file); - qCInfo(file_cache, "[%s] Found %s", _dirname.c_str(), key.c_str()); + qCDebug(file_cache, "[%s] Found %s", _dirname.c_str(), key.c_str()); emit dirty(); } else { // if not, remove the weak_ptr @@ -253,7 +253,7 @@ void FileCache::clear() { } else { manifest << file->getKey() << '\t' << file->getMetadata() << '\n'; file->_shouldPersist = true; - qCInfo(file_cache, "[%s] Persisting %s (%s)", + qCDebug(file_cache, "[%s] Persisting %s (%s)", _dirname.c_str(), file->getKey().c_str(), file->getMetadata().c_str()); } }); From 8db74413fd306e67adebf2c4641b3706c8d584c5 Mon Sep 17 00:00:00 2001 From: Bradley Austin Davis Date: Thu, 2 Mar 2017 17:40:54 -0800 Subject: [PATCH 080/302] Fix comment in GLBackend --- libraries/gpu-gl/src/gpu/gl/GLBackend.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/gpu-gl/src/gpu/gl/GLBackend.cpp b/libraries/gpu-gl/src/gpu/gl/GLBackend.cpp index 76cc64f3e3..0800c27839 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLBackend.cpp +++ b/libraries/gpu-gl/src/gpu/gl/GLBackend.cpp @@ -207,7 +207,7 @@ void GLBackend::renderPassTransfer(const Batch& batch) { } } - { // Sync all the buffers + { // Sync all the transform states PROFILE_RANGE(render_gpu_gl_detail, "syncCPUTransform"); _transform._cameras.clear(); _transform._cameraOffsets.clear(); @@ -275,7 +275,7 @@ void GLBackend::renderPassDraw(const Batch& batch) { updateInput(); updateTransform(batch); updatePipeline(); - + CommandCall call = _commandCalls[(*command)]; (this->*(call))(batch, *offset); break; From 47087add154867caae91939a283a9fbfd7a3b727 Mon Sep 17 00:00:00 2001 From: Bradley Austin Davis Date: Fri, 3 Mar 2017 08:58:55 -0800 Subject: [PATCH 081/302] Add support for fallback textures, throttling texture creation --- libraries/gpu-gl/src/gpu/gl45/GL45Backend.cpp | 6 +++ libraries/gpu-gl/src/gpu/gl45/GL45Backend.h | 1 + .../src/gpu/gl45/GL45BackendTexture.cpp | 30 ++++++------ .../gpu/gl45/GL45BackendVariableTexture.cpp | 3 ++ libraries/gpu/src/gpu/Texture.h | 4 ++ .../src/model-networking/TextureCache.cpp | 46 ++++++++++++++++++- .../src/model-networking/TextureCache.h | 1 + 7 files changed, 75 insertions(+), 16 deletions(-) diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45Backend.cpp b/libraries/gpu-gl/src/gpu/gl45/GL45Backend.cpp index f0ef2ac7a8..12c4b818f7 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45Backend.cpp +++ b/libraries/gpu-gl/src/gpu/gl45/GL45Backend.cpp @@ -18,6 +18,12 @@ Q_LOGGING_CATEGORY(gpugl45logging, "hifi.gpu.gl45") using namespace gpu; using namespace gpu::gl45; +void GL45Backend::recycle() const { + Parent::recycle(); + GL45VariableAllocationTexture::manageMemory(); + GL45VariableAllocationTexture::_frameTexturesCreated = 0; +} + void GL45Backend::do_draw(const Batch& batch, size_t paramOffset) { Primitive primitiveType = (Primitive)batch._params[paramOffset + 2]._uint; GLenum mode = gl::PRIMITIVE_TO_GL[primitiveType]; diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h b/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h index cfdcc356a6..6a9811b055 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h +++ b/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h @@ -147,6 +147,7 @@ public: using TransferQueue = std::queue>; static MemoryPressureState _memoryPressureState; protected: + static size_t _frameTexturesCreated; static std::atomic _memoryPressureStateStale; static std::list _memoryManagedTextures; static WorkQueue _transferQueue; diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp b/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp index d5ad0204bf..36aaf75e81 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp @@ -28,6 +28,7 @@ using namespace gpu::gl; using namespace gpu::gl45; #define SPARSE_PAGE_SIZE_OVERHEAD_ESTIMATE 1.3f +#define MAX_RESOURCE_TEXTURES_PER_FRAME 2 GLTexture* GL45Backend::syncGPUObject(const TexturePointer& texturePointer) { if (!texturePointer) { @@ -57,19 +58,23 @@ GLTexture* GL45Backend::syncGPUObject(const TexturePointer& texturePointer) { break; case TextureUsageType::RESOURCE: { - - GL45VariableAllocationTexture* varObject { nullptr }; + if (GL45VariableAllocationTexture::_frameTexturesCreated < MAX_RESOURCE_TEXTURES_PER_FRAME) { #if 0 - if (isTextureManagementSparseEnabled() && GL45Texture::isSparseEligible(texture)) { - varObject = new GL45SparseResourceTexture(shared_from_this(), texture); - } else { - varObject = new GL45ResourceTexture(shared_from_this(), texture); - } + if (isTextureManagementSparseEnabled() && GL45Texture::isSparseEligible(texture)) { + object = new GL45SparseResourceTexture(shared_from_this(), texture); + } else { + object = new GL45ResourceTexture(shared_from_this(), texture); + } #else - varObject = new GL45ResourceTexture(shared_from_this(), texture); + object = new GL45ResourceTexture(shared_from_this(), texture); #endif - GL45VariableAllocationTexture::addMemoryManagedTexture(texturePointer); - object = varObject; + GL45VariableAllocationTexture::addMemoryManagedTexture(texturePointer); + } else { + auto fallback = texturePointer->getFallbackTexture(); + if (fallback) { + object = static_cast(syncGPUObject(fallback)); + } + } break; } @@ -81,11 +86,6 @@ GLTexture* GL45Backend::syncGPUObject(const TexturePointer& texturePointer) { return object; } -void GL45Backend::recycle() const { - Parent::recycle(); - GL45VariableAllocationTexture::manageMemory(); -} - void GL45Backend::initTextureManagementStage() { // enable the Sparse Texture on gl45 _textureManagement._sparseCapable = true; diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp b/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp index 92251bd381..d54ad1ea4b 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp @@ -405,7 +405,10 @@ void GL45VariableAllocationTexture::manageMemory() { processWorkQueues(); } +size_t GL45VariableAllocationTexture::_frameTexturesCreated { 0 }; + GL45VariableAllocationTexture::GL45VariableAllocationTexture(const std::weak_ptr& backend, const Texture& texture) : GL45Texture(backend, texture) { + ++_frameTexturesCreated; } GL45VariableAllocationTexture::~GL45VariableAllocationTexture() { diff --git a/libraries/gpu/src/gpu/Texture.h b/libraries/gpu/src/gpu/Texture.h index d840687af2..9996026254 100755 --- a/libraries/gpu/src/gpu/Texture.h +++ b/libraries/gpu/src/gpu/Texture.h @@ -503,6 +503,9 @@ public: const Sampler& getSampler() const { return _sampler; } Stamp getSamplerStamp() const { return _samplerStamp; } + void setFallbackTexture(const TexturePointer& fallback) { _fallback = fallback; } + TexturePointer getFallbackTexture() const { return _fallback.lock(); } + void setExternalTexture(uint32 externalId, void* externalFence); void setExternalRecycler(const ExternalRecycler& recycler); ExternalRecycler getExternalRecycler() const; @@ -526,6 +529,7 @@ protected: ExternalRecycler _externalRecycler; + std::weak_ptr _fallback; // Not strictly necessary, but incredibly useful for debugging std::string _source; std::unique_ptr< Storage > _storage; diff --git a/libraries/model-networking/src/model-networking/TextureCache.cpp b/libraries/model-networking/src/model-networking/TextureCache.cpp index 1f21e9e78d..f4473ceb39 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.cpp +++ b/libraries/model-networking/src/model-networking/TextureCache.cpp @@ -186,6 +186,39 @@ NetworkTexturePointer TextureCache::getTexture(const QUrl& url, Type type, const return ResourceCache::getResource(url, QUrl(), &extra).staticCast(); } +gpu::TexturePointer getFallbackTextureForType(NetworkTexture::Type type) { + auto textureCache = DependencyManager::get(); + + gpu::TexturePointer result; + switch (type) { + case NetworkTexture::DEFAULT_TEXTURE: + case NetworkTexture::ALBEDO_TEXTURE: + case NetworkTexture::ROUGHNESS_TEXTURE: + case NetworkTexture::OCCLUSION_TEXTURE: + result = textureCache->getWhiteTexture(); + break; + + case NetworkTexture::NORMAL_TEXTURE: + result = textureCache->getBlueTexture(); + break; + + case NetworkTexture::EMISSIVE_TEXTURE: + case NetworkTexture::LIGHTMAP_TEXTURE: + result = textureCache->getBlackTexture(); + break; + + case NetworkTexture::BUMP_TEXTURE: + case NetworkTexture::SPECULAR_TEXTURE: + case NetworkTexture::GLOSS_TEXTURE: + case NetworkTexture::CUBE_TEXTURE: + case NetworkTexture::CUSTOM_TEXTURE: + case NetworkTexture::STRICT_TEXTURE: + default: + break; + } + return result; +} + NetworkTexture::TextureLoaderFunc getTextureLoaderForType(NetworkTexture::Type type, const QVariantMap& options = QVariantMap()) { @@ -299,6 +332,13 @@ NetworkTexture::TextureLoaderFunc NetworkTexture::getTextureLoader() const { return getTextureLoaderForType(_type); } +gpu::TexturePointer NetworkTexture::getFallbackTexture() const { + if (_type == CUSTOM_TEXTURE) { + return gpu::TexturePointer(); + } + return getFallbackTextureForType(_type); +} + class ImageReader : public QRunnable { public: @@ -428,7 +468,11 @@ void ImageReader::run() { auto url = _url.toString().toStdString(); PROFILE_RANGE_EX(resource_parse_image, __FUNCTION__, 0xffffff00, 0); - texture.reset(resource.dynamicCast()->getTextureLoader()(image, url)); + auto networkTexture = resource.dynamicCast(); + texture.reset(networkTexture->getTextureLoader()(image, url)); + if (texture) { + texture->setFallbackTexture(networkTexture->getFallbackTexture()); + } } // Ensure the resource has not been deleted diff --git a/libraries/model-networking/src/model-networking/TextureCache.h b/libraries/model-networking/src/model-networking/TextureCache.h index 749b5a2ebb..c77bafe447 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.h +++ b/libraries/model-networking/src/model-networking/TextureCache.h @@ -75,6 +75,7 @@ public: Type getTextureType() const { return _type; } TextureLoaderFunc getTextureLoader() const; + gpu::TexturePointer getFallbackTexture() const; signals: void networkTextureCreated(const QWeakPointer& self); From 5b1cd5443dc18aabb304b2e96e9a88fbfbf971de Mon Sep 17 00:00:00 2001 From: Bradley Austin Davis Date: Fri, 3 Mar 2017 11:13:11 -0800 Subject: [PATCH 082/302] Fix KTX includes --- tests/ktx/src/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/ktx/src/main.cpp b/tests/ktx/src/main.cpp index 34280cb263..c461f3e316 100644 --- a/tests/ktx/src/main.cpp +++ b/tests/ktx/src/main.cpp @@ -38,7 +38,7 @@ #include #include -#include + #include #include #include From de564d92b985ea443065f3f3c8d3068af4424dd5 Mon Sep 17 00:00:00 2001 From: Bradley Austin Davis Date: Fri, 3 Mar 2017 13:23:13 -0800 Subject: [PATCH 083/302] Fix KTX compile errors --- libraries/ktx/src/ktx/KTX.cpp | 2 +- libraries/ktx/src/ktx/KTX.h | 4 ++-- libraries/ktx/src/ktx/Reader.cpp | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/libraries/ktx/src/ktx/KTX.cpp b/libraries/ktx/src/ktx/KTX.cpp index cc9c1069b1..bee31e3200 100644 --- a/libraries/ktx/src/ktx/KTX.cpp +++ b/libraries/ktx/src/ktx/KTX.cpp @@ -77,7 +77,7 @@ KTX::KTX() { KTX::~KTX() { } -void KTX::resetStorage(StoragePointer& storage) { +void KTX::resetStorage(const StoragePointer& storage) { _storage = storage; } diff --git a/libraries/ktx/src/ktx/KTX.h b/libraries/ktx/src/ktx/KTX.h index 7aef33704e..76920717bf 100644 --- a/libraries/ktx/src/ktx/KTX.h +++ b/libraries/ktx/src/ktx/KTX.h @@ -420,7 +420,7 @@ namespace ktx { using Images = std::vector; class KTX { - void resetStorage(StoragePointer& src); + void resetStorage(const StoragePointer& src); KTX(); public: @@ -448,7 +448,7 @@ namespace ktx { static Images writeImages(Byte* destBytes, size_t destByteSize, const Images& images); // Parse a block of memory and create a KTX object from it - static std::unique_ptr create(StoragePointer& src); + static std::unique_ptr create(const StoragePointer& src); static bool checkHeaderFromStorage(size_t srcSize, const Byte* srcBytes); static Images parseImages(const Header& header, size_t srcSize, const Byte* srcBytes); diff --git a/libraries/ktx/src/ktx/Reader.cpp b/libraries/ktx/src/ktx/Reader.cpp index d74b45c01c..7321686de8 100644 --- a/libraries/ktx/src/ktx/Reader.cpp +++ b/libraries/ktx/src/ktx/Reader.cpp @@ -163,7 +163,7 @@ namespace ktx { return images; } - std::unique_ptr KTX::create(StoragePointer& src) { + std::unique_ptr KTX::create(const StoragePointer& src) { if (!src) { return nullptr; } From 9051c84b6dcab7e240468389f7b32c49f46a5600 Mon Sep 17 00:00:00 2001 From: Bradley Austin Davis Date: Fri, 3 Mar 2017 13:42:22 -0800 Subject: [PATCH 084/302] Fix warnings --- libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp | 1 - libraries/gpu/src/gpu/Texture.cpp | 2 -- libraries/gpu/src/gpu/Texture_ktx.cpp | 2 ++ libraries/ktx/src/ktx/KTX.h | 6 +++--- libraries/ktx/src/ktx/Reader.cpp | 8 ++++---- libraries/ktx/src/ktx/Writer.cpp | 4 ++-- libraries/model/src/model/TextureMap.cpp | 2 -- tests/ktx/src/main.cpp | 2 -- 8 files changed, 11 insertions(+), 16 deletions(-) diff --git a/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp b/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp index 80649c4d64..84806d82c3 100644 --- a/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp @@ -72,7 +72,6 @@ GL41Texture::GL41Texture(const std::weak_ptr& backend, const Texture& incrementTextureGPUCount(); withPreservedTexture([&] { GLTexelFormat texelFormat = GLTexelFormat::evalGLTexelFormat(_gpuObject.getTexelFormat(), _gpuObject.getStoredMipFormat()); - const Sampler& sampler = _gpuObject.getSampler(); auto numMips = _gpuObject.evalNumMips(); for (uint16_t mipLevel = 0; mipLevel < numMips; ++mipLevel) { // Get the mip level dimensions, accounting for the downgrade level diff --git a/libraries/gpu/src/gpu/Texture.cpp b/libraries/gpu/src/gpu/Texture.cpp index 6b9087333c..5cd1ebd31f 100755 --- a/libraries/gpu/src/gpu/Texture.cpp +++ b/libraries/gpu/src/gpu/Texture.cpp @@ -412,7 +412,6 @@ void Texture::assignStoredMip(uint16 level, storage::StoragePointer& storage) { // THen check that the mem texture passed make sense with its format Size expectedSize = evalStoredMipSize(level, getStoredMipFormat()); auto size = storage->size(); - auto bytes = storage->data(); if (storage->size() == expectedSize) { _storage->assignMipData(level, storage); _maxMip = std::max(_maxMip, level); @@ -442,7 +441,6 @@ void Texture::assignStoredMipFace(uint16 level, uint8 face, storage::StoragePoin // THen check that the mem texture passed make sense with its format Size expectedSize = evalStoredMipFaceSize(level, getStoredMipFormat()); auto size = storage->size(); - auto bytes = storage->data(); if (size == expectedSize) { _storage->assignMipFaceData(level, face, storage); _maxMip = std::max(_maxMip, level); diff --git a/libraries/gpu/src/gpu/Texture_ktx.cpp b/libraries/gpu/src/gpu/Texture_ktx.cpp index c7da499e98..bd34246f51 100644 --- a/libraries/gpu/src/gpu/Texture_ktx.cpp +++ b/libraries/gpu/src/gpu/Texture_ktx.cpp @@ -117,6 +117,7 @@ ktx::KTXUniquePointer Texture::serialize(const Texture& texture) { } auto ktxBuffer = ktx::KTX::create(header, images); +#if 0 auto expectedMipCount = texture.evalNumMips(); assert(expectedMipCount == ktxBuffer->_images.size()); assert(expectedMipCount == header.numberOfMipmapLevels); @@ -141,6 +142,7 @@ ktx::KTXUniquePointer Texture::serialize(const Texture& texture) { assert(0 == memcmp(expectedFace, actualFace, expected._faceSize)); } } +#endif return ktxBuffer; } diff --git a/libraries/ktx/src/ktx/KTX.h b/libraries/ktx/src/ktx/KTX.h index 76920717bf..3ea14b2cb5 100644 --- a/libraries/ktx/src/ktx/KTX.h +++ b/libraries/ktx/src/ktx/KTX.h @@ -402,15 +402,15 @@ namespace ktx { Image(uint32_t imageSize, uint32_t padding, const Byte* bytes) : _numFaces(1), _imageSize(imageSize), - _padding(padding), _faceSize(imageSize), + _padding(padding), _faceBytes(1, bytes) {} Image(uint32_t pageSize, uint32_t padding, const FaceBytes& cubeFaceBytes) : _numFaces(NUM_CUBEMAPFACES), _imageSize(pageSize * NUM_CUBEMAPFACES), - _padding(padding), - _faceSize(pageSize) + _faceSize(pageSize), + _padding(padding) { if (cubeFaceBytes.size() == NUM_CUBEMAPFACES) { _faceBytes = cubeFaceBytes; diff --git a/libraries/ktx/src/ktx/Reader.cpp b/libraries/ktx/src/ktx/Reader.cpp index 7321686de8..f8004cf21a 100644 --- a/libraries/ktx/src/ktx/Reader.cpp +++ b/libraries/ktx/src/ktx/Reader.cpp @@ -12,6 +12,7 @@ #include #include +#include #ifndef _MSC_VER #define NOEXCEPT noexcept @@ -68,7 +69,7 @@ namespace ktx { } // find the first null character \0 - int keyLength = 0; + uint32_t keyLength = 0; while (reinterpret_cast(src[++keyLength]) != '\0') { if (keyLength == keyValueByteSize) { // key must be null-terminated, and there must be space for the value @@ -119,8 +120,8 @@ namespace ktx { return true; } - catch (ReaderException& e) { - qWarning(e.what()); + catch (const ReaderException& e) { + qWarning() << e.what(); return false; } } @@ -128,7 +129,6 @@ namespace ktx { Images KTX::parseImages(const Header& header, size_t srcSize, const Byte* srcBytes) { Images images; auto currentPtr = srcBytes; - auto numMips = header.getNumberOfLevels(); auto numFaces = header.numberOfFaces; // Keep identifying new mip as long as we can at list query the next imageSize diff --git a/libraries/ktx/src/ktx/Writer.cpp b/libraries/ktx/src/ktx/Writer.cpp index 901571f804..005b8e9e45 100644 --- a/libraries/ktx/src/ktx/Writer.cpp +++ b/libraries/ktx/src/ktx/Writer.cpp @@ -105,14 +105,14 @@ namespace ktx { // Single face vs cubes if (srcImages[l]._numFaces == 1) { - auto copied = memcpy(currentPtr, srcImages[l]._faceBytes[0], imageSize); + memcpy(currentPtr, srcImages[l]._faceBytes[0], imageSize); destImages.emplace_back(Image((uint32_t) imageSize, padding, currentPtr)); currentPtr += imageSize; } else { Image::FaceBytes faceBytes(6); auto faceSize = srcImages[l]._faceSize; for (int face = 0; face < 6; face++) { - auto copied = memcpy(currentPtr, srcImages[l]._faceBytes[face], faceSize); + memcpy(currentPtr, srcImages[l]._faceBytes[face], faceSize); faceBytes[face] = currentPtr; currentPtr += faceSize; } diff --git a/libraries/model/src/model/TextureMap.cpp b/libraries/model/src/model/TextureMap.cpp index 23e892ba2b..4f9d57b21b 100755 --- a/libraries/model/src/model/TextureMap.cpp +++ b/libraries/model/src/model/TextureMap.cpp @@ -116,12 +116,10 @@ gpu::Texture* cacheTexture(const std::string& name, gpu::Texture* srcTexture, bo auto ktxMemory = gpu::Texture::serialize(*srcTexture); if (ktxMemory) { const auto& ktxStorage = ktxMemory->getStorage(); - auto header = ktxMemory->getHeader(); QFile outFile(cacheFilename.c_str()); if (!outFile.open(QFile::Truncate | QFile::ReadWrite)) { throw std::runtime_error("Unable to open file"); } - //auto ktxSize = sizeof(ktx::Header); // ktxStorage->size() auto ktxSize = ktxStorage->size(); outFile.resize(ktxSize); auto dest = outFile.map(0, ktxSize); diff --git a/tests/ktx/src/main.cpp b/tests/ktx/src/main.cpp index c461f3e316..aa6795e17b 100644 --- a/tests/ktx/src/main.cpp +++ b/tests/ktx/src/main.cpp @@ -99,12 +99,10 @@ int main(int argc, char** argv) { auto ktxMemory = gpu::Texture::serialize(*testTexture); { const auto& ktxStorage = ktxMemory->getStorage(); - auto header = ktxMemory->getHeader(); QFile outFile(TEST_IMAGE_KTX); if (!outFile.open(QFile::Truncate | QFile::ReadWrite)) { throw std::runtime_error("Unable to open file"); } - //auto ktxSize = sizeof(ktx::Header); // ktxStorage->size() auto ktxSize = ktxStorage->size(); outFile.resize(ktxSize); auto dest = outFile.map(0, ktxSize); From 2f51e635543a731fbd267eb810ce2a12c6757e3f Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Tue, 7 Mar 2017 01:29:20 +0100 Subject: [PATCH 085/302] Since the deck is currently only capable of handling one clip, add temporary _length reset in FIXME section --- libraries/recording/src/recording/Deck.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/libraries/recording/src/recording/Deck.cpp b/libraries/recording/src/recording/Deck.cpp index 61eb86c91f..5a32ff4984 100644 --- a/libraries/recording/src/recording/Deck.cpp +++ b/libraries/recording/src/recording/Deck.cpp @@ -33,6 +33,7 @@ void Deck::queueClip(ClipPointer clip, float timeOffset) { // FIXME disabling multiple clips for now _clips.clear(); + _length = 0.0f; // if the time offset is not zero, wrap in an OffsetClip if (timeOffset != 0.0f) { From c267b5cd099a8dee6f670a02c8a7d5a249519aa3 Mon Sep 17 00:00:00 2001 From: samcake Date: Mon, 6 Mar 2017 18:26:31 -0800 Subject: [PATCH 086/302] Adding KeyValues read/write, work in progress --- libraries/ktx/src/ktx/KTX.cpp | 29 +++++++++++++ libraries/ktx/src/ktx/KTX.h | 27 ++++++++++-- libraries/ktx/src/ktx/Reader.cpp | 72 +++++++++++++++++++------------- libraries/ktx/src/ktx/Writer.cpp | 23 ++++++---- 4 files changed, 111 insertions(+), 40 deletions(-) diff --git a/libraries/ktx/src/ktx/KTX.cpp b/libraries/ktx/src/ktx/KTX.cpp index bee31e3200..7035d8fc54 100644 --- a/libraries/ktx/src/ktx/KTX.cpp +++ b/libraries/ktx/src/ktx/KTX.cpp @@ -71,6 +71,35 @@ size_t Header::evalImageSize(uint32_t level) const { } +KeyValue::KeyValue(const std::string& key, uint32_t valueByteSize, const Byte* value) : + _byteSize((uint32_t) key.size() + valueByteSize), + _key(key), + _value(valueByteSize) +{ + if (_value.size() && value) { + memcpy(_value.data(), value, valueByteSize); + } +} + +KeyValue::KeyValue(const std::string& key, const std::string& value) : + KeyValue(key, (uint32_t) value.size(), (const Byte*) value.data()) +{ + +} + +uint32_t KeyValue::serializedByteSize() const { + return (uint32_t) (sizeof(uint32_t) + _byteSize + Header::evalPadding(_byteSize)); +} + +uint32_t KeyValue::serializedKeyValuesByteSize(const KeyValues& keyValues) { + size_t keyValuesSize = 0; + for (auto& keyval : keyValues) { + keyValuesSize += keyval.serializedByteSize(); + } + return (keyValuesSize + Header::evalPadding(keyValuesSize)); +} + + KTX::KTX() { } diff --git a/libraries/ktx/src/ktx/KTX.h b/libraries/ktx/src/ktx/KTX.h index 3ea14b2cb5..8fdbe25ac9 100644 --- a/libraries/ktx/src/ktx/KTX.h +++ b/libraries/ktx/src/ktx/KTX.h @@ -17,6 +17,7 @@ #include #include #include +#include #include #include @@ -385,9 +386,27 @@ namespace ktx { }; // Key Values - using KeyValue = std::pair; - using KeyValues = std::list; - + struct KeyValue { + uint32_t _byteSize { 0 }; + std::string _key; + std::vector _value; + + + KeyValue(const std::string& key, uint32_t valueByteSize, const Byte* value); + + KeyValue(const std::string& key, const std::string& value); + + uint32_t serializedByteSize() const; + + static KeyValue parseKeyAndValue(uint32_t keyAndValueByteSize, const Byte* bytes); + static KeyValue parseSerializedKeyAndValue(uint32_t byteSizeAhead, const Byte* bytes); + + using KeyValues = std::list; + static uint32_t serializedKeyValuesByteSize(const KeyValues& keyValues); + + }; + using KeyValues = KeyValue::KeyValues; + struct Image { using FaceBytes = std::vector; @@ -445,12 +464,14 @@ namespace ktx { // This is exactly what is done in the create function static size_t evalStorageSize(const Header& header, const Images& images, const KeyValues& keyValues = KeyValues()); static size_t write(Byte* destBytes, size_t destByteSize, const Header& header, const Images& images, const KeyValues& keyValues = KeyValues()); + static size_t writeKeyValues(Byte* destBytes, size_t destByteSize, const KeyValues& keyValues); static Images writeImages(Byte* destBytes, size_t destByteSize, const Images& images); // Parse a block of memory and create a KTX object from it static std::unique_ptr create(const StoragePointer& src); static bool checkHeaderFromStorage(size_t srcSize, const Byte* srcBytes); + static KeyValues parseKeyValues(size_t srcSize, const Byte* srcBytes); static Images parseImages(const Header& header, size_t srcSize, const Byte* srcBytes); // Access raw pointers to the main sections of the KTX diff --git a/libraries/ktx/src/ktx/Reader.cpp b/libraries/ktx/src/ktx/Reader.cpp index f8004cf21a..10a75ddfe7 100644 --- a/libraries/ktx/src/ktx/Reader.cpp +++ b/libraries/ktx/src/ktx/Reader.cpp @@ -56,38 +56,52 @@ namespace ktx { return true; } - KeyValues getKeyValues(size_t length, const Byte* src) { - KeyValues keyValues; - size_t offset = 0; - while (offset < length) { - // determine byte size - uint32_t keyValueByteSize; - memcpy(&keyValueByteSize, src, sizeof(uint32_t)); - if (keyValueByteSize > length - offset) { - throw ReaderException("invalid key-value size"); + KeyValue KeyValue::parseKeyAndValue(uint32_t keyAndValueByteSize, const Byte* bytes) { + // find the first null character \0 + uint32_t keyLength = 0; + while (reinterpret_cast(bytes[++keyLength]) != '\0') { + if (keyLength == keyAndValueByteSize) { + // key must be null-terminated, and there must be space for the value + throw ReaderException("invalid key-value " + std::string(reinterpret_cast(bytes), keyLength)); } - - // find the first null character \0 - uint32_t keyLength = 0; - while (reinterpret_cast(src[++keyLength]) != '\0') { - if (keyLength == keyValueByteSize) { - // key must be null-terminated, and there must be space for the value - throw ReaderException("invalid key-value " + std::string(reinterpret_cast(src), keyLength)); - } - } - - // populate the key-value - keyValues.emplace_back( - std::move(std::string(reinterpret_cast(src), keyLength)), - std::move(std::string(reinterpret_cast(src + keyLength), keyValueByteSize - keyLength))); - - // advance offset/src - uint32_t keyValuePadding = 3 - ((keyValueByteSize + 3) % PACKING_SIZE); - offset += keyValueByteSize + keyValuePadding; - src += keyValueByteSize + keyValuePadding; } + return KeyValue(std::string(reinterpret_cast(bytes), keyLength), keyAndValueByteSize - keyLength, bytes + keyLength); + } + + static KeyValue parseSerializedKeyAndValue(uint32_t byteSizeAhead, const Byte* bytes) { + uint32_t keyValueByteSize; + memcpy(&keyValueByteSize, bytes, sizeof(uint32_t)); + if (keyValueByteSize > byteSizeAhead) { + throw ReaderException("invalid key-value size"); + } + + auto keyValueBytes = bytes + sizeof(uint32_t); + + // parse the key-value + return KeyValue::parseKeyAndValue(keyValueByteSize, keyValueBytes); + } + + static KeyValues parseKeyValues(size_t srcSize, const Byte* srcBytes); + + KeyValues KTX::parseKeyValues(size_t srcSize, const Byte* src) { + KeyValues keyValues; + try { + uint32_t length = (uint32_t) srcSize; + uint32_t offset = 0; + while (offset < length) { + auto keyValue = parseSerializedKeyAndValue(length - offset, src); + keyValues.emplace_back(keyValue); + + // advance offset/src + offset += keyValue.serializedByteSize(); + src += keyValue.serializedByteSize(); + } + } + catch (const ReaderException& e) { + qWarning() << e.what(); + } return keyValues; } @@ -176,7 +190,7 @@ namespace ktx { result->resetStorage(src); // read metadata - // result->_keyValues = getKeyValues(result->getHeader()->bytesOfKeyValueData, result->getKeyValueData()); + result->_keyValues = parseKeyValues(result->getHeader()->bytesOfKeyValueData, result->getKeyValueData()); // populate image table result->_images = parseImages(*result->getHeader(), result->getTexelsDataSize(), result->getTexelsData()); diff --git a/libraries/ktx/src/ktx/Writer.cpp b/libraries/ktx/src/ktx/Writer.cpp index 005b8e9e45..1a1c39e2c6 100644 --- a/libraries/ktx/src/ktx/Writer.cpp +++ b/libraries/ktx/src/ktx/Writer.cpp @@ -29,9 +29,9 @@ namespace ktx { std::unique_ptr KTX::create(const Header& header, const Images& images, const KeyValues& keyValues) { StoragePointer storagePointer; { - auto storageSize = ktx::KTX::evalStorageSize(header, images); + auto storageSize = ktx::KTX::evalStorageSize(header, images, keyValues); auto memoryStorage = new storage::MemoryStorage(storageSize); - ktx::KTX::write(memoryStorage->data(), memoryStorage->size(), header, images); + ktx::KTX::write(memoryStorage->data(), memoryStorage->size(), header, images, keyValues); storagePointer.reset(memoryStorage); } return create(storagePointer); @@ -40,8 +40,9 @@ namespace ktx { size_t KTX::evalStorageSize(const Header& header, const Images& images, const KeyValues& keyValues) { size_t storageSize = sizeof(Header); - if (header.bytesOfKeyValueData && !keyValues.empty()) { - + if (!keyValues.empty()) { + size_t keyValuesSize = KeyValue::serializedKeyValuesByteSize(keyValues); + storageSize += keyValuesSize; } auto numMips = header.getNumberOfLevels(); @@ -68,11 +69,12 @@ namespace ktx { currentDestPtr += sizeof(Header); // KeyValues - // Skip for now - if (header.bytesOfKeyValueData && !keyValues.empty()) { - + if (!keyValues.empty()) { + destHeader->bytesOfKeyValueData = writeKeyValues(currentDestPtr, destByteSize - sizeof(Header), keyValues); + } else { + // Make sure the header contains the right bytesOfKeyValueData size + destHeader->bytesOfKeyValueData = 0; } - destHeader->bytesOfKeyValueData = 0; currentDestPtr += destHeader->bytesOfKeyValueData; // Images @@ -82,6 +84,11 @@ namespace ktx { return destByteSize; } + static size_t writeKeyValues(Byte* destBytes, size_t destByteSize, const KeyValues& keyValues) { + + + } + Images KTX::writeImages(Byte* destBytes, size_t destByteSize, const Images& srcImages) { Images destImages; auto imagesDataPtr = destBytes; From 1b036b7225665ee7e052d33c7b84396bea2c87b0 Mon Sep 17 00:00:00 2001 From: sam Date: Tue, 7 Mar 2017 00:58:22 -0800 Subject: [PATCH 087/302] Implement the ktx keyvalue feature for read and write and use it with gpu::Texture to store meat data required for full serialization --- interface/src/avatar/MyAvatar.cpp | 4 +- libraries/gpu/src/gpu/Texture.h | 3 +- libraries/gpu/src/gpu/Texture_ktx.cpp | 46 ++++++++++-- libraries/ktx/src/ktx/KTX.cpp | 4 +- libraries/ktx/src/ktx/KTX.h | 4 +- libraries/ktx/src/ktx/Reader.cpp | 93 +++++++++++------------- libraries/ktx/src/ktx/Writer.cpp | 39 +++++++++- libraries/model/src/model/TextureMap.cpp | 4 +- 8 files changed, 131 insertions(+), 66 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 969268c549..3a34e4f434 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -1617,8 +1617,8 @@ void MyAvatar::postUpdate(float deltaTime) { } } - DebugDraw::getInstance().updateMyAvatarPos(getPosition()); - DebugDraw::getInstance().updateMyAvatarRot(getOrientation()); +// DebugDraw::getInstance().updateMyAvatarPos(getPosition()); + // DebugDraw::getInstance().updateMyAvatarRot(getOrientation()); AnimPose postUpdateRoomPose(_sensorToWorldMatrix); diff --git a/libraries/gpu/src/gpu/Texture.h b/libraries/gpu/src/gpu/Texture.h index 9996026254..c7fb496fc0 100755 --- a/libraries/gpu/src/gpu/Texture.h +++ b/libraries/gpu/src/gpu/Texture.h @@ -143,6 +143,7 @@ public: uint8 getMinMip() const { return _desc._minMip; } uint8 getMaxMip() const { return _desc._maxMip; } + const Desc& getDesc() const { return _desc; } protected: Desc _desc; }; @@ -516,7 +517,7 @@ public: // Textures can be serialized directly to ktx data file, here is how static ktx::KTXUniquePointer serialize(const Texture& texture); - static Texture* unserialize(Usage usage, TextureUsageType usageType, const ktx::KTXUniquePointer& srcData, const Sampler& sampler = Sampler()); + static Texture* unserialize(const ktx::KTXUniquePointer& srcData, TextureUsageType usageType = TextureUsageType::RESOURCE, Usage usage = Usage(), const Sampler::Desc& sampler = Sampler::Desc()); static bool evalKTXFormat(const Element& mipFormat, const Element& texelFormat, ktx::Header& header); static bool evalTextureFormat(const ktx::Header& header, Element& mipFormat, Element& texelFormat); diff --git a/libraries/gpu/src/gpu/Texture_ktx.cpp b/libraries/gpu/src/gpu/Texture_ktx.cpp index bd34246f51..5fed4d82bc 100644 --- a/libraries/gpu/src/gpu/Texture_ktx.cpp +++ b/libraries/gpu/src/gpu/Texture_ktx.cpp @@ -18,6 +18,30 @@ using namespace gpu; using PixelsPointer = Texture::PixelsPointer; using KtxStorage = Texture::KtxStorage; +struct GPUKTXPayload { + Sampler::Desc _samplerDesc; + Texture::Usage _usage; + TextureUsageType _usageType; + + static std::string KEY; + static bool isGPUKTX(const ktx::KeyValue& val) { + return (val._key.compare(KEY) == 0); + } + + static bool findInKeyValues(const ktx::KeyValues& keyValues, GPUKTXPayload& payload) { + auto found = std::find_if(keyValues.begin(), keyValues.end(), isGPUKTX); + if (found != keyValues.end()) { + if ((*found)._value.size() == sizeof(GPUKTXPayload)) { + memcpy(&payload, (*found)._value.data(), sizeof(GPUKTXPayload)); + return true; + } + } + return false; + } +}; + +std::string GPUKTXPayload::KEY { "hifi.gpu" }; + KtxStorage::KtxStorage(ktx::KTXUniquePointer& ktxData) { // if the source ktx is valid let's config this KtxStorage correctly @@ -116,7 +140,14 @@ ktx::KTXUniquePointer Texture::serialize(const Texture& texture) { } } - auto ktxBuffer = ktx::KTX::create(header, images); + GPUKTXPayload keyval; + keyval._samplerDesc = texture.getSampler().getDesc(); + keyval._usage = texture.getUsage(); + keyval._usageType = texture.getUsageType(); + ktx::KeyValues keyValues; + keyValues.emplace_back(ktx::KeyValue(GPUKTXPayload::KEY, sizeof(GPUKTXPayload), (ktx::Byte*) &keyval)); + + auto ktxBuffer = ktx::KTX::create(header, images, keyValues); #if 0 auto expectedMipCount = texture.evalNumMips(); assert(expectedMipCount == ktxBuffer->_images.size()); @@ -146,7 +177,7 @@ ktx::KTXUniquePointer Texture::serialize(const Texture& texture) { return ktxBuffer; } -Texture* Texture::unserialize(Usage usage, TextureUsageType usageType, const ktx::KTXUniquePointer& srcData, const Sampler& sampler) { +Texture* Texture::unserialize(const ktx::KTXUniquePointer& srcData, TextureUsageType usageType, Usage usage, const Sampler::Desc& sampler) { if (!srcData) { return nullptr; } @@ -175,7 +206,12 @@ Texture* Texture::unserialize(Usage usage, TextureUsageType usageType, const ktx type = TEX_3D; } - auto tex = Texture::create( usageType, + + // If found, use the + GPUKTXPayload gpuktxKeyValue; + bool isGPUKTXPayload = GPUKTXPayload::findInKeyValues(srcData->_keyValues, gpuktxKeyValue); + + auto tex = Texture::create( (isGPUKTXPayload ? gpuktxKeyValue._usageType : usageType), type, texelFormat, header.getPixelWidth(), @@ -183,9 +219,9 @@ Texture* Texture::unserialize(Usage usage, TextureUsageType usageType, const ktx header.getPixelDepth(), 1, // num Samples header.getNumberOfSlices(), - sampler); + (isGPUKTXPayload ? gpuktxKeyValue._samplerDesc : sampler)); - tex->setUsage(usage); + tex->setUsage((isGPUKTXPayload ? gpuktxKeyValue._usage : usage)); // Assing the mips availables tex->setStoredMipFormat(mipFormat); diff --git a/libraries/ktx/src/ktx/KTX.cpp b/libraries/ktx/src/ktx/KTX.cpp index 7035d8fc54..f9bfb377f5 100644 --- a/libraries/ktx/src/ktx/KTX.cpp +++ b/libraries/ktx/src/ktx/KTX.cpp @@ -72,7 +72,7 @@ size_t Header::evalImageSize(uint32_t level) const { KeyValue::KeyValue(const std::string& key, uint32_t valueByteSize, const Byte* value) : - _byteSize((uint32_t) key.size() + valueByteSize), + _byteSize((uint32_t) key.size() + 1 + valueByteSize), // keyString size + '\0' ending char + the value size _key(key), _value(valueByteSize) { @@ -92,7 +92,7 @@ uint32_t KeyValue::serializedByteSize() const { } uint32_t KeyValue::serializedKeyValuesByteSize(const KeyValues& keyValues) { - size_t keyValuesSize = 0; + uint32_t keyValuesSize = 0; for (auto& keyval : keyValues) { keyValuesSize += keyval.serializedByteSize(); } diff --git a/libraries/ktx/src/ktx/KTX.h b/libraries/ktx/src/ktx/KTX.h index 8fdbe25ac9..8e901b1105 100644 --- a/libraries/ktx/src/ktx/KTX.h +++ b/libraries/ktx/src/ktx/KTX.h @@ -398,8 +398,8 @@ namespace ktx { uint32_t serializedByteSize() const; - static KeyValue parseKeyAndValue(uint32_t keyAndValueByteSize, const Byte* bytes); - static KeyValue parseSerializedKeyAndValue(uint32_t byteSizeAhead, const Byte* bytes); + static KeyValue parseSerializedKeyAndValue(uint32_t srcSize, const Byte* srcBytes); + static uint32_t writeSerializedKeyAndValue(Byte* destBytes, uint32_t destByteSize, const KeyValue& keyval); using KeyValues = std::list; static uint32_t serializedKeyValuesByteSize(const KeyValues& keyValues); diff --git a/libraries/ktx/src/ktx/Reader.cpp b/libraries/ktx/src/ktx/Reader.cpp index 10a75ddfe7..81f523cb69 100644 --- a/libraries/ktx/src/ktx/Reader.cpp +++ b/libraries/ktx/src/ktx/Reader.cpp @@ -56,55 +56,6 @@ namespace ktx { return true; } - - KeyValue KeyValue::parseKeyAndValue(uint32_t keyAndValueByteSize, const Byte* bytes) { - // find the first null character \0 - uint32_t keyLength = 0; - while (reinterpret_cast(bytes[++keyLength]) != '\0') { - if (keyLength == keyAndValueByteSize) { - // key must be null-terminated, and there must be space for the value - throw ReaderException("invalid key-value " + std::string(reinterpret_cast(bytes), keyLength)); - } - } - - return KeyValue(std::string(reinterpret_cast(bytes), keyLength), keyAndValueByteSize - keyLength, bytes + keyLength); - } - - static KeyValue parseSerializedKeyAndValue(uint32_t byteSizeAhead, const Byte* bytes) { - uint32_t keyValueByteSize; - memcpy(&keyValueByteSize, bytes, sizeof(uint32_t)); - if (keyValueByteSize > byteSizeAhead) { - throw ReaderException("invalid key-value size"); - } - - auto keyValueBytes = bytes + sizeof(uint32_t); - - // parse the key-value - return KeyValue::parseKeyAndValue(keyValueByteSize, keyValueBytes); - } - - static KeyValues parseKeyValues(size_t srcSize, const Byte* srcBytes); - - KeyValues KTX::parseKeyValues(size_t srcSize, const Byte* src) { - KeyValues keyValues; - try { - uint32_t length = (uint32_t) srcSize; - uint32_t offset = 0; - while (offset < length) { - auto keyValue = parseSerializedKeyAndValue(length - offset, src); - keyValues.emplace_back(keyValue); - - // advance offset/src - offset += keyValue.serializedByteSize(); - src += keyValue.serializedByteSize(); - } - } - catch (const ReaderException& e) { - qWarning() << e.what(); - } - return keyValues; - } - bool KTX::checkHeaderFromStorage(size_t srcSize, const Byte* srcBytes) { try { // validation @@ -140,6 +91,50 @@ namespace ktx { } } + KeyValue KeyValue::parseSerializedKeyAndValue(uint32_t srcSize, const Byte* srcBytes) { + uint32_t keyAndValueByteSize; + memcpy(&keyAndValueByteSize, srcBytes, sizeof(uint32_t)); + if (keyAndValueByteSize + sizeof(uint32_t) > srcSize) { + throw ReaderException("invalid key-value size"); + } + auto keyValueBytes = srcBytes + sizeof(uint32_t); + + // find the first null character \0 and extract the key + uint32_t keyLength = 0; + while (reinterpret_cast(keyValueBytes[++keyLength]) != '\0') { + if (keyLength == keyAndValueByteSize) { + // key must be null-terminated, and there must be space for the value + throw ReaderException("invalid key-value " + std::string(reinterpret_cast(keyValueBytes), keyLength)); + } + } + uint32_t valueStartOffset = keyLength + 1; + + // parse the key-value + return KeyValue(std::string(reinterpret_cast(keyValueBytes), keyLength), + keyAndValueByteSize - valueStartOffset, keyValueBytes + valueStartOffset); + } + + KeyValues KTX::parseKeyValues(size_t srcSize, const Byte* srcBytes) { + KeyValues keyValues; + try { + auto src = srcBytes; + uint32_t length = (uint32_t) srcSize; + uint32_t offset = 0; + while (offset < length) { + auto keyValue = KeyValue::parseSerializedKeyAndValue(length - offset, src); + keyValues.emplace_back(keyValue); + + // advance offset/src + offset += keyValue.serializedByteSize(); + src += keyValue.serializedByteSize(); + } + } + catch (const ReaderException& e) { + qWarning() << e.what(); + } + return keyValues; + } + Images KTX::parseImages(const Header& header, size_t srcSize, const Byte* srcBytes) { Images images; auto currentPtr = srcBytes; diff --git a/libraries/ktx/src/ktx/Writer.cpp b/libraries/ktx/src/ktx/Writer.cpp index 1a1c39e2c6..06fba326d5 100644 --- a/libraries/ktx/src/ktx/Writer.cpp +++ b/libraries/ktx/src/ktx/Writer.cpp @@ -10,6 +10,9 @@ // #include "KTX.h" + +#include +#include #ifndef _MSC_VER #define NOEXCEPT noexcept #else @@ -70,7 +73,7 @@ namespace ktx { // KeyValues if (!keyValues.empty()) { - destHeader->bytesOfKeyValueData = writeKeyValues(currentDestPtr, destByteSize - sizeof(Header), keyValues); + destHeader->bytesOfKeyValueData = (uint32_t) writeKeyValues(currentDestPtr, destByteSize - sizeof(Header), keyValues); } else { // Make sure the header contains the right bytesOfKeyValueData size destHeader->bytesOfKeyValueData = 0; @@ -84,9 +87,39 @@ namespace ktx { return destByteSize; } - static size_t writeKeyValues(Byte* destBytes, size_t destByteSize, const KeyValues& keyValues) { - + uint32_t KeyValue::writeSerializedKeyAndValue(Byte* destBytes, uint32_t destByteSize, const KeyValue& keyval) { + uint32_t keyvalSize = keyval.serializedByteSize(); + if (keyvalSize > destByteSize) { + throw WriterException("invalid key-value size"); + } + *((uint32_t*) destBytes) = keyval._byteSize; + + auto dest = destBytes + sizeof(uint32_t); + + auto keySize = keyval._key.size() + 1; // Add 1 for the '\0' character at the end of the string + memcpy(dest, keyval._key.data(), keySize); + dest += keySize; + + memcpy(dest, keyval._value.data(), keyval._value.size()); + + return keyvalSize; + } + + size_t KTX::writeKeyValues(Byte* destBytes, size_t destByteSize, const KeyValues& keyValues) { + size_t writtenByteSize = 0; + try { + auto dest = destBytes; + for (auto& keyval : keyValues) { + size_t keyvalSize = KeyValue::writeSerializedKeyAndValue(dest, (uint32_t) (destByteSize - writtenByteSize), keyval); + writtenByteSize += keyvalSize; + dest += keyvalSize; + } + } + catch (const WriterException& e) { + qWarning() << e.what(); + } + return writtenByteSize; } Images KTX::writeImages(Byte* destBytes, size_t destByteSize, const Images& srcImages) { diff --git a/libraries/model/src/model/TextureMap.cpp b/libraries/model/src/model/TextureMap.cpp index 4f9d57b21b..56b86f22d6 100755 --- a/libraries/model/src/model/TextureMap.cpp +++ b/libraries/model/src/model/TextureMap.cpp @@ -145,8 +145,8 @@ gpu::Texture* cacheTexture(const std::string& name, gpu::Texture* srcTexture, bo fclose (file); //then create a new texture out of the ktx - auto theNewTexure = Texture::unserialize(srcTexture->getUsage(), srcTexture->getUsageType(), - ktx::KTX::create(std::static_pointer_cast(storage)), srcTexture->getSampler()); + auto theNewTexure = Texture::unserialize(ktx::KTX::create(std::static_pointer_cast(storage)), + srcTexture->getUsageType(), srcTexture->getUsage(), srcTexture->getSampler().getDesc()); if (theNewTexure) { returnedTexture = theNewTexure; From 4b4f573f8ea682208a0f66b381b4a83cc65f29e8 Mon Sep 17 00:00:00 2001 From: samcake Date: Tue, 7 Mar 2017 11:44:40 -0800 Subject: [PATCH 088/302] Removing the comments to work in debug --- interface/src/avatar/MyAvatar.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 3a34e4f434..969268c549 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -1617,8 +1617,8 @@ void MyAvatar::postUpdate(float deltaTime) { } } -// DebugDraw::getInstance().updateMyAvatarPos(getPosition()); - // DebugDraw::getInstance().updateMyAvatarRot(getOrientation()); + DebugDraw::getInstance().updateMyAvatarPos(getPosition()); + DebugDraw::getInstance().updateMyAvatarRot(getOrientation()); AnimPose postUpdateRoomPose(_sensorToWorldMatrix); From 9d860a8e8179c33d3439ad322b5d2660f92853a9 Mon Sep 17 00:00:00 2001 From: humbletim Date: Thu, 16 Feb 2017 07:49:56 -0500 Subject: [PATCH 089/302] merge require/module changes into clean branch --- libraries/script-engine/src/ScriptEngine.cpp | 464 ++++++++++++++++++ libraries/script-engine/src/ScriptEngine.h | 19 + .../script-engine/src/ScriptEngineLogging.cpp | 1 + .../script-engine/src/ScriptEngineLogging.h | 1 + 4 files changed, 485 insertions(+) diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index 73a79f1bc6..8c458f71b7 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -70,6 +70,10 @@ #include "MIDIEvent.h" +const QString ScriptEngine::_SETTINGS_ENABLE_EXTENDED_MODULE_COMPAT { + "com.highfidelity.experimental.enableExtendedModuleCompatbility" +}; + static const QScriptEngine::QObjectWrapOptions DEFAULT_QOBJECT_WRAP_OPTIONS = QScriptEngine::ExcludeDeleteLater | QScriptEngine::ExcludeChildObjects; static const QScriptValue::PropertyFlags READONLY_PROP_FLAGS { QScriptValue::ReadOnly | QScriptValue::Undeletable }; @@ -532,6 +536,44 @@ static QScriptValue createScriptableResourcePrototype(QScriptEngine* engine) { return prototype; } +void ScriptEngine::resetModuleCache(bool deleteScriptCache) { + if (QThread::currentThread() != thread()) { + QMetaObject::invokeMethod(this, "resetModuleCache"); + return; + } + { + QMutexLocker locker(&_requireLock); + auto jsRequire = globalObject().property("Script").property("require"); + auto cache = jsRequire.property("cache"); + auto cacheMeta = jsRequire.data(); + + if (deleteScriptCache) { + QScriptValueIterator it(cache); + while (it.hasNext()) { + it.next(); + if (it.flags() & QScriptValue::SkipInEnumeration) + continue; + //scriptCache->deleteScript(it.name()); + qCDebug(scriptengine) << "resetModuleCache(true) -- staging " << it.name() << " for cache reset at next require"; + cacheMeta.setProperty(it.name(), true); + } + } + //_debugDump("cacheMeta", cacheMeta); + cache = newObject(); + if (!cacheMeta.isObject()) { + cacheMeta = newObject(); + cacheMeta.setProperty("id", "Script.require.cacheMeta"); + cacheMeta.setProperty("type", "cacheMeta"); + jsRequire.setData(cacheMeta); + } + cache.setProperty("__created__", (double)QDateTime::currentMSecsSinceEpoch(), QScriptValue::SkipInEnumeration); +#if DEBUG_JS_MODULES + cache.setProperty("__meta__", cacheMeta, HIDDEN_PROP_FLAGS); +#endif + jsRequire.setProperty("cache", cache, QScriptValue::ReadOnly | QScriptValue::Undeletable); + } +} + void ScriptEngine::init() { if (_isInitialized) { return; // only initialize once @@ -595,6 +637,15 @@ void ScriptEngine::init() { registerGlobalObject("Script", this); + { + // set up Script.require.resolve and Script.require.cache + auto Script = globalObject().property("Script"); + auto require = Script.property("require"); + auto resolve = Script.property("_requireResolve"); + require.setProperty("resolve", resolve, QScriptValue::ReadOnly | QScriptValue::Undeletable); + resetModuleCache(); + } + registerGlobalObject("Audio", &AudioScriptingInterface::getInstance()); registerGlobalObject("Entities", entityScriptingInterface.data()); registerGlobalObject("Quat", &_quatLibrary); @@ -1304,6 +1355,419 @@ void ScriptEngine::print(const QString& message) { emit printedMessage(message); } +static const auto MAX_MODULE_IDENTIFIER_LEN { 4096 }; +static const auto MAX_MODULE_IDENTIFIER_LOG_LEN { 60 }; + +// Script.require.resolve -- like resolvePath, but performs more validation and throws exceptions on invalid module identifiers (for consistency with Node.js) +QString ScriptEngine::_requireResolve(const QString& moduleId, const QString& relativeTo) { + QUrl defaultScriptsLoc = defaultScriptsLocation(); + QUrl url(moduleId); + + // helper to generate an exception and return a null string + auto resolverException = [=](const QString& detail) -> QString { + currentContext()->throwError( + QString("Cannot find module '%1' (%2)") + .arg(moduleId.left(MAX_MODULE_IDENTIFIER_LOG_LEN)) + .arg(detail)); + return QString(); + }; + + // de-fuzz the input a little by restricting to rational sizes + auto idLength = url.toString().length(); + if (idLength < 1 || idLength > MAX_MODULE_IDENTIFIER_LEN) { + return resolverException( + QString("rejecting invalid module id size (%1 chars [1,%2])") + .arg(idLength).arg(MAX_MODULE_IDENTIFIER_LEN) + ); + } + + // this regex matches: absolute, dotted or path-like URLs + // (ie: the kind of stuff ScriptEngine::resolvePath already handles) + QRegularExpression qualified ("^\\w+:|^/|^[.]{1,2}(/|$)"); + + // this is for module.require (which is a bound version of require that's always relative to the module path) + if (!relativeTo.isEmpty()) { + url = QUrl(relativeTo).resolved(moduleId); + url = resolvePath(url.toString()); + } else if (qualified.match(moduleId).hasMatch()) { + url = resolvePath(moduleId); + } else { + // check if the moduleId refers to a "system" module + QString defaultsPath = defaultScriptsLoc.path(); + QString systemModulePath = QString("%1/modules/%2.js").arg(defaultsPath).arg(moduleId); + url = defaultScriptsLoc; + url.setPath(systemModulePath); + if (!QFileInfo(url.toLocalFile()).isFile()) { + return resolverException("system module not found"); + } + } + + if (url.isRelative()) { + return resolverException("could not resolve module id"); + } + + // if it looks like a local file, verify that it's an allowed path and really a file + if (url.isLocalFile()) { + QFileInfo file(url.toLocalFile()); + QUrl canonical = url; + if (file.exists()) { + canonical.setPath(file.canonicalFilePath()); + } + + bool disallowOutsideFiles = !defaultScriptsLocation().isParentOf(canonical) && !currentSandboxURL.isLocalFile(); + if (disallowOutsideFiles && !PathUtils::isDescendantOf(canonical, currentSandboxURL)) { + return resolverException( + QString("path '%1' outside of origin script '%2' '%3'") + .arg(PathUtils::stripFilename(url)) + .arg(PathUtils::stripFilename(currentSandboxURL)) + .arg(canonical.toString()) + ); + } + if (!file.exists()) { + return resolverException("path does not exist: " + url.toLocalFile()); + } + if (!file.isFile()) { + return resolverException("path is not a file: " + url.toLocalFile()); + } + } + + return url.toString(); +} + +// retrieves the current parent module from the JS scope chain +QScriptValue ScriptEngine::currentModule() { + auto jsRequire = globalObject().property("Script").property("require"); + auto cache = jsRequire.property("cache"); + auto candidate = QScriptValue(); + for(auto ctx = currentContext(); ctx && !candidate.isObject(); ctx = ctx->parentContext()) { + QScriptContextInfo contextInfo { ctx }; + candidate = cache.property(contextInfo.fileName()); + } + if (!candidate.isObject()) { + return QScriptValue(); + } + return candidate; +} + +// replaces or adds "module" to "parent.children[]" array +// (for consistency with Node.js and userscript cache invalidation without "cache busters") +bool ScriptEngine::registerModuleWithParent(const QScriptValue& module, const QScriptValue& parent) { + auto children = parent.property("children"); + if (children.isArray()) { + auto key = module.property("id"); + auto length = children.property("length").toInt32(); + for(int i=0; i < length; i++) { + if (children.property(i).property("id").strictlyEquals(key)) { + qCDebug(scriptengine_module) << key.toString() << " updating parent.children[" << i << "] = module"; + children.setProperty(i, module); + return true; + } + } + qCDebug(scriptengine_module) << key.toString() << " appending parent.children[" << length << "] = module"; + children.setProperty(length, module); + return true; + } else if (parent.isValid()) { + qCDebug(scriptengine_module) << "registerModuleWithParent -- unrecognized parent" << parent.toVariant().toString(); + } + return false; +} + +// creates a new JS "module" Object with default metadata properties +QScriptValue ScriptEngine::newModule(const QString& modulePath, const QScriptValue& parent) { + auto closure = newObject(); + auto exports = newObject(); + auto module = newObject(); + qCDebug(scriptengine_module) << "newModule" << modulePath << parent.property("filename").toString(); + + closure.setProperty("module", module, READONLY_PROP_FLAGS); + + // note: this becomes the "exports" free variable, so should not be set read only + closure.setProperty("exports", exports); + + // make the closure available to module instantiation + module.setProperty("__closure__", closure, HIDDEN_PROP_FLAGS); + + // for consistency with Node.js Module + module.setProperty("id", modulePath, READONLY_PROP_FLAGS); + module.setProperty("filename", modulePath, READONLY_PROP_FLAGS); + module.setProperty("exports", exports); // not readonly + module.setProperty("loaded", false, READONLY_PROP_FLAGS); + module.setProperty("parent", parent, READONLY_PROP_FLAGS); + module.setProperty("children", newArray(), READONLY_PROP_FLAGS); + + // module.require is a bound version of require that always resolves relative to that module's path + auto boundRequire = QScriptEngine::evaluate("(function(id) { return Script.require(Script.require.resolve(id, this.filename)); })", "(boundRequire)"); + module.setProperty("require", boundRequire, READONLY_PROP_FLAGS); + + return module; +} + +// synchronously fetch a module's source code using BatchLoader +QScriptValue ScriptEngine::fetchModuleSource(const QString& modulePath, const bool forceDownload) { + using UrlMap = QMap; + auto scriptCache = DependencyManager::get(); + QScriptValue req = newObject(); + qCDebug(scriptengine_module) << "require.fetchModuleSource: " << QUrl(modulePath).fileName() << QThread::currentThread(); + + auto onload = [=, &req](const UrlMap& data, const UrlMap& _status) { + auto url = modulePath; + auto status = _status[url]; + auto contents = data[url]; + qCDebug(scriptengine_module) << "require.fetchModuleSource.onload: " << QUrl(url).fileName() << status << QThread::currentThread(); + if (isStopping()) { + req.setProperty("status", "Stopped"); + req.setProperty("success", false); + } else { + req.setProperty("url", url); + req.setProperty("status", status); + req.setProperty("success", ScriptCache::isSuccessStatus(status)); + req.setProperty("contents", contents, HIDDEN_PROP_FLAGS); + } + }; + + if (forceDownload) { + qCDebug(scriptengine_module) << "require.requestScript -- clearing cache for" << modulePath; + scriptCache->deleteScript(modulePath); + } + BatchLoader* loader = new BatchLoader({ modulePath }); + connect(loader, &BatchLoader::finished, this, onload); + connect(this, &QObject::destroyed, loader, &QObject::deleteLater); + // fail faster? (since require() blocks the engine thread while resolving dependencies) + const int MAX_RETRIES = 1; + + loader->start(MAX_RETRIES); + + if (!loader->isFinished()) { + QTimer monitor; + QEventLoop loop; + QObject::connect(loader, &BatchLoader::finished, this, [this, &monitor, &loop]{ + monitor.stop(); + loop.quit(); + }); + + // this helps detect the case where stop() is invoked during the download + // but not seen in time to abort processing in onload()... + connect(&monitor, &QTimer::timeout, this, [this, &loop, &loader]{ + if (isStopping()) { + loop.exit(-1); + } + }); + monitor.start(500); + loop.exec(); + } + loader->deleteLater(); + return req; +} + +// evaluate a pending module object using the fetched source code +QScriptValue ScriptEngine::instantiateModule(const QScriptValue& module, const QString& sourceCode) { + QScriptValue result; + auto modulePath = module.property("filename").toString(); + auto closure = module.property("__closure__"); + + qCDebug(scriptengine_module) << QString("require.instantiateModule: %1 / %2 bytes").arg(QUrl(modulePath).fileName()).arg(sourceCode.length()); + + { + ExceptionEmitter tryCatch(this, __FUNCTION__); + + if (module.property("content-type").toString() == "application/json") { + qCDebug(scriptengine_module) << "... parsing as JSON"; +#ifdef DEV_OTHER_MODULE_JSON_PARSE + auto JSON = globalObject().property("JSON"); + auto parse = JSON.property("parse"); + if (!parse.isFunction()) { + currentContext()->throwValue(makeError("global JSON.parse is not a function", "EvalError")); + return nullValue(); + } + result = parse.call(JSON, QScriptValueList({ sourceCode })); + closure.property("module").setProperty("exports", result); + return result; +#endif + closure.setProperty("__json", sourceCode); + result = evaluateInClosure(closure, { "module.exports = JSON.parse(__json)", modulePath }); + } else { + // scoped vars for consistency with Node.js + closure.setProperty("require", module.property("require")); + closure.setProperty("__filename", modulePath, HIDDEN_PROP_FLAGS); + closure.setProperty("__dirname", QString(modulePath).replace(QRegExp("/[^/]*$"), ""), HIDDEN_PROP_FLAGS); + result = evaluateInClosure(closure, { sourceCode, modulePath }); + } + } + return result; +} + +// CommonJS/Node.js like require/module support +QScriptValue ScriptEngine::require(const QString& moduleId) { + qCDebug(scriptengine_module) << "ScriptEngine::require(" << moduleId.left(MAX_MODULE_IDENTIFIER_LOG_LEN) << ")"; + if (QThread::currentThread() != thread()) { + qCDebug(scriptengine_module) << moduleId << " threads mismatch"; + return nullValue(); + } + + // serialize require calls so the ordering/caching works correctly across multiple Entities + // note: this is a Recursive mutex so nested require's should not affected + QMutexLocker locker(&_requireLock); + // _requireDepth++; + // QObject autoDecrement;connect(&autoDecrement, &QObject::destroyed, this, [this](){ _requireDepth--; }); + + auto jsRequire = globalObject().property("Script").property("require"); + auto cacheMeta = jsRequire.data(); + auto cache = jsRequire.property("cache"); + auto parent = currentModule(); + + auto throwModuleError = [this, &cache](const QString& modulePath, const QScriptValue& error) { + cache.setProperty(modulePath, nullValue()); + if (!error.isNull()) { +#ifdef DEBUG_JS_MODULES + qCWarning(scriptengine_module) << "throwing module error:" << error.toString() << modulePath << error.property("stack").toString(); +#endif + currentContext()->throwValue(makeError(error)); + } + return nullValue(); + }; + + // start by resolving the moduleId into a fully-qualified path/URL + QString modulePath = _requireResolve(moduleId); + if (hasUncaughtException()) { + // the resolver already threw an exception -- bail early + return nullValue(); + } + + // check the resolved path against the cache + auto module = cache.property(modulePath); + + // modules get cached in `Script.require.cache` and (similar to Node.js) users can access it + // to inspect particular entries and invalidate them by deleting the key: + // `delete Script.require.cache[Script.require.resolve(moduleId)];` + + // cacheMeta is just used right now to tell deleted keys apart from undefined ones + bool invalidateCache = module.isUndefined() && cacheMeta.property(moduleId).isValid(); + + // reset the cacheMeta record so invalidation won't apply next time, even if the module fails to load + cacheMeta.setProperty(modulePath, QScriptValue()); + + auto exports = module.property("exports"); + if (!invalidateCache && exports.isObject()) { + // we have found a cacheed module -- just need to possibly register it with current parent + qCDebug(scriptengine_module) << QString("require - using cached module '%1' for '%2' (loaded: %3)") + .arg(modulePath).arg(moduleId).arg(module.property("loaded").toString()); + registerModuleWithParent(module, parent); + return exports; + } + + // bootstrap / register new empty module + module = newModule(modulePath, parent); + registerModuleWithParent(module, parent); + + // add it to the cache (this is done early so any cyclic dependencies pick up) + cache.setProperty(modulePath, module); + + // download the module source + auto req = fetchModuleSource(modulePath, invalidateCache); + + if (!req.property("success").toBool()) { + auto error = QString("error retrieving script (%1)").arg(req.property("status").toString()); + return throwModuleError(modulePath, error); + } + +#if DEBUG_JS_MODULES + qCDebug(scriptengine_module) << "require.loaded: " << + QUrl(req.property("url").toString()).fileName() << req.property("status").toString(); +#endif + + auto sourceCode = req.property("contents").toString(); + + if (QUrl(modulePath).fileName().endsWith(".json", Qt::CaseInsensitive)) { + module.setProperty("content-type", "application/json"); + } else { + module.setProperty("content-type", "application/javascript"); + } + + { + // It seems that many JSON sources don't actually put .json in the URL... + // so for now as a workaround users wanting to indicate JSON parsing mode can + // do so by calling with a custom this context, eg: + // + // var ITEMS_URL = 'https://highfidelity.io/api/v1/marketplace/items'; + // var thisObject = { 'content-type': 'application/json' }; + // var items = Script.require.call(thisObject, ITEMS_URL + '?category=everything&sort=recent'); + + auto thisObject = currentContext()->thisObject(); + bool calledWithCustomThis = thisObject.isObject() && + !thisObject.strictlyEquals(globalObject()) && + !thisObject.toQObject(); + + if (calledWithCustomThis) { +#ifdef DEBUG_JS + _debugDump("this", thisObject); +#endif + _applyUserOptions(module, thisObject); + } + } + + // evaluate the module + auto result = instantiateModule(module, sourceCode); + + if (result.isError() && !result.strictlyEquals(module.property("exports"))) { + qCWarning(scriptengine_module) << "-- result.isError --" << result.toString(); + return throwModuleError(modulePath, result); + } + + // mark as fully-loaded + module.setProperty("loaded", true, READONLY_PROP_FLAGS); + + // set up a new reference point for detecting cache key deletion + cacheMeta.setProperty(modulePath, module); + + qCDebug(scriptengine_module) << "//ScriptEngine::require(" << moduleId << ")"; + + return module.property("exports"); +} + +// User-configurable override options +void ScriptEngine::_applyUserOptions(QScriptValue& module, QScriptValue& options) { + if (!options.isValid()) { + return; + } + // options['content-type'] === 'application/json' + // -- allows JSON modules to be used from URLs not ending in .json + if (options.property("content-type").isString()) { + module.setProperty("content-type", options.property("content-type")); + qCDebug(scriptengine_module) << "module['content-type'] =" << module.property("content-type").toString(); + } + + if (ScriptEngine::_enableExtendedModuleCompatbility.get()) { + auto closure = module.property("__closure__"); + + auto maybeSetToExports = [&](const QString& key) { + if (options.property(key).toString() == "exports") { + closure.setProperty(key, module.property("exports")); + qCDebug(scriptengine_module) << "module.closure[" << key << "] = exports"; + } + }; + + // options[{key}] = 'exports' + // several "agnostic" modules in the wild are just one step away from being compatible -- + // they just didn't know not to look specifically for this, self or global for attaching + // things onto. + maybeSetToExports("global"); + maybeSetToExports("self"); + maybeSetToExports("this"); + + // when options is an Object it will get used as the value of "this" during module evaluation + // (which is what one might expect when calling require.call(thisObject, ...)) + if (options.isObject()) { + closure.setProperty("this", options); + } + + // when options.global is an Object it'll get used as the global object (during evaluation only) + if (options.property("global").isObject()) { + closure.setProperty("global", options.property("global")); + qCDebug(scriptengine_module) << "module.closure['global'] = options.global"; + } + } +} + // If a callback is specified, the included files will be loaded asynchronously and the callback will be called // when all of the files have finished loading. // If no callback is specified, the included files will be loaded synchronously and will block execution until diff --git a/libraries/script-engine/src/ScriptEngine.h b/libraries/script-engine/src/ScriptEngine.h index b988ccfe90..f64dec44c7 100644 --- a/libraries/script-engine/src/ScriptEngine.h +++ b/libraries/script-engine/src/ScriptEngine.h @@ -41,6 +41,7 @@ #include "ScriptCache.h" #include "ScriptUUID.h" #include "Vec3.h" +#include "SettingHandle.h" class QScriptEngineDebugger; @@ -157,6 +158,16 @@ public: Q_INVOKABLE void include(const QStringList& includeFiles, QScriptValue callback = QScriptValue()); Q_INVOKABLE void include(const QString& includeFile, QScriptValue callback = QScriptValue()); + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // MODULE related methods + Q_INVOKABLE QScriptValue require(const QString& moduleId); + Q_INVOKABLE void resetModuleCache(bool deleteScriptCache = false); + QScriptValue currentModule(); + bool registerModuleWithParent(const QScriptValue& module, const QScriptValue& parent); + QScriptValue newModule(const QString& modulePath, const QScriptValue& parent = QScriptValue()); + QScriptValue fetchModuleSource(const QString& modulePath, const bool forceDownload = false); + QScriptValue instantiateModule(const QScriptValue& module, const QString& sourceCode); + Q_INVOKABLE QObject* setInterval(const QScriptValue& function, int intervalMS); Q_INVOKABLE QObject* setTimeout(const QScriptValue& function, int timeoutMS); Q_INVOKABLE void clearInterval(QObject* timer) { stopTimer(reinterpret_cast(timer)); } @@ -237,6 +248,9 @@ signals: protected: void init(); Q_INVOKABLE void executeOnScriptThread(std::function function, const Qt::ConnectionType& type = Qt::QueuedConnection ); + // note: this is not meant to be called directly, but just to have QMetaObject take care of wiring it up in general; + // then inside of init() we just have to do "Script.require.resolve = Script._requireResolve;" + Q_INVOKABLE QString _requireResolve(const QString& moduleId, const QString& relativeTo = QString()); QString logException(const QScriptValue& exception); void timerFired(); @@ -272,6 +286,7 @@ protected: QHash _entityScripts; QHash _occupiedScriptURLs; QList _deferredEntityLoads; + QMutex _requireLock { QMutex::Recursive }; bool _isThreaded { false }; QScriptEngineDebugger* _debugger { nullptr }; @@ -295,6 +310,10 @@ protected: std::recursive_mutex _lock; std::chrono::microseconds _totalTimerExecution { 0 }; + + static const QString _SETTINGS_ENABLE_EXTENDED_MODULE_COMPAT; + Setting::Handle _enableExtendedModuleCompatbility { _SETTINGS_ENABLE_EXTENDED_MODULE_COMPAT, false }; + void _applyUserOptions(QScriptValue& module, QScriptValue& options); }; #endif // hifi_ScriptEngine_h diff --git a/libraries/script-engine/src/ScriptEngineLogging.cpp b/libraries/script-engine/src/ScriptEngineLogging.cpp index 2e5d293728..392bc05129 100644 --- a/libraries/script-engine/src/ScriptEngineLogging.cpp +++ b/libraries/script-engine/src/ScriptEngineLogging.cpp @@ -12,3 +12,4 @@ #include "ScriptEngineLogging.h" Q_LOGGING_CATEGORY(scriptengine, "hifi.scriptengine") +Q_LOGGING_CATEGORY(scriptengine_module, "hifi.scriptengine.module") diff --git a/libraries/script-engine/src/ScriptEngineLogging.h b/libraries/script-engine/src/ScriptEngineLogging.h index 0e614dd5bf..62e46632a6 100644 --- a/libraries/script-engine/src/ScriptEngineLogging.h +++ b/libraries/script-engine/src/ScriptEngineLogging.h @@ -15,6 +15,7 @@ #include Q_DECLARE_LOGGING_CATEGORY(scriptengine) +Q_DECLARE_LOGGING_CATEGORY(scriptengine_module) #endif // hifi_ScriptEngineLogging_h From efc61c25ad6bc6a5383d1345c8a5c249f60d2327 Mon Sep 17 00:00:00 2001 From: humbletim Date: Thu, 16 Feb 2017 18:33:34 -0500 Subject: [PATCH 090/302] * use explicit int's for moduleId constants * maxTestConstructorValueSize was already limiting debug output size -- adopt its strategy, use shared MAX_DEBUG_VALUE_LENGTH const --- libraries/script-engine/src/ScriptEngine.cpp | 35 +++++++++++--------- 1 file changed, 19 insertions(+), 16 deletions(-) diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index 8c458f71b7..8fb7a88c66 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -74,13 +74,14 @@ const QString ScriptEngine::_SETTINGS_ENABLE_EXTENDED_MODULE_COMPAT { "com.highfidelity.experimental.enableExtendedModuleCompatbility" }; +static const int MAX_MODULE_ID_LENTGH { 4096 }; +static const int MAX_DEBUG_VALUE_LENGTH { 80 }; + static const QScriptEngine::QObjectWrapOptions DEFAULT_QOBJECT_WRAP_OPTIONS = QScriptEngine::ExcludeDeleteLater | QScriptEngine::ExcludeChildObjects; static const QScriptValue::PropertyFlags READONLY_PROP_FLAGS { QScriptValue::ReadOnly | QScriptValue::Undeletable }; static const QScriptValue::PropertyFlags READONLY_HIDDEN_PROP_FLAGS { READONLY_PROP_FLAGS | QScriptValue::SkipInEnumeration }; - - static const bool HIFI_AUTOREFRESH_FILE_SCRIPTS { true }; Q_DECLARE_METATYPE(QScriptEngine::FunctionSignature) @@ -551,8 +552,9 @@ void ScriptEngine::resetModuleCache(bool deleteScriptCache) { QScriptValueIterator it(cache); while (it.hasNext()) { it.next(); - if (it.flags() & QScriptValue::SkipInEnumeration) + if (it.flags() & QScriptValue::SkipInEnumeration) { continue; + } //scriptCache->deleteScript(it.name()); qCDebug(scriptengine) << "resetModuleCache(true) -- staging " << it.name() << " for cache reset at next require"; cacheMeta.setProperty(it.name(), true); @@ -1355,29 +1357,31 @@ void ScriptEngine::print(const QString& message) { emit printedMessage(message); } -static const auto MAX_MODULE_IDENTIFIER_LEN { 4096 }; -static const auto MAX_MODULE_IDENTIFIER_LOG_LEN { 60 }; - // Script.require.resolve -- like resolvePath, but performs more validation and throws exceptions on invalid module identifiers (for consistency with Node.js) QString ScriptEngine::_requireResolve(const QString& moduleId, const QString& relativeTo) { QUrl defaultScriptsLoc = defaultScriptsLocation(); QUrl url(moduleId); // helper to generate an exception and return a null string - auto resolverException = [=](const QString& detail) -> QString { - currentContext()->throwError( + auto resolverException = [=](const QString& detail, const QString& type = "Error") -> QString { + auto displayId = moduleId; + if (displayId.length() > MAX_DEBUG_VALUE_LENGTH) { + displayId = displayId.mid(0, MAX_DEBUG_VALUE_LENGTH) + "..."; + } + currentContext()->throwValue(makeError( QString("Cannot find module '%1' (%2)") - .arg(moduleId.left(MAX_MODULE_IDENTIFIER_LOG_LEN)) - .arg(detail)); + .arg(displayId) + .arg(detail), type)); return QString(); }; // de-fuzz the input a little by restricting to rational sizes auto idLength = url.toString().length(); - if (idLength < 1 || idLength > MAX_MODULE_IDENTIFIER_LEN) { + if (idLength < 1 || idLength > MAX_MODULE_ID_LENTGH) { return resolverException( QString("rejecting invalid module id size (%1 chars [1,%2])") - .arg(idLength).arg(MAX_MODULE_IDENTIFIER_LEN) + .arg(idLength).arg(MAX_MODULE_ID_LENTGH), + "RangeError" ); } @@ -1598,7 +1602,7 @@ QScriptValue ScriptEngine::instantiateModule(const QScriptValue& module, const Q // CommonJS/Node.js like require/module support QScriptValue ScriptEngine::require(const QString& moduleId) { - qCDebug(scriptengine_module) << "ScriptEngine::require(" << moduleId.left(MAX_MODULE_IDENTIFIER_LOG_LEN) << ")"; + qCDebug(scriptengine_module) << "ScriptEngine::require(" << moduleId.left(MAX_DEBUG_VALUE_LENGTH) << ")"; if (QThread::currentThread() != thread()) { qCDebug(scriptengine_module) << moduleId << " threads mismatch"; return nullValue(); @@ -2245,9 +2249,8 @@ void ScriptEngine::entityScriptContentAvailable(const EntityItemID& entityID, co testConstructorType = "empty"; } QString testConstructorValue = testConstructor.toString(); - const int maxTestConstructorValueSize = 80; - if (testConstructorValue.size() > maxTestConstructorValueSize) { - testConstructorValue = testConstructorValue.mid(0, maxTestConstructorValueSize) + "..."; + if (testConstructorValue.size() > MAX_DEBUG_VALUE_LENGTH) { + testConstructorValue = testConstructorValue.mid(0, MAX_DEBUG_VALUE_LENGTH) + "..."; } auto message = QString("failed to load entity script -- expected a function, got %1, %2") .arg(testConstructorType).arg(testConstructorValue); From 32e450e6c23c50e005edd798ca8f631e4a9ab2ef Mon Sep 17 00:00:00 2001 From: humbletim Date: Thu, 16 Feb 2017 20:19:40 -0500 Subject: [PATCH 091/302] * rework _requireResolve error throws (to avoid using lambda w/default args -- which vc++ didn't care for) * add ExceptionEmitters to require/resolve so errors get logged even if invoked indpendently from a script --- libraries/script-engine/src/ScriptEngine.cpp | 48 ++++++++++---------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index 8fb7a88c66..882fec6c72 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -1361,28 +1361,22 @@ void ScriptEngine::print(const QString& message) { QString ScriptEngine::_requireResolve(const QString& moduleId, const QString& relativeTo) { QUrl defaultScriptsLoc = defaultScriptsLocation(); QUrl url(moduleId); + ExceptionEmitter tryCatcher(this, __FUNCTION__); - // helper to generate an exception and return a null string - auto resolverException = [=](const QString& detail, const QString& type = "Error") -> QString { - auto displayId = moduleId; - if (displayId.length() > MAX_DEBUG_VALUE_LENGTH) { - displayId = displayId.mid(0, MAX_DEBUG_VALUE_LENGTH) + "..."; - } - currentContext()->throwValue(makeError( - QString("Cannot find module '%1' (%2)") - .arg(displayId) - .arg(detail), type)); - return QString(); - }; + auto displayId = moduleId; + if (displayId.length() > MAX_DEBUG_VALUE_LENGTH) { + displayId = displayId.mid(0, MAX_DEBUG_VALUE_LENGTH) + "..."; + } + auto message = QString("Cannot find module '%1' (%2)").arg(displayId); + auto ctx = currentContext(); // de-fuzz the input a little by restricting to rational sizes auto idLength = url.toString().length(); if (idLength < 1 || idLength > MAX_MODULE_ID_LENTGH) { - return resolverException( - QString("rejecting invalid module id size (%1 chars [1,%2])") - .arg(idLength).arg(MAX_MODULE_ID_LENTGH), - "RangeError" - ); + auto details = QString("rejecting invalid module id size (%1 chars [1,%2])") + .arg(idLength).arg(MAX_MODULE_ID_LENTGH); + ctx->throwValue(makeError(message.arg(details), "RangeError")); + return nullptr; } // this regex matches: absolute, dotted or path-like URLs @@ -1402,12 +1396,14 @@ QString ScriptEngine::_requireResolve(const QString& moduleId, const QString& re url = defaultScriptsLoc; url.setPath(systemModulePath); if (!QFileInfo(url.toLocalFile()).isFile()) { - return resolverException("system module not found"); + ctx->throwValue(makeError(message.arg("system module not found"))); + return nullptr; } } if (url.isRelative()) { - return resolverException("could not resolve module id"); + ctx->throwValue(makeError(message.arg("could not resolve module id"))); + return nullptr; } // if it looks like a local file, verify that it's an allowed path and really a file @@ -1420,18 +1416,21 @@ QString ScriptEngine::_requireResolve(const QString& moduleId, const QString& re bool disallowOutsideFiles = !defaultScriptsLocation().isParentOf(canonical) && !currentSandboxURL.isLocalFile(); if (disallowOutsideFiles && !PathUtils::isDescendantOf(canonical, currentSandboxURL)) { - return resolverException( + ctx->throwValue(makeError(message.arg( QString("path '%1' outside of origin script '%2' '%3'") .arg(PathUtils::stripFilename(url)) .arg(PathUtils::stripFilename(currentSandboxURL)) .arg(canonical.toString()) - ); + ))); + return nullptr; } if (!file.exists()) { - return resolverException("path does not exist: " + url.toLocalFile()); + ctx->throwValue(makeError(message.arg("path does not exist: " + url.toLocalFile()))); + return nullptr; } if (!file.isFile()) { - return resolverException("path is not a file: " + url.toLocalFile()); + ctx->throwValue(makeError(message.arg("path is not a file: " + url.toLocalFile()))); + return nullptr; } } @@ -1611,6 +1610,7 @@ QScriptValue ScriptEngine::require(const QString& moduleId) { // serialize require calls so the ordering/caching works correctly across multiple Entities // note: this is a Recursive mutex so nested require's should not affected QMutexLocker locker(&_requireLock); + ExceptionEmitter tryCatcher(this, __FUNCTION__); // _requireDepth++; // QObject autoDecrement;connect(&autoDecrement, &QObject::destroyed, this, [this](){ _requireDepth--; }); @@ -1632,7 +1632,7 @@ QScriptValue ScriptEngine::require(const QString& moduleId) { // start by resolving the moduleId into a fully-qualified path/URL QString modulePath = _requireResolve(moduleId); - if (hasUncaughtException()) { + if (modulePath.isNull() || tryCatcher.hasPending()) { // the resolver already threw an exception -- bail early return nullValue(); } From e91de1775ec192c610bf3c9596bc9cd0c30d6d53 Mon Sep 17 00:00:00 2001 From: humbletim Date: Thu, 16 Feb 2017 21:10:50 -0500 Subject: [PATCH 092/302] use a fully-qualified initializer when constructing BatchLoader --- libraries/script-engine/src/ScriptEngine.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index 882fec6c72..09d267f3ba 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -1532,7 +1532,7 @@ QScriptValue ScriptEngine::fetchModuleSource(const QString& modulePath, const bo qCDebug(scriptengine_module) << "require.requestScript -- clearing cache for" << modulePath; scriptCache->deleteScript(modulePath); } - BatchLoader* loader = new BatchLoader({ modulePath }); + BatchLoader* loader = new BatchLoader(QList({ modulePath })); connect(loader, &BatchLoader::finished, this, onload); connect(this, &QObject::destroyed, loader, &QObject::deleteLater); // fail faster? (since require() blocks the engine thread while resolving dependencies) From 9b096513373662b3d88c61a20f6d9f42cb0dfd7c Mon Sep 17 00:00:00 2001 From: humbletim Date: Wed, 1 Mar 2017 09:11:40 -0500 Subject: [PATCH 093/302] Move BaseScriptEngine from script-engine to shared. --- libraries/{script-engine => shared}/src/BaseScriptEngine.cpp | 0 libraries/{script-engine => shared}/src/BaseScriptEngine.h | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename libraries/{script-engine => shared}/src/BaseScriptEngine.cpp (100%) rename libraries/{script-engine => shared}/src/BaseScriptEngine.h (100%) diff --git a/libraries/script-engine/src/BaseScriptEngine.cpp b/libraries/shared/src/BaseScriptEngine.cpp similarity index 100% rename from libraries/script-engine/src/BaseScriptEngine.cpp rename to libraries/shared/src/BaseScriptEngine.cpp diff --git a/libraries/script-engine/src/BaseScriptEngine.h b/libraries/shared/src/BaseScriptEngine.h similarity index 100% rename from libraries/script-engine/src/BaseScriptEngine.h rename to libraries/shared/src/BaseScriptEngine.h From 40ba8185a06a02f1106d54424032c9b8456bc336 Mon Sep 17 00:00:00 2001 From: humbletim Date: Wed, 1 Mar 2017 09:14:19 -0500 Subject: [PATCH 094/302] * Update per 21114-part2 changes. * Add explicit thread safety guards. * Add Entities.queryPropertyMetdata for unit testing Entity script module support. * Cleanup / commenting pass. --- .../src/EntityTreeRenderer.cpp | 1 + .../src/EntitiesScriptEngineProvider.h | 4 +- .../entities/src/EntityScriptingInterface.cpp | 109 +++++++ .../entities/src/EntityScriptingInterface.h | 20 ++ libraries/script-engine/src/ScriptEngine.cpp | 280 ++++++++++-------- libraries/script-engine/src/ScriptEngine.h | 13 +- libraries/shared/src/BaseScriptEngine.cpp | 129 ++++++-- libraries/shared/src/BaseScriptEngine.h | 43 ++- 8 files changed, 440 insertions(+), 159 deletions(-) diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.cpp b/libraries/entities-renderer/src/EntityTreeRenderer.cpp index bd25bcf905..2c07c368f9 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.cpp +++ b/libraries/entities-renderer/src/EntityTreeRenderer.cpp @@ -146,6 +146,7 @@ void EntityTreeRenderer::clear() { void EntityTreeRenderer::reloadEntityScripts() { _entitiesScriptEngine->unloadAllEntityScripts(); + _entitiesScriptEngine->resetModuleCache(); foreach(auto entity, _entitiesInScene) { if (!entity->getScript().isEmpty()) { _entitiesScriptEngine->loadEntityScript(entity->getEntityItemID(), entity->getScript(), true); diff --git a/libraries/entities/src/EntitiesScriptEngineProvider.h b/libraries/entities/src/EntitiesScriptEngineProvider.h index 69bf73e688..d87dd105c2 100644 --- a/libraries/entities/src/EntitiesScriptEngineProvider.h +++ b/libraries/entities/src/EntitiesScriptEngineProvider.h @@ -15,11 +15,13 @@ #define hifi_EntitiesScriptEngineProvider_h #include +#include #include "EntityItemID.h" class EntitiesScriptEngineProvider { public: virtual void callEntityScriptMethod(const EntityItemID& entityID, const QString& methodName, const QStringList& params = QStringList()) = 0; + virtual QFuture getLocalEntityScriptDetails(const EntityItemID& entityID) = 0; }; -#endif // hifi_EntitiesScriptEngineProvider_h \ No newline at end of file +#endif // hifi_EntitiesScriptEngineProvider_h diff --git a/libraries/entities/src/EntityScriptingInterface.cpp b/libraries/entities/src/EntityScriptingInterface.cpp index 540eba4511..df88194f9f 100644 --- a/libraries/entities/src/EntityScriptingInterface.cpp +++ b/libraries/entities/src/EntityScriptingInterface.cpp @@ -10,6 +10,9 @@ // #include "EntityScriptingInterface.h" +#include +#include + #include "EntityItemID.h" #include #include @@ -24,6 +27,7 @@ #include "ModelEntityItem.h" #include "QVariantGLM.h" #include "SimulationOwner.h" +#include "BaseScriptEngine.h" #include "ZoneEntityItem.h" #include "WebEntityItem.h" #include @@ -680,6 +684,111 @@ bool EntityScriptingInterface::reloadServerScripts(QUuid entityID) { return client->reloadServerScript(entityID); } +bool EntityScriptingInterface::queryPropertyMetadata(QUuid entityID, QScriptValue property, QScriptValue scopeOrCallback, QScriptValue methodOrName) { + auto name = property.toString(); + auto handler = makeScopedHandlerObject(scopeOrCallback, methodOrName); + QPointer engine = dynamic_cast(handler.engine()); + if (!engine) { + qCDebug(entities) << "queryPropertyMetadata without detectable engine" << entityID << name; + return false; + } + connect(engine, &QObject::destroyed, this, [=]() { + qDebug() << "queryPropertyMetadata -- engine destroyed!" << (!engine ? "nullptr" : "engine"); + }); + if (!handler.property("callback").isFunction()) { + qDebug() << "!handler.callback.isFunction" << engine; + engine->raiseException(engine->makeError("callback is not a function", "TypeError")); + return false; + } + if (name == "userData") { + EntityItemPointer entity = _entityTree->findEntityByEntityItemID(entityID); + QScriptValue err, result; + if (entity) { + auto JSON = engine->globalObject().property("JSON"); + auto parsed = JSON.property("parse").call(JSON, QScriptValueList({ entity->getUserData() })); + if (engine->hasUncaughtException()) { + err = engine->cloneUncaughtException(__FUNCTION__); + engine->clearExceptions(); + } else { + result = parsed; + } + } else { + err = engine->makeError("entity not found"); + } + QFutureWatcher *request = new QFutureWatcher; + connect(request, &QFutureWatcher::finished, engine, [=]() mutable { + if (!engine) { + qCDebug(entities) << "queryPropertyMetadata -- engine destroyed while inflight" << entityID << name; + return; + } + callScopedHandlerObject(handler, err, result); + request->deleteLater(); + }); + request->setFuture(QtConcurrent::run([]() -> QVariant { + QThread::sleep(1); + return 1; + })); + return true; + } else if (name == "script") { + using LocalScriptStatusRequest = QFutureWatcher; + LocalScriptStatusRequest *request = new LocalScriptStatusRequest; + connect(request, &LocalScriptStatusRequest::finished, engine, [=]() mutable { + if (!engine) { + qCDebug(entities) << "queryPropertyMetadata -- engine destroyed while inflight" << entityID << name; + return; + } + auto details = request->result().toMap(); + QScriptValue err, result; + if (details.contains("isError")) { + if (!details.contains("message")) { + details["message"] = details["errorInfo"]; + } + err = engine->makeError(engine->toScriptValue(details)); + } else { + details["success"] = true; + result = engine->toScriptValue(details); + } + callScopedHandlerObject(handler, err, result); + request->deleteLater(); + }); + request->setFuture(_entitiesScriptEngine->getLocalEntityScriptDetails(entityID)); + return true; + } else if (name == "serverScripts") { + auto client = DependencyManager::get(); + auto request = client->createScriptStatusRequest(entityID); + connect(request, &GetScriptStatusRequest::finished, engine, [=](GetScriptStatusRequest* request) mutable { + if (!engine) { + qCDebug(entities) << "queryPropertyMetadata -- engine destroyed while inflight" << entityID << name; + return; + } + QVariantMap details; + details["success"] = request->getResponseReceived(); + details["isRunning"] = request->getIsRunning(); + details["status"] = EntityScriptStatus_::valueToKey(request->getStatus()).toLower(); + details["errorInfo"] = request->getErrorInfo(); + + QScriptValue err, result; + if (!details["success"].toBool()) { + if (!details.contains("message") && details.contains("errorInfo")) { + details["message"] = details["errorInfo"]; + } + if (details["message"].toString().isEmpty()) { + details["message"] = "entity server script details not found"; + } + err = engine->makeError(engine->toScriptValue(details)); + } else { + result = engine->toScriptValue(details); + } + callScopedHandlerObject(handler, err, result); + request->deleteLater(); + }); + request->start(); + return true; + } + engine->raiseException(engine->makeError("property has no mapped metadata: " + name)); + return false; +} + bool EntityScriptingInterface::getServerScriptStatus(QUuid entityID, QScriptValue callback) { auto client = DependencyManager::get(); auto request = client->createScriptStatusRequest(entityID); diff --git a/libraries/entities/src/EntityScriptingInterface.h b/libraries/entities/src/EntityScriptingInterface.h index e9f0637830..2c3c654528 100644 --- a/libraries/entities/src/EntityScriptingInterface.h +++ b/libraries/entities/src/EntityScriptingInterface.h @@ -211,6 +211,26 @@ public slots: Q_INVOKABLE RayToEntityIntersectionResult findRayIntersectionBlocking(const PickRay& ray, bool precisionPicking = false, const QScriptValue& entityIdsToInclude = QScriptValue(), const QScriptValue& entityIdsToDiscard = QScriptValue()); Q_INVOKABLE bool reloadServerScripts(QUuid entityID); + + /**jsdoc + * Query for the available metadata behind one of an Entity's "magic" properties (eg: `script` and `serverScripts`). + * + * @function Entities.queryPropertyMetadata + * @param {EntityID} entityID The ID of the entity. + * @param {string} property The name of the property extended metadata is wanted for. + * @param {ResultCallback} callback Executes callback(err, result) with the query results. + */ + /**jsdoc + * Query for the available metadata behind one of an Entity's "magic" properties (eg: `script` and `serverScripts`). + * + * @function Entities.queryPropertyMetadata + * @param {EntityID} entityID The ID of the entity. + * @param {string} property The name of the property extended metadata is wanted for. + * @param {Object} thisObject The scoping "this" context that callback will be executed within. + * @param {ResultCallback} callbackOrMethodName Executes thisObject[callbackOrMethodName](err, result) with the query results. + */ + Q_INVOKABLE bool queryPropertyMetadata(QUuid entityID, QScriptValue property, QScriptValue scopeOrCallback, QScriptValue methodOrName = QScriptValue()); + Q_INVOKABLE bool getServerScriptStatus(QUuid entityID, QScriptValue callback); Q_INVOKABLE void setLightsArePickable(bool value); diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index 09d267f3ba..c7364450a1 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -19,6 +19,9 @@ #include #include +#include +#include + #include #include @@ -74,6 +77,10 @@ const QString ScriptEngine::_SETTINGS_ENABLE_EXTENDED_MODULE_COMPAT { "com.highfidelity.experimental.enableExtendedModuleCompatbility" }; +const QString ScriptEngine::_SETTINGS_ENABLE_EXTENDED_EXCEPTIONS { + "com.highfidelity.experimental.enableExtendedJSExceptions" +}; + static const int MAX_MODULE_ID_LENTGH { 4096 }; static const int MAX_DEBUG_VALUE_LENGTH { 80 }; @@ -146,7 +153,7 @@ QString encodeEntityIdIntoEntityUrl(const QString& url, const QString& entityID) } QString ScriptEngine::logException(const QScriptValue& exception) { - auto message = formatException(exception); + auto message = formatException(exception, _enableExtendedJSExceptions.get()); scriptErrorMessage(qPrintable(message)); return message; } @@ -338,7 +345,11 @@ void ScriptEngine::runInThread() { // The thread interface cannot live on itself, and we want to move this into the thread, so // the thread cannot have this as a parent. QThread* workerThread = new QThread(); +#ifdef Q_OS_LINUX + workerThread->setObjectName(QString("js:") + getFilename()); +#else workerThread->setObjectName(QString("Script Thread:") + getFilename()); +#endif moveToThread(workerThread); // NOTE: If you connect any essential signals for proper shutdown or cleanup of @@ -539,41 +550,37 @@ static QScriptValue createScriptableResourcePrototype(QScriptEngine* engine) { void ScriptEngine::resetModuleCache(bool deleteScriptCache) { if (QThread::currentThread() != thread()) { - QMetaObject::invokeMethod(this, "resetModuleCache"); + executeOnScriptThread([=](){ resetModuleCache(deleteScriptCache); }); return; } - { - QMutexLocker locker(&_requireLock); - auto jsRequire = globalObject().property("Script").property("require"); - auto cache = jsRequire.property("cache"); - auto cacheMeta = jsRequire.data(); + auto jsRequire = globalObject().property("Script").property("require"); + auto cache = jsRequire.property("cache"); + auto cacheMeta = jsRequire.data(); - if (deleteScriptCache) { - QScriptValueIterator it(cache); - while (it.hasNext()) { - it.next(); - if (it.flags() & QScriptValue::SkipInEnumeration) { - continue; - } - //scriptCache->deleteScript(it.name()); - qCDebug(scriptengine) << "resetModuleCache(true) -- staging " << it.name() << " for cache reset at next require"; - cacheMeta.setProperty(it.name(), true); + if (deleteScriptCache) { + QScriptValueIterator it(cache); + while (it.hasNext()) { + it.next(); + if (it.flags() & QScriptValue::SkipInEnumeration) { + continue; } + //scriptCache->deleteScript(it.name()); + qCDebug(scriptengine) << "resetModuleCache(true) -- staging " << it.name() << " for cache reset at next require"; + cacheMeta.setProperty(it.name(), true); } - //_debugDump("cacheMeta", cacheMeta); - cache = newObject(); - if (!cacheMeta.isObject()) { - cacheMeta = newObject(); - cacheMeta.setProperty("id", "Script.require.cacheMeta"); - cacheMeta.setProperty("type", "cacheMeta"); - jsRequire.setData(cacheMeta); - } - cache.setProperty("__created__", (double)QDateTime::currentMSecsSinceEpoch(), QScriptValue::SkipInEnumeration); -#if DEBUG_JS_MODULES - cache.setProperty("__meta__", cacheMeta, HIDDEN_PROP_FLAGS); -#endif - jsRequire.setProperty("cache", cache, QScriptValue::ReadOnly | QScriptValue::Undeletable); } + cache = newObject(); + if (!cacheMeta.isObject()) { + cacheMeta = newObject(); + cacheMeta.setProperty("id", "Script.require.cacheMeta"); + cacheMeta.setProperty("type", "cacheMeta"); + jsRequire.setData(cacheMeta); + } + cache.setProperty("__created__", (double)QDateTime::currentMSecsSinceEpoch(), QScriptValue::SkipInEnumeration); +#if DEBUG_JS_MODULES + cache.setProperty("__meta__", cacheMeta, READONLY_HIDDEN_PROP_FLAGS); +#endif + jsRequire.setProperty("cache", cache, QScriptValue::ReadOnly | QScriptValue::Undeletable); } void ScriptEngine::init() { @@ -916,6 +923,11 @@ void ScriptEngine::addEventHandler(const EntityItemID& entityID, const QString& handlersForEvent << handlerData; // Note that the same handler can be added many times. See removeEntityEventHandler(). } +// this is not redundant -- the version in BaseScriptEngine is specifically not Q_INVOKABLE +QScriptValue ScriptEngine::evaluateInClosure(const QScriptValue& closure, const QScriptProgram& program) { + return BaseScriptEngine::evaluateInClosure(closure, program); +} + QScriptValue ScriptEngine::evaluate(const QString& sourceCode, const QString& fileName, int lineNumber) { if (DependencyManager::get()->isStopped()) { return QScriptValue(); // bail early @@ -938,29 +950,26 @@ QScriptValue ScriptEngine::evaluate(const QString& sourceCode, const QString& fi // Check syntax auto syntaxError = lintScript(sourceCode, fileName); if (syntaxError.isError()) { - if (isEvaluating()) { - currentContext()->throwValue(syntaxError); - } else { + if (!isEvaluating()) { syntaxError.setProperty("detail", "evaluate"); - emit unhandledException(syntaxError); } + raiseException(syntaxError); + maybeEmitUncaughtException(__FUNCTION__); return syntaxError; } QScriptProgram program { sourceCode, fileName, lineNumber }; if (program.isNull()) { // can this happen? auto err = makeError("could not create QScriptProgram for " + fileName); - emit unhandledException(err); + raiseException(err); + maybeEmitUncaughtException(__FUNCTION__); return err; } QScriptValue result; { result = BaseScriptEngine::evaluate(program); - if (!isEvaluating() && hasUncaughtException()) { - emit unhandledException(cloneUncaughtException(__FUNCTION__)); - clearExceptions(); - } + maybeEmitUncaughtException(__FUNCTION__); } return result; } @@ -983,10 +992,7 @@ void ScriptEngine::run() { { evaluate(_scriptContents, _fileNameString); - if (!isEvaluating() && hasUncaughtException()) { - emit unhandledException(cloneUncaughtException(__FUNCTION__)); - clearExceptions(); - } + maybeEmitUncaughtException(__FUNCTION__); } #ifdef _WIN32 // VS13 does not sleep_until unless it uses the system_clock, see: @@ -1359,24 +1365,30 @@ void ScriptEngine::print(const QString& message) { // Script.require.resolve -- like resolvePath, but performs more validation and throws exceptions on invalid module identifiers (for consistency with Node.js) QString ScriptEngine::_requireResolve(const QString& moduleId, const QString& relativeTo) { + if (!IS_THREADSAFE_INVOCATION(thread(), __FUNCTION__)) { + return QString(); + } QUrl defaultScriptsLoc = defaultScriptsLocation(); QUrl url(moduleId); - ExceptionEmitter tryCatcher(this, __FUNCTION__); auto displayId = moduleId; if (displayId.length() > MAX_DEBUG_VALUE_LENGTH) { displayId = displayId.mid(0, MAX_DEBUG_VALUE_LENGTH) + "..."; } auto message = QString("Cannot find module '%1' (%2)").arg(displayId); - auto ctx = currentContext(); + + auto throwResolveError = [&](const QScriptValue& error) -> QString { + raiseException(error); + maybeEmitUncaughtException("require.resolve"); + return nullptr; + }; // de-fuzz the input a little by restricting to rational sizes auto idLength = url.toString().length(); if (idLength < 1 || idLength > MAX_MODULE_ID_LENTGH) { auto details = QString("rejecting invalid module id size (%1 chars [1,%2])") .arg(idLength).arg(MAX_MODULE_ID_LENTGH); - ctx->throwValue(makeError(message.arg(details), "RangeError")); - return nullptr; + return throwResolveError(makeError(message.arg(details), "RangeError")); } // this regex matches: absolute, dotted or path-like URLs @@ -1396,14 +1408,12 @@ QString ScriptEngine::_requireResolve(const QString& moduleId, const QString& re url = defaultScriptsLoc; url.setPath(systemModulePath); if (!QFileInfo(url.toLocalFile()).isFile()) { - ctx->throwValue(makeError(message.arg("system module not found"))); - return nullptr; + return throwResolveError(makeError(message.arg("system module not found"))); } } if (url.isRelative()) { - ctx->throwValue(makeError(message.arg("could not resolve module id"))); - return nullptr; + return throwResolveError(makeError(message.arg("could not resolve module id"))); } // if it looks like a local file, verify that it's an allowed path and really a file @@ -1416,34 +1426,35 @@ QString ScriptEngine::_requireResolve(const QString& moduleId, const QString& re bool disallowOutsideFiles = !defaultScriptsLocation().isParentOf(canonical) && !currentSandboxURL.isLocalFile(); if (disallowOutsideFiles && !PathUtils::isDescendantOf(canonical, currentSandboxURL)) { - ctx->throwValue(makeError(message.arg( + return throwResolveError(makeError(message.arg( QString("path '%1' outside of origin script '%2' '%3'") .arg(PathUtils::stripFilename(url)) .arg(PathUtils::stripFilename(currentSandboxURL)) .arg(canonical.toString()) ))); - return nullptr; } if (!file.exists()) { - ctx->throwValue(makeError(message.arg("path does not exist: " + url.toLocalFile()))); - return nullptr; + return throwResolveError(makeError(message.arg("path does not exist: " + url.toLocalFile()))); } if (!file.isFile()) { - ctx->throwValue(makeError(message.arg("path is not a file: " + url.toLocalFile()))); - return nullptr; + return throwResolveError(makeError(message.arg("path is not a file: " + url.toLocalFile()))); } } + maybeEmitUncaughtException(__FUNCTION__); return url.toString(); } // retrieves the current parent module from the JS scope chain QScriptValue ScriptEngine::currentModule() { + if (!IS_THREADSAFE_INVOCATION(thread(), __FUNCTION__)) { + return unboundNullValue(); + } auto jsRequire = globalObject().property("Script").property("require"); auto cache = jsRequire.property("cache"); auto candidate = QScriptValue(); - for(auto ctx = currentContext(); ctx && !candidate.isObject(); ctx = ctx->parentContext()) { - QScriptContextInfo contextInfo { ctx }; + for (auto c = currentContext(); c && !candidate.isObject(); c = c->parentContext()) { + QScriptContextInfo contextInfo { c }; candidate = cache.property(contextInfo.fileName()); } if (!candidate.isObject()) { @@ -1459,7 +1470,7 @@ bool ScriptEngine::registerModuleWithParent(const QScriptValue& module, const QS if (children.isArray()) { auto key = module.property("id"); auto length = children.property("length").toInt32(); - for(int i=0; i < length; i++) { + for (int i=0; i < length; i++) { if (children.property(i).property("id").strictlyEquals(key)) { qCDebug(scriptengine_module) << key.toString() << " updating parent.children[" << i << "] = module"; children.setProperty(i, module); @@ -1488,7 +1499,7 @@ QScriptValue ScriptEngine::newModule(const QString& modulePath, const QScriptVal closure.setProperty("exports", exports); // make the closure available to module instantiation - module.setProperty("__closure__", closure, HIDDEN_PROP_FLAGS); + module.setProperty("__closure__", closure, READONLY_HIDDEN_PROP_FLAGS); // for consistency with Node.js Module module.setProperty("id", modulePath, READONLY_PROP_FLAGS); @@ -1524,7 +1535,7 @@ QScriptValue ScriptEngine::fetchModuleSource(const QString& modulePath, const bo req.setProperty("url", url); req.setProperty("status", status); req.setProperty("success", ScriptCache::isSuccessStatus(status)); - req.setProperty("contents", contents, HIDDEN_PROP_FLAGS); + req.setProperty("contents", contents, READONLY_HIDDEN_PROP_FLAGS); } }; @@ -1568,73 +1579,54 @@ QScriptValue ScriptEngine::instantiateModule(const QScriptValue& module, const Q auto modulePath = module.property("filename").toString(); auto closure = module.property("__closure__"); - qCDebug(scriptengine_module) << QString("require.instantiateModule: %1 / %2 bytes").arg(QUrl(modulePath).fileName()).arg(sourceCode.length()); + qCDebug(scriptengine_module) << QString("require.instantiateModule: %1 / %2 bytes") + .arg(QUrl(modulePath).fileName()).arg(sourceCode.length()); - { - ExceptionEmitter tryCatch(this, __FUNCTION__); - - if (module.property("content-type").toString() == "application/json") { - qCDebug(scriptengine_module) << "... parsing as JSON"; -#ifdef DEV_OTHER_MODULE_JSON_PARSE - auto JSON = globalObject().property("JSON"); - auto parse = JSON.property("parse"); - if (!parse.isFunction()) { - currentContext()->throwValue(makeError("global JSON.parse is not a function", "EvalError")); - return nullValue(); - } - result = parse.call(JSON, QScriptValueList({ sourceCode })); - closure.property("module").setProperty("exports", result); - return result; -#endif - closure.setProperty("__json", sourceCode); - result = evaluateInClosure(closure, { "module.exports = JSON.parse(__json)", modulePath }); - } else { - // scoped vars for consistency with Node.js - closure.setProperty("require", module.property("require")); - closure.setProperty("__filename", modulePath, HIDDEN_PROP_FLAGS); - closure.setProperty("__dirname", QString(modulePath).replace(QRegExp("/[^/]*$"), ""), HIDDEN_PROP_FLAGS); - result = evaluateInClosure(closure, { sourceCode, modulePath }); - } + if (module.property("content-type").toString() == "application/json") { + qCDebug(scriptengine_module) << "... parsing as JSON"; + closure.setProperty("__json", sourceCode); + result = evaluateInClosure(closure, { "module.exports = JSON.parse(__json)", modulePath }); + } else { + // scoped vars for consistency with Node.js + closure.setProperty("require", module.property("require")); + closure.setProperty("__filename", modulePath, READONLY_HIDDEN_PROP_FLAGS); + closure.setProperty("__dirname", QString(modulePath).replace(QRegExp("/[^/]*$"), ""), READONLY_HIDDEN_PROP_FLAGS); + result = evaluateInClosure(closure, { sourceCode, modulePath }); } + maybeEmitUncaughtException(__FUNCTION__); return result; } // CommonJS/Node.js like require/module support QScriptValue ScriptEngine::require(const QString& moduleId) { qCDebug(scriptengine_module) << "ScriptEngine::require(" << moduleId.left(MAX_DEBUG_VALUE_LENGTH) << ")"; - if (QThread::currentThread() != thread()) { - qCDebug(scriptengine_module) << moduleId << " threads mismatch"; - return nullValue(); + if (!IS_THREADSAFE_INVOCATION(thread(), __FUNCTION__)) { + return unboundNullValue(); } - // serialize require calls so the ordering/caching works correctly across multiple Entities - // note: this is a Recursive mutex so nested require's should not affected - QMutexLocker locker(&_requireLock); - ExceptionEmitter tryCatcher(this, __FUNCTION__); - // _requireDepth++; - // QObject autoDecrement;connect(&autoDecrement, &QObject::destroyed, this, [this](){ _requireDepth--; }); - auto jsRequire = globalObject().property("Script").property("require"); auto cacheMeta = jsRequire.data(); auto cache = jsRequire.property("cache"); auto parent = currentModule(); - auto throwModuleError = [this, &cache](const QString& modulePath, const QScriptValue& error) { + auto throwModuleError = [&](const QString& modulePath, const QScriptValue& error) { cache.setProperty(modulePath, nullValue()); if (!error.isNull()) { #ifdef DEBUG_JS_MODULES qCWarning(scriptengine_module) << "throwing module error:" << error.toString() << modulePath << error.property("stack").toString(); #endif - currentContext()->throwValue(makeError(error)); + raiseException(error); } - return nullValue(); + maybeEmitUncaughtException("module"); + return unboundNullValue(); }; // start by resolving the moduleId into a fully-qualified path/URL QString modulePath = _requireResolve(moduleId); - if (modulePath.isNull() || tryCatcher.hasPending()) { + if (modulePath.isNull() || hasUncaughtException()) { // the resolver already threw an exception -- bail early - return nullValue(); + maybeEmitUncaughtException(__FUNCTION__); + return unboundNullValue(); } // check the resolved path against the cache @@ -1656,6 +1648,7 @@ QScriptValue ScriptEngine::require(const QString& moduleId) { qCDebug(scriptengine_module) << QString("require - using cached module '%1' for '%2' (loaded: %3)") .arg(modulePath).arg(moduleId).arg(module.property("loaded").toString()); registerModuleWithParent(module, parent); + maybeEmitUncaughtException(__FUNCTION__); return exports; } @@ -1705,7 +1698,7 @@ QScriptValue ScriptEngine::require(const QString& moduleId) { #ifdef DEBUG_JS _debugDump("this", thisObject); #endif - _applyUserOptions(module, thisObject); + applyUserOptions(module, thisObject); } } @@ -1725,11 +1718,15 @@ QScriptValue ScriptEngine::require(const QString& moduleId) { qCDebug(scriptengine_module) << "//ScriptEngine::require(" << moduleId << ")"; + maybeEmitUncaughtException(__FUNCTION__); return module.property("exports"); } // User-configurable override options -void ScriptEngine::_applyUserOptions(QScriptValue& module, QScriptValue& options) { +void ScriptEngine::applyUserOptions(QScriptValue& module, QScriptValue& options) { + if (!IS_THREADSAFE_INVOCATION(thread(), __FUNCTION__)) { + return; + } if (!options.isValid()) { return; } @@ -1770,6 +1767,7 @@ void ScriptEngine::_applyUserOptions(QScriptValue& module, QScriptValue& options qCDebug(scriptengine_module) << "module.closure['global'] = options.global"; } } + maybeEmitUncaughtException(__FUNCTION__); } // If a callback is specified, the included files will be loaded asynchronously and the callback will be called @@ -1777,6 +1775,9 @@ void ScriptEngine::_applyUserOptions(QScriptValue& module, QScriptValue& options // If no callback is specified, the included files will be loaded synchronously and will block execution until // all of the files have finished loading. void ScriptEngine::include(const QStringList& includeFiles, QScriptValue callback) { + if (!IS_THREADSAFE_INVOCATION(thread(), __FUNCTION__)) { + return; + } if (DependencyManager::get()->isStopped()) { scriptWarningMessage("Script.include() while shutting down is ignored... includeFiles:" + includeFiles.join(",") + "parent script:" + getFilename()); @@ -1886,6 +1887,9 @@ void ScriptEngine::include(const QString& includeFile, QScriptValue callback) { // as a stand-alone script. To accomplish this, the ScriptEngine class just emits a signal which // the Application or other context will connect to in order to know to actually load the script void ScriptEngine::load(const QString& loadFile) { + if (!IS_THREADSAFE_INVOCATION(thread(), __FUNCTION__)) { + return; + } if (DependencyManager::get()->isStopped()) { scriptWarningMessage("Script.load() while shutting down is ignored... loadFile:" + loadFile + "parent script:" + getFilename()); @@ -1955,6 +1959,52 @@ void ScriptEngine::updateEntityScriptStatus(const EntityItemID& entityID, const emit entityScriptDetailsUpdated(); } +QVariant ScriptEngine::cloneEntityScriptDetails(const EntityItemID& entityID) { + static const QVariant NULL_VARIANT { qVariantFromValue((QObject*)nullptr) }; + QVariantMap map; + if (entityID.isNull()) { + // TODO: find better way to report JS Error across thread/process boundaries + map["isError"] = true; + map["errorInfo"] = "Error: getEntityScriptDetails -- invalid entityID"; + } else { +#ifdef DEBUG_ENTITY_STATES + qDebug() << "cloneEntityScriptDetails" << entityID << QThread::currentThread(); +#endif + EntityScriptDetails scriptDetails; + if (getEntityScriptDetails(entityID, scriptDetails)) { +#ifdef DEBUG_ENTITY_STATES + qDebug() << "gotEntityScriptDetails" << scriptDetails.status << QThread::currentThread(); +#endif + map["isRunning"] = isEntityScriptRunning(entityID); + map["status"] = EntityScriptStatus_::valueToKey(scriptDetails.status).toLower(); + map["errorInfo"] = scriptDetails.errorInfo; + map["entityID"] = entityID.toString(); +#ifdef DEBUG_ENTITY_STATES + { + auto debug = QVariantMap(); + debug["script"] = scriptDetails.scriptText; + debug["scriptObject"] = scriptDetails.scriptObject.toVariant(); + debug["lastModified"] = (qlonglong)scriptDetails.lastModified; + debug["sandboxURL"] = scriptDetails.definingSandboxURL; + map["debug"] = debug; + } +#endif + } else { +#ifdef DEBUG_ENTITY_STATES + qDebug() << "!gotEntityScriptDetails" << QThread::currentThread(); +#endif + map["isError"] = true; + map["errorInfo"] = "Entity script details unavailable"; + map["entityID"] = entityID.toString(); + } + } + return map; +} + +QFuture ScriptEngine::getLocalEntityScriptDetails(const EntityItemID& entityID) { + return QtConcurrent::run(this, &ScriptEngine::cloneEntityScriptDetails, entityID); +} + bool ScriptEngine::getEntityScriptDetails(const EntityItemID& entityID, EntityScriptDetails &details) const { auto it = _entityScripts.constFind(entityID); if (it == _entityScripts.constEnd()) { @@ -2093,10 +2143,10 @@ void ScriptEngine::loadEntityScript(const EntityItemID& entityID, const QString& auto scriptCache = DependencyManager::get(); // note: see EntityTreeRenderer.cpp for shared pointer lifecycle management - QWeakPointer weakRef(sharedFromThis()); + QWeakPointer weakRef(sharedFromThis()); scriptCache->getScriptContents(entityScript, [this, weakRef, entityScript, entityID](const QString& url, const QString& contents, bool isURL, bool success, const QString& status) { - QSharedPointer strongRef(weakRef); + QSharedPointer strongRef(weakRef); if (!strongRef) { qCWarning(scriptengine) << "loadEntityScript.contentAvailable -- ScriptEngine was deleted during getScriptContents!!"; return; @@ -2215,13 +2265,12 @@ void ScriptEngine::entityScriptContentAvailable(const EntityItemID& entityID, co timeout.setSingleShot(true); timeout.start(SANDBOX_TIMEOUT); connect(&timeout, &QTimer::timeout, [&sandbox, SANDBOX_TIMEOUT, scriptOrURL]{ - auto context = sandbox.currentContext(); - if (context) { qCDebug(scriptengine) << "ScriptEngine::entityScriptContentAvailable timeout(" << scriptOrURL << ")"; // Guard against infinite loops and non-performant code - context->throwError(QString("Timed out (entity constructors are limited to %1ms)").arg(SANDBOX_TIMEOUT)); - } + sandbox.raiseException( + sandbox.makeError(QString("Timed out (entity constructors are limited to %1ms)").arg(SANDBOX_TIMEOUT)) + ); }); testConstructor = sandbox.evaluate(program); @@ -2237,7 +2286,7 @@ void ScriptEngine::entityScriptContentAvailable(const EntityItemID& entityID, co if (exception.isError()) { // create a local copy using makeError to decouple from the sandbox engine exception = makeError(exception); - setError(formatException(exception), EntityScriptStatus::ERROR_RUNNING_SCRIPT); + setError(formatException(exception, _enableExtendedJSExceptions.get()), EntityScriptStatus::ERROR_RUNNING_SCRIPT); emit unhandledException(exception); return; } @@ -2288,7 +2337,7 @@ void ScriptEngine::entityScriptContentAvailable(const EntityItemID& entityID, co if (entityScriptObject.isError()) { auto exception = entityScriptObject; - setError(formatException(exception), EntityScriptStatus::ERROR_RUNNING_SCRIPT); + setError(formatException(exception, _enableExtendedJSExceptions.get()), EntityScriptStatus::ERROR_RUNNING_SCRIPT); emit unhandledException(exception); return; } @@ -2420,10 +2469,7 @@ void ScriptEngine::doWithEnvironment(const EntityItemID& entityID, const QUrl& s #else operation(); #endif - if (!isEvaluating() && hasUncaughtException()) { - emit unhandledException(cloneUncaughtException(!entityID.isNull() ? entityID.toString() : __FUNCTION__)); - clearExceptions(); - } + maybeEmitUncaughtException(!entityID.isNull() ? entityID.toString() : __FUNCTION__); currentEntityIdentifier = oldIdentifier; currentSandboxURL = oldSandboxURL; } diff --git a/libraries/script-engine/src/ScriptEngine.h b/libraries/script-engine/src/ScriptEngine.h index f64dec44c7..ef6f3b6896 100644 --- a/libraries/script-engine/src/ScriptEngine.h +++ b/libraries/script-engine/src/ScriptEngine.h @@ -79,7 +79,7 @@ public: QUrl definingSandboxURL { QUrl("about:EntityScript") }; }; -class ScriptEngine : public BaseScriptEngine, public EntitiesScriptEngineProvider, public QEnableSharedFromThis { +class ScriptEngine : public BaseScriptEngine, public EntitiesScriptEngineProvider { Q_OBJECT Q_PROPERTY(QString context READ getContext) public: @@ -138,6 +138,8 @@ public: /// evaluate some code in the context of the ScriptEngine and return the result Q_INVOKABLE QScriptValue evaluate(const QString& program, const QString& fileName, int lineNumber = 1); // this is also used by the script tool widget + Q_INVOKABLE QScriptValue evaluateInClosure(const QScriptValue& locals, const QScriptProgram& program); + /// if the script engine is not already running, this will download the URL and start the process of seting it up /// to run... NOTE - this is used by Application currently to load the url. We don't really want it to be exposed /// to scripts. we may not need this to be invokable @@ -181,6 +183,8 @@ public: Q_INVOKABLE bool isEntityScriptRunning(const EntityItemID& entityID) { return _entityScripts.contains(entityID) && _entityScripts[entityID].status == EntityScriptStatus::RUNNING; } + QVariant cloneEntityScriptDetails(const EntityItemID& entityID); + QFuture getLocalEntityScriptDetails(const EntityItemID& entityID) override; Q_INVOKABLE void loadEntityScript(const EntityItemID& entityID, const QString& entityScript, bool forceRedownload); Q_INVOKABLE void unloadEntityScript(const EntityItemID& entityID); // will call unload method Q_INVOKABLE void unloadAllEntityScripts(); @@ -286,7 +290,6 @@ protected: QHash _entityScripts; QHash _occupiedScriptURLs; QList _deferredEntityLoads; - QMutex _requireLock { QMutex::Recursive }; bool _isThreaded { false }; QScriptEngineDebugger* _debugger { nullptr }; @@ -312,8 +315,12 @@ protected: std::chrono::microseconds _totalTimerExecution { 0 }; static const QString _SETTINGS_ENABLE_EXTENDED_MODULE_COMPAT; + static const QString _SETTINGS_ENABLE_EXTENDED_EXCEPTIONS; + Setting::Handle _enableExtendedModuleCompatbility { _SETTINGS_ENABLE_EXTENDED_MODULE_COMPAT, false }; - void _applyUserOptions(QScriptValue& module, QScriptValue& options); + Setting::Handle _enableExtendedJSExceptions { _SETTINGS_ENABLE_EXTENDED_EXCEPTIONS, true }; + + void applyUserOptions(QScriptValue& module, QScriptValue& options); }; #endif // hifi_ScriptEngine_h diff --git a/libraries/shared/src/BaseScriptEngine.cpp b/libraries/shared/src/BaseScriptEngine.cpp index 16308c0650..d803e85ed6 100644 --- a/libraries/shared/src/BaseScriptEngine.cpp +++ b/libraries/shared/src/BaseScriptEngine.cpp @@ -10,6 +10,7 @@ // #include "BaseScriptEngine.h" +#include "SharedLogging.h" #include #include @@ -18,18 +19,26 @@ #include #include -#include "ScriptEngineLogging.h" #include "Profile.h" -const QString BaseScriptEngine::_SETTINGS_ENABLE_EXTENDED_EXCEPTIONS { - "com.highfidelity.experimental.enableExtendedJSExceptions" -}; - const QString BaseScriptEngine::SCRIPT_EXCEPTION_FORMAT { "[%0] %1 in %2:%3" }; const QString BaseScriptEngine::SCRIPT_BACKTRACE_SEP { "\n " }; +bool BaseScriptEngine::IS_THREADSAFE_INVOCATION(const QThread *thread, const QString& method) { + if (QThread::currentThread() == thread) { + return true; + } + qCCritical(shared) << QString("Scripting::%1 @ %2 -- ignoring thread-unsafe call from %3") + .arg(method).arg(thread ? thread->objectName() : "(!thread)").arg(QThread::currentThread()->objectName()); + qCDebug(shared) << "(please resolve on the calling side by using invokeMethod, executeOnScriptThread, etc.)"; + return false; +} + // engine-aware JS Error copier and factory QScriptValue BaseScriptEngine::makeError(const QScriptValue& _other, const QString& type) { + if (!IS_THREADSAFE_INVOCATION(thread(), __FUNCTION__)) { + return unboundNullValue(); + } auto other = _other; if (other.isString()) { other = newObject(); @@ -41,7 +50,7 @@ QScriptValue BaseScriptEngine::makeError(const QScriptValue& _other, const QStri } if (!proto.isFunction()) { #ifdef DEBUG_JS_EXCEPTIONS - qCDebug(scriptengine) << "BaseScriptEngine::makeError -- couldn't find constructor for" << type << " -- using Error instead"; + qCDebug(shared) << "BaseScriptEngine::makeError -- couldn't find constructor for" << type << " -- using Error instead"; #endif proto = globalObject().property("Error"); } @@ -64,6 +73,9 @@ QScriptValue BaseScriptEngine::makeError(const QScriptValue& _other, const QStri // check syntax and when there are issues returns an actual "SyntaxError" with the details QScriptValue BaseScriptEngine::lintScript(const QString& sourceCode, const QString& fileName, const int lineNumber) { + if (!IS_THREADSAFE_INVOCATION(thread(), __FUNCTION__)) { + return unboundNullValue(); + } const auto syntaxCheck = checkSyntax(sourceCode); if (syntaxCheck.state() != QScriptSyntaxCheckResult::Valid) { auto err = globalObject().property("SyntaxError") @@ -82,13 +94,16 @@ QScriptValue BaseScriptEngine::lintScript(const QString& sourceCode, const QStri } return err; } - return undefinedValue(); + return QScriptValue(); } // this pulls from the best available information to create a detailed snapshot of the current exception QScriptValue BaseScriptEngine::cloneUncaughtException(const QString& extraDetail) { + if (!IS_THREADSAFE_INVOCATION(thread(), __FUNCTION__)) { + return unboundNullValue(); + } if (!hasUncaughtException()) { - return QScriptValue(); + return unboundNullValue(); } auto exception = uncaughtException(); // ensure the error object is engine-local @@ -144,7 +159,10 @@ QScriptValue BaseScriptEngine::cloneUncaughtException(const QString& extraDetail return err; } -QString BaseScriptEngine::formatException(const QScriptValue& exception) { +QString BaseScriptEngine::formatException(const QScriptValue& exception, bool includeExtendedDetails) { + if (!IS_THREADSAFE_INVOCATION(thread(), __FUNCTION__)) { + return QString(); + } QString note { "UncaughtException" }; QString result; @@ -156,8 +174,8 @@ QString BaseScriptEngine::formatException(const QScriptValue& exception) { const auto lineNumber = exception.property("lineNumber").toString(); const auto stacktrace = exception.property("stack").toString(); - if (_enableExtendedJSExceptions.get()) { - // This setting toggles display of the hints now being added during the loading process. + if (includeExtendedDetails) { + // Display additional exception / troubleshooting hints that can be added via the custom Error .detail property // Example difference: // [UncaughtExceptions] Error: Can't find variable: foobar in atp:/myentity.js\n... // [UncaughtException (construct {1eb5d3fa-23b1-411c-af83-163af7220e3f})] Error: Can't find variable: foobar in atp:/myentity.js\n... @@ -173,14 +191,39 @@ QString BaseScriptEngine::formatException(const QScriptValue& exception) { return result; } +bool BaseScriptEngine::raiseException(const QScriptValue& exception) { + if (!IS_THREADSAFE_INVOCATION(thread(), __FUNCTION__)) { + return false; + } + if (currentContext()) { + // we have an active context / JS stack frame so throw the exception per usual + currentContext()->throwValue(makeError(exception)); + return true; + } else { + // we are within a pure C++ stack frame (ie: being called directly by other C++ code) + // in this case no context information is available so just emit the exception for reporting + emit unhandledException(makeError(exception)); + } + return false; +} + +bool BaseScriptEngine::maybeEmitUncaughtException(const QString& debugHint) { + if (!IS_THREADSAFE_INVOCATION(thread(), __FUNCTION__)) { + return false; + } + if (!isEvaluating() && hasUncaughtException()) { + emit unhandledException(cloneUncaughtException(debugHint)); + clearExceptions(); + return true; + } + return false; +} + QScriptValue BaseScriptEngine::evaluateInClosure(const QScriptValue& closure, const QScriptProgram& program) { PROFILE_RANGE(script, "evaluateInClosure"); - if (QThread::currentThread() != thread()) { - qCCritical(scriptengine) << "*** CRITICAL *** ScriptEngine::evaluateInClosure() is meant to be called from engine thread only."; - // note: a recursive mutex might be needed around below code if this method ever becomes Q_INVOKABLE - return QScriptValue(); + if (!IS_THREADSAFE_INVOCATION(thread(), __FUNCTION__)) { + return unboundNullValue(); } - const auto fileName = program.fileName(); const auto shortName = QUrl(fileName).fileName(); @@ -189,7 +232,7 @@ QScriptValue BaseScriptEngine::evaluateInClosure(const QScriptValue& closure, co auto global = closure.property("global"); if (global.isObject()) { #ifdef DEBUG_JS - qCDebug(scriptengine) << " setting global = closure.global" << shortName; + qCDebug(shared) << " setting global = closure.global" << shortName; #endif oldGlobal = globalObject(); setGlobalObject(global); @@ -200,34 +243,34 @@ QScriptValue BaseScriptEngine::evaluateInClosure(const QScriptValue& closure, co auto thiz = closure.property("this"); if (thiz.isObject()) { #ifdef DEBUG_JS - qCDebug(scriptengine) << " setting this = closure.this" << shortName; + qCDebug(shared) << " setting this = closure.this" << shortName; #endif context->setThisObject(thiz); } context->pushScope(closure); #ifdef DEBUG_JS - qCDebug(scriptengine) << QString("[%1] evaluateInClosure %2").arg(isEvaluating()).arg(shortName); + qCDebug(shared) << QString("[%1] evaluateInClosure %2").arg(isEvaluating()).arg(shortName); #endif { result = BaseScriptEngine::evaluate(program); if (hasUncaughtException()) { auto err = cloneUncaughtException(__FUNCTION__); #ifdef DEBUG_JS_EXCEPTIONS - qCWarning(scriptengine) << __FUNCTION__ << "---------- hasCaught:" << err.toString() << result.toString(); + qCWarning(shared) << __FUNCTION__ << "---------- hasCaught:" << err.toString() << result.toString(); err.setProperty("_result", result); #endif result = err; } } #ifdef DEBUG_JS - qCDebug(scriptengine) << QString("[%1] //evaluateInClosure %2").arg(isEvaluating()).arg(shortName); + qCDebug(shared) << QString("[%1] //evaluateInClosure %2").arg(isEvaluating()).arg(shortName); #endif popContext(); if (oldGlobal.isValid()) { #ifdef DEBUG_JS - qCDebug(scriptengine) << " restoring global" << shortName; + qCDebug(shared) << " restoring global" << shortName; #endif setGlobalObject(oldGlobal); } @@ -236,7 +279,6 @@ QScriptValue BaseScriptEngine::evaluateInClosure(const QScriptValue& closure, co } // Lambda - QScriptValue BaseScriptEngine::newLambdaFunction(std::function operation, const QScriptValue& data, const QScriptEngine::ValueOwnership& ownership) { auto lambda = new Lambda(this, operation, data); auto object = newQObject(lambda, ownership); @@ -262,26 +304,57 @@ Lambda::Lambda(QScriptEngine *engine, std::functionthread(), __FUNCTION__)) { + return BaseScriptEngine::unboundNullValue(); + } return operation(engine->currentContext(), engine); } +QScriptValue makeScopedHandlerObject(QScriptValue scopeOrCallback, QScriptValue methodOrName) { + auto engine = scopeOrCallback.engine(); + if (!engine) { + return scopeOrCallback; + } + auto scope = QScriptValue(); + auto callback = scopeOrCallback; + if (scopeOrCallback.isObject()) { + if (methodOrName.isString()) { + scope = scopeOrCallback; + callback = scope.property(methodOrName.toString()); + } else if (methodOrName.isFunction()) { + scope = scopeOrCallback; + callback = methodOrName; + } + } + auto handler = engine->newObject(); + handler.setProperty("scope", scope); + handler.setProperty("callback", callback); + return handler; +} + +QScriptValue callScopedHandlerObject(QScriptValue handler, QScriptValue err, QScriptValue result) { + return handler.property("callback").call(handler.property("scope"), QScriptValueList({ err, result })); +} + #ifdef DEBUG_JS void BaseScriptEngine::_debugDump(const QString& header, const QScriptValue& object, const QString& footer) { + if (!IS_THREADSAFE_INVOCATION(thread(), __FUNCTION__)) { + return; + } if (!header.isEmpty()) { - qCDebug(scriptengine) << header; + qCDebug(shared) << header; } if (!object.isObject()) { - qCDebug(scriptengine) << "(!isObject)" << object.toVariant().toString() << object.toString(); + qCDebug(shared) << "(!isObject)" << object.toVariant().toString() << object.toString(); return; } QScriptValueIterator it(object); while (it.hasNext()) { it.next(); - qCDebug(scriptengine) << it.name() << ":" << it.value().toString(); + qCDebug(shared) << it.name() << ":" << it.value().toString(); } if (!footer.isEmpty()) { - qCDebug(scriptengine) << footer; + qCDebug(shared) << footer; } } #endif - diff --git a/libraries/shared/src/BaseScriptEngine.h b/libraries/shared/src/BaseScriptEngine.h index 27a6eff33d..138e46fafa 100644 --- a/libraries/shared/src/BaseScriptEngine.h +++ b/libraries/shared/src/BaseScriptEngine.h @@ -16,38 +16,61 @@ #include #include -#include "SettingHandle.h" - // common base class for extending QScriptEngine itself -class BaseScriptEngine : public QScriptEngine { +class BaseScriptEngine : public QScriptEngine, public QEnableSharedFromThis { Q_OBJECT public: static const QString SCRIPT_EXCEPTION_FORMAT; static const QString SCRIPT_BACKTRACE_SEP; - BaseScriptEngine() {} + // threadsafe "unbound" version of QScriptEngine::nullValue() + static const QScriptValue unboundNullValue() { return QScriptValue(0, QScriptValue::NullValue); } - Q_INVOKABLE QScriptValue evaluateInClosure(const QScriptValue& locals, const QScriptProgram& program); + BaseScriptEngine() {} Q_INVOKABLE QScriptValue lintScript(const QString& sourceCode, const QString& fileName, const int lineNumber = 1); Q_INVOKABLE QScriptValue makeError(const QScriptValue& other = QScriptValue(), const QString& type = "Error"); - Q_INVOKABLE QString formatException(const QScriptValue& exception); - QScriptValue cloneUncaughtException(const QString& detail = QString()); + Q_INVOKABLE QString formatException(const QScriptValue& exception, bool includeExtendedDetails); + QScriptValue cloneUncaughtException(const QString& detail = QString()); + QScriptValue evaluateInClosure(const QScriptValue& locals, const QScriptProgram& program); + + // if there is a pending exception and we are at the top level (non-recursive) stack frame, this emits and resets it + bool maybeEmitUncaughtException(const QString& debugHint = QString()); + + // if the currentContext() is valid then throw the passed exception; otherwise, immediately emit it. + // note: this is used in cases where C++ code might call into JS API methods directly + bool raiseException(const QScriptValue& exception); + + // helper to detect and log warnings when other code invokes QScriptEngine/BaseScriptEngine in thread-unsafe ways + static bool IS_THREADSAFE_INVOCATION(const QThread *thread, const QString& method); signals: void unhandledException(const QScriptValue& exception); protected: - void _emitUnhandledException(const QScriptValue& exception); + // like `newFunction`, but allows mapping inline C++ lambdas with captures as callable QScriptValues + // even though the context/engine parameters are redundant in most cases, the function signature matches `newFunction` + // anyway so that newLambdaFunction can be used to rapidly prototype / test utility APIs and then if becoming + // permanent more easily promoted into regular static newFunction scenarios. QScriptValue newLambdaFunction(std::function operation, const QScriptValue& data = QScriptValue(), const QScriptEngine::ValueOwnership& ownership = QScriptEngine::AutoOwnership); - static const QString _SETTINGS_ENABLE_EXTENDED_EXCEPTIONS; - Setting::Handle _enableExtendedJSExceptions { _SETTINGS_ENABLE_EXTENDED_EXCEPTIONS, true }; #ifdef DEBUG_JS static void _debugDump(const QString& header, const QScriptValue& object, const QString& footer = QString()); #endif }; +// Standardized CPS callback helpers (see: http://fredkschott.com/post/2014/03/understanding-error-first-callbacks-in-node-js/) +// These two helpers allow async JS APIs that use a callback parameter to be more friendly to scripters by accepting thisObject +// context and adopting a consistent and intuitable callback signature: +// function callback(err, result) { if (err) { ... } else { /* do stuff with result */ } } +// +// To use, first pass the user-specified callback args in the same order used with optionally-scoped Qt signal connections: +// auto handler = makeScopedHandlerObject(scopeOrCallback, optionalMethodOrName); +// And then invoke the scoped handler later per CPS conventions: +// auto result = callScopedHandlerObject(handler, err, result); +QScriptValue makeScopedHandlerObject(QScriptValue scopeOrCallback, QScriptValue methodOrName); +QScriptValue callScopedHandlerObject(QScriptValue handler, QScriptValue err, QScriptValue result); + // Lambda helps create callable QScriptValues out of std::functions: // (just meant for use from within the script engine itself) class Lambda : public QObject { From 143b67e47dafd7cd1a042ec5ed6b43dd30b665b4 Mon Sep 17 00:00:00 2001 From: humbletim Date: Wed, 1 Mar 2017 09:20:53 -0500 Subject: [PATCH 095/302] Add require/module unit tests --- .../developer/libraries/jasmine/hifi-boot.js | 11 +- .../tests/unit_tests/moduleTests/cycles/a.js | 9 + .../tests/unit_tests/moduleTests/cycles/b.js | 9 + .../unit_tests/moduleTests/cycles/main.js | 13 + .../entity/entityConstructorAPIException.js | 12 + .../entity/entityConstructorModule.js | 21 ++ .../entity/entityConstructorNested.js | 13 + .../entity/entityConstructorNested2.js | 24 ++ .../entityConstructorRequireException.js | 9 + .../entity/entityPreloadAPIError.js | 12 + .../entity/entityPreloadRequire.js | 10 + .../tests/unit_tests/moduleTests/example.json | 9 + .../moduleTests/exceptions/exception.js | 3 + .../exceptions/exceptionInFunction.js | 37 ++ .../tests/unit_tests/moduleUnitTests.js | 317 ++++++++++++++++++ .../developer/tests/unit_tests/package.json | 6 + 16 files changed, 513 insertions(+), 2 deletions(-) create mode 100644 scripts/developer/tests/unit_tests/moduleTests/cycles/a.js create mode 100644 scripts/developer/tests/unit_tests/moduleTests/cycles/b.js create mode 100644 scripts/developer/tests/unit_tests/moduleTests/cycles/main.js create mode 100644 scripts/developer/tests/unit_tests/moduleTests/entity/entityConstructorAPIException.js create mode 100644 scripts/developer/tests/unit_tests/moduleTests/entity/entityConstructorModule.js create mode 100644 scripts/developer/tests/unit_tests/moduleTests/entity/entityConstructorNested.js create mode 100644 scripts/developer/tests/unit_tests/moduleTests/entity/entityConstructorNested2.js create mode 100644 scripts/developer/tests/unit_tests/moduleTests/entity/entityConstructorRequireException.js create mode 100644 scripts/developer/tests/unit_tests/moduleTests/entity/entityPreloadAPIError.js create mode 100644 scripts/developer/tests/unit_tests/moduleTests/entity/entityPreloadRequire.js create mode 100644 scripts/developer/tests/unit_tests/moduleTests/example.json create mode 100644 scripts/developer/tests/unit_tests/moduleTests/exceptions/exception.js create mode 100644 scripts/developer/tests/unit_tests/moduleTests/exceptions/exceptionInFunction.js create mode 100644 scripts/developer/tests/unit_tests/moduleUnitTests.js create mode 100644 scripts/developer/tests/unit_tests/package.json diff --git a/scripts/developer/libraries/jasmine/hifi-boot.js b/scripts/developer/libraries/jasmine/hifi-boot.js index f490a3618f..8757550ae8 100644 --- a/scripts/developer/libraries/jasmine/hifi-boot.js +++ b/scripts/developer/libraries/jasmine/hifi-boot.js @@ -6,7 +6,7 @@ var lastSpecStartTime; function ConsoleReporter(options) { var startTime = new Date().getTime(); - var errorCount = 0; + var errorCount = 0, pending = []; this.jasmineStarted = function (obj) { print('Jasmine started with ' + obj.totalSpecsDefined + ' tests.'); }; @@ -15,11 +15,14 @@ var endTime = new Date().getTime(); print('
'); if (errorCount === 0) { - print ('All tests passed!'); + print ('All enabled tests passed!'); } else { print('Tests completed with ' + errorCount + ' ' + ERROR + '.'); } + if (pending.length) + print ('disabled:
   '+ + pending.join('
   ')+'
'); print('Tests completed in ' + (endTime - startTime) + 'ms.'); }; this.suiteStarted = function(obj) { @@ -32,6 +35,10 @@ lastSpecStartTime = new Date().getTime(); }; this.specDone = function(obj) { + if (obj.status === 'pending') { + pending.push(obj.fullName); + return print('...(pending ' + obj.fullName +')'); + } var specEndTime = new Date().getTime(); var symbol = obj.status === PASSED ? '' + CHECKMARK + '' : diff --git a/scripts/developer/tests/unit_tests/moduleTests/cycles/a.js b/scripts/developer/tests/unit_tests/moduleTests/cycles/a.js new file mode 100644 index 0000000000..7934180da7 --- /dev/null +++ b/scripts/developer/tests/unit_tests/moduleTests/cycles/a.js @@ -0,0 +1,9 @@ +var a = exports; +a.done = false; +var b = require('./b.js'); +a.done = true; +a.name = 'a'; +a['a.done?'] = a.done; +a['b.done?'] = b.done; + +print('from a.js a.done =', a.done, '/ b.done =', b.done); diff --git a/scripts/developer/tests/unit_tests/moduleTests/cycles/b.js b/scripts/developer/tests/unit_tests/moduleTests/cycles/b.js new file mode 100644 index 0000000000..285f176597 --- /dev/null +++ b/scripts/developer/tests/unit_tests/moduleTests/cycles/b.js @@ -0,0 +1,9 @@ +var b = exports; +b.done = false; +var a = require('./a.js'); +b.done = true; +b.name = 'b'; +b['a.done?'] = a.done; +b['b.done?'] = b.done; + +print('from b.js a.done =', a.done, '/ b.done =', b.done); diff --git a/scripts/developer/tests/unit_tests/moduleTests/cycles/main.js b/scripts/developer/tests/unit_tests/moduleTests/cycles/main.js new file mode 100644 index 0000000000..2e9a878c82 --- /dev/null +++ b/scripts/developer/tests/unit_tests/moduleTests/cycles/main.js @@ -0,0 +1,13 @@ +print('main.js'); +var a = require('./a.js'), + b = require('./b.js'); + +print('from main.js a.done =', a.done, 'and b.done =', b.done); + +module.exports = { + name: 'main', + a: a, + b: b, + 'a.done?': a.done, + 'b.done?': b.done, +}; diff --git a/scripts/developer/tests/unit_tests/moduleTests/entity/entityConstructorAPIException.js b/scripts/developer/tests/unit_tests/moduleTests/entity/entityConstructorAPIException.js new file mode 100644 index 0000000000..b1bc0e33e4 --- /dev/null +++ b/scripts/developer/tests/unit_tests/moduleTests/entity/entityConstructorAPIException.js @@ -0,0 +1,12 @@ +// test module method exception being thrown within main constructor +(function() { + var apiMethod = Script.require('../exceptions/exceptionInFunction.js'); + print(Script.resolvePath(''), "apiMethod", apiMethod); + // this next line throws from within apiMethod + print(apiMethod()); + return { + preload: function(uuid) { + print("entityConstructorAPIException::preload -- never seen --", uuid, Script.resolvePath('')); + }, + }; +}) diff --git a/scripts/developer/tests/unit_tests/moduleTests/entity/entityConstructorModule.js b/scripts/developer/tests/unit_tests/moduleTests/entity/entityConstructorModule.js new file mode 100644 index 0000000000..5f0e8a5938 --- /dev/null +++ b/scripts/developer/tests/unit_tests/moduleTests/entity/entityConstructorModule.js @@ -0,0 +1,21 @@ +// test dual-purpose module and standalone Entity script +function MyEntity(filename) { + return { + preload: function(uuid) { + print("entityConstructorModule.js::preload"); + if (typeof module === 'object') { + print("module.filename", module.filename); + print("module.parent.filename", module.parent && module.parent.filename); + } + }, + clickDownOnEntity: function(uuid, evt) { + print("entityConstructorModule.js::clickDownOnEntity"); + }, + }; +} + +try { + module.exports = MyEntity; +} catch(e) {} +print('entityConstructorModule::MyEntity', typeof MyEntity); +(MyEntity) diff --git a/scripts/developer/tests/unit_tests/moduleTests/entity/entityConstructorNested.js b/scripts/developer/tests/unit_tests/moduleTests/entity/entityConstructorNested.js new file mode 100644 index 0000000000..5a2b8d5974 --- /dev/null +++ b/scripts/developer/tests/unit_tests/moduleTests/entity/entityConstructorNested.js @@ -0,0 +1,13 @@ +// test Entity constructor based on inherited constructor from a module +function constructor() { + print("entityConstructorNested::constructor"); + var MyEntity = Script.require('./entityConstructorModule.js'); + return new MyEntity("-- created from entityConstructorNested --"); +} + +try { + module.exports = constructor; +} catch(e) { + constructor; +} + diff --git a/scripts/developer/tests/unit_tests/moduleTests/entity/entityConstructorNested2.js b/scripts/developer/tests/unit_tests/moduleTests/entity/entityConstructorNested2.js new file mode 100644 index 0000000000..85a6b977b0 --- /dev/null +++ b/scripts/developer/tests/unit_tests/moduleTests/entity/entityConstructorNested2.js @@ -0,0 +1,24 @@ +// test Entity constructor based on nested, inherited module constructors +function constructor() { + print("entityConstructorNested2::constructor"); + + // inherit from entityConstructorNested + var Entity = Script.require('./entityConstructorNested.js'); + function SubEntity() {} + SubEntity.prototype = new MyEntity('-- created from entityConstructorNested2 --'); + + // create new instance + var entity = new SubEntity(); + // "override" clickDownOnEntity for just this new instance + entity.clickDownOnEntity = function(uuid, evt) { + print("entityConstructorNested2::clickDownOnEntity"); + SubEntity.prototype.clickDownOnEntity.apply(this, arguments); + }; + return entity; +} + +try { + module.exports = constructor; +} catch(e) { + constructor; +} diff --git a/scripts/developer/tests/unit_tests/moduleTests/entity/entityConstructorRequireException.js b/scripts/developer/tests/unit_tests/moduleTests/entity/entityConstructorRequireException.js new file mode 100644 index 0000000000..269ca8e7f0 --- /dev/null +++ b/scripts/developer/tests/unit_tests/moduleTests/entity/entityConstructorRequireException.js @@ -0,0 +1,9 @@ +// test module-related exception from within "require" evaluation itself +(function() { + var mod = Script.require('../exceptions/exception.js'); + return { + preload: function(uuid) { + print("entityConstructorRequireException::preload (never happens)", uuid, Script.resolvePath('')); + }, + }; +}) diff --git a/scripts/developer/tests/unit_tests/moduleTests/entity/entityPreloadAPIError.js b/scripts/developer/tests/unit_tests/moduleTests/entity/entityPreloadAPIError.js new file mode 100644 index 0000000000..3be0b50d43 --- /dev/null +++ b/scripts/developer/tests/unit_tests/moduleTests/entity/entityPreloadAPIError.js @@ -0,0 +1,12 @@ +// test module method exception being thrown within preload +(function() { + var apiMethod = Script.require('../exceptions/exceptionInFunction.js'); + print(Script.resolvePath(''), "apiMethod", apiMethod); + return { + preload: function(uuid) { + // this next line throws from within apiMethod + print(apiMethod()); + print("entityPreloadAPIException::preload -- never seen --", uuid, Script.resolvePath('')); + }, + }; +}) diff --git a/scripts/developer/tests/unit_tests/moduleTests/entity/entityPreloadRequire.js b/scripts/developer/tests/unit_tests/moduleTests/entity/entityPreloadRequire.js new file mode 100644 index 0000000000..fc70838c80 --- /dev/null +++ b/scripts/developer/tests/unit_tests/moduleTests/entity/entityPreloadRequire.js @@ -0,0 +1,10 @@ +// test requiring a module from within preload +(function constructor() { + return { + preload: function(uuid) { + print("entityPreloadRequire::preload"); + var example = Script.require('../example.json'); + print("entityPreloadRequire::example::name", example.name); + }, + }; +}) diff --git a/scripts/developer/tests/unit_tests/moduleTests/example.json b/scripts/developer/tests/unit_tests/moduleTests/example.json new file mode 100644 index 0000000000..42d7fe07da --- /dev/null +++ b/scripts/developer/tests/unit_tests/moduleTests/example.json @@ -0,0 +1,9 @@ +{ + "name": "Example JSON Module", + "last-modified": 1485789862, + "config": { + "title": "My Title", + "width": 800, + "height": 600 + } +} diff --git a/scripts/developer/tests/unit_tests/moduleTests/exceptions/exception.js b/scripts/developer/tests/unit_tests/moduleTests/exceptions/exception.js new file mode 100644 index 0000000000..636ee82f79 --- /dev/null +++ b/scripts/developer/tests/unit_tests/moduleTests/exceptions/exception.js @@ -0,0 +1,3 @@ +module.exports = "n/a"; +throw new Error('exception on line 2'); + diff --git a/scripts/developer/tests/unit_tests/moduleTests/exceptions/exceptionInFunction.js b/scripts/developer/tests/unit_tests/moduleTests/exceptions/exceptionInFunction.js new file mode 100644 index 0000000000..dc2ce3c438 --- /dev/null +++ b/scripts/developer/tests/unit_tests/moduleTests/exceptions/exceptionInFunction.js @@ -0,0 +1,37 @@ +// dummy lines to make sure exception line number is well below parent test script +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// + + +function myfunc() { + throw new Error('exception on line 32 in myfunc'); + return "myfunc"; +} +module.exports = myfunc; +if (Script[module.filename] === 'throw') + myfunc(); diff --git a/scripts/developer/tests/unit_tests/moduleUnitTests.js b/scripts/developer/tests/unit_tests/moduleUnitTests.js new file mode 100644 index 0000000000..c1c20d6980 --- /dev/null +++ b/scripts/developer/tests/unit_tests/moduleUnitTests.js @@ -0,0 +1,317 @@ +/* eslint-env jasmine */ + +var isNode = instrument_testrunner(); + +var NETWORK_describe = xdescribe, + INTERFACE_describe = !isNode ? describe : xdescribe, + NODE_describe = isNode ? describe : xdescribe; + +print("DESCRIBING"); +describe('require', function() { + describe('resolve', function() { + it('should resolve relative filenames', function() { + var expected = Script.resolvePath('./moduleTests/example.json'); + expect(require.resolve('./moduleTests/example.json')).toEqual(expected); + }); + }); + + describe('JSON', function() { + it('should import .json modules', function() { + var example = require('./moduleTests/example.json'); + expect(example.name).toEqual('Example JSON Module'); + }); + INTERFACE_describe('inteface', function() { + NETWORK_describe('network', function() { + //xit('should import #content-type=application/json modules', function() { + // var results = require('https://jsonip.com#content-type=application/json'); + // expect(results.ip).toMatch(/^[.0-9]+$/); + //}); + it('should import content-type: application/json modules', function() { + var scope = { 'content-type': 'application/json' }; + var results = require.call(scope, 'https://jsonip.com'); + expect(results.ip).toMatch(/^[.0-9]+$/); + }); + }); + }); + + }); + + INTERFACE_describe('system', function() { + it('require(id)', function() { + expect(require('vec3')).toEqual(jasmine.any(Function)); + }); + it('require(id).function', function() { + expect(require('vec3')().isValid).toEqual(jasmine.any(Function)); + }); + }); + + describe('exceptions', function() { + it('should reject blank "" module identifiers', function() { + expect(function() { + require.resolve(''); + }).toThrowError(/Cannot find/); + }); + it('should reject excessive identifier sizes', function() { + expect(function() { + require.resolve(new Array(8193).toString()); + }).toThrowError(/Cannot find/); + }); + it('should reject implicitly-relative filenames', function() { + expect(function() { + var mod = require.resolve('example.js'); + }).toThrowError(/Cannot find/); + }); + it('should reject non-existent filenames', function() { + expect(function() { + var mod = require.resolve('./404error.js'); + }).toThrowError(/Cannot find/); + }); + it('should reject identifiers resolving to a directory', function() { + expect(function() { + var mod = require.resolve('.'); + //console.warn('resolved(.)', mod); + }).toThrowError(/Cannot find/); + expect(function() { + var mod = require.resolve('..'); + //console.warn('resolved(..)', mod); + }).toThrowError(/Cannot find/); + expect(function() { + var mod = require.resolve('../'); + //console.warn('resolved(../)', mod); + }).toThrowError(/Cannot find/); + }); + if (typeof MODE !== 'undefined' && MODE !== 'node') { + it('should reject non-system, extensionless identifiers', function() { + expect(function() { + require.resolve('./example'); + }).toThrowError(/Cannot find/); + }); + } + }); + + describe('cache', function() { + it('should cache modules by resolved module id', function() { + var value = new Date; + var example = require('./moduleTests/example.json'); + example['.test'] = value; + var example2 = require('../../tests/unit_tests/moduleTests/example.json'); + expect(example2).toBe(example); + expect(example2['.test']).toBe(example['.test']); + }); + it('should reload cached modules set to null', function() { + var value = new Date; + var example = require('./moduleTests/example.json'); + example['.test'] = value; + require.cache[require.resolve('../../tests/unit_tests/moduleTests/example.json')] = null; + var example2 = require('../../tests/unit_tests/moduleTests/example.json'); + expect(example2).not.toBe(example); + expect(example2['.test']).not.toBe(example['.test']); + }); + it('should reload when module property is deleted', function() { + var value = new Date; + var example = require('./moduleTests/example.json'); + example['.test'] = value; + delete require.cache[require.resolve('../../tests/unit_tests/moduleTests/example.json')]; + var example2 = require('../../tests/unit_tests/moduleTests/example.json'); + expect(example2).not.toBe(example); + expect(example2['.test']).not.toBe(example['.test']); + }); + }); + + describe('cyclic dependencies', function() { + describe('should allow lazy-ref cyclic module resolution', function() { + const MODULE_PATH = './moduleTests/cycles/main.js'; + var main; + beforeEach(function() { + try { this._print = print; } catch(e) {} + // for this test print is no-op'd so it doesn't disrupt the reporter output + //console = typeof console === 'object' ? console : { log: function() {} }; + print = function() {}; + Script.resetModuleCache(); + }); + afterEach(function() { + print = this._print; + }); + it('main requirable', function() { + main = require(MODULE_PATH); + expect(main).toEqual(jasmine.any(Object)); + }); + it('main with both a and b', function() { + expect(main.a['b.done?']).toBe(true); + expect(main.b['a.done?']).toBe(false); + }); + it('a.done?', function() { + expect(main['a.done?']).toBe(true); + }); + it('b.done?', function() { + expect(main['b.done?']).toBe(true); + }); + }); + }); + + describe('JS', function() { + it('should throw catchable local file errors', function() { + expect(function() { + require('file:///dev/null/non-existent-file.js'); + }).toThrowError(/path not found|Cannot find.*non-existent-file/); + }); + it('should throw catchable invalid id errors', function() { + expect(function() { + require(new Array(4096 * 2).toString()); + }).toThrowError(/invalid.*size|Cannot find.*,{30}/); + }); + it('should throw catchable unresolved id errors', function() { + expect(function() { + require('foobar:/baz.js'); + }).toThrowError(/could not resolve|Cannot find.*foobar:/); + }); + + NETWORK_describe('network', function() { + // note: with retries these tests can take up to 60 seconds each to timeout + var timeout = 75 * 1000; + it('should throw catchable host errors', function() { + expect(function() { + var mod = require('http://non.existent.highfidelity.io/moduleUnitTest.js'); + print("mod", Object.keys(mod)); + }).toThrowError(/error retrieving script .ServerUnavailable.|Cannot find.*non.existent/); + }, timeout); + it('should throw catchable network timeouts', function() { + expect(function() { + require('http://ping.highfidelity.io:1024'); + }).toThrowError(/error retrieving script .Timeout.|Cannot find.*ping.highfidelity/); + }, timeout); + }); + }); + + INTERFACE_describe('entity', function() { + var sampleScripts = [ + 'entityConstructorAPIException.js', + 'entityConstructorModule.js', + 'entityConstructorNested2.js', + 'entityConstructorNested.js', + 'entityConstructorRequireException.js', + 'entityPreloadAPIError.js', + 'entityPreloadRequire.js', + ].filter(Boolean).map(function(id) { return Script.require.resolve('./moduleTests/entity/'+id); }); + + var uuids = []; + + for(var i=0; i < sampleScripts.length; i++) { + (function(i) { + var script = sampleScripts[ i % sampleScripts.length ]; + var shortname = '['+i+'] ' + script.split('/').pop(); + var position = MyAvatar.position; + position.y -= i/2; + it(shortname, function(done) { + var uuid = Entities.addEntity({ + text: shortname, + description: Script.resolvePath('').split('/').pop(), + type: 'Text', + position: position, + rotation: MyAvatar.orientation, + script: script, + scriptTimestamp: +new Date, + lifetime: 20, + lineHeight: 1/8, + dimensions: { x: 2, y: .5, z: .01 }, + backgroundColor: { red: 0, green: 0, blue: 0 }, + color: { red: 0xff, green: 0xff, blue: 0xff }, + }, !Entities.serversExist() || !Entities.canRezTmp()); + uuids.push(uuid); + var ii = Script.setInterval(function() { + Entities.queryPropertyMetadata(uuid, "script", function(err, result) { + if (err) { + throw new Error(err); + } + if (result.success) { + clearInterval(ii); + if (/Exception/.test(script)) + expect(result.status).toMatch(/^error_(loading|running)_script$/); + else + expect(result.status).toEqual("running"); + done(); + } else { + print('!result.success', JSON.stringify(result)); + } + }); + }, 100); + Script.setTimeout(function() { + Script.clearInterval(ii); + }, 4900); + }, 5000 /* timeout */); + })(i); + } + Script.scriptEnding.connect(function() { + uuids.forEach(function(uuid) { Entities.deleteEntity(uuid); }); + }); + }); +}); + +function run() {} +function instrument_testrunner() { + var isNode = typeof process === 'object' && process.title === 'node'; + if (isNode) { + // for consistency this still uses the same local jasmine.js library + var jasmineRequire = require('../../libraries/jasmine/jasmine.js'); + var jasmine = jasmineRequire.core(jasmineRequire); + var env = jasmine.getEnv(); + var jasmineInterface = jasmineRequire.interface(jasmine, env); + for (var p in jasmineInterface) + global[p] = jasmineInterface[p]; + env.addReporter(new (require('jasmine-console-reporter'))); + // testing mocks + Script = { + resetModuleCache: function() { + module.require.cache = {}; + }, + setTimeout: setTimeout, + clearTimeout: clearTimeout, + resolvePath: function(id) { + // this attempts to accurately emulate how Script.resolvePath works + var trace = {}; Error.captureStackTrace(trace); + var base = trace.stack.split('\n')[2].replace(/^.*[(]|[)].*$/g,'').replace(/:[0-9]+:[0-9]+.*$/,''); + if (!id) + return base; + var rel = base.replace(/[^\/]+$/, id); + console.info('rel', rel); + return require.resolve(rel); + }, + require: function(mod) { + return require(Script.require.resolve(mod)); + } + }; + Script.require.cache = require.cache; + Script.require.resolve = function(mod) { + if (mod === '.' || /^\.\.($|\/)/.test(mod)) + throw new Error("Cannot find module '"+mod+"' (is dir)"); + var path = require.resolve(mod); + //console.info('node-require-reoslved', mod, path); + try { + if (require('fs').lstatSync(path).isDirectory()) { + throw new Error("Cannot find module '"+path+"' (is directory)"); + } + //console.info('!path', path); + } catch(e) { console.info(e) } + return path; + }; + print = console.info.bind(console, '[print]'); + } else { + global = this; + // Interface Test mode + Script.require('../../../system/libraries/utils.js'); + this.jasmineRequire = Script.require('../../libraries/jasmine/jasmine.js'); + Script.require('../../libraries/jasmine/hifi-boot.js') + require = Script.require; + // polyfill console + console = { + log: print, + info: print.bind(this, '[info]'), + warn: print.bind(this, '[warn]'), + error: print.bind(this, '[error]'), + debug: print.bind(this, '[debug]'), + }; + } + run = function() { global.jasmine.getEnv().execute(); }; + return isNode; +} +run(); diff --git a/scripts/developer/tests/unit_tests/package.json b/scripts/developer/tests/unit_tests/package.json new file mode 100644 index 0000000000..91d719b687 --- /dev/null +++ b/scripts/developer/tests/unit_tests/package.json @@ -0,0 +1,6 @@ +{ + "name": "unit_tests", + "devDependencies": { + "jasmine-console-reporter": "^1.2.7" + } +} From fa0d3a18455b7bb8d7a58ec96a0f049c4c0ce26c Mon Sep 17 00:00:00 2001 From: humbletim Date: Wed, 1 Mar 2017 15:19:23 -0500 Subject: [PATCH 096/302] unit test cleanup pass --- .../tests/unit_tests/moduleTests/cycles/a.js | 1 + .../tests/unit_tests/moduleTests/cycles/b.js | 1 + .../unit_tests/moduleTests/cycles/main.js | 4 + .../entity/entityConstructorAPIException.js | 3 +- .../entity/entityConstructorModule.js | 6 +- .../entity/entityConstructorNested.js | 3 +- .../entity/entityConstructorNested2.js | 5 +- .../entityConstructorRequireException.js | 5 +- .../entity/entityPreloadAPIError.js | 3 +- .../entity/entityPreloadRequire.js | 3 +- .../moduleTests/exceptions/exception.js | 1 + .../exceptions/exceptionInFunction.js | 5 +- .../tests/unit_tests/moduleUnitTests.js | 309 ++++++++++-------- 13 files changed, 206 insertions(+), 143 deletions(-) diff --git a/scripts/developer/tests/unit_tests/moduleTests/cycles/a.js b/scripts/developer/tests/unit_tests/moduleTests/cycles/a.js index 7934180da7..265cfaa2df 100644 --- a/scripts/developer/tests/unit_tests/moduleTests/cycles/a.js +++ b/scripts/developer/tests/unit_tests/moduleTests/cycles/a.js @@ -1,3 +1,4 @@ +/* eslint-env node */ var a = exports; a.done = false; var b = require('./b.js'); diff --git a/scripts/developer/tests/unit_tests/moduleTests/cycles/b.js b/scripts/developer/tests/unit_tests/moduleTests/cycles/b.js index 285f176597..c46c872828 100644 --- a/scripts/developer/tests/unit_tests/moduleTests/cycles/b.js +++ b/scripts/developer/tests/unit_tests/moduleTests/cycles/b.js @@ -1,3 +1,4 @@ +/* eslint-env node */ var b = exports; b.done = false; var a = require('./a.js'); diff --git a/scripts/developer/tests/unit_tests/moduleTests/cycles/main.js b/scripts/developer/tests/unit_tests/moduleTests/cycles/main.js index 2e9a878c82..0ec39cd656 100644 --- a/scripts/developer/tests/unit_tests/moduleTests/cycles/main.js +++ b/scripts/developer/tests/unit_tests/moduleTests/cycles/main.js @@ -1,3 +1,7 @@ +/* eslint-env node */ +/* global print */ +/* eslint-disable comma-dangle */ + print('main.js'); var a = require('./a.js'), b = require('./b.js'); diff --git a/scripts/developer/tests/unit_tests/moduleTests/entity/entityConstructorAPIException.js b/scripts/developer/tests/unit_tests/moduleTests/entity/entityConstructorAPIException.js index b1bc0e33e4..bbe694b578 100644 --- a/scripts/developer/tests/unit_tests/moduleTests/entity/entityConstructorAPIException.js +++ b/scripts/developer/tests/unit_tests/moduleTests/entity/entityConstructorAPIException.js @@ -1,3 +1,4 @@ +/* eslint-disable comma-dangle */ // test module method exception being thrown within main constructor (function() { var apiMethod = Script.require('../exceptions/exceptionInFunction.js'); @@ -9,4 +10,4 @@ print("entityConstructorAPIException::preload -- never seen --", uuid, Script.resolvePath('')); }, }; -}) +}); diff --git a/scripts/developer/tests/unit_tests/moduleTests/entity/entityConstructorModule.js b/scripts/developer/tests/unit_tests/moduleTests/entity/entityConstructorModule.js index 5f0e8a5938..a4e8c17ab6 100644 --- a/scripts/developer/tests/unit_tests/moduleTests/entity/entityConstructorModule.js +++ b/scripts/developer/tests/unit_tests/moduleTests/entity/entityConstructorModule.js @@ -1,3 +1,5 @@ +/* global module */ +/* eslint-disable comma-dangle */ // test dual-purpose module and standalone Entity script function MyEntity(filename) { return { @@ -16,6 +18,6 @@ function MyEntity(filename) { try { module.exports = MyEntity; -} catch(e) {} +} catch (e) {} // eslint-disable-line no-empty print('entityConstructorModule::MyEntity', typeof MyEntity); -(MyEntity) +(MyEntity); diff --git a/scripts/developer/tests/unit_tests/moduleTests/entity/entityConstructorNested.js b/scripts/developer/tests/unit_tests/moduleTests/entity/entityConstructorNested.js index 5a2b8d5974..a90d979877 100644 --- a/scripts/developer/tests/unit_tests/moduleTests/entity/entityConstructorNested.js +++ b/scripts/developer/tests/unit_tests/moduleTests/entity/entityConstructorNested.js @@ -1,3 +1,4 @@ +/* global module */ // test Entity constructor based on inherited constructor from a module function constructor() { print("entityConstructorNested::constructor"); @@ -7,7 +8,7 @@ function constructor() { try { module.exports = constructor; -} catch(e) { +} catch (e) { constructor; } diff --git a/scripts/developer/tests/unit_tests/moduleTests/entity/entityConstructorNested2.js b/scripts/developer/tests/unit_tests/moduleTests/entity/entityConstructorNested2.js index 85a6b977b0..29e0ed65b1 100644 --- a/scripts/developer/tests/unit_tests/moduleTests/entity/entityConstructorNested2.js +++ b/scripts/developer/tests/unit_tests/moduleTests/entity/entityConstructorNested2.js @@ -1,9 +1,10 @@ +/* global module */ // test Entity constructor based on nested, inherited module constructors function constructor() { print("entityConstructorNested2::constructor"); // inherit from entityConstructorNested - var Entity = Script.require('./entityConstructorNested.js'); + var MyEntity = Script.require('./entityConstructorNested.js'); function SubEntity() {} SubEntity.prototype = new MyEntity('-- created from entityConstructorNested2 --'); @@ -19,6 +20,6 @@ function constructor() { try { module.exports = constructor; -} catch(e) { +} catch (e) { constructor; } diff --git a/scripts/developer/tests/unit_tests/moduleTests/entity/entityConstructorRequireException.js b/scripts/developer/tests/unit_tests/moduleTests/entity/entityConstructorRequireException.js index 269ca8e7f0..5872bce529 100644 --- a/scripts/developer/tests/unit_tests/moduleTests/entity/entityConstructorRequireException.js +++ b/scripts/developer/tests/unit_tests/moduleTests/entity/entityConstructorRequireException.js @@ -1,9 +1,10 @@ +/* eslint-disable comma-dangle */ // test module-related exception from within "require" evaluation itself (function() { var mod = Script.require('../exceptions/exception.js'); return { preload: function(uuid) { - print("entityConstructorRequireException::preload (never happens)", uuid, Script.resolvePath('')); + print("entityConstructorRequireException::preload (never happens)", uuid, Script.resolvePath(''), mod); }, }; -}) +}); diff --git a/scripts/developer/tests/unit_tests/moduleTests/entity/entityPreloadAPIError.js b/scripts/developer/tests/unit_tests/moduleTests/entity/entityPreloadAPIError.js index 3be0b50d43..eaee178b0a 100644 --- a/scripts/developer/tests/unit_tests/moduleTests/entity/entityPreloadAPIError.js +++ b/scripts/developer/tests/unit_tests/moduleTests/entity/entityPreloadAPIError.js @@ -1,3 +1,4 @@ +/* eslint-disable comma-dangle */ // test module method exception being thrown within preload (function() { var apiMethod = Script.require('../exceptions/exceptionInFunction.js'); @@ -9,4 +10,4 @@ print("entityPreloadAPIException::preload -- never seen --", uuid, Script.resolvePath('')); }, }; -}) +}); diff --git a/scripts/developer/tests/unit_tests/moduleTests/entity/entityPreloadRequire.js b/scripts/developer/tests/unit_tests/moduleTests/entity/entityPreloadRequire.js index fc70838c80..50dab9fa7c 100644 --- a/scripts/developer/tests/unit_tests/moduleTests/entity/entityPreloadRequire.js +++ b/scripts/developer/tests/unit_tests/moduleTests/entity/entityPreloadRequire.js @@ -1,3 +1,4 @@ +/* eslint-disable comma-dangle */ // test requiring a module from within preload (function constructor() { return { @@ -7,4 +8,4 @@ print("entityPreloadRequire::example::name", example.name); }, }; -}) +}); diff --git a/scripts/developer/tests/unit_tests/moduleTests/exceptions/exception.js b/scripts/developer/tests/unit_tests/moduleTests/exceptions/exception.js index 636ee82f79..8d25d6b7a4 100644 --- a/scripts/developer/tests/unit_tests/moduleTests/exceptions/exception.js +++ b/scripts/developer/tests/unit_tests/moduleTests/exceptions/exception.js @@ -1,3 +1,4 @@ +/* eslint-env node */ module.exports = "n/a"; throw new Error('exception on line 2'); diff --git a/scripts/developer/tests/unit_tests/moduleTests/exceptions/exceptionInFunction.js b/scripts/developer/tests/unit_tests/moduleTests/exceptions/exceptionInFunction.js index dc2ce3c438..69415a0741 100644 --- a/scripts/developer/tests/unit_tests/moduleTests/exceptions/exceptionInFunction.js +++ b/scripts/developer/tests/unit_tests/moduleTests/exceptions/exceptionInFunction.js @@ -1,3 +1,4 @@ +/* eslint-env node */ // dummy lines to make sure exception line number is well below parent test script // // @@ -30,8 +31,8 @@ function myfunc() { throw new Error('exception on line 32 in myfunc'); - return "myfunc"; } module.exports = myfunc; -if (Script[module.filename] === 'throw') +if (Script[module.filename] === 'throw') { myfunc(); +} diff --git a/scripts/developer/tests/unit_tests/moduleUnitTests.js b/scripts/developer/tests/unit_tests/moduleUnitTests.js index c1c20d6980..a9446d1d6e 100644 --- a/scripts/developer/tests/unit_tests/moduleUnitTests.js +++ b/scripts/developer/tests/unit_tests/moduleUnitTests.js @@ -1,18 +1,65 @@ -/* eslint-env jasmine */ +/* eslint-env jasmine, node */ +/* global print:true, Script:true, global:true, require:true */ +/* eslint-disable comma-dangle */ +var isNode = instrumentTestrunner(), + runInterfaceTests = !isNode, + runNetworkTests = true; -var isNode = instrument_testrunner(); +// describe wrappers (note: `xdescribe` indicates a disabled or "pending" jasmine test) +var INTERFACE = { describe: runInterfaceTests ? describe : xdescribe }, + NETWORK = { describe: runNetworkTests ? describe : xdescribe }; -var NETWORK_describe = xdescribe, - INTERFACE_describe = !isNode ? describe : xdescribe, - NODE_describe = isNode ? describe : xdescribe; - -print("DESCRIBING"); describe('require', function() { describe('resolve', function() { it('should resolve relative filenames', function() { var expected = Script.resolvePath('./moduleTests/example.json'); expect(require.resolve('./moduleTests/example.json')).toEqual(expected); }); + describe('exceptions', function() { + it('should reject blank "" module identifiers', function() { + expect(function() { + require.resolve(''); + }).toThrowError(/Cannot find/); + }); + it('should reject excessive identifier sizes', function() { + expect(function() { + require.resolve(new Array(8193).toString()); + }).toThrowError(/Cannot find/); + }); + it('should reject implicitly-relative filenames', function() { + expect(function() { + var mod = require.resolve('example.js'); + mod.exists; + }).toThrowError(/Cannot find/); + }); + it('should reject non-existent filenames', function() { + expect(function() { + require.resolve('./404error.js'); + }).toThrowError(/Cannot find/); + }); + it('should reject identifiers resolving to a directory', function() { + expect(function() { + var mod = require.resolve('.'); + mod.exists; + // console.warn('resolved(.)', mod); + }).toThrowError(/Cannot find/); + expect(function() { + var mod = require.resolve('..'); + mod.exists; + // console.warn('resolved(..)', mod); + }).toThrowError(/Cannot find/); + expect(function() { + var mod = require.resolve('../'); + mod.exists; + // console.warn('resolved(../)', mod); + }).toThrowError(/Cannot find/); + }); + (isNode ? xit : it)('should reject non-system, extensionless identifiers', function() { + expect(function() { + require.resolve('./example'); + }).toThrowError(/Cannot find/); + }); + }); }); describe('JSON', function() { @@ -20,12 +67,12 @@ describe('require', function() { var example = require('./moduleTests/example.json'); expect(example.name).toEqual('Example JSON Module'); }); - INTERFACE_describe('inteface', function() { - NETWORK_describe('network', function() { - //xit('should import #content-type=application/json modules', function() { - // var results = require('https://jsonip.com#content-type=application/json'); - // expect(results.ip).toMatch(/^[.0-9]+$/); - //}); + INTERFACE.describe('interface', function() { + NETWORK.describe('network', function() { + // xit('should import #content-type=application/json modules', function() { + // var results = require('https://jsonip.com#content-type=application/json'); + // expect(results.ip).toMatch(/^[.0-9]+$/); + // }); it('should import content-type: application/json modules', function() { var scope = { 'content-type': 'application/json' }; var results = require.call(scope, 'https://jsonip.com'); @@ -36,66 +83,32 @@ describe('require', function() { }); - INTERFACE_describe('system', function() { - it('require(id)', function() { + INTERFACE.describe('system', function() { + it('require("vec3")', function() { expect(require('vec3')).toEqual(jasmine.any(Function)); }); - it('require(id).function', function() { + it('require("vec3").method', function() { expect(require('vec3')().isValid).toEqual(jasmine.any(Function)); }); - }); - - describe('exceptions', function() { - it('should reject blank "" module identifiers', function() { - expect(function() { - require.resolve(''); - }).toThrowError(/Cannot find/); + it('require("vec3") as constructor', function() { + var vec3 = require('vec3'); + var v = vec3(1.1, 2.2, 3.3); + expect(v).toEqual(jasmine.any(Object)); + expect(v.isValid).toEqual(jasmine.any(Function)); + expect(v.isValid()).toBe(true); + expect(v.toString()).toEqual('[Vec3 (1.100,2.200,3.300)]'); }); - it('should reject excessive identifier sizes', function() { - expect(function() { - require.resolve(new Array(8193).toString()); - }).toThrowError(/Cannot find/); - }); - it('should reject implicitly-relative filenames', function() { - expect(function() { - var mod = require.resolve('example.js'); - }).toThrowError(/Cannot find/); - }); - it('should reject non-existent filenames', function() { - expect(function() { - var mod = require.resolve('./404error.js'); - }).toThrowError(/Cannot find/); - }); - it('should reject identifiers resolving to a directory', function() { - expect(function() { - var mod = require.resolve('.'); - //console.warn('resolved(.)', mod); - }).toThrowError(/Cannot find/); - expect(function() { - var mod = require.resolve('..'); - //console.warn('resolved(..)', mod); - }).toThrowError(/Cannot find/); - expect(function() { - var mod = require.resolve('../'); - //console.warn('resolved(../)', mod); - }).toThrowError(/Cannot find/); - }); - if (typeof MODE !== 'undefined' && MODE !== 'node') { - it('should reject non-system, extensionless identifiers', function() { - expect(function() { - require.resolve('./example'); - }).toThrowError(/Cannot find/); - }); - } }); describe('cache', function() { it('should cache modules by resolved module id', function() { var value = new Date; var example = require('./moduleTests/example.json'); + // earmark the module object with a unique value example['.test'] = value; var example2 = require('../../tests/unit_tests/moduleTests/example.json'); expect(example2).toBe(example); + // verify earmark is still the same after a second require() expect(example2['.test']).toBe(example['.test']); }); it('should reload cached modules set to null', function() { @@ -104,6 +117,7 @@ describe('require', function() { example['.test'] = value; require.cache[require.resolve('../../tests/unit_tests/moduleTests/example.json')] = null; var example2 = require('../../tests/unit_tests/moduleTests/example.json'); + // verify the earmark is *not* the same as before expect(example2).not.toBe(example); expect(example2['.test']).not.toBe(example['.test']); }); @@ -113,6 +127,7 @@ describe('require', function() { example['.test'] = value; delete require.cache[require.resolve('../../tests/unit_tests/moduleTests/example.json')]; var example2 = require('../../tests/unit_tests/moduleTests/example.json'); + // verify the earmark is *not* the same as before expect(example2).not.toBe(example); expect(example2['.test']).not.toBe(example['.test']); }); @@ -120,30 +135,29 @@ describe('require', function() { describe('cyclic dependencies', function() { describe('should allow lazy-ref cyclic module resolution', function() { - const MODULE_PATH = './moduleTests/cycles/main.js'; var main; beforeEach(function() { - try { this._print = print; } catch(e) {} - // for this test print is no-op'd so it doesn't disrupt the reporter output - //console = typeof console === 'object' ? console : { log: function() {} }; + // eslint-disable-next-line + try { this._print = print; } catch (e) {} + // during these tests print() is no-op'd so that it doesn't disrupt the reporter output print = function() {}; Script.resetModuleCache(); }); afterEach(function() { print = this._print; }); - it('main requirable', function() { - main = require(MODULE_PATH); + it('main is requirable', function() { + main = require('./moduleTests/cycles/main.js'); expect(main).toEqual(jasmine.any(Object)); }); - it('main with both a and b', function() { + it('transient a and b done values', function() { expect(main.a['b.done?']).toBe(true); expect(main.b['a.done?']).toBe(false); }); - it('a.done?', function() { + it('ultimate a.done?', function() { expect(main['a.done?']).toBe(true); }); - it('b.done?', function() { + it('ultimate b.done?', function() { expect(main['b.done?']).toBe(true); }); }); @@ -166,8 +180,8 @@ describe('require', function() { }).toThrowError(/could not resolve|Cannot find.*foobar:/); }); - NETWORK_describe('network', function() { - // note: with retries these tests can take up to 60 seconds each to timeout + NETWORK.describe('network', function() { + // note: depending on retries these tests can take up to 60 seconds each to timeout var timeout = 75 * 1000; it('should throw catchable host errors', function() { expect(function() { @@ -183,7 +197,7 @@ describe('require', function() { }); }); - INTERFACE_describe('entity', function() { + INTERFACE.describe('entity', function() { var sampleScripts = [ 'entityConstructorAPIException.js', 'entityConstructorModule.js', @@ -192,72 +206,98 @@ describe('require', function() { 'entityConstructorRequireException.js', 'entityPreloadAPIError.js', 'entityPreloadRequire.js', - ].filter(Boolean).map(function(id) { return Script.require.resolve('./moduleTests/entity/'+id); }); + ].filter(Boolean).map(function(id) { + return Script.require.resolve('./moduleTests/entity/'+id); + }); var uuids = []; - - for(var i=0; i < sampleScripts.length; i++) { - (function(i) { - var script = sampleScripts[ i % sampleScripts.length ]; - var shortname = '['+i+'] ' + script.split('/').pop(); - var position = MyAvatar.position; - position.y -= i/2; - it(shortname, function(done) { - var uuid = Entities.addEntity({ - text: shortname, - description: Script.resolvePath('').split('/').pop(), - type: 'Text', - position: position, - rotation: MyAvatar.orientation, - script: script, - scriptTimestamp: +new Date, - lifetime: 20, - lineHeight: 1/8, - dimensions: { x: 2, y: .5, z: .01 }, - backgroundColor: { red: 0, green: 0, blue: 0 }, - color: { red: 0xff, green: 0xff, blue: 0xff }, - }, !Entities.serversExist() || !Entities.canRezTmp()); - uuids.push(uuid); - var ii = Script.setInterval(function() { - Entities.queryPropertyMetadata(uuid, "script", function(err, result) { - if (err) { - throw new Error(err); - } - if (result.success) { - clearInterval(ii); - if (/Exception/.test(script)) - expect(result.status).toMatch(/^error_(loading|running)_script$/); - else - expect(result.status).toEqual("running"); - done(); - } else { - print('!result.success', JSON.stringify(result)); - } - }); - }, 100); - Script.setTimeout(function() { - Script.clearInterval(ii); - }, 4900); - }, 5000 /* timeout */); - })(i); + function cleanup() { + uuids.splice(0,uuids.length).forEach(function(uuid) { + Entities.deleteEntity(uuid); + }); + } + afterAll(cleanup); + // extra sanity check to avoid lingering entities + Script.scriptEnding.connect(cleanup); + + for (var i=0; i < sampleScripts.length; i++) { + maketest(i); + } + + function maketest(i) { + var script = sampleScripts[ i % sampleScripts.length ]; + var shortname = '['+i+'] ' + script.split('/').pop(); + var position = MyAvatar.position; + position.y -= i/2; + // define a unique jasmine test for the current entity script + it(shortname, function(done) { + var uuid = Entities.addEntity({ + text: shortname, + description: Script.resolvePath('').split('/').pop(), + type: 'Text', + position: position, + rotation: MyAvatar.orientation, + script: script, + scriptTimestamp: +new Date, + lifetime: 20, + lineHeight: 1/8, + dimensions: { x: 2, y: 0.5, z: 0.01 }, + backgroundColor: { red: 0, green: 0, blue: 0 }, + color: { red: 0xff, green: 0xff, blue: 0xff }, + }, !Entities.serversExist() || !Entities.canRezTmp()); + uuids.push(uuid); + function stopChecking() { + if (ii) { + Script.clearInterval(ii); + ii = 0; + } + } + var ii = Script.setInterval(function() { + Entities.queryPropertyMetadata(uuid, "script", function(err, result) { + if (err) { + stopChecking(); + throw new Error(err); + } + if (result.success) { + stopChecking(); + if (/Exception/.test(script)) { + expect(result.status).toMatch(/^error_(loading|running)_script$/); + } else { + expect(result.status).toEqual("running"); + } + Entities.deleteEntity(uuid); + done(); + } else { + print('!result.success', JSON.stringify(result)); + } + }); + }, 100); + Script.setTimeout(stopChecking, 4900); + }, 5000 /* jasmine async timeout */); } - Script.scriptEnding.connect(function() { - uuids.forEach(function(uuid) { Entities.deleteEntity(uuid); }); - }); }); }); +// support for isomorphic Node.js / Interface unit testing +// note: run `npm install` from unit_tests/ and then `node moduleUnitTests.js` function run() {} -function instrument_testrunner() { +function instrumentTestrunner() { var isNode = typeof process === 'object' && process.title === 'node'; + if (typeof describe === 'function') { + // already running within a test runner; assume jasmine is ready-to-go + return isNode; + } if (isNode) { - // for consistency this still uses the same local jasmine.js library + /* eslint-disable no-console */ + // Node.js test mode + // to keep things consistent Node.js uses the local jasmine.js library (instead of an npm version) var jasmineRequire = require('../../libraries/jasmine/jasmine.js'); var jasmine = jasmineRequire.core(jasmineRequire); var env = jasmine.getEnv(); var jasmineInterface = jasmineRequire.interface(jasmine, env); - for (var p in jasmineInterface) + for (var p in jasmineInterface) { global[p] = jasmineInterface[p]; + } env.addReporter(new (require('jasmine-console-reporter'))); // testing mocks Script = { @@ -270,39 +310,45 @@ function instrument_testrunner() { // this attempts to accurately emulate how Script.resolvePath works var trace = {}; Error.captureStackTrace(trace); var base = trace.stack.split('\n')[2].replace(/^.*[(]|[)].*$/g,'').replace(/:[0-9]+:[0-9]+.*$/,''); - if (!id) + if (!id) { return base; + } var rel = base.replace(/[^\/]+$/, id); console.info('rel', rel); return require.resolve(rel); }, require: function(mod) { return require(Script.require.resolve(mod)); - } + }, }; Script.require.cache = require.cache; Script.require.resolve = function(mod) { - if (mod === '.' || /^\.\.($|\/)/.test(mod)) + if (mod === '.' || /^\.\.($|\/)/.test(mod)) { throw new Error("Cannot find module '"+mod+"' (is dir)"); + } var path = require.resolve(mod); - //console.info('node-require-reoslved', mod, path); + // console.info('node-require-reoslved', mod, path); try { if (require('fs').lstatSync(path).isDirectory()) { throw new Error("Cannot find module '"+path+"' (is directory)"); } - //console.info('!path', path); - } catch(e) { console.info(e) } + // console.info('!path', path); + } catch (e) { + console.error(e); + } return path; }; print = console.info.bind(console, '[print]'); + /* eslint-enable no-console */ } else { + // Interface test mode global = this; - // Interface Test mode Script.require('../../../system/libraries/utils.js'); this.jasmineRequire = Script.require('../../libraries/jasmine/jasmine.js'); - Script.require('../../libraries/jasmine/hifi-boot.js') + Script.require('../../libraries/jasmine/hifi-boot.js'); require = Script.require; // polyfill console + /* global console:true */ console = { log: print, info: print.bind(this, '[info]'), @@ -311,6 +357,7 @@ function instrument_testrunner() { debug: print.bind(this, '[debug]'), }; } + // eslint-disable-next-line run = function() { global.jasmine.getEnv().execute(); }; return isNode; } From 5e5bba5aad5d5ac09732c386a35d1c17551aae8b Mon Sep 17 00:00:00 2001 From: humbletim Date: Thu, 2 Mar 2017 16:58:37 -0500 Subject: [PATCH 097/302] Separate EntityPropertyMetadataRequest for easier documentation and testing --- .../entities/src/EntityScriptingInterface.cpp | 246 +++++++++++------- .../entities/src/EntityScriptingInterface.h | 27 +- 2 files changed, 180 insertions(+), 93 deletions(-) diff --git a/libraries/entities/src/EntityScriptingInterface.cpp b/libraries/entities/src/EntityScriptingInterface.cpp index df88194f9f..aa241023c8 100644 --- a/libraries/entities/src/EntityScriptingInterface.cpp +++ b/libraries/entities/src/EntityScriptingInterface.cpp @@ -27,7 +27,6 @@ #include "ModelEntityItem.h" #include "QVariantGLM.h" #include "SimulationOwner.h" -#include "BaseScriptEngine.h" #include "ZoneEntityItem.h" #include "WebEntityItem.h" #include @@ -684,26 +683,24 @@ bool EntityScriptingInterface::reloadServerScripts(QUuid entityID) { return client->reloadServerScript(entityID); } -bool EntityScriptingInterface::queryPropertyMetadata(QUuid entityID, QScriptValue property, QScriptValue scopeOrCallback, QScriptValue methodOrName) { - auto name = property.toString(); - auto handler = makeScopedHandlerObject(scopeOrCallback, methodOrName); - QPointer engine = dynamic_cast(handler.engine()); +#ifdef DEBUG_ENTITY_METADATA +// baseline example -- return parsed userData as a standard CPS callback +bool EntityPropertyMetadataRequest::_userData(EntityItemID entityID, QScriptValue handler) { + QScriptValue err, result; + auto engine = _engine; if (!engine) { - qCDebug(entities) << "queryPropertyMetadata without detectable engine" << entityID << name; + qCDebug(entities) << __FUNCTION__ << " -- engine destroyed while inflight" << entityID; return false; } - connect(engine, &QObject::destroyed, this, [=]() { - qDebug() << "queryPropertyMetadata -- engine destroyed!" << (!engine ? "nullptr" : "engine"); - }); - if (!handler.property("callback").isFunction()) { - qDebug() << "!handler.callback.isFunction" << engine; - engine->raiseException(engine->makeError("callback is not a function", "TypeError")); - return false; - } - if (name == "userData") { - EntityItemPointer entity = _entityTree->findEntityByEntityItemID(entityID); - QScriptValue err, result; - if (entity) { + auto entityScriptingInterface = DependencyManager::get(); + auto entityTree = entityScriptingInterface ? entityScriptingInterface->getEntityTree() : nullptr; + if (!entityTree) { + err = engine->makeError("Entities Tree unavailable", "InternalError"); + } else { + EntityItemPointer entity = entityTree->findEntityByID(entityID); + if (!entity) { + err = engine->makeError("entity not found"); + } else { auto JSON = engine->globalObject().property("JSON"); auto parsed = JSON.property("parse").call(JSON, QScriptValueList({ entity->getUserData() })); if (engine->hasUncaughtException()) { @@ -712,81 +709,148 @@ bool EntityScriptingInterface::queryPropertyMetadata(QUuid entityID, QScriptValu } else { result = parsed; } - } else { - err = engine->makeError("entity not found"); } - QFutureWatcher *request = new QFutureWatcher; - connect(request, &QFutureWatcher::finished, engine, [=]() mutable { - if (!engine) { - qCDebug(entities) << "queryPropertyMetadata -- engine destroyed while inflight" << entityID << name; - return; - } - callScopedHandlerObject(handler, err, result); - request->deleteLater(); - }); - request->setFuture(QtConcurrent::run([]() -> QVariant { - QThread::sleep(1); - return 1; - })); - return true; - } else if (name == "script") { - using LocalScriptStatusRequest = QFutureWatcher; - LocalScriptStatusRequest *request = new LocalScriptStatusRequest; - connect(request, &LocalScriptStatusRequest::finished, engine, [=]() mutable { - if (!engine) { - qCDebug(entities) << "queryPropertyMetadata -- engine destroyed while inflight" << entityID << name; - return; - } - auto details = request->result().toMap(); - QScriptValue err, result; - if (details.contains("isError")) { - if (!details.contains("message")) { - details["message"] = details["errorInfo"]; - } - err = engine->makeError(engine->toScriptValue(details)); - } else { - details["success"] = true; - result = engine->toScriptValue(details); - } - callScopedHandlerObject(handler, err, result); - request->deleteLater(); - }); - request->setFuture(_entitiesScriptEngine->getLocalEntityScriptDetails(entityID)); - return true; - } else if (name == "serverScripts") { - auto client = DependencyManager::get(); - auto request = client->createScriptStatusRequest(entityID); - connect(request, &GetScriptStatusRequest::finished, engine, [=](GetScriptStatusRequest* request) mutable { - if (!engine) { - qCDebug(entities) << "queryPropertyMetadata -- engine destroyed while inflight" << entityID << name; - return; - } - QVariantMap details; - details["success"] = request->getResponseReceived(); - details["isRunning"] = request->getIsRunning(); - details["status"] = EntityScriptStatus_::valueToKey(request->getStatus()).toLower(); - details["errorInfo"] = request->getErrorInfo(); - - QScriptValue err, result; - if (!details["success"].toBool()) { - if (!details.contains("message") && details.contains("errorInfo")) { - details["message"] = details["errorInfo"]; - } - if (details["message"].toString().isEmpty()) { - details["message"] = "entity server script details not found"; - } - err = engine->makeError(engine->toScriptValue(details)); - } else { - result = engine->toScriptValue(details); - } - callScopedHandlerObject(handler, err, result); - request->deleteLater(); - }); - request->start(); - return true; } - engine->raiseException(engine->makeError("property has no mapped metadata: " + name)); - return false; + // this one second delay can be used with a Client script to query metadata and immediately Script.stop() + // (testing that the signal handler never gets called once the engine is destroyed) + // note: we still might want to check engine->isStopping() as an optimization in some places + QFutureWatcher *request = new QFutureWatcher; + QObject::connect(request, &QFutureWatcher::finished, engine, [=]() mutable { + if (!engine) { + qCDebug(entities) << "queryPropertyMetadata -- engine destroyed while inflight" << entityID; + return; + } + callScopedHandlerObject(handler, err, result); + request->deleteLater(); + }); + request->setFuture(QtConcurrent::run([]() -> QVariant { + QThread::sleep(1); + return QVariant(); + })); + return true; +} +#endif + +bool EntityPropertyMetadataRequest::script(EntityItemID entityID, QScriptValue handler) { + using LocalScriptStatusRequest = QFutureWatcher; + + LocalScriptStatusRequest *request = new LocalScriptStatusRequest; + QObject::connect(request, &LocalScriptStatusRequest::finished, _engine, [=]() mutable { + auto engine = _engine; + if (!engine) { + // this is just to address any lingering doubts -- when _engine is destroyed, this connect gets broken automatically + qCDebug(entities) << __FUNCTION__ << " -- engine destroyed while inflight" << entityID; + return; + } + auto details = request->result().toMap(); + QScriptValue err, result; + if (details.contains("isError")) { + if (!details.contains("message")) { + details["message"] = details["errorInfo"]; + } + err = engine->makeError(engine->toScriptValue(details)); + } else { + details["success"] = true; + result = engine->toScriptValue(details); + } + callScopedHandlerObject(handler, err, result); + request->deleteLater(); + }); + auto entityScriptingInterface = DependencyManager::get(); + entityScriptingInterface->withEntitiesScriptEngine([&](EntitiesScriptEngineProvider* entitiesScriptEngine) { + if (entitiesScriptEngine) { + request->setFuture(entitiesScriptEngine->getLocalEntityScriptDetails(entityID)); + } + }); + if (!request->isStarted()) { + request->deleteLater(); + callScopedHandlerObject(handler, _engine->makeError("Entities Scripting Provider unavailable", "InternalError"), QScriptValue()); + return false; + } + return true; +} + +bool EntityPropertyMetadataRequest::serverScripts(EntityItemID entityID, QScriptValue handler) { + auto client = DependencyManager::get(); + auto request = client->createScriptStatusRequest(entityID); + QPointer engine = _engine; + QObject::connect(request, &GetScriptStatusRequest::finished, _engine, [=](GetScriptStatusRequest* request) mutable { + auto engine = _engine; + if (!engine) { + qCDebug(entities) << __FUNCTION__ << " -- engine destroyed while inflight" << entityID; + return; + } + QVariantMap details; + details["success"] = request->getResponseReceived(); + details["isRunning"] = request->getIsRunning(); + details["status"] = EntityScriptStatus_::valueToKey(request->getStatus()).toLower(); + details["errorInfo"] = request->getErrorInfo(); + + QScriptValue err, result; + if (!details["success"].toBool()) { + if (!details.contains("message") && details.contains("errorInfo")) { + details["message"] = details["errorInfo"]; + } + if (details["message"].toString().isEmpty()) { + details["message"] = "entity server script details not found"; + } + err = engine->makeError(engine->toScriptValue(details)); + } else { + result = engine->toScriptValue(details); + } + callScopedHandlerObject(handler, err, result); + request->deleteLater(); + }); + request->start(); + return true; +} + +bool EntityScriptingInterface::queryPropertyMetadata(QUuid entityID, QScriptValue property, QScriptValue scopeOrCallback, QScriptValue methodOrName) { + auto name = property.toString(); + auto handler = makeScopedHandlerObject(scopeOrCallback, methodOrName); + QPointer engine = dynamic_cast(handler.engine()); + if (!engine) { + qCDebug(entities) << "queryPropertyMetadata without detectable engine" << entityID << name; + return false; + } +#ifdef DEBUG_ENGINE_STATE + connect(engine, &QObject::destroyed, this, [=]() { + qDebug() << "queryPropertyMetadata -- engine destroyed!" << (!engine ? "nullptr" : "engine"); + }); +#endif + if (!handler.property("callback").isFunction()) { + qDebug() << "!handler.callback.isFunction" << engine; + engine->raiseException(engine->makeError("callback is not a function", "TypeError")); + return false; + } + + // NOTE: this approach is a work-in-progress and for now just meant to work 100% correctly and provide + // some initial structure for organizing metadata adapters around. + + // The extra layer of indirection is *essential* because in real world conditions errors are often introduced + // by accident and sometimes without exact memory of "what just changed." + + // Here the scripter only needs to know an entityID and a property name -- which means all scripters can + // level this method when stuck in dead-end scenarios or to learn more about "magic" Entity properties + // like .script that work in terms of side-effects. + + // This is an async callback pattern -- so if needed C++ can easily throttle or restrict queries later. + + EntityPropertyMetadataRequest request(engine); + + if (name == "script") { + return request.script(entityID, handler); + } else if (name == "serverScripts") { + return request.serverScripts(entityID, handler); +#ifdef DEBUG_ENTITY_METADATA + } else if (name == "userData") { + return request.userData(entityID, handler); +#endif + } else { + engine->raiseException(engine->makeError("metadata for property " + name + " is not yet queryable")); + engine->maybeEmitUncaughtException(__FUNCTION__); + return false; + } } bool EntityScriptingInterface::getServerScriptStatus(QUuid entityID, QScriptValue callback) { diff --git a/libraries/entities/src/EntityScriptingInterface.h b/libraries/entities/src/EntityScriptingInterface.h index 2c3c654528..d6fe93b41e 100644 --- a/libraries/entities/src/EntityScriptingInterface.h +++ b/libraries/entities/src/EntityScriptingInterface.h @@ -34,8 +34,25 @@ #include "EntitiesScriptEngineProvider.h" #include "EntityItemProperties.h" +#include "BaseScriptEngine.h" + class EntityTree; +// helper factory to compose standardized, async metadata queries for "magic" Entity properties +// like .script and .serverScripts. This is used for automated testing of core scripting features +// as well as to provide early adopters a self-discoverable, consistent way to diagnose common +// problems with their own Entity scripts. +class EntityPropertyMetadataRequest { +public: + EntityPropertyMetadataRequest(BaseScriptEngine* engine) : _engine(engine) {}; + bool script(EntityItemID entityID, QScriptValue handler); + bool serverScripts(EntityItemID entityID, QScriptValue handler); + // this is used for internal testing and only available when DEBUG_ENTITY_METADATA is defined in the .cpp file + bool userData(EntityItemID entityID, QScriptValue handler); +private: + QPointer _engine; +}; + class RayToEntityIntersectionResult { public: RayToEntityIntersectionResult(); @@ -67,6 +84,7 @@ class EntityScriptingInterface : public OctreeScriptingInterface, public Depende Q_PROPERTY(float costMultiplier READ getCostMultiplier WRITE setCostMultiplier) Q_PROPERTY(QUuid keyboardFocusEntity READ getKeyboardFocusEntity WRITE setKeyboardFocusEntity) + friend EntityPropertyMetadataRequest; public: EntityScriptingInterface(bool bidOnSimulationOwnership); @@ -213,7 +231,7 @@ public slots: Q_INVOKABLE bool reloadServerScripts(QUuid entityID); /**jsdoc - * Query for the available metadata behind one of an Entity's "magic" properties (eg: `script` and `serverScripts`). + * Query additional metadata for "magic" Entity properties like `script` and `serverScripts`. * * @function Entities.queryPropertyMetadata * @param {EntityID} entityID The ID of the entity. @@ -221,7 +239,7 @@ public slots: * @param {ResultCallback} callback Executes callback(err, result) with the query results. */ /**jsdoc - * Query for the available metadata behind one of an Entity's "magic" properties (eg: `script` and `serverScripts`). + * Query additional metadata for "magic" Entity properties like `script` and `serverScripts`. * * @function Entities.queryPropertyMetadata * @param {EntityID} entityID The ID of the entity. @@ -343,6 +361,11 @@ signals: void webEventReceived(const EntityItemID& entityItemID, const QVariant& message); +protected: + void withEntitiesScriptEngine(std::function function) { + std::lock_guard lock(_entitiesScriptEngineLock); + function(_entitiesScriptEngine); + }; private: bool actionWorker(const QUuid& entityID, std::function actor); bool setVoxels(QUuid entityID, std::function actor); From d93047f9e2e74c6c9a7ffe452fc3ffdcc1e55548 Mon Sep 17 00:00:00 2001 From: humbletim Date: Thu, 2 Mar 2017 17:23:24 -0500 Subject: [PATCH 098/302] Switch to READONLY_PROP_FLAGS for require.cache/require.resolve properties --- libraries/script-engine/src/ScriptEngine.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index c7364450a1..9b88e2bd24 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -580,7 +580,7 @@ void ScriptEngine::resetModuleCache(bool deleteScriptCache) { #if DEBUG_JS_MODULES cache.setProperty("__meta__", cacheMeta, READONLY_HIDDEN_PROP_FLAGS); #endif - jsRequire.setProperty("cache", cache, QScriptValue::ReadOnly | QScriptValue::Undeletable); + jsRequire.setProperty("cache", cache, READONLY_PROP_FLAGS); } void ScriptEngine::init() { @@ -651,7 +651,7 @@ void ScriptEngine::init() { auto Script = globalObject().property("Script"); auto require = Script.property("require"); auto resolve = Script.property("_requireResolve"); - require.setProperty("resolve", resolve, QScriptValue::ReadOnly | QScriptValue::Undeletable); + require.setProperty("resolve", resolve, READONLY_PROP_FLAGS); resetModuleCache(); } From 6b927de9f1aa0e98c906096bc094e3139dfc06a7 Mon Sep 17 00:00:00 2001 From: humbletim Date: Thu, 2 Mar 2017 18:11:24 -0500 Subject: [PATCH 099/302] Add example vec3 system module --- scripts/modules/vec3.js | 69 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 69 insertions(+) create mode 100644 scripts/modules/vec3.js diff --git a/scripts/modules/vec3.js b/scripts/modules/vec3.js new file mode 100644 index 0000000000..f164f01374 --- /dev/null +++ b/scripts/modules/vec3.js @@ -0,0 +1,69 @@ +// Example of using a "system module" to decouple Vec3's implementation details. +// +// Users would bring Vec3 support in as a module: +// var vec3 = Script.require('vec3'); +// + +// (this example is compatible with using as a Script.include and as a Script.require module) +try { + // Script.require + module.exports = vec3; +} catch(e) { + // Script.include + Script.registerValue("vec3", vec3); +} + +vec3.fromObject = function(v) { + //return new vec3(v.x, v.y, v.z); + //... this is even faster and achieves the same effect + v.__proto__ = vec3.prototype; + return v; +}; + +vec3.prototype = { + multiply: function(v2) { + // later on could support overrides like so: + // if (v2 instanceof quat) { [...] } + // which of the below is faster (C++ or JS)? + // (dunno -- but could systematically find out and go with that version) + + // pure JS option + // return new vec3(this.x * v2.x, this.y * v2.y, this.z * v2.z); + + // hybrid C++ option + return vec3.fromObject(Vec3.multiply(this, v2)); + }, + // detects any NaN and Infinity values + isValid: function() { + return isFinite(this.x) && isFinite(this.y) && isFinite(this.z); + }, + // format Vec3's, eg: + // var v = vec3(); + // print(v); // outputs [Vec3 (0.000, 0.000, 0.000)] + toString: function() { + if (this === vec3.prototype) { + return "{Vec3 prototype}"; + } + function fixed(n) { return n.toFixed(3); } + return "[Vec3 (" + [this.x, this.y, this.z].map(fixed) + ")]"; + }, +}; + +vec3.DEBUG = true; + +function vec3(x, y, z) { + if (!(this instanceof vec3)) { + // if vec3 is called as a function then re-invoke as a constructor + // (so that `value instanceof vec3` holds true for created values) + return new vec3(x, y, z); + } + + // unfold default arguments (vec3(), vec3(.5), vec3(0,1), etc.) + this.x = x !== undefined ? x : 0; + this.y = y !== undefined ? y : this.x; + this.z = z !== undefined ? z : this.y; + + if (vec3.DEBUG && !this.isValid()) + throw new Error('vec3() -- invalid initial values ['+[].slice.call(arguments)+']'); +}; + From 8582c7af7bfbf22f6dc9678c986ba85ef2a43e61 Mon Sep 17 00:00:00 2001 From: humbletim Date: Tue, 7 Mar 2017 14:02:20 -0500 Subject: [PATCH 100/302] Use specific debug name literal instead of __FUNCTION__ from within lambda --- libraries/script-engine/src/ScriptEngine.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index 9b88e2bd24..f96b733c74 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -1840,7 +1840,7 @@ void ScriptEngine::include(const QStringList& includeFiles, QScriptValue callbac doWithEnvironment(capturedEntityIdentifier, capturedSandboxURL, operation); if (hasUncaughtException()) { - emit unhandledException(cloneUncaughtException(__FUNCTION__)); + emit unhandledException(cloneUncaughtException("evaluateInclude")); clearExceptions(); } } else { From c559838dbb50c1e33dbc68c10b7b19985b320876 Mon Sep 17 00:00:00 2001 From: humbletim Date: Tue, 7 Mar 2017 14:05:29 -0500 Subject: [PATCH 101/302] Add a few more .resolvePath characterization tests --- .../tests/unit_tests/scriptUnitTests.js | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/scripts/developer/tests/unit_tests/scriptUnitTests.js b/scripts/developer/tests/unit_tests/scriptUnitTests.js index 63b451e97f..fa8cb44608 100644 --- a/scripts/developer/tests/unit_tests/scriptUnitTests.js +++ b/scripts/developer/tests/unit_tests/scriptUnitTests.js @@ -15,10 +15,20 @@ describe('Script', function () { // characterization tests // initially these are just to capture how the app works currently var testCases = { + // special relative resolves '': filename, '.': dirname, '..': parentdir, + + // local file "magic" tilde path expansion + '/~/defaultScripts.js': ScriptDiscoveryService.defaultScriptsPath + '/defaultScripts.js', + + // these schemes appear to always get resolved to empty URLs + 'qrc://test': '', 'about:Entities 1': '', + 'ftp://host:port/path': '', + 'data:text/html;text,foo': '', + 'Entities 1': dirname + 'Entities 1', './file.js': dirname + 'file.js', 'c:/temp/': 'file:///c:/temp/', @@ -31,6 +41,12 @@ describe('Script', function () { '/~/libraries/utils.js': 'file:///~/libraries/utils.js', '/temp/file.js': 'file:///temp/file.js', '/~/': 'file:///~/', + + // these schemes appear to always get resolved to the same URL again + 'http://highfidelity.com': 'http://highfidelity.com', + 'atp:/highfidelity': 'atp:/highfidelity', + 'atp:c2d7e3a48cadf9ba75e4f8d9f4d80e75276774880405a093fdee36543aa04f': + 'atp:c2d7e3a48cadf9ba75e4f8d9f4d80e75276774880405a093fdee36543aa04f', }; describe('resolvePath', function () { Object.keys(testCases).forEach(function(input) { @@ -42,7 +58,7 @@ describe('Script', function () { describe('include', function () { var old_cache_buster; - var cache_buster = '#' + +new Date; + var cache_buster = '#' + new Date().getTime().toString(36); beforeAll(function() { old_cache_buster = Settings.getValue('cache_buster'); Settings.setValue('cache_buster', cache_buster); From b54d6cb0e1fda141c8c81749ef1a35b78b31df3f Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Wed, 8 Mar 2017 00:07:49 +0100 Subject: [PATCH 102/302] stop avatar controller while playing back a recording, which makes it not collide anymore --- interface/src/avatar/MyAvatar.cpp | 3 +++ interface/src/avatar/MyAvatar.h | 1 + 2 files changed, 4 insertions(+) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 969268c549..318608e3a8 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -154,9 +154,12 @@ MyAvatar::MyAvatar(RigPointer rig) : if (recordingInterface->getPlayFromCurrentLocation()) { setRecordingBasis(); } + _wasCharacterControllerEnabled = _characterController.isEnabled(); + _characterController.setEnabled(false); } else { clearRecordingBasis(); useFullAvatarURL(_fullAvatarURLFromPreferences, _fullAvatarModelName); + _characterController.setEnabled(_wasCharacterControllerEnabled); } auto audioIO = DependencyManager::get(); diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index 3cc665b533..00923e78cc 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -411,6 +411,7 @@ private: SharedSoundPointer _collisionSound; MyCharacterController _characterController; + bool _wasCharacterControllerEnabled { true }; AvatarWeakPointer _lookAtTargetAvatar; glm::vec3 _targetAvatarPosition; From b954d5b0e94276d752b14ab4ca4fb321d826784a Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Wed, 8 Mar 2017 00:08:57 +0100 Subject: [PATCH 103/302] stop the playback after the clip stopped playing rather than pause. Allows you to click play again without having to move the position back. --- libraries/recording/src/recording/Deck.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/recording/src/recording/Deck.cpp b/libraries/recording/src/recording/Deck.cpp index 5a32ff4984..186516e01c 100644 --- a/libraries/recording/src/recording/Deck.cpp +++ b/libraries/recording/src/recording/Deck.cpp @@ -154,8 +154,8 @@ void Deck::processFrames() { // if doing relative movement emit looped(); } else { - // otherwise pause playback - pause(); + // otherwise stop playback + stop(); } return; } From 6b26f5f3a30595f2b16389800fe3325ded88b616 Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Wed, 8 Mar 2017 00:09:56 +0100 Subject: [PATCH 104/302] Fix the timer display --- .../developer/utilities/record/recorder.js | 50 ++++++++++--------- 1 file changed, 26 insertions(+), 24 deletions(-) diff --git a/scripts/developer/utilities/record/recorder.js b/scripts/developer/utilities/record/recorder.js index 0e335116d5..7338105483 100644 --- a/scripts/developer/utilities/record/recorder.js +++ b/scripts/developer/utilities/record/recorder.js @@ -9,6 +9,8 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +/* globals HIFI_PUBLIC_BUCKET:true, Tool, ToolBar */ + HIFI_PUBLIC_BUCKET = "http://s3.amazonaws.com/hifi-public/"; Script.include("/~/system/libraries/toolBars.js"); @@ -47,7 +49,7 @@ setupTimer(); var watchStop = false; function setupToolBar() { - if (toolBar != null) { + if (toolBar !== null) { print("Multiple calls to Recorder.js:setupToolBar()"); return; } @@ -112,15 +114,15 @@ function setupTimer() { text: (0.00).toFixed(3), backgroundColor: COLOR_OFF, x: 0, y: 0, - width: 0, height: 0, - leftMargin: 10, topMargin: 10, + width: 200, height: 37, + leftMargin: 5, topMargin: 10, alpha: 1.0, backgroundAlpha: 1.0, visible: true }); slider = { x: 0, y: 0, w: 200, h: 20, - pos: 0.0, // 0.0 <= pos <= 1.0 + pos: 0.0 // 0.0 <= pos <= 1.0 }; slider.background = Overlays.addOverlay("text", { text: "", @@ -148,16 +150,17 @@ function updateTimer() { var text = ""; if (Recording.isRecording()) { text = formatTime(Recording.recorderElapsed()); - } else { - text = formatTime(Recording.playerElapsed()) + " / " + - formatTime(Recording.playerLength()); + text = formatTime(Recording.playerElapsed()) + " / " + formatTime(Recording.playerLength()); } + var timerWidth = text.length * 8 + ((Recording.isRecording()) ? 15 : 0); + Overlays.editOverlay(timer, { - text: text - }) - toolBar.changeSpacing(text.length * 8 + ((Recording.isRecording()) ? 15 : 0), spacing); + text: text, + width: timerWidth + }); + toolBar.changeSpacing(timerWidth, spacing); if (Recording.isRecording()) { slider.pos = 1.0; @@ -173,7 +176,7 @@ function updateTimer() { function formatTime(time) { var MIN_PER_HOUR = 60; var SEC_PER_MIN = 60; - var MSEC_PER_SEC = 1000; + var MSEC_DIGITS = 3; var hours = Math.floor(time / (SEC_PER_MIN * MIN_PER_HOUR)); time -= hours * (SEC_PER_MIN * MIN_PER_HOUR); @@ -184,11 +187,9 @@ function formatTime(time) { var seconds = time; var text = ""; - text += (hours > 0) ? hours + ":" : - ""; - text += (minutes > 0) ? ((minutes < 10 && text != "") ? "0" : "") + minutes + ":" : - ""; - text += ((seconds < 10 && text != "") ? "0" : "") + seconds.toFixed(3); + text += (hours > 0) ? hours + ":" : ""; + text += (minutes > 0) ? ((minutes < 10 && text !== "") ? "0" : "") + minutes + ":" : ""; + text += ((seconds < 10 && text !== "") ? "0" : "") + seconds.toFixed(MSEC_DIGITS); return text; } @@ -205,16 +206,16 @@ function moveUI() { Overlays.editOverlay(slider.background, { x: slider.x, - y: slider.y, + y: slider.y }); Overlays.editOverlay(slider.foreground, { x: slider.x, - y: slider.y, + y: slider.y }); } function mousePressEvent(event) { - clickedOverlay = Overlays.getOverlayAtPoint({ x: event.x, y: event.y }); + var clickedOverlay = Overlays.getOverlayAtPoint({ x: event.x, y: event.y }); if (recordIcon === toolBar.clicked(clickedOverlay, false) && !Recording.isPlaying()) { if (!Recording.isRecording()) { @@ -226,7 +227,8 @@ function mousePressEvent(event) { toolBar.setAlpha(ALPHA_OFF, loadIcon); } else { Recording.stopRecording(); - toolBar.selectTool(recordIcon, true ); + toolBar.selectTool(recordIcon, true); + Recording.setPlayerTime(0); Recording.loadLastRecording(); toolBar.setAlpha(ALPHA_ON, playIcon); toolBar.setAlpha(ALPHA_ON, playLoopIcon); @@ -263,7 +265,7 @@ function mousePressEvent(event) { toolBar.setAlpha(ALPHA_OFF, loadIcon); } } else if (saveIcon === toolBar.clicked(clickedOverlay)) { - if (!Recording.isRecording() && !Recording.isPlaying() && Recording.playerLength() != 0) { + if (!Recording.isRecording() && !Recording.isPlaying() && Recording.playerLength() !== 0) { recordingFile = Window.save("Save recording to file", ".", "Recordings (*.hfr)"); if (!(recordingFile === "null" || recordingFile === null || recordingFile === "")) { Recording.saveRecording(recordingFile); @@ -282,8 +284,8 @@ function mousePressEvent(event) { } } } else if (Recording.playerLength() > 0 && - slider.x < event.x && event.x < slider.x + slider.w && - slider.y < event.y && event.y < slider.y + slider.h) { + slider.x < event.x && event.x < slider.x + slider.w && + slider.y < event.y && event.y < slider.y + slider.h) { isSliding = true; slider.pos = (event.x - slider.x) / slider.w; Recording.setPlayerTime(slider.pos * Recording.playerLength()); @@ -308,7 +310,7 @@ function mouseReleaseEvent(event) { function update() { var newDimensions = Controller.getViewportDimensions(); - if (windowDimensions.x != newDimensions.x || windowDimensions.y != newDimensions.y) { + if (windowDimensions.x !== newDimensions.x || windowDimensions.y !== newDimensions.y) { windowDimensions = newDimensions; moveUI(); } From b21bd3afa9d22cd87ce2193493ddee3c65789e51 Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Wed, 8 Mar 2017 01:14:11 +0100 Subject: [PATCH 105/302] Make the time and slider follow the record.js toolbar --- .../developer/utilities/record/recorder.js | 50 +++++++++++-------- scripts/system/libraries/toolBars.js | 4 ++ 2 files changed, 33 insertions(+), 21 deletions(-) diff --git a/scripts/developer/utilities/record/recorder.js b/scripts/developer/utilities/record/recorder.js index 7338105483..dcbbe2145f 100644 --- a/scripts/developer/utilities/record/recorder.js +++ b/scripts/developer/utilities/record/recorder.js @@ -16,7 +16,7 @@ Script.include("/~/system/libraries/toolBars.js"); var recordingFile = "recording.hfr"; -function setPlayerOptions() { +function setDefaultPlayerOptions() { Recording.setPlayFromCurrentLocation(true); Recording.setPlayerUseDisplayName(false); Recording.setPlayerUseAttachments(false); @@ -40,10 +40,10 @@ var saveIcon; var loadIcon; var spacing; var timerOffset; -setupToolBar(); - var timer = null; var slider = null; + +setupToolBar(); setupTimer(); var watchStop = false; @@ -58,6 +58,8 @@ function setupToolBar() { toolBar = new ToolBar(0, 0, ToolBar.HORIZONTAL); + toolBar.onMove = onToolbarMove; + toolBar.setBack(COLOR_TOOL_BAR, ALPHA_OFF); recordIcon = toolBar.addTool({ @@ -146,6 +148,26 @@ function setupTimer() { }); } +function onToolbarMove(newX, newY, deltaX, deltaY) { + print(newX); + Overlays.editOverlay(timer, { + x: newX + timerOffset - ToolBar.SPACING, + y: newY - ToolBar.SPACING + }); + + slider.x = newX - ToolBar.SPACING; + slider.y = newY - slider.h - ToolBar.SPACING; + + Overlays.editOverlay(slider.background, { + x: slider.x, + y: slider.y + }); + Overlays.editOverlay(slider.foreground, { + x: slider.x, + y: slider.y + }); +} + function updateTimer() { var text = ""; if (Recording.isRecording()) { @@ -196,22 +218,6 @@ function formatTime(time) { function moveUI() { var relative = { x: 70, y: 40 }; toolBar.move(relative.x, windowDimensions.y - relative.y); - Overlays.editOverlay(timer, { - x: relative.x + timerOffset - ToolBar.SPACING, - y: windowDimensions.y - relative.y - ToolBar.SPACING - }); - - slider.x = relative.x - ToolBar.SPACING; - slider.y = windowDimensions.y - relative.y - slider.h - ToolBar.SPACING; - - Overlays.editOverlay(slider.background, { - x: slider.x, - y: slider.y - }); - Overlays.editOverlay(slider.foreground, { - x: slider.x, - y: slider.y - }); } function mousePressEvent(event) { @@ -228,6 +234,9 @@ function mousePressEvent(event) { } else { Recording.stopRecording(); toolBar.selectTool(recordIcon, true); + setDefaultPlayerOptions(); + // Plays the recording at the same spot as you recorded it + Recording.setPlayFromCurrentLocation(false); Recording.setPlayerTime(0); Recording.loadLastRecording(); toolBar.setAlpha(ALPHA_ON, playIcon); @@ -242,7 +251,6 @@ function mousePressEvent(event) { toolBar.setAlpha(ALPHA_ON, saveIcon); toolBar.setAlpha(ALPHA_ON, loadIcon); } else if (Recording.playerLength() > 0) { - setPlayerOptions(); Recording.setPlayerLoop(false); Recording.startPlaying(); toolBar.setAlpha(ALPHA_OFF, recordIcon); @@ -257,7 +265,6 @@ function mousePressEvent(event) { toolBar.setAlpha(ALPHA_ON, saveIcon); toolBar.setAlpha(ALPHA_ON, loadIcon); } else if (Recording.playerLength() > 0) { - setPlayerOptions(); Recording.setPlayerLoop(true); Recording.startPlaying(); toolBar.setAlpha(ALPHA_OFF, recordIcon); @@ -276,6 +283,7 @@ function mousePressEvent(event) { recordingFile = Window.browse("Load recording from file", ".", "Recordings (*.hfr *.rec *.HFR *.REC)"); if (!(recordingFile === "null" || recordingFile === null || recordingFile === "")) { Recording.loadRecording(recordingFile); + setDefaultPlayerOptions(); } if (Recording.playerLength() > 0) { toolBar.setAlpha(ALPHA_ON, playIcon); diff --git a/scripts/system/libraries/toolBars.js b/scripts/system/libraries/toolBars.js index e49f8c4004..351f10e7bd 100644 --- a/scripts/system/libraries/toolBars.js +++ b/scripts/system/libraries/toolBars.js @@ -160,6 +160,7 @@ ToolBar = function(x, y, direction, optionalPersistenceKey, optionalInitialPosit visible: false }); this.spacing = []; + this.onMove = null; this.addTool = function(properties, selectable, selected) { if (direction == ToolBar.HORIZONTAL) { @@ -254,6 +255,9 @@ ToolBar = function(x, y, direction, optionalPersistenceKey, optionalInitialPosit y: y - ToolBar.SPACING }); } + if (this.onMove !== null) { + this.onMove(x, y, dx, dy); + }; } this.setAlpha = function(alpha, tool) { From 097db9a7f98bbab309cf30033e26c4b2d461df17 Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Wed, 8 Mar 2017 01:41:20 +0100 Subject: [PATCH 106/302] - debug message removal (oops) - smaller timer textbox, allows you to grab around the edges and still drag the toolbar --- scripts/developer/utilities/record/recorder.js | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/scripts/developer/utilities/record/recorder.js b/scripts/developer/utilities/record/recorder.js index dcbbe2145f..ba1c8b0393 100644 --- a/scripts/developer/utilities/record/recorder.js +++ b/scripts/developer/utilities/record/recorder.js @@ -90,7 +90,7 @@ function setupToolBar() { visible: true }, false); - timerOffset = toolBar.width; + timerOffset = toolBar.width + ToolBar.SPACING; spacing = toolBar.addSpacing(0); saveIcon = toolBar.addTool({ @@ -116,8 +116,8 @@ function setupTimer() { text: (0.00).toFixed(3), backgroundColor: COLOR_OFF, x: 0, y: 0, - width: 200, height: 37, - leftMargin: 5, topMargin: 10, + width: 200, height: 25, + leftMargin: 5, topMargin: 3, alpha: 1.0, backgroundAlpha: 1.0, visible: true }); @@ -149,10 +149,9 @@ function setupTimer() { } function onToolbarMove(newX, newY, deltaX, deltaY) { - print(newX); Overlays.editOverlay(timer, { x: newX + timerOffset - ToolBar.SPACING, - y: newY - ToolBar.SPACING + y: newY }); slider.x = newX - ToolBar.SPACING; @@ -182,7 +181,7 @@ function updateTimer() { text: text, width: timerWidth }); - toolBar.changeSpacing(timerWidth, spacing); + toolBar.changeSpacing(timerWidth + ToolBar.SPACING, spacing); if (Recording.isRecording()) { slider.pos = 1.0; From 62fcd238ae8a9012458d8968b563a12ed1adced4 Mon Sep 17 00:00:00 2001 From: Bradley Austin Davis Date: Tue, 7 Mar 2017 13:36:51 -0800 Subject: [PATCH 107/302] Fix shared pointer usage in storage abstraction --- libraries/gpu/src/gpu/Texture.cpp | 7 ++++--- libraries/shared/src/shared/Storage.cpp | 17 +++++++++++------ libraries/shared/src/shared/Storage.h | 16 +++++----------- 3 files changed, 20 insertions(+), 20 deletions(-) diff --git a/libraries/gpu/src/gpu/Texture.cpp b/libraries/gpu/src/gpu/Texture.cpp index 5cd1ebd31f..2be495dcdb 100755 --- a/libraries/gpu/src/gpu/Texture.cpp +++ b/libraries/gpu/src/gpu/Texture.cpp @@ -389,12 +389,14 @@ const Element& Texture::getStoredMipFormat() const { } void Texture::assignStoredMip(uint16 level, Size size, const Byte* bytes) { - storage::StoragePointer storage { new storage::MemoryStorage(size, bytes) }; + auto rawStoragePointer = new storage::MemoryStorage(size, bytes); + storage::StoragePointer storage = rawStoragePointer->shared_from_this(); assignStoredMip(level, storage); } void Texture::assignStoredMipFace(uint16 level, uint8 face, Size size, const Byte* bytes) { - storage::StoragePointer storage { new storage::MemoryStorage(size, bytes) }; + auto rawStoragePointer = new storage::MemoryStorage(size, bytes); + storage::StoragePointer storage = rawStoragePointer->shared_from_this(); assignStoredMipFace(level, face, storage); } @@ -973,4 +975,3 @@ Texture::ExternalUpdates Texture::getUpdates() const { void Texture::setStorage(std::unique_ptr& newStorage) { _storage.swap(newStorage); } - diff --git a/libraries/shared/src/shared/Storage.cpp b/libraries/shared/src/shared/Storage.cpp index 7075d9c6f7..f887ec1181 100644 --- a/libraries/shared/src/shared/Storage.cpp +++ b/libraries/shared/src/shared/Storage.cpp @@ -12,7 +12,10 @@ using namespace storage; -ViewStoragePointer Storage::createView(size_t viewSize, size_t offset) const { +ViewStorage::ViewStorage(const storage::StoragePointer& owner, size_t size, const uint8_t* data) + : _owner(owner), _size(size), _data(data) {} + +StoragePointer Storage::createView(size_t viewSize, size_t offset) const { auto selfSize = size(); if (0 == viewSize) { viewSize = selfSize; @@ -20,14 +23,16 @@ ViewStoragePointer Storage::createView(size_t viewSize, size_t offset) const { if ((viewSize + offset) > selfSize) { throw std::runtime_error("Invalid mapping range"); } - return ViewStoragePointer(new ViewStorage(shared_from_this(), viewSize, data() + offset)); + auto viewPointer = new ViewStorage(shared_from_this(), viewSize, data() + offset); + return viewPointer->shared_from_this(); } -MemoryStoragePointer Storage::toMemoryStorage() const { - return MemoryStoragePointer(new MemoryStorage(size(), data())); +StoragePointer Storage::toMemoryStorage() const { + auto rawPointer = new MemoryStorage(size(), data()); + return rawPointer->shared_from_this(); } -FileStoragePointer Storage::toFileStorage(const QString& filename) const { +StoragePointer Storage::toFileStorage(const QString& filename) const { return FileStorage::create(filename, size(), data()); } @@ -38,7 +43,7 @@ MemoryStorage::MemoryStorage(size_t size, const uint8_t* data) { } } -FileStoragePointer FileStorage::create(const QString& filename, size_t size, const uint8_t* data) { +StoragePointer FileStorage::create(const QString& filename, size_t size, const uint8_t* data) { QFile file(filename); if (!file.open(QFile::ReadWrite | QIODevice::Truncate)) { throw std::runtime_error("Unable to open file for writing"); diff --git a/libraries/shared/src/shared/Storage.h b/libraries/shared/src/shared/Storage.h index b79b9d6080..4b97e14178 100644 --- a/libraries/shared/src/shared/Storage.h +++ b/libraries/shared/src/shared/Storage.h @@ -19,12 +19,6 @@ namespace storage { class Storage; using StoragePointer = std::shared_ptr; - class MemoryStorage; - using MemoryStoragePointer = std::shared_ptr; - class FileStorage; - using FileStoragePointer = std::shared_ptr; - class ViewStorage; - using ViewStoragePointer = std::shared_ptr; class Storage : public std::enable_shared_from_this { public: @@ -32,9 +26,9 @@ namespace storage { virtual const uint8_t* data() const = 0; virtual size_t size() const = 0; - ViewStoragePointer createView(size_t size = 0, size_t offset = 0) const; - FileStoragePointer toFileStorage(const QString& filename) const; - MemoryStoragePointer toMemoryStorage() const; + StoragePointer createView(size_t size = 0, size_t offset = 0) const; + StoragePointer toFileStorage(const QString& filename) const; + StoragePointer toMemoryStorage() const; // Aliases to prevent having to re-write a ton of code inline size_t getSize() const { return size(); } @@ -53,7 +47,7 @@ namespace storage { class FileStorage : public Storage { public: - static FileStoragePointer create(const QString& filename, size_t size, const uint8_t* data); + static StoragePointer create(const QString& filename, size_t size, const uint8_t* data); FileStorage(const QString& filename); ~FileStorage(); // Prevent copying @@ -69,7 +63,7 @@ namespace storage { class ViewStorage : public Storage { public: - ViewStorage(const storage::StoragePointer& owner, size_t size, const uint8_t* data) : _owner(owner), _size(size), _data(data) {} + ViewStorage(const storage::StoragePointer& owner, size_t size, const uint8_t* data); const uint8_t* data() const override { return _data; } size_t size() const override { return _size; } private: From 399270d24387cc4a7b441b59538c761fa389e57c Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Tue, 7 Mar 2017 17:21:54 -0800 Subject: [PATCH 108/302] Still trying to fix OSX build --- libraries/gpu/src/gpu/Texture.cpp | 6 ++---- libraries/shared/src/shared/Storage.cpp | 7 ++----- 2 files changed, 4 insertions(+), 9 deletions(-) diff --git a/libraries/gpu/src/gpu/Texture.cpp b/libraries/gpu/src/gpu/Texture.cpp index 2be495dcdb..28373c4856 100755 --- a/libraries/gpu/src/gpu/Texture.cpp +++ b/libraries/gpu/src/gpu/Texture.cpp @@ -389,14 +389,12 @@ const Element& Texture::getStoredMipFormat() const { } void Texture::assignStoredMip(uint16 level, Size size, const Byte* bytes) { - auto rawStoragePointer = new storage::MemoryStorage(size, bytes); - storage::StoragePointer storage = rawStoragePointer->shared_from_this(); + storage::StoragePointer storage = std::make_shared(size, bytes); assignStoredMip(level, storage); } void Texture::assignStoredMipFace(uint16 level, uint8 face, Size size, const Byte* bytes) { - auto rawStoragePointer = new storage::MemoryStorage(size, bytes); - storage::StoragePointer storage = rawStoragePointer->shared_from_this(); + storage::StoragePointer storage = std::make_shared(size, bytes); assignStoredMipFace(level, face, storage); } diff --git a/libraries/shared/src/shared/Storage.cpp b/libraries/shared/src/shared/Storage.cpp index f887ec1181..6754854c40 100644 --- a/libraries/shared/src/shared/Storage.cpp +++ b/libraries/shared/src/shared/Storage.cpp @@ -23,13 +23,11 @@ StoragePointer Storage::createView(size_t viewSize, size_t offset) const { if ((viewSize + offset) > selfSize) { throw std::runtime_error("Invalid mapping range"); } - auto viewPointer = new ViewStorage(shared_from_this(), viewSize, data() + offset); - return viewPointer->shared_from_this(); + return std::make_shared(shared_from_this(), viewSize, data() + offset); } StoragePointer Storage::toMemoryStorage() const { - auto rawPointer = new MemoryStorage(size(), data()); - return rawPointer->shared_from_this(); + return std::make_shared(size(), data()); } StoragePointer Storage::toFileStorage(const QString& filename) const { @@ -62,7 +60,6 @@ StoragePointer FileStorage::create(const QString& filename, size_t size, const u } } file.close(); - //return FileStoragePointer(new FileStorage(filename)); return std::make_shared(filename); } From 9b8a5314390a4c0750478f70c6b0d59bd1c3ce69 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Tue, 7 Mar 2017 18:13:05 -0800 Subject: [PATCH 109/302] Fix iteration --- libraries/ktx/src/ktx/Reader.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/ktx/src/ktx/Reader.cpp b/libraries/ktx/src/ktx/Reader.cpp index 81f523cb69..5aa6919844 100644 --- a/libraries/ktx/src/ktx/Reader.cpp +++ b/libraries/ktx/src/ktx/Reader.cpp @@ -101,7 +101,7 @@ namespace ktx { // find the first null character \0 and extract the key uint32_t keyLength = 0; - while (reinterpret_cast(keyValueBytes[++keyLength]) != '\0') { + while (reinterpret_cast(keyValueBytes)[++keyLength] != '\0') { if (keyLength == keyAndValueByteSize) { // key must be null-terminated, and there must be space for the value throw ReaderException("invalid key-value " + std::string(reinterpret_cast(keyValueBytes), keyLength)); From 9d56001341a6fdcfd1e252448ba0a6f87fd2f64b Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Wed, 8 Mar 2017 12:11:25 -0800 Subject: [PATCH 110/302] Fix rebase --- .../model-networking/src/model-networking/TextureCache.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/libraries/model-networking/src/model-networking/TextureCache.cpp b/libraries/model-networking/src/model-networking/TextureCache.cpp index b1ed4fcc5d..c58c157da2 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.cpp +++ b/libraries/model-networking/src/model-networking/TextureCache.cpp @@ -497,7 +497,7 @@ void FileReader::read() { gpu::Texture::Usage usage; gpu::TextureUsageType usageType(gpu::TextureUsageType::RESOURCE); gpu::Sampler sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR); - texture.reset(gpu::Texture::unserialize(usage, usageType, ktx, sampler)); + texture.reset(gpu::Texture::unserialize(ktx, usageType, usage, sampler.getDesc())); texture->setKtxBacking(ktx); } @@ -556,7 +556,8 @@ void ImageReader::read() { auto url = _url.toString().toStdString(); PROFILE_RANGE_EX(resource_parse_image, __FUNCTION__, 0xffff0000, 0); - texture.reset(resource.staticCast()->getTextureLoader()(image, url)); + auto networkTexture = resource.staticCast(); + texture.reset(networkTexture->getTextureLoader()(image, url)); texture->setSource(url); if (texture) { texture->setFallbackTexture(networkTexture->getFallbackTexture()); From ffdaa090728332c4e9b4cd893ef80d0ea3c6d905 Mon Sep 17 00:00:00 2001 From: Zach Pomerantz Date: Wed, 8 Mar 2017 15:54:32 -0500 Subject: [PATCH 111/302] use stored Usage(Type), Sampler for KTX --- .../model-networking/src/model-networking/TextureCache.cpp | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/libraries/model-networking/src/model-networking/TextureCache.cpp b/libraries/model-networking/src/model-networking/TextureCache.cpp index c58c157da2..16d84b69df 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.cpp +++ b/libraries/model-networking/src/model-networking/TextureCache.cpp @@ -494,10 +494,7 @@ void FileReader::read() { } auto ktx = resource.staticCast()->_file->getKTX(); - gpu::Texture::Usage usage; - gpu::TextureUsageType usageType(gpu::TextureUsageType::RESOURCE); - gpu::Sampler sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR); - texture.reset(gpu::Texture::unserialize(ktx, usageType, usage, sampler.getDesc())); + texture.reset(gpu::Texture::unserialize(ktx)); texture->setKtxBacking(ktx); } From 8bf9b70915002ca49b8ad100164ddd108c70093f Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Wed, 8 Mar 2017 14:08:33 -0800 Subject: [PATCH 112/302] Revert "don't blend animations for otherAvatars" This reverts commit faa272c67c103e36b5324ecc8eb14bb18e773745. --- interface/src/avatar/CauterizedModel.cpp | 6 ++ interface/src/avatar/CauterizedModel.h | 1 + interface/src/avatar/SkeletonModel.cpp | 4 +- libraries/animation/src/Rig.cpp | 71 ++++++++++-------------- 4 files changed, 39 insertions(+), 43 deletions(-) diff --git a/interface/src/avatar/CauterizedModel.cpp b/interface/src/avatar/CauterizedModel.cpp index 0c3d863649..843779dd3b 100644 --- a/interface/src/avatar/CauterizedModel.cpp +++ b/interface/src/avatar/CauterizedModel.cpp @@ -95,6 +95,12 @@ void CauterizedModel::createCollisionRenderItemSet() { Model::createCollisionRenderItemSet(); } +// Called within Model::simulate call, below. +void CauterizedModel::updateRig(float deltaTime, glm::mat4 parentTransform) { + Model::updateRig(deltaTime, parentTransform); + _needsUpdateClusterMatrices = true; +} + void CauterizedModel::updateClusterMatrices() { PerformanceTimer perfTimer("CauterizedModel::updateClusterMatrices"); diff --git a/interface/src/avatar/CauterizedModel.h b/interface/src/avatar/CauterizedModel.h index ba12aee32b..01e0b13650 100644 --- a/interface/src/avatar/CauterizedModel.h +++ b/interface/src/avatar/CauterizedModel.h @@ -37,6 +37,7 @@ public: void createVisibleRenderItemSet() override; void createCollisionRenderItemSet() override; + virtual void updateRig(float deltaTime, glm::mat4 parentTransform) override; virtual void updateClusterMatrices() override; void updateRenderItems() override; diff --git a/interface/src/avatar/SkeletonModel.cpp b/interface/src/avatar/SkeletonModel.cpp index 88590a6f69..4b77323bba 100644 --- a/interface/src/avatar/SkeletonModel.cpp +++ b/interface/src/avatar/SkeletonModel.cpp @@ -166,7 +166,7 @@ void SkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { _rig->computeMotionAnimationState(deltaTime, position, velocity, orientation, ccState); // evaluate AnimGraph animation and update jointStates. - Model::updateRig(deltaTime, parentTransform); + CauterizedModel::updateRig(deltaTime, parentTransform); Rig::EyeParameters eyeParams; eyeParams.worldHeadOrientation = headParams.worldHeadOrientation; @@ -179,7 +179,7 @@ void SkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { _rig->updateFromEyeParameters(eyeParams); } else { - Model::updateRig(deltaTime, parentTransform); + CauterizedModel::updateRig(deltaTime, parentTransform); // This is a little more work than we really want. // diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index 84e34adec7..c47da7c0b0 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -1307,50 +1307,39 @@ void Rig::copyJointsIntoJointData(QVector& jointDataVec) const { void Rig::copyJointsFromJointData(const QVector& jointDataVec) { PerformanceTimer perfTimer("copyJoints"); PROFILE_RANGE(simulation_animation_detail, "copyJoints"); - if (!_animSkeleton) { - return; - } - if (jointDataVec.size() != (int)_internalPoseSet._relativePoses.size()) { - // animations haven't fully loaded yet. - _internalPoseSet._relativePoses = _animSkeleton->getRelativeDefaultPoses(); - } + if (_animSkeleton && jointDataVec.size() == (int)_internalPoseSet._relativePoses.size()) { + // make a vector of rotations in absolute-geometry-frame + const AnimPoseVec& absoluteDefaultPoses = _animSkeleton->getAbsoluteDefaultPoses(); + std::vector rotations; + rotations.reserve(absoluteDefaultPoses.size()); + const glm::quat rigToGeometryRot(glmExtractRotation(_rigToGeometryTransform)); + for (int i = 0; i < jointDataVec.size(); i++) { + const JointData& data = jointDataVec.at(i); + if (data.rotationSet) { + // JointData rotations are in absolute rig-frame so we rotate them to absolute geometry-frame + rotations.push_back(rigToGeometryRot * data.rotation); + } else { + rotations.push_back(absoluteDefaultPoses[i].rot()); + } + } - // make a vector of rotations in absolute-geometry-frame - const AnimPoseVec& absoluteDefaultPoses = _animSkeleton->getAbsoluteDefaultPoses(); - std::vector rotations; - rotations.reserve(absoluteDefaultPoses.size()); - const glm::quat rigToGeometryRot(glmExtractRotation(_rigToGeometryTransform)); - for (int i = 0; i < jointDataVec.size(); i++) { - const JointData& data = jointDataVec.at(i); - if (data.rotationSet) { - // JointData rotations are in absolute rig-frame so we rotate them to absolute geometry-frame - rotations.push_back(rigToGeometryRot * data.rotation); - } else { - rotations.push_back(absoluteDefaultPoses[i].rot()); + // convert rotations from absolute to parent relative. + _animSkeleton->convertAbsoluteRotationsToRelative(rotations); + + // store new relative poses + const AnimPoseVec& relativeDefaultPoses = _animSkeleton->getRelativeDefaultPoses(); + for (int i = 0; i < jointDataVec.size(); i++) { + const JointData& data = jointDataVec.at(i); + _internalPoseSet._relativePoses[i].scale() = Vectors::ONE; + _internalPoseSet._relativePoses[i].rot() = rotations[i]; + if (data.translationSet) { + // JointData translations are in scaled relative-frame so we scale back to regular relative-frame + _internalPoseSet._relativePoses[i].trans() = _invGeometryOffset.scale() * data.translation; + } else { + _internalPoseSet._relativePoses[i].trans() = relativeDefaultPoses[i].trans(); + } } } - - // convert rotations from absolute to parent relative. - _animSkeleton->convertAbsoluteRotationsToRelative(rotations); - - // store new relative poses - const AnimPoseVec& relativeDefaultPoses = _animSkeleton->getRelativeDefaultPoses(); - for (int i = 0; i < jointDataVec.size(); i++) { - const JointData& data = jointDataVec.at(i); - _internalPoseSet._relativePoses[i].scale() = Vectors::ONE; - _internalPoseSet._relativePoses[i].rot() = rotations[i]; - if (data.translationSet) { - // JointData translations are in scaled relative-frame so we scale back to regular relative-frame - _internalPoseSet._relativePoses[i].trans() = _invGeometryOffset.scale() * data.translation; - } else { - _internalPoseSet._relativePoses[i].trans() = relativeDefaultPoses[i].trans(); - } - } - - // build absolute poses and copy to externalPoseSet - buildAbsoluteRigPoses(_internalPoseSet._relativePoses, _internalPoseSet._absolutePoses); - QWriteLocker writeLock(&_externalPoseSetLock); - _externalPoseSet = _internalPoseSet; } void Rig::computeAvatarBoundingCapsule( From 9a9f07df7111322e21371ffa4938b4691b235c24 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Wed, 8 Mar 2017 14:08:48 -0800 Subject: [PATCH 113/302] Revert "expand avatar update time budget" This reverts commit 4bbbcb61be14c8233987941ad1fc89d4a6934e32. --- interface/src/avatar/AvatarManager.cpp | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index 7417f73102..6b426bcde8 100644 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -157,6 +157,7 @@ void AvatarManager::updateOtherAvatars(float deltaTime) { lock.unlock(); PerformanceTimer perfTimer("otherAvatars"); + uint64_t startTime = usecTimestampNow(); auto avatarMap = getHashCopy(); QList avatarList = avatarMap.values(); @@ -193,9 +194,10 @@ void AvatarManager::updateOtherAvatars(float deltaTime) { }); render::PendingChanges pendingChanges; - uint64_t startTime = usecTimestampNow(); - const uint64_t UPDATE_BUDGET = 2000; // usec - uint64_t updateExpiry = startTime + UPDATE_BUDGET; + const uint64_t RENDER_UPDATE_BUDGET = 1500; // usec + const uint64_t MAX_UPDATE_BUDGET = 2000; // usec + uint64_t renderExpiry = startTime + RENDER_UPDATE_BUDGET; + uint64_t maxExpiry = startTime + MAX_UPDATE_BUDGET; int numAvatarsUpdated = 0; int numAVatarsNotUpdated = 0; @@ -221,7 +223,7 @@ void AvatarManager::updateOtherAvatars(float deltaTime) { const float OUT_OF_VIEW_THRESHOLD = 0.5f * AvatarData::OUT_OF_VIEW_PENALTY; uint64_t now = usecTimestampNow(); - if (now < updateExpiry) { + if (now < renderExpiry) { // we're within budget bool inView = sortData.priority > OUT_OF_VIEW_THRESHOLD; if (inView && avatar->hasNewJointData()) { @@ -230,13 +232,21 @@ void AvatarManager::updateOtherAvatars(float deltaTime) { avatar->simulate(deltaTime, inView); avatar->updateRenderItem(pendingChanges); avatar->setLastRenderUpdateTime(startTime); + } else if (now < maxExpiry) { + // we've spent most of our time budget, but we still simulate() the avatar as it if were out of view + // --> some avatars may freeze until their priority trickles up + bool inView = sortData.priority > OUT_OF_VIEW_THRESHOLD; + if (inView && avatar->hasNewJointData()) { + numAVatarsNotUpdated++; + } + avatar->simulate(deltaTime, false); } else { - // we've spent our full time budget --> bail on the rest of the avatar updates + // we've spent ALL of our time budget --> bail on the rest of the avatar updates // --> more avatars may freeze until their priority trickles up // --> some scale or fade animations may glitch // --> some avatar velocity measurements may be a little off - // no time simulate, but we take the time to count how many were tragically missed + // HACK: no time simulate, but we will take the time to count how many were tragically missed bool inView = sortData.priority > OUT_OF_VIEW_THRESHOLD; if (!inView) { break; From 188287e345ff43dcc11c79bf20376fba775d5699 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Wed, 8 Mar 2017 14:08:52 -0800 Subject: [PATCH 114/302] Revert "minor cleanup" This reverts commit 14e7392a89e3291891bb00bdb44349995f0aeac1. --- interface/src/avatar/Avatar.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index 6e1f44f5ac..f5fe82ef4b 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -351,6 +351,7 @@ void Avatar::simulate(float deltaTime, bool inView) { _jointDataSimulationRate.increment(); _skeletonModel->simulate(deltaTime, true); + _skeletonModelSimulationRate.increment(); locationChanged(); // joints changed, so if there are any children, update them. _hasNewJointData = false; @@ -366,8 +367,8 @@ void Avatar::simulate(float deltaTime, bool inView) { } else { // a non-full update is still required so that the position, rotation, scale and bounds of the skeletonModel are updated. _skeletonModel->simulate(deltaTime, false); + _skeletonModelSimulationRate.increment(); } - _skeletonModelSimulationRate.increment(); } // update animation for display name fade in/out From a68674adccedab1004e1dbc0ec9e811eb5f2d4b4 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Wed, 8 Mar 2017 14:08:54 -0800 Subject: [PATCH 115/302] Revert "remove unnecessary context brackets" This reverts commit 0657ca1e5e4096daa30e28791e10f5aa05ca48c8. --- libraries/avatars/src/AvatarData.cpp | 78 ++++++++++++++-------------- 1 file changed, 40 insertions(+), 38 deletions(-) diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index 8025c680ca..6e4553ff8a 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -2332,48 +2332,50 @@ void AvatarData::sortAvatars( std::function getBoundingRadius, std::function shouldIgnore) { - PROFILE_RANGE(simulation, "sort"); - uint64_t now = usecTimestampNow(); + { + PROFILE_RANGE(simulation, "sort"); + uint64_t now = usecTimestampNow(); - glm::vec3 frustumCenter = cameraView.getPosition(); - const glm::vec3& forward = cameraView.getDirection(); - for (int32_t i = 0; i < avatarList.size(); ++i) { - const auto& avatar = avatarList.at(i); + glm::vec3 frustumCenter = cameraView.getPosition(); + const glm::vec3& forward = cameraView.getDirection(); + for (int32_t i = 0; i < avatarList.size(); ++i) { + const auto& avatar = avatarList.at(i); - if (shouldIgnore(avatar)) { - continue; - } - - // priority = weighted linear combination of: - // (a) apparentSize - // (b) proximity to center of view - // (c) time since last update - glm::vec3 avatarPosition = avatar->getPosition(); - glm::vec3 offset = avatarPosition - frustumCenter; - float distance = glm::length(offset) + 0.001f; // add 1mm to avoid divide by zero - - // FIXME - AvatarData has something equivolent to this - float radius = getBoundingRadius(avatar); - - float apparentSize = 2.0f * radius / distance; - float cosineAngle = glm::dot(offset, forward) / distance; - float age = (float)(now - getLastUpdated(avatar)) / (float)(USECS_PER_SECOND); - - // NOTE: we are adding values of different units to get a single measure of "priority". - // Thus we multiply each component by a conversion "weight" that scales its units relative to the others. - // These weights are pure magic tuning and should be hard coded in the relation below, - // but are currently exposed for anyone who would like to explore fine tuning: - float priority = _avatarSortCoefficientSize * apparentSize - + _avatarSortCoefficientCenter * cosineAngle - + _avatarSortCoefficientAge * age; - - // decrement priority of avatars outside keyhole - if (distance > cameraView.getCenterRadius()) { - if (!cameraView.sphereIntersectsFrustum(avatarPosition, radius)) { - priority += OUT_OF_VIEW_PENALTY; + if (shouldIgnore(avatar)) { + continue; } + + // priority = weighted linear combination of: + // (a) apparentSize + // (b) proximity to center of view + // (c) time since last update + glm::vec3 avatarPosition = avatar->getPosition(); + glm::vec3 offset = avatarPosition - frustumCenter; + float distance = glm::length(offset) + 0.001f; // add 1mm to avoid divide by zero + + // FIXME - AvatarData has something equivolent to this + float radius = getBoundingRadius(avatar); + + float apparentSize = 2.0f * radius / distance; + float cosineAngle = glm::dot(offset, forward) / distance; + float age = (float)(now - getLastUpdated(avatar)) / (float)(USECS_PER_SECOND); + + // NOTE: we are adding values of different units to get a single measure of "priority". + // Thus we multiply each component by a conversion "weight" that scales its units relative to the others. + // These weights are pure magic tuning and should be hard coded in the relation below, + // but are currently exposed for anyone who would like to explore fine tuning: + float priority = _avatarSortCoefficientSize * apparentSize + + _avatarSortCoefficientCenter * cosineAngle + + _avatarSortCoefficientAge * age; + + // decrement priority of avatars outside keyhole + if (distance > cameraView.getCenterRadius()) { + if (!cameraView.sphereIntersectsFrustum(avatarPosition, radius)) { + priority += OUT_OF_VIEW_PENALTY; + } + } + sortedAvatarsOut.push(AvatarPriority(avatar, priority)); } - sortedAvatarsOut.push(AvatarPriority(avatar, priority)); } } From 50288bd4ba3591aa0cffadfc994606143f349ea5 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Wed, 8 Mar 2017 14:08:56 -0800 Subject: [PATCH 116/302] Revert "remove unused variable" This reverts commit 92a32b46512094c1acd82fcd6c65c1d6ee49713b. --- assignment-client/src/avatars/AvatarMixerSlave.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/assignment-client/src/avatars/AvatarMixerSlave.cpp b/assignment-client/src/avatars/AvatarMixerSlave.cpp index 49b4b1ced4..584d3d0e6a 100644 --- a/assignment-client/src/avatars/AvatarMixerSlave.cpp +++ b/assignment-client/src/avatars/AvatarMixerSlave.cpp @@ -168,6 +168,7 @@ void AvatarMixerSlave::broadcastAvatarData(const SharedNodePointer& node) { QList avatarList; std::unordered_map avatarDataToNodes; + int listItem = 0; std::for_each(_begin, _end, [&](const SharedNodePointer& otherNode) { const AvatarMixerClientData* otherNodeData = reinterpret_cast(otherNode->getLinkedData()); @@ -175,6 +176,7 @@ void AvatarMixerSlave::broadcastAvatarData(const SharedNodePointer& node) { // but not have yet sent data that's linked to the node. Check for that case and don't // consider those nodes. if (otherNodeData) { + listItem++; AvatarSharedPointer otherAvatar = otherNodeData->getAvatarSharedPointer(); avatarList << otherAvatar; avatarDataToNodes[otherAvatar] = otherNode; From 97c376a09c99f567b0fa90abd490eb542cd93c42 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Wed, 8 Mar 2017 14:08:58 -0800 Subject: [PATCH 117/302] Revert "avoid unnecessary copy of priority_queue" This reverts commit e54812430e871190fca541e2d3d744aee5e997d3. --- .../src/avatars/AvatarMixerSlave.cpp | 4 +-- interface/src/avatar/AvatarManager.cpp | 4 +-- libraries/avatars/src/AvatarData.cpp | 28 ++++++++++--------- libraries/avatars/src/AvatarData.h | 3 +- 4 files changed, 20 insertions(+), 19 deletions(-) diff --git a/assignment-client/src/avatars/AvatarMixerSlave.cpp b/assignment-client/src/avatars/AvatarMixerSlave.cpp index 584d3d0e6a..dd25aa4c4b 100644 --- a/assignment-client/src/avatars/AvatarMixerSlave.cpp +++ b/assignment-client/src/avatars/AvatarMixerSlave.cpp @@ -185,8 +185,8 @@ void AvatarMixerSlave::broadcastAvatarData(const SharedNodePointer& node) { AvatarSharedPointer thisAvatar = nodeData->getAvatarSharedPointer(); ViewFrustum cameraView = nodeData->getViewFrustom(); - std::priority_queue sortedAvatars; - AvatarData::sortAvatars(avatarList, cameraView, sortedAvatars, + std::priority_queue sortedAvatars = AvatarData::sortAvatars( + avatarList, cameraView, [&](AvatarSharedPointer avatar)->uint64_t{ auto avatarNode = avatarDataToNodes[avatar]; diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index 6b426bcde8..d806c042b9 100644 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -164,8 +164,8 @@ void AvatarManager::updateOtherAvatars(float deltaTime) { ViewFrustum cameraView; qApp->copyDisplayViewFrustum(cameraView); - std::priority_queue sortedAvatars; - AvatarData::sortAvatars(avatarList, cameraView, sortedAvatars, + std::priority_queue sortedAvatars = AvatarData::sortAvatars( + avatarList, cameraView, [](AvatarSharedPointer avatar)->uint64_t{ return std::static_pointer_cast(avatar)->getLastRenderUpdateTime(); diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index 6e4553ff8a..e7ec201aa1 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -2324,20 +2324,20 @@ float AvatarData::_avatarSortCoefficientSize { 0.5f }; float AvatarData::_avatarSortCoefficientCenter { 0.25 }; float AvatarData::_avatarSortCoefficientAge { 1.0f }; -void AvatarData::sortAvatars( - QList avatarList, - const ViewFrustum& cameraView, - std::priority_queue& sortedAvatarsOut, - std::function getLastUpdated, - std::function getBoundingRadius, - std::function shouldIgnore) { +std::priority_queue AvatarData::sortAvatars( + QList avatarList, + const ViewFrustum& cameraView, + std::function getLastUpdated, + std::function getBoundingRadius, + std::function shouldIgnore) { + uint64_t startTime = usecTimestampNow(); + + glm::vec3 frustumCenter = cameraView.getPosition(); + + std::priority_queue sortedAvatars; { PROFILE_RANGE(simulation, "sort"); - uint64_t now = usecTimestampNow(); - - glm::vec3 frustumCenter = cameraView.getPosition(); - const glm::vec3& forward = cameraView.getDirection(); for (int32_t i = 0; i < avatarList.size(); ++i) { const auto& avatar = avatarList.at(i); @@ -2356,9 +2356,10 @@ void AvatarData::sortAvatars( // FIXME - AvatarData has something equivolent to this float radius = getBoundingRadius(avatar); + const glm::vec3& forward = cameraView.getDirection(); float apparentSize = 2.0f * radius / distance; float cosineAngle = glm::dot(offset, forward) / distance; - float age = (float)(now - getLastUpdated(avatar)) / (float)(USECS_PER_SECOND); + float age = (float)(startTime - getLastUpdated(avatar)) / (float)(USECS_PER_SECOND); // NOTE: we are adding values of different units to get a single measure of "priority". // Thus we multiply each component by a conversion "weight" that scales its units relative to the others. @@ -2374,9 +2375,10 @@ void AvatarData::sortAvatars( priority += OUT_OF_VIEW_PENALTY; } } - sortedAvatarsOut.push(AvatarPriority(avatar, priority)); + sortedAvatars.push(AvatarPriority(avatar, priority)); } } + return sortedAvatars; } QScriptValue AvatarEntityMapToScriptValue(QScriptEngine* engine, const AvatarEntityMap& value) { diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index c2240f400f..12209d9c31 100644 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -597,10 +597,9 @@ public: static const float OUT_OF_VIEW_PENALTY; - static void sortAvatars( + static std::priority_queue sortAvatars( QList avatarList, const ViewFrustum& cameraView, - std::priority_queue& sortedAvatarsOut, std::function getLastUpdated, std::function getBoundingRadius, std::function shouldIgnore); From a0befa7f958543445ffbc9d4a7975e92752cb127 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Wed, 8 Mar 2017 14:28:44 -0800 Subject: [PATCH 118/302] Trivial PR to trigger build --- interface/resources/qml/controls/README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/interface/resources/qml/controls/README.md b/interface/resources/qml/controls/README.md index 7f05f32a63..1245e42e97 100644 --- a/interface/resources/qml/controls/README.md +++ b/interface/resources/qml/controls/README.md @@ -1,2 +1,3 @@ These are our own custom controls with the same names as existing controls, but customized for readability / usability in VR. + From 8a438948b977bdca4076054708aadb3cb5f7a2cc Mon Sep 17 00:00:00 2001 From: Bradley Austin Davis Date: Thu, 9 Mar 2017 10:42:56 -0800 Subject: [PATCH 119/302] Fix OSX link failure --- libraries/networking/src/FileCache.cpp | 7 +++++++ libraries/networking/src/FileCache.h | 8 +++----- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/libraries/networking/src/FileCache.cpp b/libraries/networking/src/FileCache.cpp index c94d0e1b8c..88f2c048e5 100644 --- a/libraries/networking/src/FileCache.cpp +++ b/libraries/networking/src/FileCache.cpp @@ -19,6 +19,7 @@ #include #include +#include Q_LOGGING_CATEGORY(file_cache, "hifi.file_cache", QtWarningMsg) @@ -26,6 +27,12 @@ using namespace cache; static const std::string MANIFEST_NAME = "manifest"; +static const size_t BYTES_PER_MEGABYTES = 1024 * 1024; +static const size_t BYTES_PER_GIGABYTES = 1024 * BYTES_PER_MEGABYTES; +const size_t FileCache::DEFAULT_UNUSED_MAX_SIZE = 5 * BYTES_PER_GIGABYTES; // 5GB +const size_t FileCache::MAX_UNUSED_MAX_SIZE = 100 * BYTES_PER_GIGABYTES; // 100GB +const size_t FileCache::DEFAULT_OFFLINE_MAX_SIZE = 2 * BYTES_PER_GIGABYTES; // 2GB + void FileCache::setUnusedFileCacheSize(size_t unusedFilesMaxSize) { _unusedFilesMaxSize = std::min(unusedFilesMaxSize, MAX_UNUSED_MAX_SIZE); reserve(0); diff --git a/libraries/networking/src/FileCache.h b/libraries/networking/src/FileCache.h index b19f2d10cd..945d43b224 100644 --- a/libraries/networking/src/FileCache.h +++ b/libraries/networking/src/FileCache.h @@ -35,11 +35,9 @@ class FileCache : public QObject { Q_PROPERTY(size_t sizeTotal READ getSizeTotalFiles NOTIFY dirty) Q_PROPERTY(size_t sizeCached READ getSizeCachedFiles NOTIFY dirty) - static const size_t BYTES_PER_MEGABYTES = 1024 * 1024; - static const size_t BYTES_PER_GIGABYTES = 1024 * BYTES_PER_MEGABYTES; - static const size_t DEFAULT_UNUSED_MAX_SIZE = 5 * BYTES_PER_GIGABYTES; // 5GB - static const size_t MAX_UNUSED_MAX_SIZE = 100 * BYTES_PER_GIGABYTES; // 100GB - static const size_t DEFAULT_OFFLINE_MAX_SIZE = 2 * BYTES_PER_GIGABYTES; // 2GB + static const size_t DEFAULT_UNUSED_MAX_SIZE; + static const size_t MAX_UNUSED_MAX_SIZE; + static const size_t DEFAULT_OFFLINE_MAX_SIZE; public: size_t getNumTotalFiles() const { return _numTotalFiles; } From d4abdcb6c8e1df93e9286a7a62458feb2bf31ebc Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Mon, 6 Mar 2017 14:09:10 -0800 Subject: [PATCH 120/302] comments, namechange, and temporary debug code --- interface/src/Application.cpp | 4 +- interface/src/avatar/AvatarManager.cpp | 2 +- interface/src/avatar/AvatarManager.h | 2 +- libraries/entities/src/EntityItem.cpp | 21 ++++-- libraries/physics/src/EntityMotionState.cpp | 67 +++++++++++++++++-- .../physics/src/PhysicalEntitySimulation.cpp | 5 +- .../physics/src/PhysicalEntitySimulation.h | 4 +- .../physics/src/ThreadSafeDynamicsWorld.cpp | 3 + 8 files changed, 92 insertions(+), 16 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index f870bd9f83..4894ae55ec 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -4440,8 +4440,8 @@ void Application::update(float deltaTime) { getEntities()->getTree()->withWriteLock([&] { PerformanceTimer perfTimer("handleOutgoingChanges"); const VectorOfMotionStates& outgoingChanges = _physicsEngine->getOutgoingChanges(); - _entitySimulation->handleOutgoingChanges(outgoingChanges); - avatarManager->handleOutgoingChanges(outgoingChanges); + _entitySimulation->handleChangedMotionStates(outgoingChanges); + avatarManager->handleChangedMotionStates(outgoingChanges); }); if (!_aboutToQuit) { diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index 94ce444416..6152148887 100644 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -424,7 +424,7 @@ void AvatarManager::getObjectsToChange(VectorOfMotionStates& result) { } } -void AvatarManager::handleOutgoingChanges(const VectorOfMotionStates& motionStates) { +void AvatarManager::handleChangedMotionStates(const VectorOfMotionStates& motionStates) { // TODO: extract the MyAvatar results once we use a MotionState for it. } diff --git a/interface/src/avatar/AvatarManager.h b/interface/src/avatar/AvatarManager.h index e1f5a3b411..b94f9e6a96 100644 --- a/interface/src/avatar/AvatarManager.h +++ b/interface/src/avatar/AvatarManager.h @@ -70,7 +70,7 @@ public: void getObjectsToRemoveFromPhysics(VectorOfMotionStates& motionStates); void getObjectsToAddToPhysics(VectorOfMotionStates& motionStates); void getObjectsToChange(VectorOfMotionStates& motionStates); - void handleOutgoingChanges(const VectorOfMotionStates& motionStates); + void handleChangedMotionStates(const VectorOfMotionStates& motionStates); void handleCollisionEvents(const CollisionEvents& collisionEvents); Q_INVOKABLE float getAvatarDataRate(const QUuid& sessionID, const QString& rateName = QString("")) const; diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index 3ef1648fae..db253b639f 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -655,13 +655,11 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef // pack SimulationOwner and terse update properties near each other - // NOTE: the server is authoritative for changes to simOwnerID so we always unpack ownership data // even when we would otherwise ignore the rest of the packet. bool filterRejection = false; if (propertyFlags.getHasProperty(PROP_SIMULATION_OWNER)) { - QByteArray simOwnerData; int bytes = OctreePacketData::unpackDataFromBytes(dataAt, simOwnerData); SimulationOwner newSimOwner; @@ -676,6 +674,13 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef // or rejects a set of properties, it clears this. In such cases, we don't want those custom // setters to ignore what the server says. filterRejection = newSimOwner.getID().isNull(); + bool verbose = getName() == "fubar"; // adebug + if (verbose && _simulationOwner != newSimOwner) { + std::cout << (void*)(this) << " adebug ownership changed " + << _simulationOwner.getID().toString().toStdString() << "." << (int)_simulationOwner.getPriority() << "-->" + << newSimOwner.getID().toString().toStdString() << "." << (int)newSimOwner.getPriority() + << std::endl; // adebug + } if (weOwnSimulation) { if (newSimOwner.getID().isNull() && !_simulationOwner.pendingRelease(lastEditedFromBufferAdjusted)) { // entity-server is trying to clear our ownership (probably at our own request) @@ -1879,12 +1884,16 @@ void EntityItem::setSimulationOwner(const SimulationOwner& owner) { } void EntityItem::updateSimulationOwner(const SimulationOwner& owner) { + // NOTE: this method only used by EntityServer. The Interface uses special code in readEntityDataFromBuffer(). if (wantTerseEditLogging() && _simulationOwner != owner) { qCDebug(entities) << "sim ownership for" << getDebugName() << "is now" << owner; } if (_simulationOwner.set(owner)) { _dirtyFlags |= Simulation::DIRTY_SIMULATOR_ID; + if (getName() == "fubar") { + std::cout << "debug updateSimulationOwner() " << _simulationOwner.getID().toString().toStdString() << "." << (int)(_simulationOwner.getPriority()) << std::endl; // adebug + } } } @@ -1892,10 +1901,14 @@ void EntityItem::clearSimulationOwnership() { if (wantTerseEditLogging() && !_simulationOwner.isNull()) { qCDebug(entities) << "sim ownership for" << getDebugName() << "is now null"; } + if (getName() == "fubar") { + std::cout << "debug clearSimulationOwnership()" << std::endl; // adebug + } _simulationOwner.clear(); - // don't bother setting the DIRTY_SIMULATOR_ID flag because clearSimulationOwnership() - // is only ever called on the entity-server and the flags are only used client-side + // don't bother setting the DIRTY_SIMULATOR_ID flag because: + // (a) when entity-server calls clearSimulationOwnership() the dirty-flags are meaningless (only used by interface) + // (b) the interface only calls clearSimulationOwnership() in a context that already knows best about dirty flags //_dirtyFlags |= Simulation::DIRTY_SIMULATOR_ID; } diff --git a/libraries/physics/src/EntityMotionState.cpp b/libraries/physics/src/EntityMotionState.cpp index c175a836cc..77c5a4f697 100644 --- a/libraries/physics/src/EntityMotionState.cpp +++ b/libraries/physics/src/EntityMotionState.cpp @@ -27,6 +27,18 @@ #include "EntityTree.h" #endif +// adebug TODO BOOKMARK: +// The problem is that userB may deactivate and disown and object before userA deactivates +// userA will sometimes insert non-zero velocities (and position errors) into the Entity before it is deactivated locally +// +// It would be nice to prevent data export from Bullet to Entity for unowned objects except in cases where it is really needed (?) +// Maybe can recycle _serverPosition and friends to store the "before simulationStep" data to more efficiently figure out +// if data should be used for non-owned objects. +// +// If we do that, we should convert _serverPosition and friends to use Bullet data types for efficiency. +// +// adebug + const uint8_t LOOPS_FOR_SIMULATION_ORPHAN = 50; const quint64 USECS_BETWEEN_OWNERSHIP_BIDS = USECS_PER_SECOND / 5; @@ -111,6 +123,10 @@ void EntityMotionState::handleEasyChanges(uint32_t& flags) { flags &= ~Simulation::DIRTY_PHYSICS_ACTIVATION; _body->setActivationState(WANTS_DEACTIVATION); _outgoingPriority = 0; + bool verbose = _entity->getName() == "fubar"; // adebug + if (verbose) { + std::cout << (void*)(this) << " adebug flag for deactivation" << std::endl; // adebug + } } else { // disowned object is still moving --> start timer for ownership bid // TODO? put a delay in here proportional to distance from object? @@ -118,6 +134,13 @@ void EntityMotionState::handleEasyChanges(uint32_t& flags) { _nextOwnershipBid = usecTimestampNow() + USECS_BETWEEN_OWNERSHIP_BIDS; } _loopsWithoutOwner = 0; + + // adebug BOOKMARK: the problem is that userB may deactivate and disown the Object + // but it may still be active for userA... who will store slightly non-zero velocities into EntityItem in the meantime + // + // It would be nice if we could ignore slight outgoing changes for unowned objects that WANT_DEACTIVATION until... + // (a) the changes exceed some threshold (--> bid for ownership) or... + // (b) they actually get deactivated (--> slam RigidBody positions to agree with EntityItem) } else if (_entity->getSimulatorID() == Physics::getSessionUUID()) { // we just inherited ownership, make sure our desired priority matches what we have upgradeOutgoingPriority(_entity->getSimulationPriority()); @@ -223,11 +246,19 @@ void EntityMotionState::getWorldTransform(btTransform& worldTrans) const { // This callback is invoked by the physics simulation at the end of each simulation step... // iff the corresponding RigidBody is DYNAMIC and has moved. void EntityMotionState::setWorldTransform(const btTransform& worldTrans) { - if (!_entity) { - return; - } - + assert(_entity); assert(entityTreeIsLocked()); + bool verbose = _entity->getName() == "fubar"; // adebug + // adebug BOOKMARK: the problem is that userB may deactivate and disown the Object + // but it may still be active for userA... who will store slightly non-zero velocities into EntityItem in the meantime + // so what we need to do is ignore setWorldTransform() events for unowned objects when the bullet data is not helpful + // until either the data passes some threshold (bid for it) or + // it goes inactive (at which point we should slam bullet to agree with entity) + if (_body->getActivationState() == WANTS_DEACTIVATION && !_entity->isMoving()) { + if (verbose) { + std::cout << (void*)(this) << " adebug v = " << _body->getLinearVelocity().length() << " w = " << _body->getAngularVelocity().length() << std::endl; // adebug + } + } measureBodyAcceleration(); bool positionSuccess; _entity->setPosition(bulletToGLM(worldTrans.getOrigin()) + ObjectMotionState::getWorldOffset(), positionSuccess, false); @@ -245,6 +276,12 @@ void EntityMotionState::setWorldTransform(const btTransform& worldTrans) { "setOrientation failed.*"); qCDebug(physics) << "EntityMotionState::setWorldTransform setOrientation failed" << _entity->getID(); } + if (verbose + && (glm::length(getBodyLinearVelocity()) > 0.0f || glm::length(getBodyAngularVelocity()) > 0.0f) + && _entity->getSimulationOwner().getID().isNull()) { + std::cout << (void*)(this) << " adebug set non-zero v on unowned object AS = " << _body->getActivationState() << std::endl; // adebug + + } _entity->setVelocity(getBodyLinearVelocity()); _entity->setAngularVelocity(getBodyAngularVelocity()); _entity->setLastSimulated(usecTimestampNow()); @@ -293,6 +330,18 @@ bool EntityMotionState::isCandidateForOwnership() const { assert(_body); assert(_entity); assert(entityTreeIsLocked()); + + /* adebug + bool verbose = _entity->getName() == "fubar"; // adebug + if (verbose) { + bool isCandidate = _outgoingPriority != 0 + || Physics::getSessionUUID() == _entity->getSimulatorID() + || _entity->actionDataNeedsTransmit(); + if (!isCandidate) { + std::cout << (void*)(this) << " adebug not candidate --> erase" << std::endl; // adebug + } + } + */ return _outgoingPriority != 0 || Physics::getSessionUUID() == _entity->getSimulatorID() || _entity->actionDataNeedsTransmit(); @@ -491,6 +540,7 @@ bool EntityMotionState::shouldSendUpdate(uint32_t simulationStep) { void EntityMotionState::sendUpdate(OctreeEditPacketSender* packetSender, uint32_t step) { assert(_entity); assert(entityTreeIsLocked()); + bool verbose = _entity->getName() == "fubar"; // adebug if (!_body->isActive()) { // make sure all derivatives are zero @@ -576,6 +626,9 @@ void EntityMotionState::sendUpdate(OctreeEditPacketSender* packetSender, uint32_ properties.clearSimulationOwner(); _outgoingPriority = 0; _entity->setPendingOwnershipPriority(_outgoingPriority, now); + if (verbose) { + std::cout << (void*)(this) << " adebug sendUpdate() clearOwnership numInactiveUpdates = " << (int)_numInactiveUpdates << std::endl; // adebug + } } else if (Physics::getSessionUUID() != _entity->getSimulatorID()) { // we don't own the simulation for this entity yet, but we're sending a bid for it quint8 bidPriority = glm::max(_outgoingPriority, VOLUNTEER_SIMULATION_PRIORITY); @@ -586,6 +639,9 @@ void EntityMotionState::sendUpdate(OctreeEditPacketSender* packetSender, uint32_ // don't forget to remember that we have made a bid _entity->rememberHasSimulationOwnershipBid(); // ...then reset _outgoingPriority in preparation for the next frame + if (verbose) { + std::cout << (void*)(this) << " adebug sendUpdate() bidOwnership at " << (int)_outgoingPriority << std::endl; // adebug + } _outgoingPriority = 0; } else if (_outgoingPriority != _entity->getSimulationPriority()) { // we own the simulation but our desired priority has changed @@ -597,6 +653,9 @@ void EntityMotionState::sendUpdate(OctreeEditPacketSender* packetSender, uint32_ properties.setSimulationOwner(Physics::getSessionUUID(), _outgoingPriority); } _entity->setPendingOwnershipPriority(_outgoingPriority, now); + if (verbose) { + std::cout << (void*)(this) << " adebug sendUpdate() changePriority to " << (int)_outgoingPriority << std::endl; // adebug + } } EntityItemID id(_entity->getID()); diff --git a/libraries/physics/src/PhysicalEntitySimulation.cpp b/libraries/physics/src/PhysicalEntitySimulation.cpp index 903b160a5e..0b0bbcb6fc 100644 --- a/libraries/physics/src/PhysicalEntitySimulation.cpp +++ b/libraries/physics/src/PhysicalEntitySimulation.cpp @@ -259,13 +259,14 @@ void PhysicalEntitySimulation::getObjectsToChange(VectorOfMotionStates& result) _pendingChanges.clear(); } -void PhysicalEntitySimulation::handleOutgoingChanges(const VectorOfMotionStates& motionStates) { +void PhysicalEntitySimulation::handleChangedMotionStates(const VectorOfMotionStates& motionStates) { QMutexLocker lock(&_mutex); // walk the motionStates looking for those that correspond to entities for (auto stateItr : motionStates) { ObjectMotionState* state = &(*stateItr); - if (state && state->getType() == MOTIONSTATE_TYPE_ENTITY) { + assert(state); + if (state->getType() == MOTIONSTATE_TYPE_ENTITY) { EntityMotionState* entityState = static_cast(state); EntityItemPointer entity = entityState->getEntity(); assert(entity.get()); diff --git a/libraries/physics/src/PhysicalEntitySimulation.h b/libraries/physics/src/PhysicalEntitySimulation.h index af5def9775..9035308741 100644 --- a/libraries/physics/src/PhysicalEntitySimulation.h +++ b/libraries/physics/src/PhysicalEntitySimulation.h @@ -56,7 +56,7 @@ public: void setObjectsToChange(const VectorOfMotionStates& objectsToChange); void getObjectsToChange(VectorOfMotionStates& result); - void handleOutgoingChanges(const VectorOfMotionStates& motionStates); + void handleChangedMotionStates(const VectorOfMotionStates& motionStates); void handleCollisionEvents(const CollisionEvents& collisionEvents); EntityEditPacketSender* getPacketSender() { return _entityPacketSender; } @@ -67,7 +67,7 @@ private: SetOfEntities _entitiesToAddToPhysics; SetOfEntityMotionStates _pendingChanges; // EntityMotionStates already in PhysicsEngine that need their physics changed - SetOfEntityMotionStates _outgoingChanges; // EntityMotionStates for which we need to send updates to entity-server + SetOfEntityMotionStates _outgoingChanges; // EntityMotionStates for which we may need to send updates to entity-server SetOfMotionStates _physicalObjects; // MotionStates of entities in PhysicsEngine diff --git a/libraries/physics/src/ThreadSafeDynamicsWorld.cpp b/libraries/physics/src/ThreadSafeDynamicsWorld.cpp index 5fe99f137c..c9cbc6a2be 100644 --- a/libraries/physics/src/ThreadSafeDynamicsWorld.cpp +++ b/libraries/physics/src/ThreadSafeDynamicsWorld.cpp @@ -120,6 +120,9 @@ void ThreadSafeDynamicsWorld::synchronizeMotionState(btRigidBody* body) { void ThreadSafeDynamicsWorld::synchronizeMotionStates() { BT_PROFILE("synchronizeMotionStates"); _changedMotionStates.clear(); + + // NOTE: m_synchronizeAllMotionStates is 'false' by default for optimization. + // See PhysicsEngine::init() where we call _dynamicsWorld->setForceUpdateAllAabbs(false) if (m_synchronizeAllMotionStates) { //iterate over all collision objects for (int i=0;i Date: Thu, 9 Mar 2017 17:25:22 -0800 Subject: [PATCH 121/302] restore transform of deactivated entities --- interface/src/Application.cpp | 5 ++- libraries/entities/src/EntityItem.cpp | 2 +- libraries/physics/src/EntityMotionState.cpp | 39 +++++++++++++++---- libraries/physics/src/EntityMotionState.h | 1 + .../physics/src/PhysicalEntitySimulation.cpp | 13 +++++++ .../physics/src/PhysicalEntitySimulation.h | 1 + libraries/physics/src/PhysicsEngine.cpp | 2 +- libraries/physics/src/PhysicsEngine.h | 3 +- .../physics/src/ThreadSafeDynamicsWorld.cpp | 27 +++++++++---- .../physics/src/ThreadSafeDynamicsWorld.h | 4 ++ 10 files changed, 77 insertions(+), 20 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 4894ae55ec..368541490a 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -4439,7 +4439,10 @@ void Application::update(float deltaTime) { getEntities()->getTree()->withWriteLock([&] { PerformanceTimer perfTimer("handleOutgoingChanges"); - const VectorOfMotionStates& outgoingChanges = _physicsEngine->getOutgoingChanges(); + const VectorOfMotionStates& deactivations = _physicsEngine->getDeactivatedMotionStates(); + _entitySimulation->handleDeactivatedMotionStates(deactivations); + + const VectorOfMotionStates& outgoingChanges = _physicsEngine->getChangedMotionStates(); _entitySimulation->handleChangedMotionStates(outgoingChanges); avatarManager->handleChangedMotionStates(outgoingChanges); }); diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index db253b639f..888de44505 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -676,7 +676,7 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef filterRejection = newSimOwner.getID().isNull(); bool verbose = getName() == "fubar"; // adebug if (verbose && _simulationOwner != newSimOwner) { - std::cout << (void*)(this) << " adebug ownership changed " + std::cout << (void*)(this) << " " << secTimestampNow() << " adebug ownership changed " << _simulationOwner.getID().toString().toStdString() << "." << (int)_simulationOwner.getPriority() << "-->" << newSimOwner.getID().toString().toStdString() << "." << (int)newSimOwner.getPriority() << std::endl; // adebug diff --git a/libraries/physics/src/EntityMotionState.cpp b/libraries/physics/src/EntityMotionState.cpp index 77c5a4f697..5da9d52169 100644 --- a/libraries/physics/src/EntityMotionState.cpp +++ b/libraries/physics/src/EntityMotionState.cpp @@ -10,6 +10,7 @@ // #include +#include // adebug #include #include @@ -28,7 +29,8 @@ #endif // adebug TODO BOOKMARK: -// The problem is that userB may deactivate and disown and object before userA deactivates +// Consider an object near deactivation owned by userB and in view of userA... +// The problem is that userB may deactivate and disown the object before userA deactivates // userA will sometimes insert non-zero velocities (and position errors) into the Entity before it is deactivated locally // // It would be nice to prevent data export from Bullet to Entity for unowned objects except in cases where it is really needed (?) @@ -109,6 +111,24 @@ void EntityMotionState::updateServerPhysicsVariables() { _serverActionData = _entity->getActionData(); } +void EntityMotionState::handleDeactivation() { + // adebug + glm::vec3 pos = _entity->getPosition(); + float dx = glm::distance(pos, _serverPosition); + glm::vec3 v = _entity->getVelocity(); + float dv = glm::distance(v, _serverVelocity); + std::cout << "adebug deactivate '" << _entity->getName().toStdString() + << "' dx = " << dx << " dv = " << dv << std::endl; // adebug + // adebug + + // copy _server data to entity + bool success; + _entity->setPosition(_serverPosition, success, false); + _entity->setOrientation(_serverRotation, success, false); + _entity->setVelocity(ENTITY_ITEM_ZERO_VEC3); + _entity->setAngularVelocity(ENTITY_ITEM_ZERO_VEC3); +} + // virtual void EntityMotionState::handleEasyChanges(uint32_t& flags) { assert(entityTreeIsLocked()); @@ -125,7 +145,7 @@ void EntityMotionState::handleEasyChanges(uint32_t& flags) { _outgoingPriority = 0; bool verbose = _entity->getName() == "fubar"; // adebug if (verbose) { - std::cout << (void*)(this) << " adebug flag for deactivation" << std::endl; // adebug + std::cout << (void*)(this) << " " << secTimestampNow() << " adebug flag for deactivation" << std::endl; // adebug } } else { // disowned object is still moving --> start timer for ownership bid @@ -244,7 +264,7 @@ void EntityMotionState::getWorldTransform(btTransform& worldTrans) const { } // This callback is invoked by the physics simulation at the end of each simulation step... -// iff the corresponding RigidBody is DYNAMIC and has moved. +// iff the corresponding RigidBody is DYNAMIC and ACTIVE. void EntityMotionState::setWorldTransform(const btTransform& worldTrans) { assert(_entity); assert(entityTreeIsLocked()); @@ -256,7 +276,10 @@ void EntityMotionState::setWorldTransform(const btTransform& worldTrans) { // it goes inactive (at which point we should slam bullet to agree with entity) if (_body->getActivationState() == WANTS_DEACTIVATION && !_entity->isMoving()) { if (verbose) { - std::cout << (void*)(this) << " adebug v = " << _body->getLinearVelocity().length() << " w = " << _body->getAngularVelocity().length() << std::endl; // adebug + std::cout << (void*)(this) << " " << secTimestampNow() << " adebug entity at rest but physics is not?" + << " v = " << _body->getLinearVelocity().length() + << " w = " << _body->getAngularVelocity().length() + << std::endl; // adebug } } measureBodyAcceleration(); @@ -279,7 +302,7 @@ void EntityMotionState::setWorldTransform(const btTransform& worldTrans) { if (verbose && (glm::length(getBodyLinearVelocity()) > 0.0f || glm::length(getBodyAngularVelocity()) > 0.0f) && _entity->getSimulationOwner().getID().isNull()) { - std::cout << (void*)(this) << " adebug set non-zero v on unowned object AS = " << _body->getActivationState() << std::endl; // adebug + std::cout << (void*)(this) << " " << secTimestampNow() << " adebug set non-zero v on unowned object AS = " << _body->getActivationState() << std::endl; // adebug } _entity->setVelocity(getBodyLinearVelocity()); @@ -627,7 +650,7 @@ void EntityMotionState::sendUpdate(OctreeEditPacketSender* packetSender, uint32_ _outgoingPriority = 0; _entity->setPendingOwnershipPriority(_outgoingPriority, now); if (verbose) { - std::cout << (void*)(this) << " adebug sendUpdate() clearOwnership numInactiveUpdates = " << (int)_numInactiveUpdates << std::endl; // adebug + std::cout << (void*)(this) << " " << secTimestampNow() << " adebug sendUpdate() clearOwnership numInactiveUpdates = " << (int)_numInactiveUpdates << std::endl; // adebug } } else if (Physics::getSessionUUID() != _entity->getSimulatorID()) { // we don't own the simulation for this entity yet, but we're sending a bid for it @@ -640,7 +663,7 @@ void EntityMotionState::sendUpdate(OctreeEditPacketSender* packetSender, uint32_ _entity->rememberHasSimulationOwnershipBid(); // ...then reset _outgoingPriority in preparation for the next frame if (verbose) { - std::cout << (void*)(this) << " adebug sendUpdate() bidOwnership at " << (int)_outgoingPriority << std::endl; // adebug + std::cout << (void*)(this) << " " << secTimestampNow() << " adebug sendUpdate() bidOwnership at " << (int)_outgoingPriority << std::endl; // adebug } _outgoingPriority = 0; } else if (_outgoingPriority != _entity->getSimulationPriority()) { @@ -654,7 +677,7 @@ void EntityMotionState::sendUpdate(OctreeEditPacketSender* packetSender, uint32_ } _entity->setPendingOwnershipPriority(_outgoingPriority, now); if (verbose) { - std::cout << (void*)(this) << " adebug sendUpdate() changePriority to " << (int)_outgoingPriority << std::endl; // adebug + std::cout << (void*)(this) << " " << secTimestampNow() << " adebug sendUpdate() changePriority to " << (int)_outgoingPriority << std::endl; // adebug } } diff --git a/libraries/physics/src/EntityMotionState.h b/libraries/physics/src/EntityMotionState.h index feac47d8ec..380edf3927 100644 --- a/libraries/physics/src/EntityMotionState.h +++ b/libraries/physics/src/EntityMotionState.h @@ -29,6 +29,7 @@ public: virtual ~EntityMotionState(); void updateServerPhysicsVariables(); + void handleDeactivation(); virtual void handleEasyChanges(uint32_t& flags) override; virtual bool handleHardAndEasyChanges(uint32_t& flags, PhysicsEngine* engine) override; diff --git a/libraries/physics/src/PhysicalEntitySimulation.cpp b/libraries/physics/src/PhysicalEntitySimulation.cpp index 0b0bbcb6fc..bd76b2d70f 100644 --- a/libraries/physics/src/PhysicalEntitySimulation.cpp +++ b/libraries/physics/src/PhysicalEntitySimulation.cpp @@ -259,6 +259,19 @@ void PhysicalEntitySimulation::getObjectsToChange(VectorOfMotionStates& result) _pendingChanges.clear(); } +void PhysicalEntitySimulation::handleDeactivatedMotionStates(const VectorOfMotionStates& motionStates) { + for (auto stateItr : motionStates) { + ObjectMotionState* state = &(*stateItr); + assert(state); + if (state->getType() == MOTIONSTATE_TYPE_ENTITY) { + EntityMotionState* entityState = static_cast(state); + entityState->handleDeactivation(); + EntityItemPointer entity = entityState->getEntity(); + _entitiesToSort.insert(entity); + } + } +} + void PhysicalEntitySimulation::handleChangedMotionStates(const VectorOfMotionStates& motionStates) { QMutexLocker lock(&_mutex); diff --git a/libraries/physics/src/PhysicalEntitySimulation.h b/libraries/physics/src/PhysicalEntitySimulation.h index 9035308741..5f6185add3 100644 --- a/libraries/physics/src/PhysicalEntitySimulation.h +++ b/libraries/physics/src/PhysicalEntitySimulation.h @@ -56,6 +56,7 @@ public: void setObjectsToChange(const VectorOfMotionStates& objectsToChange); void getObjectsToChange(VectorOfMotionStates& result); + void handleDeactivatedMotionStates(const VectorOfMotionStates& motionStates); void handleChangedMotionStates(const VectorOfMotionStates& motionStates); void handleCollisionEvents(const CollisionEvents& collisionEvents); diff --git a/libraries/physics/src/PhysicsEngine.cpp b/libraries/physics/src/PhysicsEngine.cpp index f57be4eab3..7d97d25135 100644 --- a/libraries/physics/src/PhysicsEngine.cpp +++ b/libraries/physics/src/PhysicsEngine.cpp @@ -449,7 +449,7 @@ const CollisionEvents& PhysicsEngine::getCollisionEvents() { return _collisionEvents; } -const VectorOfMotionStates& PhysicsEngine::getOutgoingChanges() { +const VectorOfMotionStates& PhysicsEngine::getChangedMotionStates() { BT_PROFILE("copyOutgoingChanges"); // Bullet will not deactivate static objects (it doesn't expect them to be active) // so we must deactivate them ourselves diff --git a/libraries/physics/src/PhysicsEngine.h b/libraries/physics/src/PhysicsEngine.h index bbafbb06b6..b2ebe58f08 100644 --- a/libraries/physics/src/PhysicsEngine.h +++ b/libraries/physics/src/PhysicsEngine.h @@ -65,7 +65,8 @@ public: bool hasOutgoingChanges() const { return _hasOutgoingChanges; } /// \return reference to list of changed MotionStates. The list is only valid until beginning of next simulation loop. - const VectorOfMotionStates& getOutgoingChanges(); + const VectorOfMotionStates& getChangedMotionStates(); + const VectorOfMotionStates& getDeactivatedMotionStates() const { return _dynamicsWorld->getDeactivatedMotionStates(); } /// \return reference to list of Collision events. The list is only valid until beginning of next simulation loop. const CollisionEvents& getCollisionEvents(); diff --git a/libraries/physics/src/ThreadSafeDynamicsWorld.cpp b/libraries/physics/src/ThreadSafeDynamicsWorld.cpp index c9cbc6a2be..3c05257fc5 100644 --- a/libraries/physics/src/ThreadSafeDynamicsWorld.cpp +++ b/libraries/physics/src/ThreadSafeDynamicsWorld.cpp @@ -128,25 +128,36 @@ void ThreadSafeDynamicsWorld::synchronizeMotionStates() { for (int i=0;igetMotionState()) { - synchronizeMotionState(body); - _changedMotionStates.push_back(static_cast(body->getMotionState())); - } + if (body && body->getMotionState()) { + synchronizeMotionState(body); + _changedMotionStates.push_back(static_cast(body->getMotionState())); } } } else { //iterate over all active rigid bodies + // TODO? if this becomes a performance bottleneck we could derive our own SimulationIslandManager + // that remembers a list of objects it recently deactivated + _activeStates.clear(); + _deactivatedStates.clear(); for (int i=0;iisActive()) { - if (body->getMotionState()) { + ObjectMotionState* motionState = static_cast(body->getMotionState()); + if (motionState) { + if (body->isActive()) { synchronizeMotionState(body); - _changedMotionStates.push_back(static_cast(body->getMotionState())); + _changedMotionStates.push_back(motionState); + _activeStates.insert(motionState); + } else if (_lastActiveStates.find(motionState) != _lastActiveStates.end()) { + // this object was active last frame but is no longer + _deactivatedStates.push_back(motionState); } } } } + if (_deactivatedStates.size() > 0) { + std::cout << secTimestampNow() << " adebug num deactivated = " << _deactivatedStates.size() << std::endl; // adebug + } + _activeStates.swap(_lastActiveStates); } void ThreadSafeDynamicsWorld::saveKinematicState(btScalar timeStep) { diff --git a/libraries/physics/src/ThreadSafeDynamicsWorld.h b/libraries/physics/src/ThreadSafeDynamicsWorld.h index 68062d8d29..b4fcca8cdb 100644 --- a/libraries/physics/src/ThreadSafeDynamicsWorld.h +++ b/libraries/physics/src/ThreadSafeDynamicsWorld.h @@ -49,12 +49,16 @@ public: float getLocalTimeAccumulation() const { return m_localTime; } const VectorOfMotionStates& getChangedMotionStates() const { return _changedMotionStates; } + const VectorOfMotionStates& getDeactivatedMotionStates() const { return _deactivatedStates; } private: // call this instead of non-virtual btDiscreteDynamicsWorld::synchronizeSingleMotionState() void synchronizeMotionState(btRigidBody* body); VectorOfMotionStates _changedMotionStates; + VectorOfMotionStates _deactivatedStates; + SetOfMotionStates _activeStates; + SetOfMotionStates _lastActiveStates; }; #endif // hifi_ThreadSafeDynamicsWorld_h From be3012181fee72dcdefaa39c189f5447b82b3058 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Thu, 9 Mar 2017 17:53:17 -0800 Subject: [PATCH 122/302] force local deactivation on remote deactivation --- libraries/physics/src/EntityMotionState.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/libraries/physics/src/EntityMotionState.cpp b/libraries/physics/src/EntityMotionState.cpp index 5da9d52169..03bcc43362 100644 --- a/libraries/physics/src/EntityMotionState.cpp +++ b/libraries/physics/src/EntityMotionState.cpp @@ -143,6 +143,8 @@ void EntityMotionState::handleEasyChanges(uint32_t& flags) { flags &= ~Simulation::DIRTY_PHYSICS_ACTIVATION; _body->setActivationState(WANTS_DEACTIVATION); _outgoingPriority = 0; + const float ACTIVATION_EXPIRY = 3.0f; // something larger than the 2.0 hard coded in Bullet + _body->setDeactivationTime(ACTIVATION_EXPIRY); bool verbose = _entity->getName() == "fubar"; // adebug if (verbose) { std::cout << (void*)(this) << " " << secTimestampNow() << " adebug flag for deactivation" << std::endl; // adebug From a16760278e5f84844604a8eda1e76dc379b19cfc Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Thu, 9 Mar 2017 17:58:53 -0800 Subject: [PATCH 123/302] remove debug code --- libraries/entities/src/EntityItem.cpp | 13 ---- libraries/physics/src/EntityMotionState.cpp | 76 ------------------- .../physics/src/ThreadSafeDynamicsWorld.cpp | 5 +- 3 files changed, 1 insertion(+), 93 deletions(-) diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index 888de44505..0bb085459e 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -674,13 +674,6 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef // or rejects a set of properties, it clears this. In such cases, we don't want those custom // setters to ignore what the server says. filterRejection = newSimOwner.getID().isNull(); - bool verbose = getName() == "fubar"; // adebug - if (verbose && _simulationOwner != newSimOwner) { - std::cout << (void*)(this) << " " << secTimestampNow() << " adebug ownership changed " - << _simulationOwner.getID().toString().toStdString() << "." << (int)_simulationOwner.getPriority() << "-->" - << newSimOwner.getID().toString().toStdString() << "." << (int)newSimOwner.getPriority() - << std::endl; // adebug - } if (weOwnSimulation) { if (newSimOwner.getID().isNull() && !_simulationOwner.pendingRelease(lastEditedFromBufferAdjusted)) { // entity-server is trying to clear our ownership (probably at our own request) @@ -1891,9 +1884,6 @@ void EntityItem::updateSimulationOwner(const SimulationOwner& owner) { if (_simulationOwner.set(owner)) { _dirtyFlags |= Simulation::DIRTY_SIMULATOR_ID; - if (getName() == "fubar") { - std::cout << "debug updateSimulationOwner() " << _simulationOwner.getID().toString().toStdString() << "." << (int)(_simulationOwner.getPriority()) << std::endl; // adebug - } } } @@ -1901,9 +1891,6 @@ void EntityItem::clearSimulationOwnership() { if (wantTerseEditLogging() && !_simulationOwner.isNull()) { qCDebug(entities) << "sim ownership for" << getDebugName() << "is now null"; } - if (getName() == "fubar") { - std::cout << "debug clearSimulationOwnership()" << std::endl; // adebug - } _simulationOwner.clear(); // don't bother setting the DIRTY_SIMULATOR_ID flag because: diff --git a/libraries/physics/src/EntityMotionState.cpp b/libraries/physics/src/EntityMotionState.cpp index 03bcc43362..d80615e2c6 100644 --- a/libraries/physics/src/EntityMotionState.cpp +++ b/libraries/physics/src/EntityMotionState.cpp @@ -10,7 +10,6 @@ // #include -#include // adebug #include #include @@ -28,19 +27,6 @@ #include "EntityTree.h" #endif -// adebug TODO BOOKMARK: -// Consider an object near deactivation owned by userB and in view of userA... -// The problem is that userB may deactivate and disown the object before userA deactivates -// userA will sometimes insert non-zero velocities (and position errors) into the Entity before it is deactivated locally -// -// It would be nice to prevent data export from Bullet to Entity for unowned objects except in cases where it is really needed (?) -// Maybe can recycle _serverPosition and friends to store the "before simulationStep" data to more efficiently figure out -// if data should be used for non-owned objects. -// -// If we do that, we should convert _serverPosition and friends to use Bullet data types for efficiency. -// -// adebug - const uint8_t LOOPS_FOR_SIMULATION_ORPHAN = 50; const quint64 USECS_BETWEEN_OWNERSHIP_BIDS = USECS_PER_SECOND / 5; @@ -112,15 +98,6 @@ void EntityMotionState::updateServerPhysicsVariables() { } void EntityMotionState::handleDeactivation() { - // adebug - glm::vec3 pos = _entity->getPosition(); - float dx = glm::distance(pos, _serverPosition); - glm::vec3 v = _entity->getVelocity(); - float dv = glm::distance(v, _serverVelocity); - std::cout << "adebug deactivate '" << _entity->getName().toStdString() - << "' dx = " << dx << " dv = " << dv << std::endl; // adebug - // adebug - // copy _server data to entity bool success; _entity->setPosition(_serverPosition, success, false); @@ -145,10 +122,6 @@ void EntityMotionState::handleEasyChanges(uint32_t& flags) { _outgoingPriority = 0; const float ACTIVATION_EXPIRY = 3.0f; // something larger than the 2.0 hard coded in Bullet _body->setDeactivationTime(ACTIVATION_EXPIRY); - bool verbose = _entity->getName() == "fubar"; // adebug - if (verbose) { - std::cout << (void*)(this) << " " << secTimestampNow() << " adebug flag for deactivation" << std::endl; // adebug - } } else { // disowned object is still moving --> start timer for ownership bid // TODO? put a delay in here proportional to distance from object? @@ -156,13 +129,6 @@ void EntityMotionState::handleEasyChanges(uint32_t& flags) { _nextOwnershipBid = usecTimestampNow() + USECS_BETWEEN_OWNERSHIP_BIDS; } _loopsWithoutOwner = 0; - - // adebug BOOKMARK: the problem is that userB may deactivate and disown the Object - // but it may still be active for userA... who will store slightly non-zero velocities into EntityItem in the meantime - // - // It would be nice if we could ignore slight outgoing changes for unowned objects that WANT_DEACTIVATION until... - // (a) the changes exceed some threshold (--> bid for ownership) or... - // (b) they actually get deactivated (--> slam RigidBody positions to agree with EntityItem) } else if (_entity->getSimulatorID() == Physics::getSessionUUID()) { // we just inherited ownership, make sure our desired priority matches what we have upgradeOutgoingPriority(_entity->getSimulationPriority()); @@ -270,20 +236,6 @@ void EntityMotionState::getWorldTransform(btTransform& worldTrans) const { void EntityMotionState::setWorldTransform(const btTransform& worldTrans) { assert(_entity); assert(entityTreeIsLocked()); - bool verbose = _entity->getName() == "fubar"; // adebug - // adebug BOOKMARK: the problem is that userB may deactivate and disown the Object - // but it may still be active for userA... who will store slightly non-zero velocities into EntityItem in the meantime - // so what we need to do is ignore setWorldTransform() events for unowned objects when the bullet data is not helpful - // until either the data passes some threshold (bid for it) or - // it goes inactive (at which point we should slam bullet to agree with entity) - if (_body->getActivationState() == WANTS_DEACTIVATION && !_entity->isMoving()) { - if (verbose) { - std::cout << (void*)(this) << " " << secTimestampNow() << " adebug entity at rest but physics is not?" - << " v = " << _body->getLinearVelocity().length() - << " w = " << _body->getAngularVelocity().length() - << std::endl; // adebug - } - } measureBodyAcceleration(); bool positionSuccess; _entity->setPosition(bulletToGLM(worldTrans.getOrigin()) + ObjectMotionState::getWorldOffset(), positionSuccess, false); @@ -301,12 +253,6 @@ void EntityMotionState::setWorldTransform(const btTransform& worldTrans) { "setOrientation failed.*"); qCDebug(physics) << "EntityMotionState::setWorldTransform setOrientation failed" << _entity->getID(); } - if (verbose - && (glm::length(getBodyLinearVelocity()) > 0.0f || glm::length(getBodyAngularVelocity()) > 0.0f) - && _entity->getSimulationOwner().getID().isNull()) { - std::cout << (void*)(this) << " " << secTimestampNow() << " adebug set non-zero v on unowned object AS = " << _body->getActivationState() << std::endl; // adebug - - } _entity->setVelocity(getBodyLinearVelocity()); _entity->setAngularVelocity(getBodyAngularVelocity()); _entity->setLastSimulated(usecTimestampNow()); @@ -355,18 +301,6 @@ bool EntityMotionState::isCandidateForOwnership() const { assert(_body); assert(_entity); assert(entityTreeIsLocked()); - - /* adebug - bool verbose = _entity->getName() == "fubar"; // adebug - if (verbose) { - bool isCandidate = _outgoingPriority != 0 - || Physics::getSessionUUID() == _entity->getSimulatorID() - || _entity->actionDataNeedsTransmit(); - if (!isCandidate) { - std::cout << (void*)(this) << " adebug not candidate --> erase" << std::endl; // adebug - } - } - */ return _outgoingPriority != 0 || Physics::getSessionUUID() == _entity->getSimulatorID() || _entity->actionDataNeedsTransmit(); @@ -565,7 +499,6 @@ bool EntityMotionState::shouldSendUpdate(uint32_t simulationStep) { void EntityMotionState::sendUpdate(OctreeEditPacketSender* packetSender, uint32_t step) { assert(_entity); assert(entityTreeIsLocked()); - bool verbose = _entity->getName() == "fubar"; // adebug if (!_body->isActive()) { // make sure all derivatives are zero @@ -651,9 +584,6 @@ void EntityMotionState::sendUpdate(OctreeEditPacketSender* packetSender, uint32_ properties.clearSimulationOwner(); _outgoingPriority = 0; _entity->setPendingOwnershipPriority(_outgoingPriority, now); - if (verbose) { - std::cout << (void*)(this) << " " << secTimestampNow() << " adebug sendUpdate() clearOwnership numInactiveUpdates = " << (int)_numInactiveUpdates << std::endl; // adebug - } } else if (Physics::getSessionUUID() != _entity->getSimulatorID()) { // we don't own the simulation for this entity yet, but we're sending a bid for it quint8 bidPriority = glm::max(_outgoingPriority, VOLUNTEER_SIMULATION_PRIORITY); @@ -664,9 +594,6 @@ void EntityMotionState::sendUpdate(OctreeEditPacketSender* packetSender, uint32_ // don't forget to remember that we have made a bid _entity->rememberHasSimulationOwnershipBid(); // ...then reset _outgoingPriority in preparation for the next frame - if (verbose) { - std::cout << (void*)(this) << " " << secTimestampNow() << " adebug sendUpdate() bidOwnership at " << (int)_outgoingPriority << std::endl; // adebug - } _outgoingPriority = 0; } else if (_outgoingPriority != _entity->getSimulationPriority()) { // we own the simulation but our desired priority has changed @@ -678,9 +605,6 @@ void EntityMotionState::sendUpdate(OctreeEditPacketSender* packetSender, uint32_ properties.setSimulationOwner(Physics::getSessionUUID(), _outgoingPriority); } _entity->setPendingOwnershipPriority(_outgoingPriority, now); - if (verbose) { - std::cout << (void*)(this) << " " << secTimestampNow() << " adebug sendUpdate() changePriority to " << (int)_outgoingPriority << std::endl; // adebug - } } EntityItemID id(_entity->getID()); diff --git a/libraries/physics/src/ThreadSafeDynamicsWorld.cpp b/libraries/physics/src/ThreadSafeDynamicsWorld.cpp index 3c05257fc5..24cfbc2609 100644 --- a/libraries/physics/src/ThreadSafeDynamicsWorld.cpp +++ b/libraries/physics/src/ThreadSafeDynamicsWorld.cpp @@ -136,7 +136,7 @@ void ThreadSafeDynamicsWorld::synchronizeMotionStates() { } else { //iterate over all active rigid bodies // TODO? if this becomes a performance bottleneck we could derive our own SimulationIslandManager - // that remembers a list of objects it recently deactivated + // that remembers a list of objects deactivated last step _activeStates.clear(); _deactivatedStates.clear(); for (int i=0;i 0) { - std::cout << secTimestampNow() << " adebug num deactivated = " << _deactivatedStates.size() << std::endl; // adebug - } _activeStates.swap(_lastActiveStates); } From 43d8433d9a3c92ccc8dfb8910019cb068452fdc7 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Fri, 10 Mar 2017 09:19:00 -0800 Subject: [PATCH 124/302] Removing unused include --- libraries/networking/src/FileCache.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/libraries/networking/src/FileCache.cpp b/libraries/networking/src/FileCache.cpp index 88f2c048e5..b851539b1b 100644 --- a/libraries/networking/src/FileCache.cpp +++ b/libraries/networking/src/FileCache.cpp @@ -19,7 +19,6 @@ #include #include -#include Q_LOGGING_CATEGORY(file_cache, "hifi.file_cache", QtWarningMsg) From a974282bdfbf4a456c5255a3f2bd6e49835c6339 Mon Sep 17 00:00:00 2001 From: kunalgosar Date: Fri, 10 Mar 2017 11:55:36 -0800 Subject: [PATCH 125/302] bolding timestamps on log --- interface/resources/styles/log_dialog.qss | 4 +-- interface/src/ui/BaseLogDialog.cpp | 41 ++++++++++++++++++----- interface/src/ui/BaseLogDialog.h | 4 +-- 3 files changed, 36 insertions(+), 13 deletions(-) diff --git a/interface/resources/styles/log_dialog.qss b/interface/resources/styles/log_dialog.qss index 1fc4df0717..9ce5aeccc2 100644 --- a/interface/resources/styles/log_dialog.qss +++ b/interface/resources/styles/log_dialog.qss @@ -1,6 +1,6 @@ QPlainTextEdit { - font-family: Inconsolata, Lucida Console, Andale Mono, Monaco; + font-family: Courier New, Courier, Monotype; font-size: 16px; padding-left: 28px; padding-top: 7px; @@ -11,7 +11,7 @@ QPlainTextEdit { } QLineEdit { - font-family: Inconsolata, Lucida Console, Andale Mono, Monaco; + font-family: Courier New, Courier, Monotype; padding-left: 7px; background-color: #CCCCCC; border-width: 0; diff --git a/interface/src/ui/BaseLogDialog.cpp b/interface/src/ui/BaseLogDialog.cpp index 7e0027e0a8..c316206e7a 100644 --- a/interface/src/ui/BaseLogDialog.cpp +++ b/interface/src/ui/BaseLogDialog.cpp @@ -28,17 +28,22 @@ const int SEARCH_BUTTON_LEFT = 25; const int SEARCH_BUTTON_WIDTH = 20; const int SEARCH_TOGGLE_BUTTON_WIDTH = 50; const int SEARCH_TEXT_WIDTH = 240; +const int TIME_STAMP_LENGTH = 16; const QColor HIGHLIGHT_COLOR = QColor("#3366CC"); +const QColor BOLD_COLOR = QColor("#445c8c"); +const QString BOLD_PATTERN = "\\[\\d*\\/.*:\\d*:\\d*\\]"; -class KeywordHighlighter : public QSyntaxHighlighter { +class Highlighter : public QSyntaxHighlighter { public: - KeywordHighlighter(QTextDocument* parent = nullptr); + Highlighter(QTextDocument* parent = nullptr); + void setBold(int indexToBold); QString keyword; - + protected: void highlightBlock(const QString& text) override; private: + QTextCharFormat boldFormat; QTextCharFormat keywordFormat; }; @@ -101,9 +106,8 @@ void BaseLogDialog::initControls() { _logTextBox = new QPlainTextEdit(this); _logTextBox->setReadOnly(true); _logTextBox->show(); - _highlighter = new KeywordHighlighter(_logTextBox->document()); + _highlighter = new Highlighter(_logTextBox->document()); connect(_logTextBox, SIGNAL(selectionChanged()), SLOT(updateSelection())); - } void BaseLogDialog::showEvent(QShowEvent* event) { @@ -116,7 +120,9 @@ void BaseLogDialog::resizeEvent(QResizeEvent* event) { void BaseLogDialog::appendLogLine(QString logLine) { if (logLine.contains(_searchTerm, Qt::CaseInsensitive)) { + int indexToBold = _logTextBox->document()->characterCount(); _logTextBox->appendPlainText(logLine.trimmed()); + _highlighter->setBold(indexToBold); } } @@ -175,6 +181,7 @@ void BaseLogDialog::showLogData() { _logTextBox->clear(); _logTextBox->appendPlainText(getCurrentLog()); _logTextBox->ensureCursorVisible(); + _highlighter->rehighlight(); } void BaseLogDialog::updateSelection() { @@ -187,16 +194,28 @@ void BaseLogDialog::updateSelection() { } } -KeywordHighlighter::KeywordHighlighter(QTextDocument* parent) : QSyntaxHighlighter(parent) { +Highlighter::Highlighter(QTextDocument* parent) : QSyntaxHighlighter(parent) { + boldFormat.setFontWeight(75); + boldFormat.setForeground(BOLD_COLOR); keywordFormat.setForeground(HIGHLIGHT_COLOR); -} + } -void KeywordHighlighter::highlightBlock(const QString& text) { +void Highlighter::highlightBlock(const QString& text) { + QRegExp expression(BOLD_PATTERN); + + int index = text.indexOf(expression, 0); + + while (index >= 0) { + int length = expression.matchedLength(); + setFormat(index, length, boldFormat); + index = text.indexOf(expression, index + length); + } + if (keyword.isNull() || keyword.isEmpty()) { return; } - int index = text.indexOf(keyword, 0, Qt::CaseInsensitive); + index = text.indexOf(keyword, 0, Qt::CaseInsensitive); int length = keyword.length(); while (index >= 0) { @@ -204,3 +223,7 @@ void KeywordHighlighter::highlightBlock(const QString& text) { index = text.indexOf(keyword, index + length, Qt::CaseInsensitive); } } + +void Highlighter::setBold(int indexToBold) { + setFormat(indexToBold, TIME_STAMP_LENGTH, boldFormat); +} diff --git a/interface/src/ui/BaseLogDialog.h b/interface/src/ui/BaseLogDialog.h index d097010bae..e18d23937f 100644 --- a/interface/src/ui/BaseLogDialog.h +++ b/interface/src/ui/BaseLogDialog.h @@ -23,7 +23,7 @@ const int BUTTON_MARGIN = 8; class QPushButton; class QLineEdit; class QPlainTextEdit; -class KeywordHighlighter; +class Highlighter; class BaseLogDialog : public QDialog { Q_OBJECT @@ -56,7 +56,7 @@ private: QPushButton* _searchPrevButton { nullptr }; QPushButton* _searchNextButton { nullptr }; QString _searchTerm; - KeywordHighlighter* _highlighter { nullptr }; + Highlighter* _highlighter { nullptr }; void initControls(); void showLogData(); From 9c9eb879255d3136b321622d0b221196a27e96d9 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Fri, 10 Mar 2017 12:55:16 -0800 Subject: [PATCH 126/302] sync RigidBody on deactivation --- libraries/physics/src/EntityMotionState.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/libraries/physics/src/EntityMotionState.cpp b/libraries/physics/src/EntityMotionState.cpp index d80615e2c6..bfa15537fc 100644 --- a/libraries/physics/src/EntityMotionState.cpp +++ b/libraries/physics/src/EntityMotionState.cpp @@ -104,6 +104,11 @@ void EntityMotionState::handleDeactivation() { _entity->setOrientation(_serverRotation, success, false); _entity->setVelocity(ENTITY_ITEM_ZERO_VEC3); _entity->setAngularVelocity(ENTITY_ITEM_ZERO_VEC3); + // and also to RigidBody + btTransform worldTrans; + worldTrans.setOrigin(glmToBullet(_serverPosition)); + worldTrans.setRotation(glmToBullet(_serverRotation)); + // no need to update velocities... should already be zero } // virtual From eb4dee0495d292eb97e4193c592c2cf044e21e3f Mon Sep 17 00:00:00 2001 From: Triplelexx Date: Fri, 10 Mar 2017 23:37:29 +0000 Subject: [PATCH 127/302] create audioMuteOverlay.js client scipt that creates an overlay to provide mute feedback --- scripts/system/audioMuteOverlay.js | 122 +++++++++++++++++++++++++++++ 1 file changed, 122 insertions(+) create mode 100644 scripts/system/audioMuteOverlay.js diff --git a/scripts/system/audioMuteOverlay.js b/scripts/system/audioMuteOverlay.js new file mode 100644 index 0000000000..bb1eb70aa9 --- /dev/null +++ b/scripts/system/audioMuteOverlay.js @@ -0,0 +1,122 @@ +"use strict"; +/* jslint vars: true, plusplus: true, forin: true*/ +/* globals Tablet, Script, AvatarList, Users, Entities, MyAvatar, Camera, Overlays, Vec3, Quat, Controller, print, getControllerWorldLocation */ +/* eslint indent: ["error", 4, { "outerIIFEBody": 0 }] */ +// +// audioMuteOverlay.js +// +// client scipt that creates an overlay to provide mute feedback +// +// Created by Triplelexx on 17/03/09 +// Copyright 2017 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 +// + +(function() { // BEGIN LOCAL_SCOPE + var TWEEN_SPEED = 0.025; + var LERP_AMOUNT = 0.25; + + var overlayPosition = Vec3.ZERO; + var tweenPosition = 0; + var startColor = { + red: 150, + green: 150, + blue: 150 + }; + var endColor = { + red: 255, + green: 0, + blue: 0 + }; + var overlayID; + + AudioDevice.muteToggled.connect(onMuteToggled); + Script.update.connect(update); + Script.scriptEnding.connect(cleanup); + + function update(dt) { + if (!AudioDevice.getMuted()) { + return; + } + updateTween(); + } + + function lerp(a, b, val) { + return (1 - val) * a + val * b; + } + + function getOffsetPosition() { + return Vec3.sum(MyAvatar.getHeadPosition(), Quat.getFront(MyAvatar.headOrientation)); + } + + function updateTween() { + // increase the tween value based on speed till it's complete + if (tweenPosition < 1) { + tweenPosition += TWEEN_SPEED; + } else { + // after tween completion reset to zero and flip values to ping pong + tweenPosition = 0; + var buf = startColor.red; + startColor.red = endColor.red; + endColor.red = buf; + buf = startColor.green; + startColor.green = endColor.green; + endColor.green = buf; + buf = startColor.blue; + startColor.blue = endColor.blue; + endColor.blue = buf; + } + + // update position based on LERP_AMOUNT + var offsetPosition = getOffsetPosition(); + overlayPosition.x = lerp(overlayPosition.x, offsetPosition.x, LERP_AMOUNT); + overlayPosition.y = lerp(overlayPosition.y, offsetPosition.y, LERP_AMOUNT); + overlayPosition.z = lerp(overlayPosition.z, offsetPosition.z, LERP_AMOUNT); + + Overlays.editOverlay(overlayID, { + color: { + red: lerp(startColor.red, endColor.red, tweenPosition), + green: lerp(startColor.green, endColor.green, tweenPosition), + blue: lerp(startColor.blue, endColor.blue, tweenPosition) + }, + position: overlayPosition, + rotation: MyAvatar.orientation, + }); + } + + function onMuteToggled() { + if (AudioDevice.getMuted()) { + createOverlay(); + } else { + deleteOverlay(); + } + } + + function createOverlay() { + overlayPosition = getOffsetPosition(); + overlayID = Overlays.addOverlay("sphere", { + name: "muteSphere", + position: overlayPosition, + rotation: MyAvatar.orientation, + alpha: 0.9, + dimensions: 0.1, + solid: true, + ignoreRayIntersection: true, + visible: true + }); + } + + function deleteOverlay() { + Overlays.deleteOverlay(overlayID); + } + + function cleanup() { + if (overlayID) { + deleteOverlay(); + } + AudioDevice.muteToggled.disconnect(onMuteToggled); + Script.update.disconnect(update); + } +}()); // END LOCAL_SCOPE From 8a0dd55275879480db31736b33b7efd1f5dd727f Mon Sep 17 00:00:00 2001 From: Triplelexx Date: Sat, 11 Mar 2017 00:19:02 +0000 Subject: [PATCH 128/302] change naming and comment --- scripts/system/audioMuteOverlay.js | 53 +++++++++++++++--------------- 1 file changed, 26 insertions(+), 27 deletions(-) diff --git a/scripts/system/audioMuteOverlay.js b/scripts/system/audioMuteOverlay.js index bb1eb70aa9..8ffa1655b7 100644 --- a/scripts/system/audioMuteOverlay.js +++ b/scripts/system/audioMuteOverlay.js @@ -40,7 +40,7 @@ if (!AudioDevice.getMuted()) { return; } - updateTween(); + updateOverlay(); } function lerp(a, b, val) { @@ -49,10 +49,31 @@ function getOffsetPosition() { return Vec3.sum(MyAvatar.getHeadPosition(), Quat.getFront(MyAvatar.headOrientation)); - } - - function updateTween() { - // increase the tween value based on speed till it's complete + } + + function onMuteToggled() { + if (AudioDevice.getMuted()) { + createOverlay(); + } else { + deleteOverlay(); + } + } + + function createOverlay() { + overlayPosition = getOffsetPosition(); + overlayID = Overlays.addOverlay("sphere", { + position: overlayPosition, + rotation: MyAvatar.orientation, + alpha: 0.9, + dimensions: 0.1, + solid: true, + ignoreRayIntersection: true, + visible: true + }); + } + + function updateOverlay() { + // increase by TWEEN_SPEED until completion if (tweenPosition < 1) { tweenPosition += TWEEN_SPEED; } else { @@ -86,28 +107,6 @@ }); } - function onMuteToggled() { - if (AudioDevice.getMuted()) { - createOverlay(); - } else { - deleteOverlay(); - } - } - - function createOverlay() { - overlayPosition = getOffsetPosition(); - overlayID = Overlays.addOverlay("sphere", { - name: "muteSphere", - position: overlayPosition, - rotation: MyAvatar.orientation, - alpha: 0.9, - dimensions: 0.1, - solid: true, - ignoreRayIntersection: true, - visible: true - }); - } - function deleteOverlay() { Overlays.deleteOverlay(overlayID); } From bde2222dfd758292f8ac0ca6c3e26ec3dd6c492b Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Fri, 10 Mar 2017 18:30:56 -0800 Subject: [PATCH 129/302] actually use worldTrans --- libraries/physics/src/EntityMotionState.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/libraries/physics/src/EntityMotionState.cpp b/libraries/physics/src/EntityMotionState.cpp index bfa15537fc..d383f4c199 100644 --- a/libraries/physics/src/EntityMotionState.cpp +++ b/libraries/physics/src/EntityMotionState.cpp @@ -108,6 +108,7 @@ void EntityMotionState::handleDeactivation() { btTransform worldTrans; worldTrans.setOrigin(glmToBullet(_serverPosition)); worldTrans.setRotation(glmToBullet(_serverRotation)); + _body->setWorldTransform(worldTrans); // no need to update velocities... should already be zero } From 873a9aa39ec0f5b8ceb85a3641b84adc2acfa9de Mon Sep 17 00:00:00 2001 From: Triplelexx Date: Sat, 11 Mar 2017 07:40:32 +0000 Subject: [PATCH 130/302] change color swapping to be more concise --- scripts/system/audioMuteOverlay.js | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/scripts/system/audioMuteOverlay.js b/scripts/system/audioMuteOverlay.js index 8ffa1655b7..973faa57a8 100644 --- a/scripts/system/audioMuteOverlay.js +++ b/scripts/system/audioMuteOverlay.js @@ -79,15 +79,11 @@ } else { // after tween completion reset to zero and flip values to ping pong tweenPosition = 0; - var buf = startColor.red; - startColor.red = endColor.red; - endColor.red = buf; - buf = startColor.green; - startColor.green = endColor.green; - endColor.green = buf; - buf = startColor.blue; - startColor.blue = endColor.blue; - endColor.blue = buf; + for (var color in startColor) { + var storedColor = startColor[color]; + startColor[color] = endColor[color]; + endColor[color] = storedColor; + } } // update position based on LERP_AMOUNT From ce5ee429617caa63cfbee068eba08ed8b6e95e97 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Tue, 21 Feb 2017 10:07:46 -0800 Subject: [PATCH 131/302] Update LightOverlayManager to work with particles --- scripts/system/edit.js | 18 +++++++++++-- ...Manager.js => entityIconOverlayManager.js} | 26 ++++++++++++------- 2 files changed, 32 insertions(+), 12 deletions(-) rename scripts/system/libraries/{lightOverlayManager.js => entityIconOverlayManager.js} (80%) diff --git a/scripts/system/edit.js b/scripts/system/edit.js index a440fec1ac..d195864ed6 100644 --- a/scripts/system/edit.js +++ b/scripts/system/edit.js @@ -33,13 +33,27 @@ Script.include([ "libraries/gridTool.js", "libraries/entityList.js", "particle_explorer/particleExplorerTool.js", - "libraries/lightOverlayManager.js" + "libraries/entityIconOverlayManager.js" ]); var selectionDisplay = SelectionDisplay; var selectionManager = SelectionManager; -var lightOverlayManager = new LightOverlayManager(); +const PARTICLE_SYSTEM_URL = Script.resolvePath("assets/images/icon-particles.svg"); +const POINT_LIGHT_URL = Script.resolvePath("assets/images/icon-point-light.svg"); +const SPOT_LIGHT_URL = Script.resolvePath("assets/images/icon-spot-light.svg"); +var lightOverlayManager = new EntityIconOverlayManager(['Light', 'ParticleEffect'], function(entityID) { + var properties = Entities.getEntityProperties(entityID, ['type', 'isSpotlight']); + if (properties.type === 'Light') { + return { + url: properties.isSpotlight ? SPOT_LIGHT_URL : POINT_LIGHT_URL, + } + } else { + return { + url: PARTICLE_SYSTEM_URL, + } + } +}); var cameraManager = new CameraManager(); diff --git a/scripts/system/libraries/lightOverlayManager.js b/scripts/system/libraries/entityIconOverlayManager.js similarity index 80% rename from scripts/system/libraries/lightOverlayManager.js rename to scripts/system/libraries/entityIconOverlayManager.js index 2d3618096b..aafb0725f3 100644 --- a/scripts/system/libraries/lightOverlayManager.js +++ b/scripts/system/libraries/entityIconOverlayManager.js @@ -1,7 +1,4 @@ -var POINT_LIGHT_URL = "http://s3.amazonaws.com/hifi-public/images/tools/point-light.svg"; -var SPOT_LIGHT_URL = "http://s3.amazonaws.com/hifi-public/images/tools/spot-light.svg"; - -LightOverlayManager = function() { +EntityIconOverlayManager = function(entityTypes, getOverlayPropertiesFunc) { var self = this; var visible = false; @@ -79,24 +76,33 @@ LightOverlayManager = function() { } function addEntity(entityID) { - var properties = Entities.getEntityProperties(entityID); - if (properties.type == "Light" && !(entityID in entityOverlays)) { + var properties = Entities.getEntityProperties(entityID, ['position', 'type']); + if (entityTypes.indexOf(properties.type) > -1 && !(entityID in entityOverlays)) { var overlay = getOverlay(); entityOverlays[entityID] = overlay; entityIDs[entityID] = entityID; - Overlays.editOverlay(overlay, { + var overlayProperties = { position: properties.position, - url: properties.isSpotlight ? SPOT_LIGHT_URL : POINT_LIGHT_URL, rotation: Quat.fromPitchYawRollDegrees(0, 0, 270), visible: visible, alpha: 0.9, scale: 0.5, + drawInFront: true, + isFacingAvatar: true, color: { red: 255, green: 255, blue: 255 } - }); + }; + if (getOverlayPropertiesFunc) { + var customProperties = getOverlayPropertiesFunc(entityID, properties); + for (var key in customProperties) { + overlayProperties[key] = customProperties[key]; + } + } + print("overlay:", properties.type, JSON.stringify(overlayProperties)); + Overlays.editOverlay(overlay, overlayProperties); } } @@ -130,4 +136,4 @@ LightOverlayManager = function() { Overlays.deleteOverlay(allOverlays[i]); } }); -}; \ No newline at end of file +}; From 86116aaad7ba4ef035a078081c3ead090f4c80d4 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Tue, 21 Feb 2017 10:20:24 -0800 Subject: [PATCH 132/302] Update light/particel icons to be shown by default in edit mode --- scripts/system/edit.js | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/scripts/system/edit.js b/scripts/system/edit.js index d195864ed6..d77b5804c8 100644 --- a/scripts/system/edit.js +++ b/scripts/system/edit.js @@ -96,13 +96,13 @@ var DEFAULT_LIGHT_DIMENSIONS = Vec3.multiply(20, DEFAULT_DIMENSIONS); var MENU_AUTO_FOCUS_ON_SELECT = "Auto Focus on Select"; var MENU_EASE_ON_FOCUS = "Ease Orientation on Focus"; -var MENU_SHOW_LIGHTS_IN_EDIT_MODE = "Show Lights in Edit Mode"; +var MENU_SHOW_LIGHTS_AND_PARTICLES_IN_EDIT_MODE = "Show Lights and Particle Systems in Edit Mode"; var MENU_SHOW_ZONES_IN_EDIT_MODE = "Show Zones in Edit Mode"; var SETTING_INSPECT_TOOL_ENABLED = "inspectToolEnabled"; var SETTING_AUTO_FOCUS_ON_SELECT = "autoFocusOnSelect"; var SETTING_EASE_ON_FOCUS = "cameraEaseOnFocus"; -var SETTING_SHOW_LIGHTS_IN_EDIT_MODE = "showLightsInEditMode"; +var SETTING_SHOW_LIGHTS_AND_PARTICLES_IN_EDIT_MODE = "showLightsAndParticlesInEditMode"; var SETTING_SHOW_ZONES_IN_EDIT_MODE = "showZonesInEditMode"; @@ -520,7 +520,7 @@ var toolBar = (function () { toolBar.writeProperty("shown", false); toolBar.writeProperty("shown", true); } - lightOverlayManager.setVisible(isActive && Menu.isOptionChecked(MENU_SHOW_LIGHTS_IN_EDIT_MODE)); + lightOverlayManager.setVisible(isActive && Menu.isOptionChecked(MENU_SHOW_LIGHTS_AND_PARTICLES_IN_EDIT_MODE)); Entities.setDrawZoneBoundaries(isActive && Menu.isOptionChecked(MENU_SHOW_ZONES_IN_EDIT_MODE)); }; @@ -959,18 +959,18 @@ function setupModelMenus() { }); Menu.addMenuItem({ menuName: "Edit", - menuItemName: MENU_SHOW_LIGHTS_IN_EDIT_MODE, + menuItemName: MENU_SHOW_LIGHTS_AND_PARTICLES_IN_EDIT_MODE, afterItem: MENU_EASE_ON_FOCUS, isCheckable: true, - isChecked: Settings.getValue(SETTING_SHOW_LIGHTS_IN_EDIT_MODE) === "true", + isChecked: Settings.getValue(SETTING_SHOW_LIGHTS_AND_PARTICLES_IN_EDIT_MODE) !== "false", grouping: "Advanced" }); Menu.addMenuItem({ menuName: "Edit", menuItemName: MENU_SHOW_ZONES_IN_EDIT_MODE, - afterItem: MENU_SHOW_LIGHTS_IN_EDIT_MODE, + afterItem: MENU_SHOW_LIGHTS_AND_PARTICLES_IN_EDIT_MODE, isCheckable: true, - isChecked: Settings.getValue(SETTING_SHOW_ZONES_IN_EDIT_MODE) === "true", + isChecked: Settings.getValue(SETTING_SHOW_ZONES_IN_EDIT_MODE) !== "false", grouping: "Advanced" }); @@ -1001,7 +1001,7 @@ function cleanupModelMenus() { Menu.removeMenuItem("Edit", MENU_AUTO_FOCUS_ON_SELECT); Menu.removeMenuItem("Edit", MENU_EASE_ON_FOCUS); - Menu.removeMenuItem("Edit", MENU_SHOW_LIGHTS_IN_EDIT_MODE); + Menu.removeMenuItem("Edit", MENU_SHOW_LIGHTS_AND_PARTICLES_IN_EDIT_MODE); Menu.removeMenuItem("Edit", MENU_SHOW_ZONES_IN_EDIT_MODE); } @@ -1009,7 +1009,7 @@ Script.scriptEnding.connect(function () { toolBar.setActive(false); Settings.setValue(SETTING_AUTO_FOCUS_ON_SELECT, Menu.isOptionChecked(MENU_AUTO_FOCUS_ON_SELECT)); Settings.setValue(SETTING_EASE_ON_FOCUS, Menu.isOptionChecked(MENU_EASE_ON_FOCUS)); - Settings.setValue(SETTING_SHOW_LIGHTS_IN_EDIT_MODE, Menu.isOptionChecked(MENU_SHOW_LIGHTS_IN_EDIT_MODE)); + Settings.setValue(SETTING_SHOW_LIGHTS_AND_PARTICLES_IN_EDIT_MODE, Menu.isOptionChecked(MENU_SHOW_LIGHTS_AND_PARTICLES_IN_EDIT_MODE)); Settings.setValue(SETTING_SHOW_ZONES_IN_EDIT_MODE, Menu.isOptionChecked(MENU_SHOW_ZONES_IN_EDIT_MODE)); progressDialog.cleanup(); @@ -1297,8 +1297,8 @@ function handeMenuEvent(menuItem) { selectAllEtitiesInCurrentSelectionBox(false); } else if (menuItem === "Select All Entities Touching Box") { selectAllEtitiesInCurrentSelectionBox(true); - } else if (menuItem === MENU_SHOW_LIGHTS_IN_EDIT_MODE) { - lightOverlayManager.setVisible(isActive && Menu.isOptionChecked(MENU_SHOW_LIGHTS_IN_EDIT_MODE)); + } else if (menuItem === MENU_SHOW_LIGHTS_AND_PARTICLES_IN_EDIT_MODE) { + lightOverlayManager.setVisible(isActive && Menu.isOptionChecked(MENU_SHOW_LIGHTS_AND_PARTICLES_IN_EDIT_MODE)); } else if (menuItem === MENU_SHOW_ZONES_IN_EDIT_MODE) { Entities.setDrawZoneBoundaries(isActive && Menu.isOptionChecked(MENU_SHOW_ZONES_IN_EDIT_MODE)); } From 42d518fafbfd10ec2426a6bbeef8fb793838e6cf Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Tue, 21 Feb 2017 10:24:55 -0800 Subject: [PATCH 133/302] Remove debug log in entityIconOverlayManager --- scripts/system/libraries/entityIconOverlayManager.js | 1 - 1 file changed, 1 deletion(-) diff --git a/scripts/system/libraries/entityIconOverlayManager.js b/scripts/system/libraries/entityIconOverlayManager.js index aafb0725f3..2623b6d6cb 100644 --- a/scripts/system/libraries/entityIconOverlayManager.js +++ b/scripts/system/libraries/entityIconOverlayManager.js @@ -101,7 +101,6 @@ EntityIconOverlayManager = function(entityTypes, getOverlayPropertiesFunc) { overlayProperties[key] = customProperties[key]; } } - print("overlay:", properties.type, JSON.stringify(overlayProperties)); Overlays.editOverlay(overlay, overlayProperties); } } From dbcd7fd9e9479ccb136640a89c86582e962777d9 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Tue, 21 Feb 2017 10:31:44 -0800 Subject: [PATCH 134/302] Fix light icon overlay not updating to represent light type --- scripts/system/libraries/entityIconOverlayManager.js | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/scripts/system/libraries/entityIconOverlayManager.js b/scripts/system/libraries/entityIconOverlayManager.js index 2623b6d6cb..ec86057ab0 100644 --- a/scripts/system/libraries/entityIconOverlayManager.js +++ b/scripts/system/libraries/entityIconOverlayManager.js @@ -19,9 +19,16 @@ EntityIconOverlayManager = function(entityTypes, getOverlayPropertiesFunc) { for (var id in entityIDs) { var entityID = entityIDs[id]; var properties = Entities.getEntityProperties(entityID); - Overlays.editOverlay(entityOverlays[entityID], { + var overlayProperties = { position: properties.position - }); + }; + if (getOverlayPropertiesFunc) { + var customProperties = getOverlayPropertiesFunc(entityID, properties); + for (var key in customProperties) { + overlayProperties[key] = customProperties[key]; + } + } + Overlays.editOverlay(entityOverlays[entityID], overlayProperties); } }; From c2564800dc50d7afcf1c2f3a41fd1bedb13fee06 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Wed, 22 Feb 2017 15:18:08 -0800 Subject: [PATCH 135/302] Add edit.js particle + light icons --- .../system/assets/images/icon-particles.svg | 29 ++++++++++ .../system/assets/images/icon-point-light.svg | 57 +++++++++++++++++++ .../system/assets/images/icon-spot-light.svg | 37 ++++++++++++ 3 files changed, 123 insertions(+) create mode 100644 scripts/system/assets/images/icon-particles.svg create mode 100644 scripts/system/assets/images/icon-point-light.svg create mode 100644 scripts/system/assets/images/icon-spot-light.svg diff --git a/scripts/system/assets/images/icon-particles.svg b/scripts/system/assets/images/icon-particles.svg new file mode 100644 index 0000000000..5e0105d7cd --- /dev/null +++ b/scripts/system/assets/images/icon-particles.svg @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + diff --git a/scripts/system/assets/images/icon-point-light.svg b/scripts/system/assets/images/icon-point-light.svg new file mode 100644 index 0000000000..896c35b63b --- /dev/null +++ b/scripts/system/assets/images/icon-point-light.svg @@ -0,0 +1,57 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/scripts/system/assets/images/icon-spot-light.svg b/scripts/system/assets/images/icon-spot-light.svg new file mode 100644 index 0000000000..ac2f87bb27 --- /dev/null +++ b/scripts/system/assets/images/icon-spot-light.svg @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From a0c56618b36ef9032ef90e920b407ce53f473e53 Mon Sep 17 00:00:00 2001 From: Zach Pomerantz Date: Fri, 10 Mar 2017 03:57:36 +0000 Subject: [PATCH 136/302] update FileCache to rm manifest --- libraries/networking/src/FileCache.cpp | 145 ++++++++----------------- libraries/networking/src/FileCache.h | 43 ++++---- 2 files changed, 66 insertions(+), 122 deletions(-) diff --git a/libraries/networking/src/FileCache.cpp b/libraries/networking/src/FileCache.cpp index b851539b1b..78008562ed 100644 --- a/libraries/networking/src/FileCache.cpp +++ b/libraries/networking/src/FileCache.cpp @@ -60,45 +60,17 @@ void FileCache::initialize() { QDir dir(_dirpath.c_str()); if (dir.exists()) { - std::unordered_map> persistedEntries; - if (dir.exists(MANIFEST_NAME.c_str())) { - std::ifstream manifest; - manifest.open(dir.absoluteFilePath(MANIFEST_NAME.c_str()).toStdString()); - while (manifest.good()) { - std::string key, metadata; - std::getline(manifest, key, '\t'); - std::getline(manifest, metadata, '\n'); - if (!key.empty()) { - qCDebug(file_cache, "[%s] Manifest contains %s (%s)", _dirname.c_str(), key.c_str(), metadata.c_str()); - auto filename = key + '.' + _ext; - persistedEntries[filename] = { key, metadata }; - } - } - } else { - qCWarning(file_cache, "[%s] Missing manifest", _dirname.c_str()); - } + auto nameFilters = QStringList(("*." + _ext).c_str()); + auto filters = QDir::Filters(QDir::NoDotAndDotDot | QDir::Files); + auto sort = QDir::SortFlags(QDir::Time); + auto files = dir.entryList(nameFilters, filters, sort); - std::unordered_map entries; - - foreach(QString filename, dir.entryList(QDir::Filters(QDir::NoDotAndDotDot | QDir::Files))) { - const auto& it = persistedEntries.find(filename.toStdString()); - if (it == persistedEntries.cend()) { - // unlink extra files - dir.remove(filename); - qCDebug(file_cache, "[%s] Cleaned %s", _dirname.c_str(), filename.toStdString().c_str()); - } else { - // load existing files - const Key& key = it->second.first; - const std::string& metadata = it->second.second; - const std::string filepath = dir.filePath(filename).toStdString(); - const size_t length = std::ifstream(filepath, std::ios::binary | std::ios::ate).tellg(); - - FilePointer file(loadFile(key, filepath, length, metadata).release(), &fileDeleter); - file->_cache = this; - _files[key] = file; - _numTotalFiles += 1; - _totalFilesSize += length; - } + // load persisted files + foreach(QString filename, files) { + const Key key = filename.section('.', 0, 1).toStdString(); + const std::string filepath = dir.filePath(filename).toStdString(); + const size_t length = std::ifstream(filepath, std::ios::binary | std::ios::ate).tellg(); + addFile(Metadata(key, length), filepath); } qCDebug(file_cache, "[%s] Initialized %s", _dirname.c_str(), _dirpath.c_str()); @@ -110,32 +82,40 @@ void FileCache::initialize() { _initialized = true; } -FilePointer FileCache::writeFile(const Key& key, const char* data, size_t length, void* extra) { +FilePointer FileCache::addFile(Metadata&& metadata, const std::string& filepath) { + FilePointer file(createFile(std::move(metadata), filepath).release(), &fileDeleter); + if (file) { + _numTotalFiles += 1; + _totalFilesSize += file->getLength(); + file->_cache = this; + emit dirty(); + + Lock lock(_filesMutex); + _files[file->getKey()] = file; + } + return file; +} + +FilePointer FileCache::writeFile(const char* data, File::Metadata&& metadata) { assert(_initialized); - std::string filepath = getFilepath(key); + std::string filepath = getFilepath(metadata.key); Lock lock(_filesMutex); // if file already exists, return it - FilePointer file = getFile(key); + FilePointer file = getFile(metadata.key); if (file) { - qCWarning(file_cache, "[%s] Attempted to overwrite %s", _dirname.c_str(), key.c_str()); + qCWarning(file_cache, "[%s] Attempted to overwrite %s", _dirname.c_str(), metadata.key.c_str()); return file; } // write the new file FILE* saveFile = fopen(filepath.c_str(), "wb"); - if (saveFile != nullptr && fwrite(data, length, 1, saveFile) && fclose(saveFile) == 0) { - file.reset(createFile(key, filepath, length, extra).release(), &fileDeleter); - file->_cache = this; - _files[key] = file; - _numTotalFiles += 1; - _totalFilesSize += length; - - emit dirty(); + if (saveFile != nullptr && fwrite(data, metadata.length, 1, saveFile) && fclose(saveFile) == 0) { + file = addFile(std::move(metadata), filepath); } else { - qCWarning(file_cache, "[%s] Failed to write %s (%s)", _dirname.c_str(), key.c_str(), strerror(errno)); + qCWarning(file_cache, "[%s] Failed to write %s (%s)", _dirname.c_str(), metadata.key.c_str(), strerror(errno)); errno = 0; } @@ -149,7 +129,7 @@ FilePointer FileCache::getFile(const Key& key) { Lock lock(_filesMutex); - // check if file already exists + // check if file exists const auto it = _files.find(key); if (it != _files.cend()) { file = it->second.lock(); @@ -221,58 +201,22 @@ void FileCache::reserve(size_t length) { _numUnusedFiles -= 1; _totalFilesSize -= length; _unusedFilesSize -= length; - - unusedLock.unlock(); - evictedFile(file); - unusedLock.lock(); } } void FileCache::clear() { - auto forAllFiles = [&](std::function functor) { - Lock unusedFilesLock(_unusedFilesMutex); - for (const auto& pair : _unusedFiles) { - functor(pair.second); + Lock unusedFilesLock(_unusedFilesMutex); + for (const auto& pair : _unusedFiles) { + auto& file = pair.second; + file->_cache = nullptr; + + if (_totalFilesSize > _offlineFilesMaxSize) { + _totalFilesSize -= file->getLength(); + } else { + file->_shouldPersist = true; + qCDebug(file_cache, "[%s] Persisting %s", _dirname.c_str(), file->getKey().c_str()); } - // clear files so they are not reiterated from _files - _unusedFiles.clear(); - unusedFilesLock.unlock(); - - Lock filesLock(_filesMutex); - for (const auto& pair : _files) { - FilePointer file; - if ((file = pair.second.lock())) { - functor(file); - } - } - }; - - try { - std::string manifestPath= _dirpath + '/' + MANIFEST_NAME; - std::ofstream manifest(manifestPath); - - forAllFiles([&](const FilePointer& file) { - file->_cache = nullptr; - - if (_totalFilesSize > _offlineFilesMaxSize) { - _totalFilesSize -= file->getLength(); - } else { - manifest << file->getKey() << '\t' << file->getMetadata() << '\n'; - file->_shouldPersist = true; - qCDebug(file_cache, "[%s] Persisting %s (%s)", - _dirname.c_str(), file->getKey().c_str(), file->getMetadata().c_str()); - } - }); - } catch (std::exception& e) { - qCWarning(file_cache, "[%s] Failed to write manifest (%s)", _dirname.c_str(), e.what()); - - forAllFiles([](const FilePointer& file) { - file->_cache = nullptr; - file->_shouldPersist = false; - }); } - - Lock lock(_unusedFilesMutex); _unusedFiles.clear(); } @@ -285,6 +229,11 @@ void File::deleter() { } } +File::File(Metadata&& metadata, const std::string& filepath) : + _key(std::move(metadata.key)), + _length(metadata.length), + _filepath(filepath) {} + File::~File() { QFile file(getFilepath().c_str()); if (file.exists() && !_shouldPersist) { diff --git a/libraries/networking/src/FileCache.h b/libraries/networking/src/FileCache.h index 945d43b224..f77db555bc 100644 --- a/libraries/networking/src/FileCache.h +++ b/libraries/networking/src/FileCache.h @@ -52,21 +52,24 @@ public: // initialize FileCache with a directory name (not a path, ex.: "temp_jpgs") and an ext (ex.: "jpg") FileCache(const std::string& dirname, const std::string& ext, QObject* parent = nullptr); - // precondition: there should be no references to Files when FileCache is destroyed virtual ~FileCache(); - // derived classes are left to implement hashing of the files on their own using Key = std::string; + struct Metadata { + Metadata(const Key& key, size_t length) : + key(key), length(length) {} + Key key; + size_t length; + }; // derived classes should implement a setter/getter, for example, for a FileCache backing a network cache: // - // DerivedFilePointer writeFile(const DerivedData& data) { - // return writeFile(data->key, data->data, data->length, &data); + // DerivedFilePointer writeFile(const char* data, DerivedMetadata&& metadata) { + // return writeFile(data, std::forward(metadata)); // } // // DerivedFilePointer getFile(const QUrl& url) { - // // assuming storage/removal of url->hash in createFile/evictedFile overrides - // auto key = lookup_hash_for(url); + // auto key = lookup_hash_for(url); // assuming hashing url in create/evictedFile overrides // return getFile(key); // } @@ -77,15 +80,11 @@ protected: /// must be called after construction to create the cache on the fs and restore persisted files void initialize(); - FilePointer writeFile(const Key& key, const char* data, size_t length, void* extra); + FilePointer writeFile(const char* data, Metadata&& metadata); FilePointer getFile(const Key& key); - /// create a file (ex.: create a class derived from File and store it in a secondary map with extra->url) - virtual std::unique_ptr createFile(const Key& key, const std::string& filepath, size_t length, void* extra) = 0; - /// load a file - virtual std::unique_ptr loadFile(const Key& key, const std::string& filepath, size_t length, const std::string& metadata) = 0; - /// take action when a file is evicted from the cache (ex.: evict it from a secondary map) - virtual void evictedFile(const FilePointer& file) = 0; + /// create a file + virtual std::unique_ptr createFile(Metadata&& metadata, const std::string& filepath) = 0; private: using Mutex = std::recursive_mutex; @@ -95,6 +94,7 @@ private: std::string getFilepath(const Key& key); + FilePointer addFile(Metadata&& metadata, const std::string& filepath); void addUnusedFile(const FilePointer file); void removeUnusedFile(const FilePointer file); void reserve(size_t length); @@ -126,31 +126,26 @@ class File : public QObject { public: using Key = FileCache::Key; + using Metadata = FileCache::Metadata; - std::string getFilepath() const { return _filepath; } Key getKey() const { return _key; } size_t getLength() const { return _length; } + std::string getFilepath() const { return _filepath; } - // the destructor should handle unlinking of the actual filepath virtual ~File(); - // overrides should call File::deleter to maintain caching behavior + /// overrides should call File::deleter to maintain caching behavior virtual void deleter(); protected: - // when constructed, the file has already been created/written - File(const Key& key, const std::string& filepath, size_t length) : - _filepath(filepath), _key(key), _length(length) {} - - /// get metadata to store with a file between instances (ex.: return the url of a hash) - virtual std::string getMetadata() const = 0; - - const std::string _filepath; + /// when constructed, the file has already been created/written + File(Metadata&& metadata, const std::string& filepath); private: friend class FileCache; const Key _key; const size_t _length; + const std::string _filepath; FileCache* _cache; int _LRUKey { 0 }; From a27d88da1a3acafc35f9abf5765815d3c7743a3d Mon Sep 17 00:00:00 2001 From: Zach Pomerantz Date: Sun, 12 Mar 2017 20:14:38 -0400 Subject: [PATCH 137/302] update TextureCache to use FileCache --- .../src/model-networking/KTXCache.cpp | 54 ++--- .../src/model-networking/KTXCache.h | 38 +-- .../src/model-networking/TextureCache.cpp | 220 ++++++++---------- .../src/model-networking/TextureCache.h | 6 +- 4 files changed, 120 insertions(+), 198 deletions(-) diff --git a/libraries/model-networking/src/model-networking/KTXCache.cpp b/libraries/model-networking/src/model-networking/KTXCache.cpp index d0380c7635..908d3245ce 100644 --- a/libraries/model-networking/src/model-networking/KTXCache.cpp +++ b/libraries/model-networking/src/model-networking/KTXCache.cpp @@ -16,55 +16,27 @@ using File = cache::File; using FilePointer = cache::FilePointer; -KTXFilePointer KTXCache::writeFile(Data data) { - return std::static_pointer_cast(FileCache::writeFile(data.key, data.data, data.length, (void*)&data)); +KTXCache::KTXCache(const std::string& dir, const std::string& ext) : + FileCache(dir, ext) { + initialize(); } -KTXFilePointer KTXCache::getFile(const QUrl& url) { - Key key; - { - Lock lock(_urlMutex); - const auto it = _urlMap.find(url); - if (it != _urlMap.cend()) { - key = it->second; - } - } - - KTXFilePointer file; - if (!key.empty()) { - file = std::static_pointer_cast(FileCache::getFile(key)); - } - - return file; +KTXFilePointer KTXCache::writeFile(const char* data, Metadata&& metadata) { + FilePointer file = FileCache::writeFile(data, std::move(metadata)); + return std::static_pointer_cast(file); } -std::unique_ptr KTXCache::createKTXFile(const Key& key, const std::string& filepath, size_t length, const QUrl& url) { - Lock lock(_urlMutex); - _urlMap[url] = key; - return std::unique_ptr(new KTXFile(key, filepath, length, url)); +KTXFilePointer KTXCache::getFile(const Key& key) { + return std::static_pointer_cast(FileCache::getFile(key)); } -std::unique_ptr KTXCache::createFile(const Key& key, const std::string& filepath, size_t length, void* extra) { - const QUrl& url = reinterpret_cast(extra)->url; - qCInfo(file_cache) << "Wrote KTX" << key.c_str() << url; - return createKTXFile(key, filepath, length, url); +std::unique_ptr KTXCache::createFile(Metadata&& metadata, const std::string& filepath) { + qCInfo(file_cache) << "Wrote KTX" << metadata.key.c_str(); + return std::unique_ptr(new KTXFile(std::move(metadata), filepath)); } -std::unique_ptr KTXCache::loadFile(const Key& key, const std::string& filepath, size_t length, const std::string& metadata) { - const QUrl url = QString(metadata.c_str()); - qCInfo(file_cache) << "Loaded KTX" << key.c_str() << url; - return createKTXFile(key, filepath, length, url); -} - -void KTXCache::evictedFile(const FilePointer& file) { - const QUrl url = std::static_pointer_cast(file)->getUrl(); - Lock lock(_urlMutex); - _urlMap.erase(url); -} - -std::string KTXFile::getMetadata() const { - return _url.toString().toStdString(); -} +KTXFile::KTXFile(Metadata&& metadata, const std::string& filepath) : + cache::File(std::move(metadata), filepath) {} std::unique_ptr KTXFile::getKTX() const { ktx::StoragePointer storage = std::make_shared(getFilepath().c_str()); diff --git a/libraries/model-networking/src/model-networking/KTXCache.h b/libraries/model-networking/src/model-networking/KTXCache.h index 84dda48ee2..4ef5e52721 100644 --- a/libraries/model-networking/src/model-networking/KTXCache.h +++ b/libraries/model-networking/src/model-networking/KTXCache.h @@ -27,53 +27,25 @@ class KTXCache : public cache::FileCache { Q_OBJECT public: - KTXCache(const std::string& dir, const std::string& ext) : FileCache(dir, ext) { initialize(); } + KTXCache(const std::string& dir, const std::string& ext); - struct Data { - Data(const QUrl& url, const Key& key, const char* data, size_t length) : - url(url), key(key), data(data), length(length) {} - const QUrl url; - const Key key; - const char* data; - size_t length; - }; - - KTXFilePointer writeFile(Data data); - KTXFilePointer getFile(const QUrl& url); + KTXFilePointer writeFile(const char* data, Metadata&& metadata); + KTXFilePointer getFile(const Key& key); protected: - std::unique_ptr createFile(const Key& key, const std::string& filepath, size_t length, void* extra) override final; - std::unique_ptr loadFile(const Key& key, const std::string& filepath, size_t length, const std::string& metadata) override final; - void evictedFile(const cache::FilePointer& file) override final; - -private: - std::unique_ptr createKTXFile(const Key& key, const std::string& filepath, size_t length, const QUrl& url); - - using Mutex = std::mutex; - using Lock = std::lock_guard; - struct QUrlHasher { std::size_t operator()(QUrl const& url) const { return qHash(url); } }; - - std::unordered_map _urlMap; - Mutex _urlMutex; + std::unique_ptr createFile(Metadata&& metadata, const std::string& filepath) override final; }; class KTXFile : public cache::File { Q_OBJECT public: - QUrl getUrl() const { return _url; } std::unique_ptr getKTX() const; protected: - KTXFile(const Key& key, const std::string& filepath, size_t length, const QUrl& url) : - File(key, filepath, length), _url(url) {} - - std::string getMetadata() const override final; - -private: friend class KTXCache; - const QUrl _url; + KTXFile(Metadata&& metadata, const std::string& filepath); }; #endif // hifi_KTXCache_h diff --git a/libraries/model-networking/src/model-networking/TextureCache.cpp b/libraries/model-networking/src/model-networking/TextureCache.cpp index 16d84b69df..b9977e33d8 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.cpp +++ b/libraries/model-networking/src/model-networking/TextureCache.cpp @@ -43,8 +43,8 @@ #include Q_LOGGING_CATEGORY(trace_resource_parse_image, "trace.resource.parse.image") -Q_LOGGING_CATEGORY(trace_resource_parse_ktx, "trace.resource.parse.ktx") -Q_LOGGING_CATEGORY(trace_resource_cache_ktx, "trace.resource.cache.ktx") +Q_LOGGING_CATEGORY(trace_resource_parse_image_raw, "trace.resource.parse.image.raw") +Q_LOGGING_CATEGORY(trace_resource_parse_image_ktx, "trace.resource.parse.image.ktx") const std::string TextureCache::KTX_DIRNAME { "ktx_cache" }; const std::string TextureCache::KTX_EXT { "ktx" }; @@ -296,34 +296,14 @@ gpu::TexturePointer TextureCache::getImageTexture(const QString& path, Type type QSharedPointer TextureCache::createResource(const QUrl& url, const QSharedPointer& fallback, const void* extra) { - KTXFilePointer file = _ktxCache.getFile(url); const TextureExtra* textureExtra = static_cast(extra); auto type = textureExtra ? textureExtra->type : Type::DEFAULT_TEXTURE; - - NetworkTexture* texture; - if (file) { - texture = new NetworkTexture(url, type, file); - } else { - auto content = textureExtra ? textureExtra->content : QByteArray(); - auto maxNumPixels = textureExtra ? textureExtra->maxNumPixels : ABSOLUTE_MAX_TEXTURE_NUM_PIXELS; - texture = new NetworkTexture(url, type, content, maxNumPixels); - } - + auto content = textureExtra ? textureExtra->content : QByteArray(); + auto maxNumPixels = textureExtra ? textureExtra->maxNumPixels : ABSOLUTE_MAX_TEXTURE_NUM_PIXELS; + NetworkTexture* texture = new NetworkTexture(url, type, content, maxNumPixels); return QSharedPointer(texture, &Resource::deleter); } -NetworkTexture::NetworkTexture(const QUrl& url, Type type, const KTXFilePointer& file) : - Resource(url), - _type(type), - _file(file) { - _textureSource = std::make_shared(); - - if (file) { - _startedLoading = true; - QMetaObject::invokeMethod(this, "loadFile", Qt::QueuedConnection); - } -} - NetworkTexture::NetworkTexture(const QUrl& url, Type type, const QByteArray& content, int maxNumPixels) : Resource(url), _type(type), @@ -342,12 +322,6 @@ NetworkTexture::NetworkTexture(const QUrl& url, Type type, const QByteArray& con } } -NetworkTexture::NetworkTexture(const QUrl& url, const TextureLoaderFunc& textureLoader, const QByteArray& content) : - NetworkTexture(url, CUSTOM_TEXTURE, content, ABSOLUTE_MAX_TEXTURE_NUM_PIXELS) -{ - _textureLoader = textureLoader; -} - NetworkTexture::TextureLoaderFunc NetworkTexture::getTextureLoader() const { if (_type == CUSTOM_TEXTURE) { return _textureLoader; @@ -387,30 +361,8 @@ gpu::TexturePointer NetworkTexture::getFallbackTexture() const { class Reader : public QRunnable { public: - Reader(const QWeakPointer& resource, const QUrl& url) : _resource(resource), _url(url) { - DependencyManager::get()->incrementStat("PendingProcessing"); - } - void run() override final { - DependencyManager::get()->decrementStat("PendingProcessing"); - CounterStat counter("Processing"); - - // Run this with low priority, then restore thread priority - auto originalPriority = QThread::currentThread()->priority(); - if (originalPriority == QThread::InheritPriority) { - originalPriority = QThread::NormalPriority; - } - QThread::currentThread()->setPriority(QThread::LowPriority); - Finally restorePriority([originalPriority]{ - QThread::currentThread()->setPriority(originalPriority); - }); - - if (!_resource.lock()) { // to ensure the resource is still needed - qCDebug(modelnetworking) << _url << "loading stopped; resource out of scope"; - return; - } - - read(); - } + Reader(const QWeakPointer& resource, const QUrl& url); + void run() override final; virtual void read() = 0; protected: @@ -418,42 +370,83 @@ protected: QUrl _url; }; -class FileReader : public Reader { -public: - FileReader(const QWeakPointer& resource, const QUrl& url) : Reader(resource, url) {} - void read() override final; -}; - class ImageReader : public Reader { public: ImageReader(const QWeakPointer& resource, const QUrl& url, - const QByteArray& data, int maxNumPixels = ABSOLUTE_MAX_TEXTURE_NUM_PIXELS); + const QByteArray& data, const std::string& hash, int maxNumPixels); void read() override final; private: static void listSupportedImageFormats(); QByteArray _content; + std::string _hash; int _maxNumPixels; }; -void NetworkTexture::downloadFinished(const QByteArray& data) { - // send the reader off to the thread pool - QThreadPool::globalInstance()->start(new ImageReader(_self, _url, data)); -} +class KTXReader : public Reader { +public: + KTXReader(const QWeakPointer& resource, const QUrl& url, const KTXFilePointer& ktxFile); + void read() override final; -void NetworkTexture::loadFile() { - QThreadPool::globalInstance()->start(new FileReader(_self, _url)); +private: + KTXFilePointer _file; +}; + +void NetworkTexture::downloadFinished(const QByteArray& data) { + loadContent(data); } void NetworkTexture::loadContent(const QByteArray& content) { - QThreadPool::globalInstance()->start(new ImageReader(_self, _url, content, _maxNumPixels)); + // Hash the source image to for KTX caching + std::string hash; + { + QCryptographicHash hasher(QCryptographicHash::Md5); + hasher.addData(content); + hash = hasher.result().toHex().toStdString(); + } + + std::unique_ptr reader; + KTXFilePointer ktxFile; + if (!_cache.isNull() && (ktxFile = static_cast(_cache.data())->_ktxCache.getFile(hash))) { + reader.reset(new KTXReader(_self, _url, ktxFile)); + } else { + reader.reset(new ImageReader(_self, _url, content, hash, _maxNumPixels)); + } + + QThreadPool::globalInstance()->start(reader.release()); +} + +Reader::Reader(const QWeakPointer& resource, const QUrl& url) : + _resource(resource), _url(url) { + DependencyManager::get()->incrementStat("PendingProcessing"); +} + +void Reader::run() { + PROFILE_RANGE_EX(resource_parse_image, __FUNCTION__, 0xffff0000, 0, { { "url", _url.toString() } }); + DependencyManager::get()->decrementStat("PendingProcessing"); + CounterStat counter("Processing"); + + auto originalPriority = QThread::currentThread()->priority(); + if (originalPriority == QThread::InheritPriority) { + originalPriority = QThread::NormalPriority; + } + QThread::currentThread()->setPriority(QThread::LowPriority); + Finally restorePriority([originalPriority]{ QThread::currentThread()->setPriority(originalPriority); }); + + if (!_resource.data()) { + qCWarning(modelnetworking) << "Abandoning load of" << _url << "; could not get strong ref"; + return; + } + + read(); } ImageReader::ImageReader(const QWeakPointer& resource, const QUrl& url, - const QByteArray& data, int maxNumPixels) : - Reader(resource, url), _content(data), _maxNumPixels(maxNumPixels) { + const QByteArray& data, const std::string& hash, int maxNumPixels) : + Reader(resource, url), _content(data), _hash(hash), _maxNumPixels(maxNumPixels) { listSupportedImageFormats(); + #if DEBUG_DUMP_TEXTURE_LOADS static auto start = usecTimestampNow() / USECS_PER_MSEC; auto now = usecTimestampNow() / USECS_PER_MSEC - start; @@ -482,36 +475,7 @@ void ImageReader::listSupportedImageFormats() { }); } -void FileReader::read() { - PROFILE_RANGE_EX(resource_parse_ktx, __FUNCTION__, 0xffff0000, 0, { { "url", _url.toString() } }); - - gpu::TexturePointer texture; - { - auto resource = _resource.lock(); // to ensure the resource is still needed - if (!resource) { - qCDebug(modelnetworking) << _url << "loading stopped; resource out of scope"; - return; - } - - auto ktx = resource.staticCast()->_file->getKTX(); - texture.reset(gpu::Texture::unserialize(ktx)); - texture->setKtxBacking(ktx); - } - - auto resource = _resource.lock(); // to ensure the resource is still needed - if (resource) { - QMetaObject::invokeMethod(resource.data(), "setImage", - Q_ARG(gpu::TexturePointer, texture), - Q_ARG(int, texture->getWidth()), Q_ARG(int, texture->getHeight())); - } else { - qCDebug(modelnetworking) << _url << "loading stopped; resource out of scope"; - } - -} - void ImageReader::read() { - PROFILE_RANGE_EX(resource_parse_image, __FUNCTION__, 0xffff0000, 0, { { "url", _url.toString() } }); - // Help the QImage loader by extracting the image file format from the url filename ext. // Some tga are not created properly without it. auto filename = _url.fileName().toStdString(); @@ -541,7 +505,6 @@ void ImageReader::read() { QSize(imageWidth, imageHeight) << ")"; } - // Load the image into a gpu::Texture gpu::TexturePointer texture = nullptr; { auto resource = _resource.lock(); // to ensure the resource is still needed @@ -552,37 +515,22 @@ void ImageReader::read() { auto url = _url.toString().toStdString(); - PROFILE_RANGE_EX(resource_parse_image, __FUNCTION__, 0xffff0000, 0); + PROFILE_RANGE_EX(resource_parse_image_raw, __FUNCTION__, 0xffff0000, 0); + // Load the image into a gpu::Texture auto networkTexture = resource.staticCast(); texture.reset(networkTexture->getTextureLoader()(image, url)); texture->setSource(url); if (texture) { texture->setFallbackTexture(networkTexture->getFallbackTexture()); } - } - // Hash the source image to use as a filename for on-disk caching - std::string hash; - { - QCryptographicHash hasher(QCryptographicHash::Md5); - hasher.addData((const char*)image.bits(), image.byteCount() * sizeof(char)); - hash = hasher.result().toHex().toStdString(); - } - - { - auto resource = _resource.lock(); // to ensure the resource is still needed - if (!resource) { - qCDebug(modelnetworking) << _url << "loading stopped; resource out of scope"; - return; - } - - PROFILE_RANGE_EX(resource_cache_ktx, __FUNCTION__, 0xffffff00, 0); + // Save the image into a KTXFile auto ktx = gpu::Texture::serialize(*texture); const char* data = reinterpret_cast(ktx->_storage->data()); size_t length = ktx->_storage->size(); KTXFilePointer file; auto& ktxCache = DependencyManager::get()->_ktxCache; - if (!ktx || !(file = ktxCache.writeFile({ _url, hash, data, length }))) { + if (!ktx || !(file = ktxCache.writeFile(data, KTXCache::Metadata(_hash, length)))) { qCWarning(modelnetworking) << _url << "file cache failed"; } else { resource.staticCast()->_file = file; @@ -600,3 +548,35 @@ void ImageReader::read() { qCDebug(modelnetworking) << _url << "loading stopped; resource out of scope"; } } + +KTXReader::KTXReader(const QWeakPointer& resource, const QUrl& url, + const KTXFilePointer& ktxFile) : + Reader(resource, url), _file(ktxFile) {} + +void KTXReader::read() { + PROFILE_RANGE_EX(resource_parse_image_ktx, __FUNCTION__, 0xffff0000, 0, { { "url", _url.toString() } }); + + gpu::TexturePointer texture; + { + auto resource = _resource.lock(); // to ensure the resource is still needed + if (!resource) { + qCDebug(modelnetworking) << _url << "loading stopped; resource out of scope"; + return; + } + + resource.staticCast()->_file = _file; + auto ktx = _file->getKTX(); + texture.reset(gpu::Texture::unserialize(ktx)); + texture->setKtxBacking(ktx); + } + + auto resource = _resource.lock(); // to ensure the resource is still needed + if (resource) { + QMetaObject::invokeMethod(resource.data(), "setImage", + Q_ARG(gpu::TexturePointer, texture), + Q_ARG(int, texture->getWidth()), Q_ARG(int, texture->getHeight())); + } else { + qCDebug(modelnetworking) << _url << "loading stopped; resource out of scope"; + } + +} diff --git a/libraries/model-networking/src/model-networking/TextureCache.h b/libraries/model-networking/src/model-networking/TextureCache.h index ad628c6f2c..8ee3f59416 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.h +++ b/libraries/model-networking/src/model-networking/TextureCache.h @@ -65,9 +65,7 @@ public: typedef gpu::Texture* TextureLoader(const QImage& image, const std::string& srcImageName); using TextureLoaderFunc = std::function; - NetworkTexture(const QUrl& url, Type type, const KTXFilePointer& file); NetworkTexture(const QUrl& url, Type type, const QByteArray& content, int maxNumPixels); - NetworkTexture(const QUrl& url, const TextureLoaderFunc& textureLoader, const QByteArray& content); QString getType() const override { return "NetworkTexture"; } @@ -89,11 +87,10 @@ protected: virtual void downloadFinished(const QByteArray& data) override; Q_INVOKABLE void loadContent(const QByteArray& content); - Q_INVOKABLE void loadFile(); Q_INVOKABLE void setImage(gpu::TexturePointer texture, int originalWidth, int originalHeight); private: - friend class FileReader; + friend class KTXReader; friend class ImageReader; Type _type; @@ -149,6 +146,7 @@ protected: private: friend class ImageReader; + friend class NetworkTexture; friend class DilatableNetworkTexture; TextureCache(); From b7783dcffe4c5bbf9e4cc2649be57ed505dfc0c4 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Mon, 13 Mar 2017 11:26:34 -0700 Subject: [PATCH 138/302] Update edit.js light/particle overlays to be clickable when behind something --- scripts/system/edit.js | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/scripts/system/edit.js b/scripts/system/edit.js index d77b5804c8..2f7e0fabd7 100644 --- a/scripts/system/edit.js +++ b/scripts/system/edit.js @@ -594,18 +594,12 @@ function findClickedEntity(event) { var result; - if (!entityResult.intersects && !lightResult.intersects) { - return null; - } else if (entityResult.intersects && !lightResult.intersects) { - result = entityResult; - } else if (!entityResult.intersects && lightResult.intersects) { + if (lightResult.intersects) { result = lightResult; + } else if (entityResult.intersects) { + result = entityResult; } else { - if (entityResult.distance < lightResult.distance) { - result = entityResult; - } else { - result = lightResult; - } + return null; } if (!result.accurate) { From d4ade8278c1d4f8305c3b02ccdc153f3ccfc5ad0 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Mon, 13 Mar 2017 11:27:12 -0700 Subject: [PATCH 139/302] Fix js linting errors in entityIconOverlayManager.js --- .../libraries/entityIconOverlayManager.js | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/scripts/system/libraries/entityIconOverlayManager.js b/scripts/system/libraries/entityIconOverlayManager.js index ec86057ab0..7f7a293bc3 100644 --- a/scripts/system/libraries/entityIconOverlayManager.js +++ b/scripts/system/libraries/entityIconOverlayManager.js @@ -1,6 +1,6 @@ -EntityIconOverlayManager = function(entityTypes, getOverlayPropertiesFunc) { - var self = this; +/* globals EntityIconOverlayManager:true */ +EntityIconOverlayManager = function(entityTypes, getOverlayPropertiesFunc) { var visible = false; // List of all created overlays @@ -38,7 +38,7 @@ EntityIconOverlayManager = function(entityTypes, getOverlayPropertiesFunc) { if (result.intersects) { for (var id in entityOverlays) { - if (result.overlayID == entityOverlays[id]) { + if (result.overlayID === entityOverlays[id]) { result.entityID = entityIDs[id]; found = true; break; @@ -54,7 +54,7 @@ EntityIconOverlayManager = function(entityTypes, getOverlayPropertiesFunc) { }; this.setVisible = function(isVisible) { - if (visible != isVisible) { + if (visible !== isVisible) { visible = isVisible; for (var id in entityOverlays) { Overlays.editOverlay(entityOverlays[id], { @@ -66,12 +66,13 @@ EntityIconOverlayManager = function(entityTypes, getOverlayPropertiesFunc) { // Allocate or get an unused overlay function getOverlay() { - if (unusedOverlays.length == 0) { - var overlay = Overlays.addOverlay("image3d", {}); + var overlay; + if (unusedOverlays.length === 0) { + overlay = Overlays.addOverlay("image3d", {}); allOverlays.push(overlay); } else { - var overlay = unusedOverlays.pop(); - }; + overlay = unusedOverlays.pop(); + } return overlay; } From 95fcb1df7a57b3350520eb08b5ca9513bf2692cd Mon Sep 17 00:00:00 2001 From: Triplelexx Date: Mon, 13 Mar 2017 18:32:53 +0000 Subject: [PATCH 140/302] fix spelling mistake --- scripts/system/audioMuteOverlay.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/system/audioMuteOverlay.js b/scripts/system/audioMuteOverlay.js index 973faa57a8..f112381040 100644 --- a/scripts/system/audioMuteOverlay.js +++ b/scripts/system/audioMuteOverlay.js @@ -5,7 +5,7 @@ // // audioMuteOverlay.js // -// client scipt that creates an overlay to provide mute feedback +// client script that creates an overlay to provide mute feedback // // Created by Triplelexx on 17/03/09 // Copyright 2017 High Fidelity, Inc. From 075574b428d098cb6c989bb035ff81671e8f983c Mon Sep 17 00:00:00 2001 From: humbletim Date: Mon, 13 Mar 2017 16:33:42 -0400 Subject: [PATCH 141/302] log cleanup per CR; add more specific hints (instead of relying on __FUNCTION__) --- libraries/script-engine/src/ScriptEngine.cpp | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index f96b733c74..615c385a52 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -564,7 +564,6 @@ void ScriptEngine::resetModuleCache(bool deleteScriptCache) { if (it.flags() & QScriptValue::SkipInEnumeration) { continue; } - //scriptCache->deleteScript(it.name()); qCDebug(scriptengine) << "resetModuleCache(true) -- staging " << it.name() << " for cache reset at next require"; cacheMeta.setProperty(it.name(), true); } @@ -594,8 +593,8 @@ void ScriptEngine::init() { entityScriptingInterface->init(); connect(entityScriptingInterface.data(), &EntityScriptingInterface::deletingEntity, this, [this](const EntityItemID& entityID) { if (_entityScripts.contains(entityID)) { - if (isEntityScriptRunning(entityID)) { - qCWarning(scriptengine) << "deletingEntity while entity script is still running!" << entityID; + if (isEntityScriptRunning(entityID) && !isStopping()) { + qCWarning(scriptengine) << "deletingEntity while entity script is still running" << entityID; } _entityScripts.remove(entityID); emit entityScriptDetailsUpdated(); @@ -954,7 +953,7 @@ QScriptValue ScriptEngine::evaluate(const QString& sourceCode, const QString& fi syntaxError.setProperty("detail", "evaluate"); } raiseException(syntaxError); - maybeEmitUncaughtException(__FUNCTION__); + maybeEmitUncaughtException("lint"); return syntaxError; } QScriptProgram program { sourceCode, fileName, lineNumber }; @@ -962,14 +961,14 @@ QScriptValue ScriptEngine::evaluate(const QString& sourceCode, const QString& fi // can this happen? auto err = makeError("could not create QScriptProgram for " + fileName); raiseException(err); - maybeEmitUncaughtException(__FUNCTION__); + maybeEmitUncaughtException("compile"); return err; } QScriptValue result; { result = BaseScriptEngine::evaluate(program); - maybeEmitUncaughtException(__FUNCTION__); + maybeEmitUncaughtException("evaluate"); } return result; } @@ -1644,11 +1643,11 @@ QScriptValue ScriptEngine::require(const QString& moduleId) { auto exports = module.property("exports"); if (!invalidateCache && exports.isObject()) { - // we have found a cacheed module -- just need to possibly register it with current parent + // we have found a cached module -- just need to possibly register it with current parent qCDebug(scriptengine_module) << QString("require - using cached module '%1' for '%2' (loaded: %3)") .arg(modulePath).arg(moduleId).arg(module.property("loaded").toString()); registerModuleWithParent(module, parent); - maybeEmitUncaughtException(__FUNCTION__); + maybeEmitUncaughtException("cached module"); return exports; } @@ -2380,9 +2379,12 @@ void ScriptEngine::unloadEntityScript(const EntityItemID& entityID) { const EntityScriptDetails &oldDetails = _entityScripts[entityID]; if (isEntityScriptRunning(entityID)) { callEntityScriptMethod(entityID, "unload"); - } else { + } +#ifdef DEBUG_ENTITY_STATES + else { qCDebug(scriptengine) << "unload called while !running" << entityID << oldDetails.status; } +#endif if (oldDetails.status != EntityScriptStatus::UNLOADED) { EntityScriptDetails newDetails; newDetails.status = EntityScriptStatus::UNLOADED; From 172a638ef0dac11f9587ddf8a83db579cdda1818 Mon Sep 17 00:00:00 2001 From: Zach Pomerantz Date: Sun, 12 Mar 2017 20:44:17 -0400 Subject: [PATCH 142/302] move resource paths to PathUtils --- assignment-client/src/assets/AssetServer.cpp | 4 +-- assignment-client/src/octree/OctreeServer.cpp | 8 ++--- domain-server/src/DomainServer.cpp | 4 +-- .../src/model-networking/TextureCache.cpp | 2 -- libraries/networking/src/Assignment.cpp | 1 - libraries/networking/src/FileCache.cpp | 4 +-- libraries/shared/src/HifiConfigVariantMap.cpp | 6 ++-- libraries/shared/src/PathUtils.cpp | 18 +++++++++-- libraries/shared/src/PathUtils.h | 5 ++- libraries/shared/src/ServerPathUtils.cpp | 31 ------------------- libraries/shared/src/ServerPathUtils.h | 22 ------------- 11 files changed, 33 insertions(+), 72 deletions(-) delete mode 100644 libraries/shared/src/ServerPathUtils.cpp delete mode 100644 libraries/shared/src/ServerPathUtils.h diff --git a/assignment-client/src/assets/AssetServer.cpp b/assignment-client/src/assets/AssetServer.cpp index 82dd23a9de..4cdeddb943 100644 --- a/assignment-client/src/assets/AssetServer.cpp +++ b/assignment-client/src/assets/AssetServer.cpp @@ -24,7 +24,7 @@ #include #include -#include +#include #include "NetworkLogging.h" #include "NodeType.h" @@ -162,7 +162,7 @@ void AssetServer::completeSetup() { if (assetsPath.isRelative()) { // if the domain settings passed us a relative path, make an absolute path that is relative to the // default data directory - absoluteFilePath = ServerPathUtils::getDataFilePath("assets/" + assetsPathString); + absoluteFilePath = PathUtils::getDataFilePath("assets/" + assetsPathString); } _resourcesDirectory = QDir(absoluteFilePath); diff --git a/assignment-client/src/octree/OctreeServer.cpp b/assignment-client/src/octree/OctreeServer.cpp index 2eee2ee229..d657fafaeb 100644 --- a/assignment-client/src/octree/OctreeServer.cpp +++ b/assignment-client/src/octree/OctreeServer.cpp @@ -29,7 +29,7 @@ #include "OctreeQueryNode.h" #include "OctreeServerConsts.h" #include -#include +#include #include int OctreeServer::_clientCount = 0; @@ -280,7 +280,7 @@ OctreeServer::~OctreeServer() { void OctreeServer::initHTTPManager(int port) { // setup the embedded web server - QString documentRoot = QString("%1/web").arg(ServerPathUtils::getDataDirectory()); + QString documentRoot = QString("%1/web").arg(PathUtils::getDataDirectory()); // setup an httpManager with us as the request handler and the parent _httpManager = new HTTPManager(QHostAddress::AnyIPv4, port, documentRoot, this, this); @@ -1179,7 +1179,7 @@ void OctreeServer::domainSettingsRequestComplete() { if (persistPath.isRelative()) { // if the domain settings passed us a relative path, make an absolute path that is relative to the // default data directory - persistAbsoluteFilePath = QDir(ServerPathUtils::getDataFilePath("entities/")).absoluteFilePath(_persistFilePath); + persistAbsoluteFilePath = QDir(PathUtils::getDataFilePath("entities/")).absoluteFilePath(_persistFilePath); } static const QString ENTITY_PERSIST_EXTENSION = ".json.gz"; @@ -1245,7 +1245,7 @@ void OctreeServer::domainSettingsRequestComplete() { QDir backupDirectory { _backupDirectoryPath }; QString absoluteBackupDirectory; if (backupDirectory.isRelative()) { - absoluteBackupDirectory = QDir(ServerPathUtils::getDataFilePath("entities/")).absoluteFilePath(_backupDirectoryPath); + absoluteBackupDirectory = QDir(PathUtils::getDataFilePath("entities/")).absoluteFilePath(_backupDirectoryPath); absoluteBackupDirectory = QDir(absoluteBackupDirectory).absolutePath(); } else { absoluteBackupDirectory = backupDirectory.absolutePath(); diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index c741c22b83..9b8d2842e6 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -38,7 +38,7 @@ #include #include #include -#include +#include #include #include "DomainServerNodeData.h" @@ -1618,7 +1618,7 @@ QJsonObject DomainServer::jsonObjectForNode(const SharedNodePointer& node) { QDir pathForAssignmentScriptsDirectory() { static const QString SCRIPTS_DIRECTORY_NAME = "/scripts/"; - QDir directory(ServerPathUtils::getDataDirectory() + SCRIPTS_DIRECTORY_NAME); + QDir directory(PathUtils::getDataDirectory() + SCRIPTS_DIRECTORY_NAME); if (!directory.exists()) { directory.mkpath("."); qInfo() << "Created path to " << directory.path(); diff --git a/libraries/model-networking/src/model-networking/TextureCache.cpp b/libraries/model-networking/src/model-networking/TextureCache.cpp index b9977e33d8..7c355ee3b2 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.cpp +++ b/libraries/model-networking/src/model-networking/TextureCache.cpp @@ -35,8 +35,6 @@ #include #include -#include -#include #include "ModelNetworkingLogging.h" #include diff --git a/libraries/networking/src/Assignment.cpp b/libraries/networking/src/Assignment.cpp index 9efad15398..27d4a31ccf 100644 --- a/libraries/networking/src/Assignment.cpp +++ b/libraries/networking/src/Assignment.cpp @@ -12,7 +12,6 @@ #include "udt/PacketHeaders.h" #include "SharedUtil.h" #include "UUID.h" -#include "ServerPathUtils.h" #include diff --git a/libraries/networking/src/FileCache.cpp b/libraries/networking/src/FileCache.cpp index 78008562ed..8c71e6b632 100644 --- a/libraries/networking/src/FileCache.cpp +++ b/libraries/networking/src/FileCache.cpp @@ -18,7 +18,7 @@ #include -#include +#include Q_LOGGING_CATEGORY(file_cache, "hifi.file_cache", QtWarningMsg) @@ -46,7 +46,7 @@ FileCache::FileCache(const std::string& dirname, const std::string& ext, QObject QObject(parent), _ext(ext), _dirname(dirname), - _dirpath(ServerPathUtils::getDataFilePath(dirname.c_str()).toStdString()) {} + _dirpath(PathUtils::getDataFilePath(dirname.c_str()).toStdString()) {} FileCache::~FileCache() { clear(); diff --git a/libraries/shared/src/HifiConfigVariantMap.cpp b/libraries/shared/src/HifiConfigVariantMap.cpp index 5be6b2cd74..95fbb67942 100644 --- a/libraries/shared/src/HifiConfigVariantMap.cpp +++ b/libraries/shared/src/HifiConfigVariantMap.cpp @@ -21,7 +21,7 @@ #include #include -#include "ServerPathUtils.h" +#include "PathUtils.h" #include "SharedLogging.h" QVariantMap HifiConfigVariantMap::mergeCLParametersWithJSONConfig(const QStringList& argumentList) { @@ -127,7 +127,7 @@ void HifiConfigVariantMap::loadConfig(const QStringList& argumentList) { _userConfigFilename = argumentList[userConfigIndex + 1]; } else { // we weren't passed a user config path - _userConfigFilename = ServerPathUtils::getDataFilePath(USER_CONFIG_FILE_NAME); + _userConfigFilename = PathUtils::getDataFilePath(USER_CONFIG_FILE_NAME); // as of 1/19/2016 this path was moved so we attempt a migration for first run post migration here @@ -153,7 +153,7 @@ void HifiConfigVariantMap::loadConfig(const QStringList& argumentList) { // we have the old file and not the new file - time to copy the file // make the destination directory if it doesn't exist - auto dataDirectory = ServerPathUtils::getDataDirectory(); + auto dataDirectory = PathUtils::getDataDirectory(); if (QDir().mkpath(dataDirectory)) { if (oldConfigFile.copy(_userConfigFilename)) { qCDebug(shared) << "Migrated config file from" << oldConfigFilename << "to" << _userConfigFilename; diff --git a/libraries/shared/src/PathUtils.cpp b/libraries/shared/src/PathUtils.cpp index 265eaaa5b6..ee43749a87 100644 --- a/libraries/shared/src/PathUtils.cpp +++ b/libraries/shared/src/PathUtils.cpp @@ -30,11 +30,15 @@ const QString& PathUtils::resourcesPath() { return staticResourcePath; } -QString PathUtils::getRootDataDirectory() { +QString PathUtils::getRootDataDirectory(bool roaming) { auto dataPath = QStandardPaths::writableLocation(QStandardPaths::HomeLocation); #ifdef Q_OS_WIN - dataPath += "/AppData/Roaming/"; + if (roaming) { + dataPath += "/AppData/Roaming/"; + } else { + dataPath += "/AppData/Local/"; + } #elif defined(Q_OS_OSX) dataPath += "/Library/Application Support/"; #else @@ -44,6 +48,16 @@ QString PathUtils::getRootDataDirectory() { return dataPath; } +QString PathUtils::getDataDirectory(bool roaming) { + auto dataPath = getRootDataDirectory(roaming); + dataPath += qApp->organizationName() + "/" + qApp->applicationName(); + return QDir::cleanPath(dataPath); +} + +QString PathUtils::getDataFilePath(QString filename, bool roaming) { + return QDir(getDataDirectory(roaming)).absoluteFilePath(filename); +} + QString fileNameWithoutExtension(const QString& fileName, const QVector possibleExtensions) { QString fileNameLowered = fileName.toLower(); foreach (const QString possibleExtension, possibleExtensions) { diff --git a/libraries/shared/src/PathUtils.h b/libraries/shared/src/PathUtils.h index 1f7dcbe466..e9f1a7f78d 100644 --- a/libraries/shared/src/PathUtils.h +++ b/libraries/shared/src/PathUtils.h @@ -27,7 +27,10 @@ class PathUtils : public QObject, public Dependency { Q_PROPERTY(QString resources READ resourcesPath) public: static const QString& resourcesPath(); - static QString getRootDataDirectory(); + static QString getRootDataDirectory(bool roaming = true); + + static QString getDataDirectory(bool roaming = true); + static QString getDataFilePath(QString filename, bool roaming = true); static Qt::CaseSensitivity getFSCaseSensitivity(); static QString stripFilename(const QUrl& url); diff --git a/libraries/shared/src/ServerPathUtils.cpp b/libraries/shared/src/ServerPathUtils.cpp deleted file mode 100644 index cf52875c5f..0000000000 --- a/libraries/shared/src/ServerPathUtils.cpp +++ /dev/null @@ -1,31 +0,0 @@ -// -// ServerPathUtils.cpp -// libraries/shared/src -// -// Created by Ryan Huffman on 01/12/16. -// Copyright 2016 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 "ServerPathUtils.h" - -#include -#include -#include -#include - -#include "PathUtils.h" - -QString ServerPathUtils::getDataDirectory() { - auto dataPath = PathUtils::getRootDataDirectory(); - - dataPath += qApp->organizationName() + "/" + qApp->applicationName(); - - return QDir::cleanPath(dataPath); -} - -QString ServerPathUtils::getDataFilePath(QString filename) { - return QDir(getDataDirectory()).absoluteFilePath(filename); -} - diff --git a/libraries/shared/src/ServerPathUtils.h b/libraries/shared/src/ServerPathUtils.h deleted file mode 100644 index 28a9a71f0d..0000000000 --- a/libraries/shared/src/ServerPathUtils.h +++ /dev/null @@ -1,22 +0,0 @@ -// -// ServerPathUtils.h -// libraries/shared/src -// -// Created by Ryan Huffman on 01/12/16. -// Copyright 2016 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#ifndef hifi_ServerPathUtils_h -#define hifi_ServerPathUtils_h - -#include - -namespace ServerPathUtils { - QString getDataDirectory(); - QString getDataFilePath(QString filename); -} - -#endif // hifi_ServerPathUtils_h \ No newline at end of file From 463f6454f887044648a780121a7845446ada0a97 Mon Sep 17 00:00:00 2001 From: Zach Pomerantz Date: Sun, 12 Mar 2017 20:44:28 -0400 Subject: [PATCH 143/302] use Local instead of Roaming for FileCache --- libraries/networking/src/FileCache.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/networking/src/FileCache.cpp b/libraries/networking/src/FileCache.cpp index 8c71e6b632..82d3baa4e2 100644 --- a/libraries/networking/src/FileCache.cpp +++ b/libraries/networking/src/FileCache.cpp @@ -46,7 +46,7 @@ FileCache::FileCache(const std::string& dirname, const std::string& ext, QObject QObject(parent), _ext(ext), _dirname(dirname), - _dirpath(PathUtils::getDataFilePath(dirname.c_str()).toStdString()) {} + _dirpath(PathUtils::getDataFilePath(dirname.c_str(), false).toStdString()) {} FileCache::~FileCache() { clear(); From 808973d7d25087341e9058f509ecfe8544243f68 Mon Sep 17 00:00:00 2001 From: Zach Pomerantz Date: Mon, 13 Mar 2017 17:24:57 -0400 Subject: [PATCH 144/302] use QStandardPaths instead of hardcoded paths --- assignment-client/src/assets/AssetServer.cpp | 2 +- assignment-client/src/octree/OctreeServer.cpp | 7 ++-- domain-server/src/DomainServer.cpp | 2 +- interface/src/Application.cpp | 2 +- libraries/networking/src/FileCache.cpp | 2 +- libraries/shared/src/HifiConfigVariantMap.cpp | 4 +-- libraries/shared/src/PathUtils.cpp | 32 ++++++------------- libraries/shared/src/PathUtils.h | 8 +++-- 8 files changed, 24 insertions(+), 35 deletions(-) diff --git a/assignment-client/src/assets/AssetServer.cpp b/assignment-client/src/assets/AssetServer.cpp index 4cdeddb943..3886ff8d92 100644 --- a/assignment-client/src/assets/AssetServer.cpp +++ b/assignment-client/src/assets/AssetServer.cpp @@ -162,7 +162,7 @@ void AssetServer::completeSetup() { if (assetsPath.isRelative()) { // if the domain settings passed us a relative path, make an absolute path that is relative to the // default data directory - absoluteFilePath = PathUtils::getDataFilePath("assets/" + assetsPathString); + absoluteFilePath = PathUtils::getAppDataFilePath("assets/" + assetsPathString); } _resourcesDirectory = QDir(absoluteFilePath); diff --git a/assignment-client/src/octree/OctreeServer.cpp b/assignment-client/src/octree/OctreeServer.cpp index d657fafaeb..f2dbe5d1d2 100644 --- a/assignment-client/src/octree/OctreeServer.cpp +++ b/assignment-client/src/octree/OctreeServer.cpp @@ -279,8 +279,7 @@ OctreeServer::~OctreeServer() { void OctreeServer::initHTTPManager(int port) { // setup the embedded web server - - QString documentRoot = QString("%1/web").arg(PathUtils::getDataDirectory()); + QString documentRoot = QString("%1/web").arg(PathUtils::getAppDataPath()); // setup an httpManager with us as the request handler and the parent _httpManager = new HTTPManager(QHostAddress::AnyIPv4, port, documentRoot, this, this); @@ -1179,7 +1178,7 @@ void OctreeServer::domainSettingsRequestComplete() { if (persistPath.isRelative()) { // if the domain settings passed us a relative path, make an absolute path that is relative to the // default data directory - persistAbsoluteFilePath = QDir(PathUtils::getDataFilePath("entities/")).absoluteFilePath(_persistFilePath); + persistAbsoluteFilePath = QDir(PathUtils::getAppDataFilePath("entities/")).absoluteFilePath(_persistFilePath); } static const QString ENTITY_PERSIST_EXTENSION = ".json.gz"; @@ -1245,7 +1244,7 @@ void OctreeServer::domainSettingsRequestComplete() { QDir backupDirectory { _backupDirectoryPath }; QString absoluteBackupDirectory; if (backupDirectory.isRelative()) { - absoluteBackupDirectory = QDir(PathUtils::getDataFilePath("entities/")).absoluteFilePath(_backupDirectoryPath); + absoluteBackupDirectory = QDir(PathUtils::getAppDataFilePath("entities/")).absoluteFilePath(_backupDirectoryPath); absoluteBackupDirectory = QDir(absoluteBackupDirectory).absolutePath(); } else { absoluteBackupDirectory = backupDirectory.absolutePath(); diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index 9b8d2842e6..620b11d8ad 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -1618,7 +1618,7 @@ QJsonObject DomainServer::jsonObjectForNode(const SharedNodePointer& node) { QDir pathForAssignmentScriptsDirectory() { static const QString SCRIPTS_DIRECTORY_NAME = "/scripts/"; - QDir directory(PathUtils::getDataDirectory() + SCRIPTS_DIRECTORY_NAME); + QDir directory(PathUtils::getAppDataPath() + SCRIPTS_DIRECTORY_NAME); if (!directory.exists()) { directory.mkpath("."); qInfo() << "Created path to " << directory.path(); diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index f870bd9f83..1d1adcdf96 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1439,7 +1439,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo scriptEngines->loadScript(testScript, false); } else { // Get sandbox content set version, if available - auto acDirPath = PathUtils::getRootDataDirectory() + BuildInfo::MODIFIED_ORGANIZATION + "/assignment-client/"; + auto acDirPath = PathUtils::getAppDataPath() + "../../" + BuildInfo::MODIFIED_ORGANIZATION + "/assignment-client/"; auto contentVersionPath = acDirPath + "content-version.txt"; qCDebug(interfaceapp) << "Checking " << contentVersionPath << " for content version"; auto contentVersion = 0; diff --git a/libraries/networking/src/FileCache.cpp b/libraries/networking/src/FileCache.cpp index 82d3baa4e2..f8a86903cb 100644 --- a/libraries/networking/src/FileCache.cpp +++ b/libraries/networking/src/FileCache.cpp @@ -46,7 +46,7 @@ FileCache::FileCache(const std::string& dirname, const std::string& ext, QObject QObject(parent), _ext(ext), _dirname(dirname), - _dirpath(PathUtils::getDataFilePath(dirname.c_str(), false).toStdString()) {} + _dirpath(PathUtils::getAppLocalDataFilePath(dirname.c_str()).toStdString()) {} FileCache::~FileCache() { clear(); diff --git a/libraries/shared/src/HifiConfigVariantMap.cpp b/libraries/shared/src/HifiConfigVariantMap.cpp index 95fbb67942..d0fb14e104 100644 --- a/libraries/shared/src/HifiConfigVariantMap.cpp +++ b/libraries/shared/src/HifiConfigVariantMap.cpp @@ -127,7 +127,7 @@ void HifiConfigVariantMap::loadConfig(const QStringList& argumentList) { _userConfigFilename = argumentList[userConfigIndex + 1]; } else { // we weren't passed a user config path - _userConfigFilename = PathUtils::getDataFilePath(USER_CONFIG_FILE_NAME); + _userConfigFilename = PathUtils::getAppDataFilePath(USER_CONFIG_FILE_NAME); // as of 1/19/2016 this path was moved so we attempt a migration for first run post migration here @@ -153,7 +153,7 @@ void HifiConfigVariantMap::loadConfig(const QStringList& argumentList) { // we have the old file and not the new file - time to copy the file // make the destination directory if it doesn't exist - auto dataDirectory = PathUtils::getDataDirectory(); + auto dataDirectory = PathUtils::getAppDataPath(); if (QDir().mkpath(dataDirectory)) { if (oldConfigFile.copy(_userConfigFilename)) { qCDebug(shared) << "Migrated config file from" << oldConfigFilename << "to" << _userConfigFilename; diff --git a/libraries/shared/src/PathUtils.cpp b/libraries/shared/src/PathUtils.cpp index ee43749a87..6e3acc5e99 100644 --- a/libraries/shared/src/PathUtils.cpp +++ b/libraries/shared/src/PathUtils.cpp @@ -30,32 +30,20 @@ const QString& PathUtils::resourcesPath() { return staticResourcePath; } -QString PathUtils::getRootDataDirectory(bool roaming) { - auto dataPath = QStandardPaths::writableLocation(QStandardPaths::HomeLocation); - -#ifdef Q_OS_WIN - if (roaming) { - dataPath += "/AppData/Roaming/"; - } else { - dataPath += "/AppData/Local/"; - } -#elif defined(Q_OS_OSX) - dataPath += "/Library/Application Support/"; -#else - dataPath += "/.local/share/"; -#endif - - return dataPath; +QString PathUtils::getAppDataPath() { + return QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/"; } -QString PathUtils::getDataDirectory(bool roaming) { - auto dataPath = getRootDataDirectory(roaming); - dataPath += qApp->organizationName() + "/" + qApp->applicationName(); - return QDir::cleanPath(dataPath); +QString PathUtils::getAppLocalDataPath() { + return QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation) + "/"; } -QString PathUtils::getDataFilePath(QString filename, bool roaming) { - return QDir(getDataDirectory(roaming)).absoluteFilePath(filename); +QString PathUtils::getAppDataFilePath(const QString& filename) { + return QDir(getAppDataPath()).absoluteFilePath(filename); +} + +QString PathUtils::getAppLocalDataFilePath(const QString& filename) { + return QDir(getAppLocalDataPath()).absoluteFilePath(filename); } QString fileNameWithoutExtension(const QString& fileName, const QVector possibleExtensions) { diff --git a/libraries/shared/src/PathUtils.h b/libraries/shared/src/PathUtils.h index e9f1a7f78d..a7af44221c 100644 --- a/libraries/shared/src/PathUtils.h +++ b/libraries/shared/src/PathUtils.h @@ -27,10 +27,12 @@ class PathUtils : public QObject, public Dependency { Q_PROPERTY(QString resources READ resourcesPath) public: static const QString& resourcesPath(); - static QString getRootDataDirectory(bool roaming = true); - static QString getDataDirectory(bool roaming = true); - static QString getDataFilePath(QString filename, bool roaming = true); + static QString getAppDataPath(); + static QString getAppLocalDataPath(); + + static QString getAppDataFilePath(const QString& filename); + static QString getAppLocalDataFilePath(const QString& filename); static Qt::CaseSensitivity getFSCaseSensitivity(); static QString stripFilename(const QUrl& url); From 1bba59a45903f4c94378bef89990a5bc0e857ad6 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Mon, 13 Mar 2017 14:38:02 -0700 Subject: [PATCH 145/302] Revert "Revert "avoid unnecessary copy of priority_queue"" This reverts commit 97c376a09c99f567b0fa90abd490eb542cd93c42. --- .../src/avatars/AvatarMixerSlave.cpp | 4 +-- interface/src/avatar/AvatarManager.cpp | 4 +-- libraries/avatars/src/AvatarData.cpp | 28 +++++++++---------- libraries/avatars/src/AvatarData.h | 3 +- 4 files changed, 19 insertions(+), 20 deletions(-) diff --git a/assignment-client/src/avatars/AvatarMixerSlave.cpp b/assignment-client/src/avatars/AvatarMixerSlave.cpp index dd25aa4c4b..584d3d0e6a 100644 --- a/assignment-client/src/avatars/AvatarMixerSlave.cpp +++ b/assignment-client/src/avatars/AvatarMixerSlave.cpp @@ -185,8 +185,8 @@ void AvatarMixerSlave::broadcastAvatarData(const SharedNodePointer& node) { AvatarSharedPointer thisAvatar = nodeData->getAvatarSharedPointer(); ViewFrustum cameraView = nodeData->getViewFrustom(); - std::priority_queue sortedAvatars = AvatarData::sortAvatars( - avatarList, cameraView, + std::priority_queue sortedAvatars; + AvatarData::sortAvatars(avatarList, cameraView, sortedAvatars, [&](AvatarSharedPointer avatar)->uint64_t{ auto avatarNode = avatarDataToNodes[avatar]; diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index d806c042b9..6b426bcde8 100644 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -164,8 +164,8 @@ void AvatarManager::updateOtherAvatars(float deltaTime) { ViewFrustum cameraView; qApp->copyDisplayViewFrustum(cameraView); - std::priority_queue sortedAvatars = AvatarData::sortAvatars( - avatarList, cameraView, + std::priority_queue sortedAvatars; + AvatarData::sortAvatars(avatarList, cameraView, sortedAvatars, [](AvatarSharedPointer avatar)->uint64_t{ return std::static_pointer_cast(avatar)->getLastRenderUpdateTime(); diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index e7ec201aa1..6e4553ff8a 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -2324,20 +2324,20 @@ float AvatarData::_avatarSortCoefficientSize { 0.5f }; float AvatarData::_avatarSortCoefficientCenter { 0.25 }; float AvatarData::_avatarSortCoefficientAge { 1.0f }; -std::priority_queue AvatarData::sortAvatars( - QList avatarList, - const ViewFrustum& cameraView, - std::function getLastUpdated, - std::function getBoundingRadius, - std::function shouldIgnore) { +void AvatarData::sortAvatars( + QList avatarList, + const ViewFrustum& cameraView, + std::priority_queue& sortedAvatarsOut, + std::function getLastUpdated, + std::function getBoundingRadius, + std::function shouldIgnore) { - uint64_t startTime = usecTimestampNow(); - - glm::vec3 frustumCenter = cameraView.getPosition(); - - std::priority_queue sortedAvatars; { PROFILE_RANGE(simulation, "sort"); + uint64_t now = usecTimestampNow(); + + glm::vec3 frustumCenter = cameraView.getPosition(); + const glm::vec3& forward = cameraView.getDirection(); for (int32_t i = 0; i < avatarList.size(); ++i) { const auto& avatar = avatarList.at(i); @@ -2356,10 +2356,9 @@ std::priority_queue AvatarData::sortAvatars( // FIXME - AvatarData has something equivolent to this float radius = getBoundingRadius(avatar); - const glm::vec3& forward = cameraView.getDirection(); float apparentSize = 2.0f * radius / distance; float cosineAngle = glm::dot(offset, forward) / distance; - float age = (float)(startTime - getLastUpdated(avatar)) / (float)(USECS_PER_SECOND); + float age = (float)(now - getLastUpdated(avatar)) / (float)(USECS_PER_SECOND); // NOTE: we are adding values of different units to get a single measure of "priority". // Thus we multiply each component by a conversion "weight" that scales its units relative to the others. @@ -2375,10 +2374,9 @@ std::priority_queue AvatarData::sortAvatars( priority += OUT_OF_VIEW_PENALTY; } } - sortedAvatars.push(AvatarPriority(avatar, priority)); + sortedAvatarsOut.push(AvatarPriority(avatar, priority)); } } - return sortedAvatars; } QScriptValue AvatarEntityMapToScriptValue(QScriptEngine* engine, const AvatarEntityMap& value) { diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index 12209d9c31..c2240f400f 100644 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -597,9 +597,10 @@ public: static const float OUT_OF_VIEW_PENALTY; - static std::priority_queue sortAvatars( + static void sortAvatars( QList avatarList, const ViewFrustum& cameraView, + std::priority_queue& sortedAvatarsOut, std::function getLastUpdated, std::function getBoundingRadius, std::function shouldIgnore); From 4637fcefe9321d217c7d30c1c250faf00221186e Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Mon, 13 Mar 2017 14:38:04 -0700 Subject: [PATCH 146/302] Revert "Revert "remove unused variable"" This reverts commit 50288bd4ba3591aa0cffadfc994606143f349ea5. --- assignment-client/src/avatars/AvatarMixerSlave.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/assignment-client/src/avatars/AvatarMixerSlave.cpp b/assignment-client/src/avatars/AvatarMixerSlave.cpp index 584d3d0e6a..49b4b1ced4 100644 --- a/assignment-client/src/avatars/AvatarMixerSlave.cpp +++ b/assignment-client/src/avatars/AvatarMixerSlave.cpp @@ -168,7 +168,6 @@ void AvatarMixerSlave::broadcastAvatarData(const SharedNodePointer& node) { QList avatarList; std::unordered_map avatarDataToNodes; - int listItem = 0; std::for_each(_begin, _end, [&](const SharedNodePointer& otherNode) { const AvatarMixerClientData* otherNodeData = reinterpret_cast(otherNode->getLinkedData()); @@ -176,7 +175,6 @@ void AvatarMixerSlave::broadcastAvatarData(const SharedNodePointer& node) { // but not have yet sent data that's linked to the node. Check for that case and don't // consider those nodes. if (otherNodeData) { - listItem++; AvatarSharedPointer otherAvatar = otherNodeData->getAvatarSharedPointer(); avatarList << otherAvatar; avatarDataToNodes[otherAvatar] = otherNode; From 71eb24386b058fd20394fff10f3b6544531a8de5 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Mon, 13 Mar 2017 14:38:06 -0700 Subject: [PATCH 147/302] Revert "Revert "remove unnecessary context brackets"" This reverts commit a68674adccedab1004e1dbc0ec9e811eb5f2d4b4. --- libraries/avatars/src/AvatarData.cpp | 80 ++++++++++++++-------------- 1 file changed, 39 insertions(+), 41 deletions(-) diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index 6e4553ff8a..8025c680ca 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -2332,50 +2332,48 @@ void AvatarData::sortAvatars( std::function getBoundingRadius, std::function shouldIgnore) { - { - PROFILE_RANGE(simulation, "sort"); - uint64_t now = usecTimestampNow(); + PROFILE_RANGE(simulation, "sort"); + uint64_t now = usecTimestampNow(); - glm::vec3 frustumCenter = cameraView.getPosition(); - const glm::vec3& forward = cameraView.getDirection(); - for (int32_t i = 0; i < avatarList.size(); ++i) { - const auto& avatar = avatarList.at(i); + glm::vec3 frustumCenter = cameraView.getPosition(); + const glm::vec3& forward = cameraView.getDirection(); + for (int32_t i = 0; i < avatarList.size(); ++i) { + const auto& avatar = avatarList.at(i); - if (shouldIgnore(avatar)) { - continue; - } - - // priority = weighted linear combination of: - // (a) apparentSize - // (b) proximity to center of view - // (c) time since last update - glm::vec3 avatarPosition = avatar->getPosition(); - glm::vec3 offset = avatarPosition - frustumCenter; - float distance = glm::length(offset) + 0.001f; // add 1mm to avoid divide by zero - - // FIXME - AvatarData has something equivolent to this - float radius = getBoundingRadius(avatar); - - float apparentSize = 2.0f * radius / distance; - float cosineAngle = glm::dot(offset, forward) / distance; - float age = (float)(now - getLastUpdated(avatar)) / (float)(USECS_PER_SECOND); - - // NOTE: we are adding values of different units to get a single measure of "priority". - // Thus we multiply each component by a conversion "weight" that scales its units relative to the others. - // These weights are pure magic tuning and should be hard coded in the relation below, - // but are currently exposed for anyone who would like to explore fine tuning: - float priority = _avatarSortCoefficientSize * apparentSize - + _avatarSortCoefficientCenter * cosineAngle - + _avatarSortCoefficientAge * age; - - // decrement priority of avatars outside keyhole - if (distance > cameraView.getCenterRadius()) { - if (!cameraView.sphereIntersectsFrustum(avatarPosition, radius)) { - priority += OUT_OF_VIEW_PENALTY; - } - } - sortedAvatarsOut.push(AvatarPriority(avatar, priority)); + if (shouldIgnore(avatar)) { + continue; } + + // priority = weighted linear combination of: + // (a) apparentSize + // (b) proximity to center of view + // (c) time since last update + glm::vec3 avatarPosition = avatar->getPosition(); + glm::vec3 offset = avatarPosition - frustumCenter; + float distance = glm::length(offset) + 0.001f; // add 1mm to avoid divide by zero + + // FIXME - AvatarData has something equivolent to this + float radius = getBoundingRadius(avatar); + + float apparentSize = 2.0f * radius / distance; + float cosineAngle = glm::dot(offset, forward) / distance; + float age = (float)(now - getLastUpdated(avatar)) / (float)(USECS_PER_SECOND); + + // NOTE: we are adding values of different units to get a single measure of "priority". + // Thus we multiply each component by a conversion "weight" that scales its units relative to the others. + // These weights are pure magic tuning and should be hard coded in the relation below, + // but are currently exposed for anyone who would like to explore fine tuning: + float priority = _avatarSortCoefficientSize * apparentSize + + _avatarSortCoefficientCenter * cosineAngle + + _avatarSortCoefficientAge * age; + + // decrement priority of avatars outside keyhole + if (distance > cameraView.getCenterRadius()) { + if (!cameraView.sphereIntersectsFrustum(avatarPosition, radius)) { + priority += OUT_OF_VIEW_PENALTY; + } + } + sortedAvatarsOut.push(AvatarPriority(avatar, priority)); } } From 031d144c0404d67264c59826b03ce8bbcf8fb72d Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Mon, 13 Mar 2017 14:38:08 -0700 Subject: [PATCH 148/302] Revert "Revert "minor cleanup"" This reverts commit 188287e345ff43dcc11c79bf20376fba775d5699. --- interface/src/avatar/Avatar.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index f5fe82ef4b..6e1f44f5ac 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -351,7 +351,6 @@ void Avatar::simulate(float deltaTime, bool inView) { _jointDataSimulationRate.increment(); _skeletonModel->simulate(deltaTime, true); - _skeletonModelSimulationRate.increment(); locationChanged(); // joints changed, so if there are any children, update them. _hasNewJointData = false; @@ -367,8 +366,8 @@ void Avatar::simulate(float deltaTime, bool inView) { } else { // a non-full update is still required so that the position, rotation, scale and bounds of the skeletonModel are updated. _skeletonModel->simulate(deltaTime, false); - _skeletonModelSimulationRate.increment(); } + _skeletonModelSimulationRate.increment(); } // update animation for display name fade in/out From 203325fd2a5761ef623ace5f67da477e0a380c48 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Mon, 13 Mar 2017 14:38:09 -0700 Subject: [PATCH 149/302] Revert "Revert "expand avatar update time budget"" This reverts commit 9a9f07df7111322e21371ffa4938b4691b235c24. --- interface/src/avatar/AvatarManager.cpp | 22 ++++++---------------- 1 file changed, 6 insertions(+), 16 deletions(-) diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index 6b426bcde8..7417f73102 100644 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -157,7 +157,6 @@ void AvatarManager::updateOtherAvatars(float deltaTime) { lock.unlock(); PerformanceTimer perfTimer("otherAvatars"); - uint64_t startTime = usecTimestampNow(); auto avatarMap = getHashCopy(); QList avatarList = avatarMap.values(); @@ -194,10 +193,9 @@ void AvatarManager::updateOtherAvatars(float deltaTime) { }); render::PendingChanges pendingChanges; - const uint64_t RENDER_UPDATE_BUDGET = 1500; // usec - const uint64_t MAX_UPDATE_BUDGET = 2000; // usec - uint64_t renderExpiry = startTime + RENDER_UPDATE_BUDGET; - uint64_t maxExpiry = startTime + MAX_UPDATE_BUDGET; + uint64_t startTime = usecTimestampNow(); + const uint64_t UPDATE_BUDGET = 2000; // usec + uint64_t updateExpiry = startTime + UPDATE_BUDGET; int numAvatarsUpdated = 0; int numAVatarsNotUpdated = 0; @@ -223,7 +221,7 @@ void AvatarManager::updateOtherAvatars(float deltaTime) { const float OUT_OF_VIEW_THRESHOLD = 0.5f * AvatarData::OUT_OF_VIEW_PENALTY; uint64_t now = usecTimestampNow(); - if (now < renderExpiry) { + if (now < updateExpiry) { // we're within budget bool inView = sortData.priority > OUT_OF_VIEW_THRESHOLD; if (inView && avatar->hasNewJointData()) { @@ -232,21 +230,13 @@ void AvatarManager::updateOtherAvatars(float deltaTime) { avatar->simulate(deltaTime, inView); avatar->updateRenderItem(pendingChanges); avatar->setLastRenderUpdateTime(startTime); - } else if (now < maxExpiry) { - // we've spent most of our time budget, but we still simulate() the avatar as it if were out of view - // --> some avatars may freeze until their priority trickles up - bool inView = sortData.priority > OUT_OF_VIEW_THRESHOLD; - if (inView && avatar->hasNewJointData()) { - numAVatarsNotUpdated++; - } - avatar->simulate(deltaTime, false); } else { - // we've spent ALL of our time budget --> bail on the rest of the avatar updates + // we've spent our full time budget --> bail on the rest of the avatar updates // --> more avatars may freeze until their priority trickles up // --> some scale or fade animations may glitch // --> some avatar velocity measurements may be a little off - // HACK: no time simulate, but we will take the time to count how many were tragically missed + // no time simulate, but we take the time to count how many were tragically missed bool inView = sortData.priority > OUT_OF_VIEW_THRESHOLD; if (!inView) { break; From 9880d40cfbaee075a2177ae684724c0677e5ffec Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Mon, 13 Mar 2017 14:38:56 -0700 Subject: [PATCH 150/302] Update edit.js light icons to now use the name entityIcon --- scripts/system/edit.js | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/scripts/system/edit.js b/scripts/system/edit.js index 2f7e0fabd7..acf22018b8 100644 --- a/scripts/system/edit.js +++ b/scripts/system/edit.js @@ -42,7 +42,7 @@ var selectionManager = SelectionManager; const PARTICLE_SYSTEM_URL = Script.resolvePath("assets/images/icon-particles.svg"); const POINT_LIGHT_URL = Script.resolvePath("assets/images/icon-point-light.svg"); const SPOT_LIGHT_URL = Script.resolvePath("assets/images/icon-spot-light.svg"); -var lightOverlayManager = new EntityIconOverlayManager(['Light', 'ParticleEffect'], function(entityID) { +var entityIconOverlayManager = new EntityIconOverlayManager(['Light', 'ParticleEffect'], function(entityID) { var properties = Entities.getEntityProperties(entityID, ['type', 'isSpotlight']); if (properties.type === 'Light') { return { @@ -67,7 +67,7 @@ var entityListTool = new EntityListTool(); selectionManager.addEventListener(function () { selectionDisplay.updateHandles(); - lightOverlayManager.updatePositions(); + entityIconOverlayManager.updatePositions(); }); const KEY_P = 80; //Key code for letter p used for Parenting hotkey. @@ -520,7 +520,7 @@ var toolBar = (function () { toolBar.writeProperty("shown", false); toolBar.writeProperty("shown", true); } - lightOverlayManager.setVisible(isActive && Menu.isOptionChecked(MENU_SHOW_LIGHTS_AND_PARTICLES_IN_EDIT_MODE)); + entityIconOverlayManager.setVisible(isActive && Menu.isOptionChecked(MENU_SHOW_LIGHTS_AND_PARTICLES_IN_EDIT_MODE)); Entities.setDrawZoneBoundaries(isActive && Menu.isOptionChecked(MENU_SHOW_ZONES_IN_EDIT_MODE)); }; @@ -585,8 +585,8 @@ function findClickedEntity(event) { } var entityResult = Entities.findRayIntersection(pickRay, true); // want precision picking - var lightResult = lightOverlayManager.findRayIntersection(pickRay); - lightResult.accurate = true; + var iconResult = entityIconOverlayManager.findRayIntersection(pickRay); + iconResult.accurate = true; if (pickZones) { Entities.setZonesArePickable(false); @@ -594,8 +594,8 @@ function findClickedEntity(event) { var result; - if (lightResult.intersects) { - result = lightResult; + if (iconResult.intersects) { + result = iconResult; } else if (entityResult.intersects) { result = entityResult; } else { @@ -1292,7 +1292,7 @@ function handeMenuEvent(menuItem) { } else if (menuItem === "Select All Entities Touching Box") { selectAllEtitiesInCurrentSelectionBox(true); } else if (menuItem === MENU_SHOW_LIGHTS_AND_PARTICLES_IN_EDIT_MODE) { - lightOverlayManager.setVisible(isActive && Menu.isOptionChecked(MENU_SHOW_LIGHTS_AND_PARTICLES_IN_EDIT_MODE)); + entityIconOverlayManager.setVisible(isActive && Menu.isOptionChecked(MENU_SHOW_LIGHTS_AND_PARTICLES_IN_EDIT_MODE)); } else if (menuItem === MENU_SHOW_ZONES_IN_EDIT_MODE) { Entities.setDrawZoneBoundaries(isActive && Menu.isOptionChecked(MENU_SHOW_ZONES_IN_EDIT_MODE)); } From 8a1bd781f083a42dab34ba93fb713d0f041e3872 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Mon, 13 Mar 2017 14:44:14 -0700 Subject: [PATCH 151/302] Fix overlay findRayIntersection not properly handing multiple drawInFront overlays --- interface/src/ui/overlays/Overlays.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/interface/src/ui/overlays/Overlays.cpp b/interface/src/ui/overlays/Overlays.cpp index ad7fbd6cc2..2b52f0817e 100644 --- a/interface/src/ui/overlays/Overlays.cpp +++ b/interface/src/ui/overlays/Overlays.cpp @@ -431,7 +431,9 @@ RayToOverlayIntersectionResult Overlays::findRayIntersectionInternal(const PickR if (thisOverlay->findRayIntersectionExtraInfo(ray.origin, ray.direction, thisDistance, thisFace, thisSurfaceNormal, thisExtraInfo)) { bool isDrawInFront = thisOverlay->getDrawInFront(); - if (thisDistance < bestDistance && (!bestIsFront || isDrawInFront)) { + if ((bestIsFront && isDrawInFront && thisDistance < bestDistance) + || (!bestIsFront && (isDrawInFront || thisDistance < bestDistance))) { + bestIsFront = isDrawInFront; bestDistance = thisDistance; result.intersects = true; From 0926b2df2a1b1c880d44f33a3d0ce0004d2697f1 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Mon, 13 Mar 2017 15:14:20 -0700 Subject: [PATCH 152/302] add LX snap turn to standard mapping --- interface/resources/controllers/standard.json | 32 ++++++++++++------- ...oggleAdvancedMovementForHandControllers.js | 1 - 2 files changed, 21 insertions(+), 12 deletions(-) diff --git a/interface/resources/controllers/standard.json b/interface/resources/controllers/standard.json index 04a3f560b6..b0654daaa7 100644 --- a/interface/resources/controllers/standard.json +++ b/interface/resources/controllers/standard.json @@ -2,6 +2,18 @@ "name": "Standard to Action", "channels": [ { "from": "Standard.LY", "to": "Actions.TranslateZ" }, + + { "from": "Standard.LX", + "when": [ "Application.InHMD", "Application.SnapTurn" ], + "to": "Actions.StepYaw", + "filters": + [ + { "type": "deadZone", "min": 0.15 }, + "constrainToInteger", + { "type": "pulse", "interval": 0.25 }, + { "type": "scale", "scale": 22.5 } + ] + }, { "from": "Standard.LX", "to": "Actions.TranslateX" }, { "from": "Standard.RX", @@ -15,29 +27,27 @@ { "type": "scale", "scale": 22.5 } ] }, - { "from": "Standard.RX", "to": "Actions.Yaw" }, - { "from": "Standard.RY", - "when": "Application.Grounded", - "to": "Actions.Up", - "filters": + + { "from": "Standard.RY", + "when": "Application.Grounded", + "to": "Actions.Up", + "filters": [ { "type": "deadZone", "min": 0.6 }, "invert" ] - }, + }, - { "from": "Standard.RY", "to": "Actions.Up", "filters": "invert"}, + { "from": "Standard.RY", "to": "Actions.Up", "filters": "invert"}, { "from": "Standard.Back", "to": "Actions.CycleCamera" }, { "from": "Standard.Start", "to": "Actions.ContextMenu" }, - { "from": "Standard.LT", "to": "Actions.LeftHandClick" }, + { "from": "Standard.LT", "to": "Actions.LeftHandClick" }, { "from": "Standard.RT", "to": "Actions.RightHandClick" }, - { "from": "Standard.LeftHand", "to": "Actions.LeftHand" }, + { "from": "Standard.LeftHand", "to": "Actions.LeftHand" }, { "from": "Standard.RightHand", "to": "Actions.RightHand" } ] } - - diff --git a/scripts/system/controllers/toggleAdvancedMovementForHandControllers.js b/scripts/system/controllers/toggleAdvancedMovementForHandControllers.js index 46464dc2e1..61d2df5d9e 100644 --- a/scripts/system/controllers/toggleAdvancedMovementForHandControllers.js +++ b/scripts/system/controllers/toggleAdvancedMovementForHandControllers.js @@ -72,7 +72,6 @@ function registerBasicMapping() { } return; }); - basicMapping.from(Controller.Standard.LX).to(Controller.Standard.RX); basicMapping.from(Controller.Standard.RY).to(function(value) { if (isDisabled) { return; From 8246dbafabe8e529688c572ba253af0033211f44 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Mon, 13 Mar 2017 15:15:36 -0700 Subject: [PATCH 153/302] Update particle explorer to work outside of entity list --- scripts/system/edit.js | 74 +++++++++++++++++++++++------------------- 1 file changed, 41 insertions(+), 33 deletions(-) diff --git a/scripts/system/edit.js b/scripts/system/edit.js index acf22018b8..42ebb1b8db 100644 --- a/scripts/system/edit.js +++ b/scripts/system/edit.js @@ -68,6 +68,44 @@ var entityListTool = new EntityListTool(); selectionManager.addEventListener(function () { selectionDisplay.updateHandles(); entityIconOverlayManager.updatePositions(); + + // Update particle explorer + var needToDestroyParticleExplorer = false; + if (selectionManager.selections.length === 1) { + var selectedEntityID = selectionManager.selections[0]; + if (selectedEntityID === selectedParticleEntityID) { + return; + } + var type = Entities.getEntityProperties(selectedEntityID, "type").type; + if (type === "ParticleEffect") { + // Destroy the old particles web view first + particleExplorerTool.destroyWebView(); + particleExplorerTool.createWebView(); + var properties = Entities.getEntityProperties(selectedEntityID); + var particleData = { + messageType: "particle_settings", + currentProperties: properties + }; + selectedParticleEntityID = selectedEntityID; + particleExplorerTool.setActiveParticleEntity(selectedParticleEntityID); + + particleExplorerTool.webView.webEventReceived.connect(function (data) { + data = JSON.parse(data); + if (data.messageType === "page_loaded") { + particleExplorerTool.webView.emitScriptEvent(JSON.stringify(particleData)); + } + }); + } else { + needToDestroyParticleExplorer = true; + } + } else { + needToDestroyParticleExplorer = true; + } + + if (needToDestroyParticleExplorer && selectedParticleEntityID !== null) { + selectedParticleEntityID = null; + particleExplorerTool.destroyWebView(); + } }); const KEY_P = 80; //Key code for letter p used for Parenting hotkey. @@ -1192,7 +1230,7 @@ function parentSelectedEntities() { } function deleteSelectedEntities() { if (SelectionManager.hasSelection()) { - selectedParticleEntity = 0; + selectedParticleEntityID = null; particleExplorerTool.destroyWebView(); SelectionManager.saveProperties(); var savedProperties = []; @@ -1967,43 +2005,13 @@ var showMenuItem = propertyMenu.addMenuItem("Show in Marketplace"); var propertiesTool = new PropertiesTool(); var particleExplorerTool = new ParticleExplorerTool(); -var selectedParticleEntity = 0; +var selectedParticleEntityID = null; entityListTool.webView.webEventReceived.connect(function (data) { data = JSON.parse(data); - if(data.type === 'parent') { + if (data.type === 'parent') { parentSelectedEntities(); } else if(data.type === 'unparent') { unparentSelectedEntities(); - } else if (data.type === "selectionUpdate") { - var ids = data.entityIds; - if (ids.length === 1) { - if (Entities.getEntityProperties(ids[0], "type").type === "ParticleEffect") { - if (JSON.stringify(selectedParticleEntity) === JSON.stringify(ids[0])) { - // This particle entity is already selected, so return - return; - } - // Destroy the old particles web view first - particleExplorerTool.destroyWebView(); - particleExplorerTool.createWebView(); - var properties = Entities.getEntityProperties(ids[0]); - var particleData = { - messageType: "particle_settings", - currentProperties: properties - }; - selectedParticleEntity = ids[0]; - particleExplorerTool.setActiveParticleEntity(ids[0]); - - particleExplorerTool.webView.webEventReceived.connect(function (data) { - data = JSON.parse(data); - if (data.messageType === "page_loaded") { - particleExplorerTool.webView.emitScriptEvent(JSON.stringify(particleData)); - } - }); - } else { - selectedParticleEntity = 0; - particleExplorerTool.destroyWebView(); - } - } } }); From 0b237fa644265458868f8c93668bd95ca992347c Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Mon, 13 Mar 2017 15:17:35 -0700 Subject: [PATCH 154/302] Revert "Revert "don't blend animations for otherAvatars"" This reverts commit 8bf9b70915002ca49b8ad100164ddd108c70093f. --- interface/src/avatar/CauterizedModel.cpp | 6 -- interface/src/avatar/CauterizedModel.h | 1 - interface/src/avatar/SkeletonModel.cpp | 4 +- libraries/animation/src/Rig.cpp | 71 ++++++++++++++---------- 4 files changed, 43 insertions(+), 39 deletions(-) diff --git a/interface/src/avatar/CauterizedModel.cpp b/interface/src/avatar/CauterizedModel.cpp index 843779dd3b..0c3d863649 100644 --- a/interface/src/avatar/CauterizedModel.cpp +++ b/interface/src/avatar/CauterizedModel.cpp @@ -95,12 +95,6 @@ void CauterizedModel::createCollisionRenderItemSet() { Model::createCollisionRenderItemSet(); } -// Called within Model::simulate call, below. -void CauterizedModel::updateRig(float deltaTime, glm::mat4 parentTransform) { - Model::updateRig(deltaTime, parentTransform); - _needsUpdateClusterMatrices = true; -} - void CauterizedModel::updateClusterMatrices() { PerformanceTimer perfTimer("CauterizedModel::updateClusterMatrices"); diff --git a/interface/src/avatar/CauterizedModel.h b/interface/src/avatar/CauterizedModel.h index 01e0b13650..ba12aee32b 100644 --- a/interface/src/avatar/CauterizedModel.h +++ b/interface/src/avatar/CauterizedModel.h @@ -37,7 +37,6 @@ public: void createVisibleRenderItemSet() override; void createCollisionRenderItemSet() override; - virtual void updateRig(float deltaTime, glm::mat4 parentTransform) override; virtual void updateClusterMatrices() override; void updateRenderItems() override; diff --git a/interface/src/avatar/SkeletonModel.cpp b/interface/src/avatar/SkeletonModel.cpp index 4b77323bba..88590a6f69 100644 --- a/interface/src/avatar/SkeletonModel.cpp +++ b/interface/src/avatar/SkeletonModel.cpp @@ -166,7 +166,7 @@ void SkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { _rig->computeMotionAnimationState(deltaTime, position, velocity, orientation, ccState); // evaluate AnimGraph animation and update jointStates. - CauterizedModel::updateRig(deltaTime, parentTransform); + Model::updateRig(deltaTime, parentTransform); Rig::EyeParameters eyeParams; eyeParams.worldHeadOrientation = headParams.worldHeadOrientation; @@ -179,7 +179,7 @@ void SkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { _rig->updateFromEyeParameters(eyeParams); } else { - CauterizedModel::updateRig(deltaTime, parentTransform); + Model::updateRig(deltaTime, parentTransform); // This is a little more work than we really want. // diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index c47da7c0b0..84e34adec7 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -1307,39 +1307,50 @@ void Rig::copyJointsIntoJointData(QVector& jointDataVec) const { void Rig::copyJointsFromJointData(const QVector& jointDataVec) { PerformanceTimer perfTimer("copyJoints"); PROFILE_RANGE(simulation_animation_detail, "copyJoints"); - if (_animSkeleton && jointDataVec.size() == (int)_internalPoseSet._relativePoses.size()) { - // make a vector of rotations in absolute-geometry-frame - const AnimPoseVec& absoluteDefaultPoses = _animSkeleton->getAbsoluteDefaultPoses(); - std::vector rotations; - rotations.reserve(absoluteDefaultPoses.size()); - const glm::quat rigToGeometryRot(glmExtractRotation(_rigToGeometryTransform)); - for (int i = 0; i < jointDataVec.size(); i++) { - const JointData& data = jointDataVec.at(i); - if (data.rotationSet) { - // JointData rotations are in absolute rig-frame so we rotate them to absolute geometry-frame - rotations.push_back(rigToGeometryRot * data.rotation); - } else { - rotations.push_back(absoluteDefaultPoses[i].rot()); - } - } + if (!_animSkeleton) { + return; + } + if (jointDataVec.size() != (int)_internalPoseSet._relativePoses.size()) { + // animations haven't fully loaded yet. + _internalPoseSet._relativePoses = _animSkeleton->getRelativeDefaultPoses(); + } - // convert rotations from absolute to parent relative. - _animSkeleton->convertAbsoluteRotationsToRelative(rotations); - - // store new relative poses - const AnimPoseVec& relativeDefaultPoses = _animSkeleton->getRelativeDefaultPoses(); - for (int i = 0; i < jointDataVec.size(); i++) { - const JointData& data = jointDataVec.at(i); - _internalPoseSet._relativePoses[i].scale() = Vectors::ONE; - _internalPoseSet._relativePoses[i].rot() = rotations[i]; - if (data.translationSet) { - // JointData translations are in scaled relative-frame so we scale back to regular relative-frame - _internalPoseSet._relativePoses[i].trans() = _invGeometryOffset.scale() * data.translation; - } else { - _internalPoseSet._relativePoses[i].trans() = relativeDefaultPoses[i].trans(); - } + // make a vector of rotations in absolute-geometry-frame + const AnimPoseVec& absoluteDefaultPoses = _animSkeleton->getAbsoluteDefaultPoses(); + std::vector rotations; + rotations.reserve(absoluteDefaultPoses.size()); + const glm::quat rigToGeometryRot(glmExtractRotation(_rigToGeometryTransform)); + for (int i = 0; i < jointDataVec.size(); i++) { + const JointData& data = jointDataVec.at(i); + if (data.rotationSet) { + // JointData rotations are in absolute rig-frame so we rotate them to absolute geometry-frame + rotations.push_back(rigToGeometryRot * data.rotation); + } else { + rotations.push_back(absoluteDefaultPoses[i].rot()); } } + + // convert rotations from absolute to parent relative. + _animSkeleton->convertAbsoluteRotationsToRelative(rotations); + + // store new relative poses + const AnimPoseVec& relativeDefaultPoses = _animSkeleton->getRelativeDefaultPoses(); + for (int i = 0; i < jointDataVec.size(); i++) { + const JointData& data = jointDataVec.at(i); + _internalPoseSet._relativePoses[i].scale() = Vectors::ONE; + _internalPoseSet._relativePoses[i].rot() = rotations[i]; + if (data.translationSet) { + // JointData translations are in scaled relative-frame so we scale back to regular relative-frame + _internalPoseSet._relativePoses[i].trans() = _invGeometryOffset.scale() * data.translation; + } else { + _internalPoseSet._relativePoses[i].trans() = relativeDefaultPoses[i].trans(); + } + } + + // build absolute poses and copy to externalPoseSet + buildAbsoluteRigPoses(_internalPoseSet._relativePoses, _internalPoseSet._absolutePoses); + QWriteLocker writeLock(&_externalPoseSetLock); + _externalPoseSet = _internalPoseSet; } void Rig::computeAvatarBoundingCapsule( From db73c80ba19f21416eac33e66db1cb929004d366 Mon Sep 17 00:00:00 2001 From: humbletim Date: Mon, 13 Mar 2017 19:16:00 -0400 Subject: [PATCH 155/302] remove unused meta property adapter --- .../entities/src/EntityScriptingInterface.cpp | 52 ------------------- .../entities/src/EntityScriptingInterface.h | 2 - 2 files changed, 54 deletions(-) diff --git a/libraries/entities/src/EntityScriptingInterface.cpp b/libraries/entities/src/EntityScriptingInterface.cpp index aa241023c8..54efa3d89f 100644 --- a/libraries/entities/src/EntityScriptingInterface.cpp +++ b/libraries/entities/src/EntityScriptingInterface.cpp @@ -683,54 +683,6 @@ bool EntityScriptingInterface::reloadServerScripts(QUuid entityID) { return client->reloadServerScript(entityID); } -#ifdef DEBUG_ENTITY_METADATA -// baseline example -- return parsed userData as a standard CPS callback -bool EntityPropertyMetadataRequest::_userData(EntityItemID entityID, QScriptValue handler) { - QScriptValue err, result; - auto engine = _engine; - if (!engine) { - qCDebug(entities) << __FUNCTION__ << " -- engine destroyed while inflight" << entityID; - return false; - } - auto entityScriptingInterface = DependencyManager::get(); - auto entityTree = entityScriptingInterface ? entityScriptingInterface->getEntityTree() : nullptr; - if (!entityTree) { - err = engine->makeError("Entities Tree unavailable", "InternalError"); - } else { - EntityItemPointer entity = entityTree->findEntityByID(entityID); - if (!entity) { - err = engine->makeError("entity not found"); - } else { - auto JSON = engine->globalObject().property("JSON"); - auto parsed = JSON.property("parse").call(JSON, QScriptValueList({ entity->getUserData() })); - if (engine->hasUncaughtException()) { - err = engine->cloneUncaughtException(__FUNCTION__); - engine->clearExceptions(); - } else { - result = parsed; - } - } - } - // this one second delay can be used with a Client script to query metadata and immediately Script.stop() - // (testing that the signal handler never gets called once the engine is destroyed) - // note: we still might want to check engine->isStopping() as an optimization in some places - QFutureWatcher *request = new QFutureWatcher; - QObject::connect(request, &QFutureWatcher::finished, engine, [=]() mutable { - if (!engine) { - qCDebug(entities) << "queryPropertyMetadata -- engine destroyed while inflight" << entityID; - return; - } - callScopedHandlerObject(handler, err, result); - request->deleteLater(); - }); - request->setFuture(QtConcurrent::run([]() -> QVariant { - QThread::sleep(1); - return QVariant(); - })); - return true; -} -#endif - bool EntityPropertyMetadataRequest::script(EntityItemID entityID, QScriptValue handler) { using LocalScriptStatusRequest = QFutureWatcher; @@ -842,10 +794,6 @@ bool EntityScriptingInterface::queryPropertyMetadata(QUuid entityID, QScriptValu return request.script(entityID, handler); } else if (name == "serverScripts") { return request.serverScripts(entityID, handler); -#ifdef DEBUG_ENTITY_METADATA - } else if (name == "userData") { - return request.userData(entityID, handler); -#endif } else { engine->raiseException(engine->makeError("metadata for property " + name + " is not yet queryable")); engine->maybeEmitUncaughtException(__FUNCTION__); diff --git a/libraries/entities/src/EntityScriptingInterface.h b/libraries/entities/src/EntityScriptingInterface.h index d6fe93b41e..7631541b3e 100644 --- a/libraries/entities/src/EntityScriptingInterface.h +++ b/libraries/entities/src/EntityScriptingInterface.h @@ -47,8 +47,6 @@ public: EntityPropertyMetadataRequest(BaseScriptEngine* engine) : _engine(engine) {}; bool script(EntityItemID entityID, QScriptValue handler); bool serverScripts(EntityItemID entityID, QScriptValue handler); - // this is used for internal testing and only available when DEBUG_ENTITY_METADATA is defined in the .cpp file - bool userData(EntityItemID entityID, QScriptValue handler); private: QPointer _engine; }; From 1eedce98251f726e87afe8130c18ebdc7227b3d8 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Mon, 13 Mar 2017 18:18:26 -0700 Subject: [PATCH 156/302] unmangle merge --- libraries/entities/src/EntityScriptingInterface.cpp | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/libraries/entities/src/EntityScriptingInterface.cpp b/libraries/entities/src/EntityScriptingInterface.cpp index 087518a393..ae34024bbc 100644 --- a/libraries/entities/src/EntityScriptingInterface.cpp +++ b/libraries/entities/src/EntityScriptingInterface.cpp @@ -881,7 +881,6 @@ bool EntityScriptingInterface::setPoints(QUuid entityID, std::function Date: Tue, 14 Mar 2017 01:23:04 +0000 Subject: [PATCH 157/302] change offsetPosition to use Camera.orientation --- scripts/system/audioMuteOverlay.js | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/scripts/system/audioMuteOverlay.js b/scripts/system/audioMuteOverlay.js index f112381040..db2b706b32 100644 --- a/scripts/system/audioMuteOverlay.js +++ b/scripts/system/audioMuteOverlay.js @@ -48,7 +48,7 @@ } function getOffsetPosition() { - return Vec3.sum(MyAvatar.getHeadPosition(), Quat.getFront(MyAvatar.headOrientation)); + return Vec3.sum(MyAvatar.getHeadPosition(), Quat.getFront(Camera.orientation)); } function onMuteToggled() { @@ -63,12 +63,11 @@ overlayPosition = getOffsetPosition(); overlayID = Overlays.addOverlay("sphere", { position: overlayPosition, - rotation: MyAvatar.orientation, + rotation: Camera.orientation, alpha: 0.9, dimensions: 0.1, solid: true, - ignoreRayIntersection: true, - visible: true + ignoreRayIntersection: true }); } @@ -99,7 +98,7 @@ blue: lerp(startColor.blue, endColor.blue, tweenPosition) }, position: overlayPosition, - rotation: MyAvatar.orientation, + rotation: Camera.orientation }); } From 2e0cac4676f902db8732481db90ee8e01d0d84e6 Mon Sep 17 00:00:00 2001 From: Triplelexx Date: Tue, 14 Mar 2017 01:25:39 +0000 Subject: [PATCH 158/302] add extra safeguards --- scripts/system/audioMuteOverlay.js | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/scripts/system/audioMuteOverlay.js b/scripts/system/audioMuteOverlay.js index db2b706b32..a037e9ac72 100644 --- a/scripts/system/audioMuteOverlay.js +++ b/scripts/system/audioMuteOverlay.js @@ -38,9 +38,16 @@ function update(dt) { if (!AudioDevice.getMuted()) { + if (overlayID) { + deleteOverlay(); + } return; } - updateOverlay(); + if (!overlayID) { + createOverlay(); + } else { + updateOverlay(); + } } function lerp(a, b, val) { From 34889d38c9e096f3ab3222fe13be9294cf85690a Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Wed, 1 Mar 2017 17:49:52 -0800 Subject: [PATCH 159/302] Override all roles instead of just fly --- scripts/tutorials/entity_scripts/sit.js | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/scripts/tutorials/entity_scripts/sit.js b/scripts/tutorials/entity_scripts/sit.js index 2ba19231e0..7968cde5f1 100644 --- a/scripts/tutorials/entity_scripts/sit.js +++ b/scripts/tutorials/entity_scripts/sit.js @@ -2,7 +2,7 @@ Script.include("/~/system/libraries/utils.js"); var SETTING_KEY = "com.highfidelity.avatar.isSitting"; - var ROLE = "fly"; + var ROLES = MyAvatar.getAnimationRoles(); var ANIMATION_URL = "https://s3-us-west-1.amazonaws.com/hifi-content/clement/production/animations/sitting_idle.fbx"; var ANIMATION_FPS = 30; var ANIMATION_FIRST_FRAME = 1; @@ -85,7 +85,9 @@ if (previousValue === "") { MyAvatar.characterControllerEnabled = false; MyAvatar.hmdLeanRecenterEnabled = false; - MyAvatar.overrideRoleAnimation(ROLE, ANIMATION_URL, ANIMATION_FPS, true, ANIMATION_FIRST_FRAME, ANIMATION_LAST_FRAME); + for (i in ROLES) { + MyAvatar.overrideRoleAnimation(ROLES[i], ANIMATION_URL, ANIMATION_FPS, true, ANIMATION_FIRST_FRAME, ANIMATION_LAST_FRAME); + } MyAvatar.resetSensorsAndBody(); } @@ -110,8 +112,10 @@ this.sitUp = function() { this.setSeatUser(null); - if (Settings.getValue(SETTING_KEY) === this.entityID) { - MyAvatar.restoreRoleAnimation(ROLE); + if (Settings.getValue(SETTING_KEY) === this.entityID) { + for (i in ROLES) { + MyAvatar.restoreRoleAnimation(ROLES[i]); + } MyAvatar.characterControllerEnabled = true; MyAvatar.hmdLeanRecenterEnabled = true; From 1a375432e09d02c3a0c43c1f558a8634d0842659 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Mon, 6 Mar 2017 16:09:31 -0800 Subject: [PATCH 160/302] Only grab primary clicks --- scripts/tutorials/entity_scripts/sit.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/tutorials/entity_scripts/sit.js b/scripts/tutorials/entity_scripts/sit.js index 7968cde5f1..9e41ba2824 100644 --- a/scripts/tutorials/entity_scripts/sit.js +++ b/scripts/tutorials/entity_scripts/sit.js @@ -250,11 +250,11 @@ this.cleanupOverlay(); } - this.clickDownOnEntity = function () { + this.clickDownOnEntity = function (id, event) { if (isInEditMode() || (MyAvatar.sessionUUID === this.getSeatUser())) { return; } - if (this.canSitDesktop()) { + if (event.isPrimaryButton && this.canSitDesktop()) { this.sitDown(); } } From f0d971fc6f47dfb444ad7289611f3c1abeaa38ea Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Mon, 6 Mar 2017 16:10:11 -0800 Subject: [PATCH 161/302] Put avatar in front of chair on situp to avoid infinite collisions --- scripts/tutorials/entity_scripts/sit.js | 41 +++++++++++++++++++++---- 1 file changed, 35 insertions(+), 6 deletions(-) diff --git a/scripts/tutorials/entity_scripts/sit.js b/scripts/tutorials/entity_scripts/sit.js index 9e41ba2824..1412740f85 100644 --- a/scripts/tutorials/entity_scripts/sit.js +++ b/scripts/tutorials/entity_scripts/sit.js @@ -14,6 +14,7 @@ var DESKTOP_UI_CHECK_INTERVAL = 250; var DESKTOP_MAX_DISTANCE = 5; var SIT_DELAY = 25 + var MAX_RESET_DISTANCE = 0.5 this.entityID = null; this.timers = {}; @@ -24,7 +25,7 @@ } this.unload = function() { if (MyAvatar.sessionUUID === this.getSeatUser()) { - this.sitUp(this.entityID); + this.sitUp(); } if (this.interval) { Script.clearInterval(this.interval); @@ -110,9 +111,13 @@ } this.sitUp = function() { + if (MyAvatar.sessionUUID !== this.getSeatUser()) { + // Duplicate call, bail + return; + } this.setSeatUser(null); - if (Settings.getValue(SETTING_KEY) === this.entityID) { + if (Settings.getValue(SETTING_KEY) === this.entityID) { for (i in ROLES) { MyAvatar.restoreRoleAnimation(ROLES[i]); } @@ -187,15 +192,26 @@ } } - this.update = function(dt) { if (MyAvatar.sessionUUID === this.getSeatUser()) { - var properties = Entities.getEntityProperties(this.entityID, ["position"]); + var properties = Entities.getEntityProperties(this.entityID); var avatarDistance = Vec3.distance(MyAvatar.position, properties.position); var ikError = MyAvatar.getIKErrorOnLastSolve(); if (avatarDistance > RELEASE_DISTANCE || ikError > MAX_IK_ERROR) { print("IK error: " + ikError + ", distance from chair: " + avatarDistance); - this.sitUp(this.entityID); + + // Move avatar in front of the chair to avoid getting stuck in collision hulls + if (avatarDistance < MAX_RESET_DISTANCE) { + var offset = { x: 0, y: 1.0, z: -0.5 - properties.dimensions.z * properties.registrationPoint.z }; + Vec3.print("offset:", Vec3.multiplyQbyV(properties.rotation, offset)); + var position = Vec3.sum(properties.position, Vec3.multiplyQbyV(properties.rotation, offset)); + MyAvatar.position = position; + } + + var that = this; + Script.setTimeout(function() { + that.sitUp(); + }, SIT_DELAY); } } } @@ -207,7 +223,20 @@ if (RELEASE_KEYS.indexOf(event.text) !== -1) { var that = this; this.timers[event.text] = Script.setTimeout(function() { - that.sitUp(); + var properties = Entities.getEntityProperties(that.entityID); + var avatarDistance = Vec3.distance(MyAvatar.position, properties.position); + + // Move avatar in front of the chair to avoid getting stuck in collision hulls + if (avatarDistance < MAX_RESET_DISTANCE) { + var offset = { x: 0, y: 1.0, z: -0.5 - properties.dimensions.z * properties.registrationPoint.z }; + Vec3.print("offset1:", Vec3.multiplyQbyV(properties.rotation, offset)); + var position = Vec3.sum(properties.position, Vec3.multiplyQbyV(properties.rotation, offset)); + MyAvatar.position = position; + } + + Script.setTimeout(function() { + that.sitUp(); + }, SIT_DELAY); }, RELEASE_TIME); } } From 9e6312ba0e66741327f31fc39dc5583515856bb8 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Mon, 13 Mar 2017 17:22:06 -0800 Subject: [PATCH 162/302] Make sit script more stable. --- scripts/tutorials/entity_scripts/sit.js | 147 ++++++++++++------------ 1 file changed, 76 insertions(+), 71 deletions(-) diff --git a/scripts/tutorials/entity_scripts/sit.js b/scripts/tutorials/entity_scripts/sit.js index 1412740f85..8dc73b0bd8 100644 --- a/scripts/tutorials/entity_scripts/sit.js +++ b/scripts/tutorials/entity_scripts/sit.js @@ -10,8 +10,8 @@ var RELEASE_KEYS = ['w', 'a', 's', 'd', 'UP', 'LEFT', 'DOWN', 'RIGHT']; var RELEASE_TIME = 500; // ms var RELEASE_DISTANCE = 0.2; // meters - var MAX_IK_ERROR = 20; - var DESKTOP_UI_CHECK_INTERVAL = 250; + var MAX_IK_ERROR = 30; + var DESKTOP_UI_CHECK_INTERVAL = 100; var DESKTOP_MAX_DISTANCE = 5; var SIT_DELAY = 25 var MAX_RESET_DISTANCE = 0.5 @@ -19,15 +19,16 @@ this.entityID = null; this.timers = {}; this.animStateHandlerID = null; + this.interval = null; this.preload = function(entityID) { this.entityID = entityID; } this.unload = function() { - if (MyAvatar.sessionUUID === this.getSeatUser()) { + if (Settings.getValue(SETTING_KEY) === this.entityID) { this.sitUp(); } - if (this.interval) { + if (this.interval !== null) { Script.clearInterval(this.interval); this.interval = null; } @@ -35,42 +36,60 @@ } this.setSeatUser = function(user) { - var userData = Entities.getEntityProperties(this.entityID, ["userData"]).userData; - userData = JSON.parse(userData); + try { + var userData = Entities.getEntityProperties(this.entityID, ["userData"]).userData; + userData = JSON.parse(userData); - if (user) { - userData.seat.user = user; - } else { - delete userData.seat.user; + if (user !== null) { + userData.seat.user = user; + } else { + delete userData.seat.user; + } + + Entities.editEntity(this.entityID, { + userData: JSON.stringify(userData) + }); + } catch (e) { + // Do Nothing } - - Entities.editEntity(this.entityID, { - userData: JSON.stringify(userData) - }); } this.getSeatUser = function() { - var properties = Entities.getEntityProperties(this.entityID, ["userData", "position"]); - var userData = JSON.parse(properties.userData); + try { + var properties = Entities.getEntityProperties(this.entityID, ["userData", "position"]); + var userData = JSON.parse(properties.userData); - if (userData.seat.user && userData.seat.user !== MyAvatar.sessionUUID) { - var avatar = AvatarList.getAvatar(userData.seat.user); - if (avatar && Vec3.distance(avatar.position, properties.position) > RELEASE_DISTANCE) { - return null; + // If MyAvatar return my uuid + if (userData.seat.user === MyAvatar.sessionUUID) { + return userData.seat.user; } + + + // If Avatar appears to be sitting + if (userData.seat.user) { + var avatar = AvatarList.getAvatar(userData.seat.user); + if (avatar && (Vec3.distance(avatar.position, properties.position) < RELEASE_DISTANCE)) { + return userData.seat.user; + } + } + } catch (e) { + // Do nothing } - return userData.seat.user; + + // Nobody on the seat + return null; } + // Is the seat used this.checkSeatForAvatar = function() { var seatUser = this.getSeatUser(); - var avatarIdentifiers = AvatarList.getAvatarIdentifiers(); - for (var i in avatarIdentifiers) { - var avatar = AvatarList.getAvatar(avatarIdentifiers[i]); - if (avatar && avatar.sessionUUID === seatUser) { - return true; - } + + // If MyAvatar appears to be sitting + if (seatUser === MyAvatar.sessionUUID) { + var properties = Entities.getEntityProperties(this.entityID, ["position"]); + return Vec3.distance(MyAvatar.position, properties.position) < RELEASE_DISTANCE; } - return false; + + return seatUser !== null; } this.sitDown = function() { @@ -79,10 +98,9 @@ return; } - this.setSeatUser(MyAvatar.sessionUUID); - var previousValue = Settings.getValue(SETTING_KEY); Settings.setValue(SETTING_KEY, this.entityID); + this.setSeatUser(MyAvatar.sessionUUID); if (previousValue === "") { MyAvatar.characterControllerEnabled = false; MyAvatar.hmdLeanRecenterEnabled = false; @@ -92,32 +110,34 @@ MyAvatar.resetSensorsAndBody(); } - var that = this; - Script.setTimeout(function() { - var properties = Entities.getEntityProperties(that.entityID, ["position", "rotation"]); - var index = MyAvatar.getJointIndex("Hips"); - MyAvatar.pinJoint(index, properties.position, properties.rotation); + var properties = Entities.getEntityProperties(this.entityID, ["position", "rotation"]); + var index = MyAvatar.getJointIndex("Hips"); + MyAvatar.pinJoint(index, properties.position, properties.rotation); - that.animStateHandlerID = MyAvatar.addAnimationStateHandler(function(properties) { - return { headType: 0 }; - }, ["headType"]); - Script.update.connect(that, that.update); - Controller.keyPressEvent.connect(that, that.keyPressed); - Controller.keyReleaseEvent.connect(that, that.keyReleased); - for (var i in RELEASE_KEYS) { - Controller.captureKeyEvents({ text: RELEASE_KEYS[i] }); - } - }, SIT_DELAY); + this.animStateHandlerID = MyAvatar.addAnimationStateHandler(function(properties) { + return { headType: 0 }; + }, ["headType"]); + Script.update.connect(this, this.update); + Controller.keyPressEvent.connect(this, this.keyPressed); + Controller.keyReleaseEvent.connect(this, this.keyReleased); + for (var i in RELEASE_KEYS) { + Controller.captureKeyEvents({ text: RELEASE_KEYS[i] }); + } } this.sitUp = function() { - if (MyAvatar.sessionUUID !== this.getSeatUser()) { - // Duplicate call, bail - return; + MyAvatar.removeAnimationStateHandler(this.animStateHandlerID); + Script.update.disconnect(this, this.update); + Controller.keyPressEvent.disconnect(this, this.keyPressed); + Controller.keyReleaseEvent.disconnect(this, this.keyReleased); + for (var i in RELEASE_KEYS) { + Controller.releaseKeyEvents({ text: RELEASE_KEYS[i] }); } - this.setSeatUser(null); + this.setSeatUser(null); if (Settings.getValue(SETTING_KEY) === this.entityID) { + Settings.setValue(SETTING_KEY, ""); + for (i in ROLES) { MyAvatar.restoreRoleAnimation(ROLES[i]); } @@ -133,16 +153,6 @@ MyAvatar.bodyPitch = 0.0; MyAvatar.bodyRoll = 0.0; }, SIT_DELAY); - - Settings.setValue(SETTING_KEY, ""); - } - - MyAvatar.removeAnimationStateHandler(this.animStateHandlerID); - Script.update.disconnect(this, this.update); - Controller.keyPressEvent.disconnect(this, this.keyPressed); - Controller.keyReleaseEvent.disconnect(this, this.keyReleased); - for (var i in RELEASE_KEYS) { - Controller.releaseKeyEvents({ text: RELEASE_KEYS[i] }); } } @@ -203,15 +213,11 @@ // Move avatar in front of the chair to avoid getting stuck in collision hulls if (avatarDistance < MAX_RESET_DISTANCE) { var offset = { x: 0, y: 1.0, z: -0.5 - properties.dimensions.z * properties.registrationPoint.z }; - Vec3.print("offset:", Vec3.multiplyQbyV(properties.rotation, offset)); var position = Vec3.sum(properties.position, Vec3.multiplyQbyV(properties.rotation, offset)); MyAvatar.position = position; } - var that = this; - Script.setTimeout(function() { - that.sitUp(); - }, SIT_DELAY); + this.sitUp(); } } } @@ -223,20 +229,19 @@ if (RELEASE_KEYS.indexOf(event.text) !== -1) { var that = this; this.timers[event.text] = Script.setTimeout(function() { + delete that.timers[event.text]; + var properties = Entities.getEntityProperties(that.entityID); var avatarDistance = Vec3.distance(MyAvatar.position, properties.position); // Move avatar in front of the chair to avoid getting stuck in collision hulls if (avatarDistance < MAX_RESET_DISTANCE) { var offset = { x: 0, y: 1.0, z: -0.5 - properties.dimensions.z * properties.registrationPoint.z }; - Vec3.print("offset1:", Vec3.multiplyQbyV(properties.rotation, offset)); var position = Vec3.sum(properties.position, Vec3.multiplyQbyV(properties.rotation, offset)); MyAvatar.position = position; } - Script.setTimeout(function() { - that.sitUp(); - }, SIT_DELAY); + that.sitUp(); }, RELEASE_TIME); } } @@ -256,7 +261,7 @@ } this.hoverEnterEntity = function(event) { - if (isInEditMode() || (MyAvatar.sessionUUID === this.getSeatUser())) { + if (isInEditMode() || this.interval !== null) { return; } @@ -272,7 +277,7 @@ }, DESKTOP_UI_CHECK_INTERVAL); } this.hoverLeaveEntity = function(event) { - if (this.interval) { + if (this.interval !== null) { Script.clearInterval(this.interval); this.interval = null; } @@ -280,7 +285,7 @@ } this.clickDownOnEntity = function (id, event) { - if (isInEditMode() || (MyAvatar.sessionUUID === this.getSeatUser())) { + if (isInEditMode()) { return; } if (event.isPrimaryButton && this.canSitDesktop()) { From c729ceccb5a4491bda3d0caa63a2a038bd651e7e Mon Sep 17 00:00:00 2001 From: Triplelexx Date: Tue, 14 Mar 2017 03:59:24 +0000 Subject: [PATCH 163/302] change color lerp to use HSV color --- scripts/system/audioMuteOverlay.js | 61 +++++++++++++++++++++--------- 1 file changed, 44 insertions(+), 17 deletions(-) diff --git a/scripts/system/audioMuteOverlay.js b/scripts/system/audioMuteOverlay.js index a037e9ac72..f83a3871ff 100644 --- a/scripts/system/audioMuteOverlay.js +++ b/scripts/system/audioMuteOverlay.js @@ -20,15 +20,16 @@ var overlayPosition = Vec3.ZERO; var tweenPosition = 0; + // colors are in HSV, h needs to be a value from 0-1 var startColor = { - red: 150, - green: 150, - blue: 150 + h: 0, + s: 0, + v: 0.67 }; var endColor = { - red: 255, - green: 0, - blue: 0 + h: 0, + s: 1, + v: 1 }; var overlayID; @@ -43,17 +44,39 @@ } return; } - if (!overlayID) { - createOverlay(); - } else { - updateOverlay(); - } + updateOverlay(); } function lerp(a, b, val) { return (1 - val) * a + val * b; } + // hsv conversion expects 0-1 values + function hsvToRgb(h, s, v) { + var r, g, b; + + var i = Math.floor(h * 6); + var f = h * 6 - i; + var p = v * (1 - s); + var q = v * (1 - f * s); + var t = v * (1 - (1 - f) * s); + + switch (i % 6) { + case 0: r = v, g = t, b = p; break; + case 1: r = q, g = v, b = p; break; + case 2: r = p, g = v, b = t; break; + case 3: r = p, g = q, b = v; break; + case 4: r = t, g = p, b = v; break; + case 5: r = v, g = p, b = q; break; + } + + return { + red: r * 255, + green: g * 255, + blue: b * 255 + } + } + function getOffsetPosition() { return Vec3.sum(MyAvatar.getHeadPosition(), Quat.getFront(Camera.orientation)); } @@ -98,11 +121,17 @@ overlayPosition.y = lerp(overlayPosition.y, offsetPosition.y, LERP_AMOUNT); overlayPosition.z = lerp(overlayPosition.z, offsetPosition.z, LERP_AMOUNT); + var rgbColor = hsvToRgb( + lerp(startColor.h, endColor.h, tweenPosition), + lerp(startColor.s, endColor.s, tweenPosition), + lerp(startColor.v, endColor.v, tweenPosition) + ); + Overlays.editOverlay(overlayID, { color: { - red: lerp(startColor.red, endColor.red, tweenPosition), - green: lerp(startColor.green, endColor.green, tweenPosition), - blue: lerp(startColor.blue, endColor.blue, tweenPosition) + red: rgbColor.red, + green: rgbColor.green, + blue: rgbColor.blue }, position: overlayPosition, rotation: Camera.orientation @@ -114,9 +143,7 @@ } function cleanup() { - if (overlayID) { - deleteOverlay(); - } + deleteOverlay(); AudioDevice.muteToggled.disconnect(onMuteToggled); Script.update.disconnect(update); } From 30c16984c06c5c16dae7e646560bfcad42ea4c47 Mon Sep 17 00:00:00 2001 From: Triplelexx Date: Tue, 14 Mar 2017 06:07:22 +0000 Subject: [PATCH 164/302] change offsetPosition to use Camera.position --- scripts/system/audioMuteOverlay.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/system/audioMuteOverlay.js b/scripts/system/audioMuteOverlay.js index f83a3871ff..49f2b5c618 100644 --- a/scripts/system/audioMuteOverlay.js +++ b/scripts/system/audioMuteOverlay.js @@ -78,7 +78,7 @@ } function getOffsetPosition() { - return Vec3.sum(MyAvatar.getHeadPosition(), Quat.getFront(Camera.orientation)); + return Vec3.sum(Camera.position, Quat.getFront(Camera.orientation)); } function onMuteToggled() { From 4a1be69be402e4bba02cb0868ee84cf19db6e5e8 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Tue, 14 Mar 2017 07:11:38 -0700 Subject: [PATCH 165/302] writeOBJToString --- libraries/fbx/src/OBJWriter.cpp | 11 +++++++++++ libraries/fbx/src/OBJWriter.h | 2 +- .../script-engine/src/ModelScriptingInterface.cpp | 9 +-------- 3 files changed, 13 insertions(+), 9 deletions(-) diff --git a/libraries/fbx/src/OBJWriter.cpp b/libraries/fbx/src/OBJWriter.cpp index b2d391fea3..c424290b9b 100644 --- a/libraries/fbx/src/OBJWriter.cpp +++ b/libraries/fbx/src/OBJWriter.cpp @@ -126,3 +126,14 @@ bool writeOBJToFile(QString path, QList meshes) { file.close(); return success; } + +QString writeOBJToString(QList meshes) { + QString result; + QTextStream outStream(&result, QIODevice::ReadWrite); + bool success; + success = writeOBJToTextStream(outStream, meshes); + if (success) { + return result; + } + return QString(""); +} diff --git a/libraries/fbx/src/OBJWriter.h b/libraries/fbx/src/OBJWriter.h index dcf5a3ee39..b6e20e1ae6 100644 --- a/libraries/fbx/src/OBJWriter.h +++ b/libraries/fbx/src/OBJWriter.h @@ -21,6 +21,6 @@ using MeshPointer = std::shared_ptr; bool writeOBJToTextStream(QTextStream& out, QList meshes); bool writeOBJToFile(QString path, QList meshes); - +QString writeOBJToString(QList meshes); #endif // hifi_objwriter_h diff --git a/libraries/script-engine/src/ModelScriptingInterface.cpp b/libraries/script-engine/src/ModelScriptingInterface.cpp index 922dee2e8f..4ba82edf7d 100644 --- a/libraries/script-engine/src/ModelScriptingInterface.cpp +++ b/libraries/script-engine/src/ModelScriptingInterface.cpp @@ -44,17 +44,10 @@ void meshesFromScriptValue(const QScriptValue& value, MeshProxyList &out) { } QString ModelScriptingInterface::meshToOBJ(MeshProxyList in) { - bool success; - QString filename = "/tmp/okokok.obj"; - QList meshes; foreach (const MeshProxy* meshProxy, in) { meshes.append(meshProxy->getMeshPointer()); } - success = writeOBJToFile(filename, meshes); - if (!success) { - return ""; - } - return filename; + return writeOBJToString(meshes); } From 1ccf715e3b8a4378926c423166772e53aeab9177 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Tue, 14 Mar 2017 09:09:14 -0700 Subject: [PATCH 166/302] Don't crash when failing to convert a texture to KTX format --- .../src/model-networking/TextureCache.cpp | 22 +++++++++++-------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/libraries/model-networking/src/model-networking/TextureCache.cpp b/libraries/model-networking/src/model-networking/TextureCache.cpp index b9977e33d8..386e9239a4 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.cpp +++ b/libraries/model-networking/src/model-networking/TextureCache.cpp @@ -526,16 +526,20 @@ void ImageReader::read() { // Save the image into a KTXFile auto ktx = gpu::Texture::serialize(*texture); - const char* data = reinterpret_cast(ktx->_storage->data()); - size_t length = ktx->_storage->size(); - KTXFilePointer file; - auto& ktxCache = DependencyManager::get()->_ktxCache; - if (!ktx || !(file = ktxCache.writeFile(data, KTXCache::Metadata(_hash, length)))) { - qCWarning(modelnetworking) << _url << "file cache failed"; + if (ktx) { + const char* data = reinterpret_cast(ktx->_storage->data()); + size_t length = ktx->_storage->size(); + KTXFilePointer file; + auto& ktxCache = DependencyManager::get()->_ktxCache; + if (!ktx || !(file = ktxCache.writeFile(data, KTXCache::Metadata(_hash, length)))) { + qCWarning(modelnetworking) << _url << "file cache failed"; + } else { + resource.staticCast()->_file = file; + auto ktx = file->getKTX(); + texture->setKtxBacking(ktx); + } } else { - resource.staticCast()->_file = file; - auto ktx = file->getKTX(); - texture->setKtxBacking(ktx); + qCWarning(modelnetworking) << "Unable to serialize texture to KTX " << _url; } } From 4dd9a7c37fdd93c4b97f9f27c547fa8c87999faa Mon Sep 17 00:00:00 2001 From: Triplelexx Date: Tue, 14 Mar 2017 16:51:43 +0000 Subject: [PATCH 167/302] add simple easing function --- scripts/system/audioMuteOverlay.js | 3 --- 1 file changed, 3 deletions(-) diff --git a/scripts/system/audioMuteOverlay.js b/scripts/system/audioMuteOverlay.js index 49f2b5c618..8cf86c4395 100644 --- a/scripts/system/audioMuteOverlay.js +++ b/scripts/system/audioMuteOverlay.js @@ -122,9 +122,6 @@ overlayPosition.z = lerp(overlayPosition.z, offsetPosition.z, LERP_AMOUNT); var rgbColor = hsvToRgb( - lerp(startColor.h, endColor.h, tweenPosition), - lerp(startColor.s, endColor.s, tweenPosition), - lerp(startColor.v, endColor.v, tweenPosition) ); Overlays.editOverlay(overlayID, { From 8a39ce16bdc7a0d296f8c89f0fa2bbee5c2949d2 Mon Sep 17 00:00:00 2001 From: Triplelexx Date: Tue, 14 Mar 2017 16:57:45 +0000 Subject: [PATCH 168/302] add simple easing function dodgy add push --- scripts/system/audioMuteOverlay.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/scripts/system/audioMuteOverlay.js b/scripts/system/audioMuteOverlay.js index 8cf86c4395..826498d909 100644 --- a/scripts/system/audioMuteOverlay.js +++ b/scripts/system/audioMuteOverlay.js @@ -51,6 +51,10 @@ return (1 - val) * a + val * b; } + function easeIn(t) { + return Math.pow(t / 1, 5); + } + // hsv conversion expects 0-1 values function hsvToRgb(h, s, v) { var r, g, b; @@ -122,6 +126,9 @@ overlayPosition.z = lerp(overlayPosition.z, offsetPosition.z, LERP_AMOUNT); var rgbColor = hsvToRgb( + lerp(startColor.h, endColor.h, easeIn(tweenPosition)), + lerp(startColor.s, endColor.s, easeIn(tweenPosition)), + lerp(startColor.v, endColor.v, easeIn(tweenPosition)) ); Overlays.editOverlay(overlayID, { From 5b28f3a3c427659c694146d870c5fe0406eabf11 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Tue, 14 Mar 2017 10:08:34 -0700 Subject: [PATCH 169/302] cleanups --- libraries/fbx/src/OBJWriter.cpp | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/libraries/fbx/src/OBJWriter.cpp b/libraries/fbx/src/OBJWriter.cpp index c424290b9b..4983dfef60 100644 --- a/libraries/fbx/src/OBJWriter.cpp +++ b/libraries/fbx/src/OBJWriter.cpp @@ -13,6 +13,7 @@ #include #include "model/Geometry.h" #include "OBJWriter.h" +#include "ModelFormatLogging.h" static QString formatFloat(double n) { // limit precision to 6, but don't output trailing zeros. @@ -23,6 +24,18 @@ static QString formatFloat(double n) { if (s.endsWith(".")) { s.remove(s.size() - 1, 1); } + + // check for non-numbers. if we get NaN or inf or scientific notation, just return 0 + for (int i = 0; i < s.length(); i++) { + auto c = s.at(i).toLatin1(); + if (c != '-' && + c != '.' && + (c < '0' || c > '9')) { + qCDebug(modelformat) << "OBJWriter zeroing bad vertex coordinate:" << s << "because of" << c; + return QString("0"); + } + } + return s; } @@ -70,7 +83,7 @@ bool writeOBJToTextStream(QTextStream& out, QList meshes) { out << "g part-" << nth++ << "\n"; // model::Mesh::TRIANGLES - // XXX handle other formats + // TODO -- handle other formats gpu::BufferView::Iterator indexItr = indexBuffer.cbegin(); indexItr += part._startIndex; @@ -108,13 +121,13 @@ bool writeOBJToTextStream(QTextStream& out, QList meshes) { bool writeOBJToFile(QString path, QList meshes) { if (QFileInfo(path).exists() && !QFile::remove(path)) { - qDebug() << "OBJ writer failed, file exists:" << path; // XXX qCDebug + qCDebug(modelformat) << "OBJ writer failed, file exists:" << path; return false; } QFile file(path); if (!file.open(QIODevice::WriteOnly)) { - qDebug() << "OBJ writer failed to open output file:" << path; // XXX qCDebug + qCDebug(modelformat) << "OBJ writer failed to open output file:" << path; return false; } From 6971aeb081f8d81869959dfb797a461f16001245 Mon Sep 17 00:00:00 2001 From: Triplelexx Date: Tue, 14 Mar 2017 17:18:11 +0000 Subject: [PATCH 170/302] additional reference information --- scripts/system/audioMuteOverlay.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/scripts/system/audioMuteOverlay.js b/scripts/system/audioMuteOverlay.js index 826498d909..c72b62f1f7 100644 --- a/scripts/system/audioMuteOverlay.js +++ b/scripts/system/audioMuteOverlay.js @@ -55,7 +55,10 @@ return Math.pow(t / 1, 5); } - // hsv conversion expects 0-1 values + // Adapted from a blog post on http://mjijackson.com + // Michael J. I. Jackson + // Converts an HSV color value to RGB. Conversion formula adapted from http: //en.wikipedia.org/wiki/HSV_color_space. + // Assumes h, s, and v are contained in the set[0, 1] function hsvToRgb(h, s, v) { var r, g, b; From b14a4faf610269a56554be1b05f2b6ea1310cb34 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Tue, 14 Mar 2017 10:54:22 -0800 Subject: [PATCH 171/302] Guard against script loading before avatar --- scripts/tutorials/entity_scripts/sit.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/tutorials/entity_scripts/sit.js b/scripts/tutorials/entity_scripts/sit.js index 8dc73b0bd8..ffce196572 100644 --- a/scripts/tutorials/entity_scripts/sit.js +++ b/scripts/tutorials/entity_scripts/sit.js @@ -2,7 +2,6 @@ Script.include("/~/system/libraries/utils.js"); var SETTING_KEY = "com.highfidelity.avatar.isSitting"; - var ROLES = MyAvatar.getAnimationRoles(); var ANIMATION_URL = "https://s3-us-west-1.amazonaws.com/hifi-content/clement/production/animations/sitting_idle.fbx"; var ANIMATION_FPS = 30; var ANIMATION_FIRST_FRAME = 1; @@ -104,6 +103,7 @@ if (previousValue === "") { MyAvatar.characterControllerEnabled = false; MyAvatar.hmdLeanRecenterEnabled = false; + var ROLES = MyAvatar.getAnimationRoles(); for (i in ROLES) { MyAvatar.overrideRoleAnimation(ROLES[i], ANIMATION_URL, ANIMATION_FPS, true, ANIMATION_FIRST_FRAME, ANIMATION_LAST_FRAME); } @@ -137,7 +137,7 @@ this.setSeatUser(null); if (Settings.getValue(SETTING_KEY) === this.entityID) { Settings.setValue(SETTING_KEY, ""); - + var ROLES = MyAvatar.getAnimationRoles(); for (i in ROLES) { MyAvatar.restoreRoleAnimation(ROLES[i]); } From 5e716f15b544a7fe2bb308e339c072a48b61168a Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Tue, 14 Mar 2017 13:48:24 -0700 Subject: [PATCH 172/302] Protect against missing texture cache on shutdown --- .../src/model-networking/TextureCache.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/libraries/model-networking/src/model-networking/TextureCache.cpp b/libraries/model-networking/src/model-networking/TextureCache.cpp index 85513f038a..e51b016904 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.cpp +++ b/libraries/model-networking/src/model-networking/TextureCache.cpp @@ -189,9 +189,13 @@ NetworkTexturePointer TextureCache::getTexture(const QUrl& url, Type type, const } gpu::TexturePointer getFallbackTextureForType(NetworkTexture::Type type) { - auto textureCache = DependencyManager::get(); - gpu::TexturePointer result; + auto textureCache = DependencyManager::get(); + // Since this can be called on a background thread, there's a chance that the cache + // will be destroyed by the time we request it + if (!textureCache) { + return result; + } switch (type) { case NetworkTexture::DEFAULT_TEXTURE: case NetworkTexture::ALBEDO_TEXTURE: From 1fe02477b0457945d4d8da808e666e3166f391a5 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 14 Mar 2017 14:21:21 -0700 Subject: [PATCH 173/302] fix LX behaviour in snap/advanced/basic --- interface/resources/controllers/standard.json | 18 ++++++++++++++---- interface/src/Application.cpp | 7 ++++++- interface/src/avatar/MyAvatar.cpp | 1 + interface/src/avatar/MyAvatar.h | 3 +++ 4 files changed, 24 insertions(+), 5 deletions(-) diff --git a/interface/resources/controllers/standard.json b/interface/resources/controllers/standard.json index b0654daaa7..9e3b2f4d13 100644 --- a/interface/resources/controllers/standard.json +++ b/interface/resources/controllers/standard.json @@ -4,7 +4,10 @@ { "from": "Standard.LY", "to": "Actions.TranslateZ" }, { "from": "Standard.LX", - "when": [ "Application.InHMD", "Application.SnapTurn" ], + "when": [ + "Application.InHMD", "!Application.AdvancedMovement", + "Application.SnapTurn", "!Standard.RX" + ], "to": "Actions.StepYaw", "filters": [ @@ -14,7 +17,12 @@ { "type": "scale", "scale": 22.5 } ] }, - { "from": "Standard.LX", "to": "Actions.TranslateX" }, + { "from": "Standard.LX", "to": "Actions.TranslateX", + "when": [ "Application.AdvancedMovement" ] + }, + { "from": "Standard.LX", "to": "Actions.Yaw", + "when": [ "!Application.AdvancedMovement", "!Application.SnapTurn" ] + }, { "from": "Standard.RX", "when": [ "Application.InHMD", "Application.SnapTurn" ], @@ -27,8 +35,10 @@ { "type": "scale", "scale": 22.5 } ] }, - { "from": "Standard.RX", "to": "Actions.Yaw" }, - + { "from": "Standard.RX", "to": "Actions.Yaw", + "when": [ "!Application.SnapTurn" ] + }, + { "from": "Standard.RY", "when": "Application.Grounded", "to": "Actions.Up", diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 4bebc80bd1..706fd424fd 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -423,6 +423,7 @@ static const QString STATE_CAMERA_THIRD_PERSON = "CameraThirdPerson"; static const QString STATE_CAMERA_ENTITY = "CameraEntity"; static const QString STATE_CAMERA_INDEPENDENT = "CameraIndependent"; static const QString STATE_SNAP_TURN = "SnapTurn"; +static const QString STATE_ADVANCED_MOVEMENT_CONTROLS = "AdvancedMovement"; static const QString STATE_GROUNDED = "Grounded"; static const QString STATE_NAV_FOCUSED = "NavigationFocused"; @@ -513,7 +514,7 @@ bool setupEssentials(int& argc, char** argv) { DependencyManager::set(); controller::StateController::setStateVariables({ { STATE_IN_HMD, STATE_CAMERA_FULL_SCREEN_MIRROR, STATE_CAMERA_FIRST_PERSON, STATE_CAMERA_THIRD_PERSON, STATE_CAMERA_ENTITY, STATE_CAMERA_INDEPENDENT, - STATE_SNAP_TURN, STATE_GROUNDED, STATE_NAV_FOCUSED } }); + STATE_SNAP_TURN, STATE_ADVANCED_MOVEMENT_CONTROLS, STATE_GROUNDED, STATE_NAV_FOCUSED } }); DependencyManager::set(); DependencyManager::set(); DependencyManager::set(); @@ -1127,6 +1128,10 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo _applicationStateDevice->setInputVariant(STATE_SNAP_TURN, []() -> float { return qApp->getMyAvatar()->getSnapTurn() ? 1 : 0; }); + _applicationStateDevice->setInputVariant(STATE_ADVANCED_MOVEMENT_CONTROLS, []() -> float { + return qApp->getMyAvatar()->useAdvancedMovementControls() ? 1 : 0; + }); + _applicationStateDevice->setInputVariant(STATE_GROUNDED, []() -> float { return qApp->getMyAvatar()->getCharacterController()->onGround() ? 1 : 0; }); diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 969268c549..b045d2c005 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -104,6 +104,7 @@ MyAvatar::MyAvatar(RigPointer rig) : _eyeContactTarget(LEFT_EYE), _realWorldFieldOfView("realWorldFieldOfView", DEFAULT_REAL_WORLD_FIELD_OF_VIEW_DEGREES), + _useAdvancedMovementControls("advancedMovementForHandControllersIsChecked", false), _hmdSensorMatrix(), _hmdSensorOrientation(), _hmdSensorPosition(), diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index 3cc665b533..1a5883edca 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -171,6 +171,8 @@ public: Q_INVOKABLE void setHMDLeanRecenterEnabled(bool value) { _hmdLeanRecenterEnabled = value; } Q_INVOKABLE bool getHMDLeanRecenterEnabled() const { return _hmdLeanRecenterEnabled; } + bool useAdvancedMovementControls() const { return _useAdvancedMovementControls.get(); } + // get/set avatar data void saveData(); void loadData(); @@ -423,6 +425,7 @@ private: glm::vec3 _trackedHeadPosition; Setting::Handle _realWorldFieldOfView; + Setting::Handle _useAdvancedMovementControls; // private methods void updateOrientation(float deltaTime); From 490cb834899c80edd94ad143b0bfc6df0acd606d Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 14 Mar 2017 15:03:01 -0700 Subject: [PATCH 174/302] move advanced movement control changes to MyAvatar --- interface/src/avatar/MyAvatar.h | 3 +++ .../toggleAdvancedMovementForHandControllers.js | 14 ++++++-------- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index 1a5883edca..f892a8d8b7 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -86,6 +86,7 @@ class MyAvatar : public Avatar { Q_PROPERTY(bool hmdLeanRecenterEnabled READ getHMDLeanRecenterEnabled WRITE setHMDLeanRecenterEnabled) Q_PROPERTY(bool characterControllerEnabled READ getCharacterControllerEnabled WRITE setCharacterControllerEnabled) + Q_PROPERTY(bool useAdvancedMovementControls READ useAdvancedMovementControls WRITE setUseAdvancedMovementControls) public: explicit MyAvatar(RigPointer rig); @@ -172,6 +173,8 @@ public: Q_INVOKABLE bool getHMDLeanRecenterEnabled() const { return _hmdLeanRecenterEnabled; } bool useAdvancedMovementControls() const { return _useAdvancedMovementControls.get(); } + void setUseAdvancedMovementControls(bool useAdvancedMovementControls) + { _useAdvancedMovementControls.set(useAdvancedMovementControls); } // get/set avatar data void saveData(); diff --git a/scripts/system/controllers/toggleAdvancedMovementForHandControllers.js b/scripts/system/controllers/toggleAdvancedMovementForHandControllers.js index 61d2df5d9e..a453e50d1b 100644 --- a/scripts/system/controllers/toggleAdvancedMovementForHandControllers.js +++ b/scripts/system/controllers/toggleAdvancedMovementForHandControllers.js @@ -17,15 +17,14 @@ var mappingName, basicMapping, isChecked; var TURN_RATE = 1000; var MENU_ITEM_NAME = "Advanced Movement For Hand Controllers"; -var SETTINGS_KEY = 'advancedMovementForHandControllersIsChecked'; var isDisabled = false; -var previousSetting = Settings.getValue(SETTINGS_KEY); -if (previousSetting === '' || previousSetting === false || previousSetting === 'false') { +var previousSetting = MyAvatar.useAdvancedMovementControls; +if (previousSetting === false) { previousSetting = false; isChecked = false; } -if (previousSetting === true || previousSetting === 'true') { +if (previousSetting === true) { previousSetting = true; isChecked = true; } @@ -37,7 +36,6 @@ function addAdvancedMovementItemToSettingsMenu() { isCheckable: true, isChecked: previousSetting }); - } function rotate180() { @@ -111,10 +109,10 @@ function menuItemEvent(menuItem) { if (menuItem == MENU_ITEM_NAME) { isChecked = Menu.isOptionChecked(MENU_ITEM_NAME); if (isChecked === true) { - Settings.setValue(SETTINGS_KEY, true); + MyAvatar.setUseAdvancedMovementControls(true); disableMappings(); - } else if (isChecked === false) { - Settings.setValue(SETTINGS_KEY, false); + } else if (isChecked === false) + MyAvatar.setUseAdvancedMovementControls(true); enableMappings(); } } From 900615fcd5ac071eed64046c16c3f496786390d1 Mon Sep 17 00:00:00 2001 From: samcake Date: Tue, 14 Mar 2017 15:53:29 -0700 Subject: [PATCH 175/302] Fix the unsupported ktx format, it was coming from the BUmp map case --- libraries/model/src/model/TextureMap.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/libraries/model/src/model/TextureMap.cpp b/libraries/model/src/model/TextureMap.cpp index e41b5d0454..d07eae2166 100755 --- a/libraries/model/src/model/TextureMap.cpp +++ b/libraries/model/src/model/TextureMap.cpp @@ -354,7 +354,6 @@ gpu::Texture* TextureUsage::createNormalTextureFromBumpImage(const QImage& srcIm const double pStrength = 2.0; int width = image.width(); int height = image.height(); - // QImage result(width, height, QImage::Format_RGB888); QImage result(width, height, QImage::Format_ARGB32); From a9658ce9fb7be82821759539fb1bc0820998f3c8 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Tue, 14 Mar 2017 17:15:23 -0700 Subject: [PATCH 176/302] Revert "Trivial PR to trigger build" This reverts commit a0befa7f958543445ffbc9d4a7975e92752cb127. --- interface/resources/qml/controls/README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/interface/resources/qml/controls/README.md b/interface/resources/qml/controls/README.md index 1245e42e97..7f05f32a63 100644 --- a/interface/resources/qml/controls/README.md +++ b/interface/resources/qml/controls/README.md @@ -1,3 +1,2 @@ These are our own custom controls with the same names as existing controls, but customized for readability / usability in VR. - From b1e5e43faa068dcf3f2092d7b9c24022d5931324 Mon Sep 17 00:00:00 2001 From: Zach Pomerantz Date: Mon, 13 Mar 2017 18:52:31 -0400 Subject: [PATCH 177/302] mv AudioNoiseGate to audio --- libraries/audio-client/src/AudioClient.h | 2 +- libraries/{audio-client => audio}/src/AudioNoiseGate.cpp | 8 ++++---- libraries/{audio-client => audio}/src/AudioNoiseGate.h | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) rename libraries/{audio-client => audio}/src/AudioNoiseGate.cpp (99%) rename libraries/{audio-client => audio}/src/AudioNoiseGate.h (96%) diff --git a/libraries/audio-client/src/AudioClient.h b/libraries/audio-client/src/AudioClient.h index 512b4bb3c1..7e9acc0586 100644 --- a/libraries/audio-client/src/AudioClient.h +++ b/libraries/audio-client/src/AudioClient.h @@ -45,13 +45,13 @@ #include #include #include +#include #include #include #include "AudioIOStats.h" -#include "AudioNoiseGate.h" #ifdef _WIN32 #pragma warning( push ) diff --git a/libraries/audio-client/src/AudioNoiseGate.cpp b/libraries/audio/src/AudioNoiseGate.cpp similarity index 99% rename from libraries/audio-client/src/AudioNoiseGate.cpp rename to libraries/audio/src/AudioNoiseGate.cpp index 8a9134b5dc..c99b31b989 100644 --- a/libraries/audio-client/src/AudioNoiseGate.cpp +++ b/libraries/audio/src/AudioNoiseGate.cpp @@ -1,6 +1,6 @@ // // AudioNoiseGate.cpp -// interface/src/audio +// libraries/audio // // Created by Stephen Birarda on 2014-12-16. // Copyright 2014 High Fidelity, Inc. @@ -9,12 +9,12 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +#include "AudioNoiseGate.h" + #include #include -#include - -#include "AudioNoiseGate.h" +#include "AudioConstants.h" const float AudioNoiseGate::CLIPPING_THRESHOLD = 0.90f; diff --git a/libraries/audio-client/src/AudioNoiseGate.h b/libraries/audio/src/AudioNoiseGate.h similarity index 96% rename from libraries/audio-client/src/AudioNoiseGate.h rename to libraries/audio/src/AudioNoiseGate.h index f72e92b0d5..d47cebf853 100644 --- a/libraries/audio-client/src/AudioNoiseGate.h +++ b/libraries/audio/src/AudioNoiseGate.h @@ -1,6 +1,6 @@ // // AudioNoiseGate.h -// interface/src/audio +// libraries/audio // // Created by Stephen Birarda on 2014-12-16. // Copyright 2014 High Fidelity, Inc. @@ -48,4 +48,4 @@ private: int _blocksToClose; }; -#endif // hifi_AudioNoiseGate_h \ No newline at end of file +#endif // hifi_AudioNoiseGate_h From e6fbdf8f0699c5bf2741668befc4eda072cb19a2 Mon Sep 17 00:00:00 2001 From: Zach Pomerantz Date: Wed, 15 Feb 2017 06:03:50 +0000 Subject: [PATCH 178/302] add isNoiseGateEnabled to Agent --- assignment-client/src/Agent.cpp | 8 ++++++++ assignment-client/src/Agent.h | 6 ++++++ 2 files changed, 14 insertions(+) diff --git a/assignment-client/src/Agent.cpp b/assignment-client/src/Agent.cpp index be23dcfa25..3562975c4d 100644 --- a/assignment-client/src/Agent.cpp +++ b/assignment-client/src/Agent.cpp @@ -483,6 +483,14 @@ void Agent::setIsListeningToAudioStream(bool isListeningToAudioStream) { _isListeningToAudioStream = isListeningToAudioStream; } +void Agent::setIsNoiseGateEnabled(bool isNoiseGateEnabled) { + if (QThread::currentThread() != thread()) { + QMetaObject::invokeMethod(this, "setIsNoiseGateEnabled", Q_ARG(bool, isNoiseGateEnabled)); + return; + } + _isNoiseGateEnabled = isNoiseGateEnabled; +} + void Agent::setIsAvatar(bool isAvatar) { // this must happen on Agent's main thread if (QThread::currentThread() != thread()) { diff --git a/assignment-client/src/Agent.h b/assignment-client/src/Agent.h index 0ce7b71d5d..6bece4e945 100644 --- a/assignment-client/src/Agent.h +++ b/assignment-client/src/Agent.h @@ -38,6 +38,7 @@ class Agent : public ThreadedAssignment { Q_PROPERTY(bool isAvatar READ isAvatar WRITE setIsAvatar) Q_PROPERTY(bool isPlayingAvatarSound READ isPlayingAvatarSound) Q_PROPERTY(bool isListeningToAudioStream READ isListeningToAudioStream WRITE setIsListeningToAudioStream) + Q_PROPERTY(bool isNoiseGateEnabled READ isNoiseGateEnabled WRITE setIsNoiseGateEnabled) Q_PROPERTY(float lastReceivedAudioLoudness READ getLastReceivedAudioLoudness) Q_PROPERTY(QUuid sessionUUID READ getSessionUUID) @@ -52,6 +53,9 @@ public: bool isListeningToAudioStream() const { return _isListeningToAudioStream; } void setIsListeningToAudioStream(bool isListeningToAudioStream); + bool isNoiseGateEnabled() const { return _isNoiseGateEnabled; } + void setIsNoiseGateEnabled(bool isNoiseGateEnabled); + float getLastReceivedAudioLoudness() const { return _lastReceivedAudioLoudness; } QUuid getSessionUUID() const; @@ -106,6 +110,8 @@ private: QTimer* _avatarIdentityTimer = nullptr; QHash _outgoingScriptAudioSequenceNumbers; + bool _isNoiseGateEnabled { false }; + CodecPluginPointer _codec; QString _selectedCodecName; Encoder* _encoder { nullptr }; From 188530590a18ebe8efc02b94b1f5e60a1304aafc Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 14 Mar 2017 17:16:49 -0700 Subject: [PATCH 179/302] add missing curly brace to script --- .../controllers/toggleAdvancedMovementForHandControllers.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/system/controllers/toggleAdvancedMovementForHandControllers.js b/scripts/system/controllers/toggleAdvancedMovementForHandControllers.js index a453e50d1b..8371259915 100644 --- a/scripts/system/controllers/toggleAdvancedMovementForHandControllers.js +++ b/scripts/system/controllers/toggleAdvancedMovementForHandControllers.js @@ -111,7 +111,7 @@ function menuItemEvent(menuItem) { if (isChecked === true) { MyAvatar.setUseAdvancedMovementControls(true); disableMappings(); - } else if (isChecked === false) + } else if (isChecked === false) { MyAvatar.setUseAdvancedMovementControls(true); enableMappings(); } From 40b87171a9c73675244e9d6e7ebc985d10ad0a7d Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Tue, 14 Mar 2017 17:49:39 -0700 Subject: [PATCH 180/302] quiet warnings --- .../entities-renderer/src/RenderablePolyVoxEntityItem.cpp | 6 +++--- libraries/fbx/src/FBXReader_Node.cpp | 3 ++- libraries/fbx/src/OBJWriter.cpp | 6 +++--- libraries/script-engine/src/MeshProxy.h | 2 +- 4 files changed, 9 insertions(+), 8 deletions(-) diff --git a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp index d60c2081e2..88a0d25b7f 100644 --- a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp @@ -1272,9 +1272,9 @@ void RenderablePolyVoxEntityItem::recomputeMesh() { gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::RAW))); std::vector parts; - parts.emplace_back(model::Mesh::Part(0, // startIndex - vecIndices.size(), // numIndices - 0, // baseVertex + parts.emplace_back(model::Mesh::Part((model::Index)0, // startIndex + (model::Index)vecIndices.size(), // numIndices + (model::Index)0, // baseVertex model::Mesh::TRIANGLES)); // topology mesh->setPartBuffer(gpu::BufferView(new gpu::Buffer(parts.size() * sizeof(model::Mesh::Part), (gpu::Byte*) parts.data()), gpu::Element::PART_DRAWCALL)); diff --git a/libraries/fbx/src/FBXReader_Node.cpp b/libraries/fbx/src/FBXReader_Node.cpp index d814f58dab..d987f885eb 100644 --- a/libraries/fbx/src/FBXReader_Node.cpp +++ b/libraries/fbx/src/FBXReader_Node.cpp @@ -54,7 +54,8 @@ template QVariant readBinaryArray(QDataStream& in, int& position) { in.readRawData(compressed.data() + sizeof(quint32), compressedLength); position += compressedLength; arrayData = qUncompress(compressed); - if (arrayData.isEmpty() || arrayData.size() != (sizeof(T) * arrayLength)) { // answers empty byte array if corrupt + if (arrayData.isEmpty() || + (unsigned int)arrayData.size() != (sizeof(T) * arrayLength)) { // answers empty byte array if corrupt throw QString("corrupt fbx file"); } } else { diff --git a/libraries/fbx/src/OBJWriter.cpp b/libraries/fbx/src/OBJWriter.cpp index 4983dfef60..11f87d900a 100644 --- a/libraries/fbx/src/OBJWriter.cpp +++ b/libraries/fbx/src/OBJWriter.cpp @@ -75,7 +75,7 @@ bool writeOBJToTextStream(QTextStream& out, QList meshes) { const gpu::BufferView& indexBuffer = mesh->getIndexBuffer(); // const gpu::BufferView& vertexBuffer = mesh->getVertexBuffer(); - int partCount = mesh->getNumParts(); + size_t partCount = mesh->getNumParts(); qDebug() << "writeOBJToTextStream part count is" << partCount; for (int partIndex = 0; partIndex < partCount; partIndex++) { const model::Mesh::Part& part = partBuffer.get(partIndex); @@ -88,11 +88,11 @@ bool writeOBJToTextStream(QTextStream& out, QList meshes) { indexItr += part._startIndex; int indexCount = 0; - while (indexItr != indexBuffer.cend() && indexItr != part._numIndices) { + while (indexItr != indexBuffer.cend() && (indexItr != part._numIndices)) { uint32_t index0 = *indexItr; indexItr++; indexCount++; - if (indexItr == indexBuffer.cend() || indexItr == part._numIndices) { + if (indexItr == indexBuffer.cend() || (indexItr == part._numIndices)) { qDebug() << "OBJWriter -- index buffer length isn't multiple of 3"; break; } diff --git a/libraries/script-engine/src/MeshProxy.h b/libraries/script-engine/src/MeshProxy.h index 93c5ce5918..3504d11e8b 100644 --- a/libraries/script-engine/src/MeshProxy.h +++ b/libraries/script-engine/src/MeshProxy.h @@ -25,7 +25,7 @@ public: MeshPointer getMeshPointer() const { return _mesh; } - Q_INVOKABLE int getNumVertices() const { return _mesh->getNumVertices(); } + Q_INVOKABLE int getNumVertices() const { (int)return _mesh->getNumVertices(); } Q_INVOKABLE glm::vec3 getPos3(int index) const { return _mesh->getPos3(index); } From 08ad56d25427759a84b1fb235522da2ec1290599 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Tue, 14 Mar 2017 18:05:53 -0700 Subject: [PATCH 181/302] quiet warnings --- libraries/fbx/src/OBJWriter.cpp | 2 +- libraries/script-engine/src/MeshProxy.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/fbx/src/OBJWriter.cpp b/libraries/fbx/src/OBJWriter.cpp index 11f87d900a..dc6ad2e58b 100644 --- a/libraries/fbx/src/OBJWriter.cpp +++ b/libraries/fbx/src/OBJWriter.cpp @@ -75,7 +75,7 @@ bool writeOBJToTextStream(QTextStream& out, QList meshes) { const gpu::BufferView& indexBuffer = mesh->getIndexBuffer(); // const gpu::BufferView& vertexBuffer = mesh->getVertexBuffer(); - size_t partCount = mesh->getNumParts(); + model::Index partCount = mesh->getNumParts(); qDebug() << "writeOBJToTextStream part count is" << partCount; for (int partIndex = 0; partIndex < partCount; partIndex++) { const model::Mesh::Part& part = partBuffer.get(partIndex); diff --git a/libraries/script-engine/src/MeshProxy.h b/libraries/script-engine/src/MeshProxy.h index 3504d11e8b..82f5038348 100644 --- a/libraries/script-engine/src/MeshProxy.h +++ b/libraries/script-engine/src/MeshProxy.h @@ -25,7 +25,7 @@ public: MeshPointer getMeshPointer() const { return _mesh; } - Q_INVOKABLE int getNumVertices() const { (int)return _mesh->getNumVertices(); } + Q_INVOKABLE int getNumVertices() const { return (int)_mesh->getNumVertices(); } Q_INVOKABLE glm::vec3 getPos3(int index) const { return _mesh->getPos3(index); } From 7ddb5ff7707318083f69ef94faff43524b8f801d Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 14 Mar 2017 18:09:17 -0700 Subject: [PATCH 182/302] use correct boolean when changing AMC --- .../controllers/toggleAdvancedMovementForHandControllers.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/system/controllers/toggleAdvancedMovementForHandControllers.js b/scripts/system/controllers/toggleAdvancedMovementForHandControllers.js index 8371259915..5cf27a0ec5 100644 --- a/scripts/system/controllers/toggleAdvancedMovementForHandControllers.js +++ b/scripts/system/controllers/toggleAdvancedMovementForHandControllers.js @@ -112,7 +112,7 @@ function menuItemEvent(menuItem) { MyAvatar.setUseAdvancedMovementControls(true); disableMappings(); } else if (isChecked === false) { - MyAvatar.setUseAdvancedMovementControls(true); + MyAvatar.setUseAdvancedMovementControls(false); enableMappings(); } } From 0785172398baef65342fe0bca25247cdf27f8c25 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Tue, 14 Mar 2017 19:01:13 -0700 Subject: [PATCH 183/302] quiet warnings --- libraries/fbx/src/OBJWriter.cpp | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/libraries/fbx/src/OBJWriter.cpp b/libraries/fbx/src/OBJWriter.cpp index dc6ad2e58b..e0269be080 100644 --- a/libraries/fbx/src/OBJWriter.cpp +++ b/libraries/fbx/src/OBJWriter.cpp @@ -40,8 +40,6 @@ static QString formatFloat(double n) { } bool writeOBJToTextStream(QTextStream& out, QList meshes) { - qDebug() << "writeOBJToTextStream mesh count is" << meshes.size(); - // each mesh's vertices are numbered from zero. We're combining all their vertices into one list here, // so keep track of the start index for each mesh. QList meshVertexStartOffset; @@ -75,8 +73,7 @@ bool writeOBJToTextStream(QTextStream& out, QList meshes) { const gpu::BufferView& indexBuffer = mesh->getIndexBuffer(); // const gpu::BufferView& vertexBuffer = mesh->getVertexBuffer(); - model::Index partCount = mesh->getNumParts(); - qDebug() << "writeOBJToTextStream part count is" << partCount; + model::Index partCount = (model::Index)mesh->getNumParts(); for (int partIndex = 0; partIndex < partCount; partIndex++) { const model::Mesh::Part& part = partBuffer.get(partIndex); @@ -88,19 +85,30 @@ bool writeOBJToTextStream(QTextStream& out, QList meshes) { indexItr += part._startIndex; int indexCount = 0; - while (indexItr != indexBuffer.cend() && (indexItr != part._numIndices)) { + while (indexItr != indexBuffer.cend()) { + if (indexItr == part._numIndices) { + break; + } uint32_t index0 = *indexItr; indexItr++; indexCount++; - if (indexItr == indexBuffer.cend() || (indexItr == part._numIndices)) { - qDebug() << "OBJWriter -- index buffer length isn't multiple of 3"; + if (indexItr == indexBuffer.cend()) { + qCDebug(modelformat) << "OBJWriter -- index buffer length isn't multiple of 3"; + break; + } + if (indexItr == part._numIndices) { + qCDebug(modelformat) << "OBJWriter -- index buffer length isn't multiple of 3"; break; } uint32_t index1 = *indexItr; indexItr++; indexCount++; - if (indexItr == indexBuffer.cend() || indexItr == part._numIndices) { - qDebug() << "OBJWriter -- index buffer length isn't multiple of 3"; + if (indexItr == indexBuffer.cend()) { + qCDebug(modelformat) << "OBJWriter -- index buffer length isn't multiple of 3"; + break; + } + if (indexItr == part._numIndices) { + qCDebug(modelformat) << "OBJWriter -- index buffer length isn't multiple of 3"; break; } uint32_t index2 = *indexItr; From 7afe091c52c5ec02572347e159a25225a3f19553 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Tue, 14 Mar 2017 19:47:30 -0700 Subject: [PATCH 184/302] quiet warnings --- libraries/fbx/src/OBJWriter.cpp | 17 +++-------------- 1 file changed, 3 insertions(+), 14 deletions(-) diff --git a/libraries/fbx/src/OBJWriter.cpp b/libraries/fbx/src/OBJWriter.cpp index e0269be080..42b5774245 100644 --- a/libraries/fbx/src/OBJWriter.cpp +++ b/libraries/fbx/src/OBJWriter.cpp @@ -85,29 +85,18 @@ bool writeOBJToTextStream(QTextStream& out, QList meshes) { indexItr += part._startIndex; int indexCount = 0; - while (indexItr != indexBuffer.cend()) { - if (indexItr == part._numIndices) { - break; - } + while (indexItr != indexBuffer.cend() && indexCount < part._numIndices) { uint32_t index0 = *indexItr; indexItr++; indexCount++; - if (indexItr == indexBuffer.cend()) { - qCDebug(modelformat) << "OBJWriter -- index buffer length isn't multiple of 3"; - break; - } - if (indexItr == part._numIndices) { + if (indexItr == indexBuffer.cend() || indexCount >= part._numIndices) { qCDebug(modelformat) << "OBJWriter -- index buffer length isn't multiple of 3"; break; } uint32_t index1 = *indexItr; indexItr++; indexCount++; - if (indexItr == indexBuffer.cend()) { - qCDebug(modelformat) << "OBJWriter -- index buffer length isn't multiple of 3"; - break; - } - if (indexItr == part._numIndices) { + if (indexItr == indexBuffer.cend() || indexCount >= part._numIndices) { qCDebug(modelformat) << "OBJWriter -- index buffer length isn't multiple of 3"; break; } From 84d157ab18503c188457d3febdab35e7058c2b18 Mon Sep 17 00:00:00 2001 From: Triplelexx Date: Wed, 15 Mar 2017 10:31:09 +0000 Subject: [PATCH 185/302] handle overlay lifetime during update, remove color conv --- scripts/system/audioMuteOverlay.js | 77 ++++++++---------------------- 1 file changed, 19 insertions(+), 58 deletions(-) diff --git a/scripts/system/audioMuteOverlay.js b/scripts/system/audioMuteOverlay.js index c72b62f1f7..458efad6d0 100644 --- a/scripts/system/audioMuteOverlay.js +++ b/scripts/system/audioMuteOverlay.js @@ -20,31 +20,31 @@ var overlayPosition = Vec3.ZERO; var tweenPosition = 0; - // colors are in HSV, h needs to be a value from 0-1 var startColor = { - h: 0, - s: 0, - v: 0.67 + red: 170, + green: 170, + blue: 170 }; var endColor = { - h: 0, - s: 1, - v: 1 + red: 255, + green: 0, + blue: 0 }; var overlayID; - AudioDevice.muteToggled.connect(onMuteToggled); Script.update.connect(update); Script.scriptEnding.connect(cleanup); function update(dt) { if (!AudioDevice.getMuted()) { - if (overlayID) { + if (hasOverlay()) { deleteOverlay(); } - return; - } - updateOverlay(); + } else if (!hasOverlay()) { + createOverlay(); + } else { + updateOverlay(); + } } function lerp(a, b, val) { @@ -55,47 +55,10 @@ return Math.pow(t / 1, 5); } - // Adapted from a blog post on http://mjijackson.com - // Michael J. I. Jackson - // Converts an HSV color value to RGB. Conversion formula adapted from http: //en.wikipedia.org/wiki/HSV_color_space. - // Assumes h, s, and v are contained in the set[0, 1] - function hsvToRgb(h, s, v) { - var r, g, b; - - var i = Math.floor(h * 6); - var f = h * 6 - i; - var p = v * (1 - s); - var q = v * (1 - f * s); - var t = v * (1 - (1 - f) * s); - - switch (i % 6) { - case 0: r = v, g = t, b = p; break; - case 1: r = q, g = v, b = p; break; - case 2: r = p, g = v, b = t; break; - case 3: r = p, g = q, b = v; break; - case 4: r = t, g = p, b = v; break; - case 5: r = v, g = p, b = q; break; - } - - return { - red: r * 255, - green: g * 255, - blue: b * 255 - } - } - function getOffsetPosition() { return Vec3.sum(Camera.position, Quat.getFront(Camera.orientation)); } - function onMuteToggled() { - if (AudioDevice.getMuted()) { - createOverlay(); - } else { - deleteOverlay(); - } - } - function createOverlay() { overlayPosition = getOffsetPosition(); overlayID = Overlays.addOverlay("sphere", { @@ -108,6 +71,10 @@ }); } + function hasOverlay() { + return Overlays.getProperty(overlayID, "position") !== undefined; + } + function updateOverlay() { // increase by TWEEN_SPEED until completion if (tweenPosition < 1) { @@ -128,17 +95,11 @@ overlayPosition.y = lerp(overlayPosition.y, offsetPosition.y, LERP_AMOUNT); overlayPosition.z = lerp(overlayPosition.z, offsetPosition.z, LERP_AMOUNT); - var rgbColor = hsvToRgb( - lerp(startColor.h, endColor.h, easeIn(tweenPosition)), - lerp(startColor.s, endColor.s, easeIn(tweenPosition)), - lerp(startColor.v, endColor.v, easeIn(tweenPosition)) - ); - Overlays.editOverlay(overlayID, { color: { - red: rgbColor.red, - green: rgbColor.green, - blue: rgbColor.blue + red: lerp(startColor.red, endColor.red, easeIn(tweenPosition)), + green: lerp(startColor.green, endColor.green, easeIn(tweenPosition)), + blue: lerp(startColor.blue, endColor.blue, easeIn(tweenPosition)) }, position: overlayPosition, rotation: Camera.orientation From 9e0091ebe07c77683cce149e24e6b6ef91fff9fc Mon Sep 17 00:00:00 2001 From: Triplelexx Date: Wed, 15 Mar 2017 11:08:54 +0000 Subject: [PATCH 186/302] use utils.js, move easing function, use mixes some cleanup --- scripts/developer/libraries/utils.js | 3 +++ scripts/system/audioMuteOverlay.js | 27 ++++++--------------------- 2 files changed, 9 insertions(+), 21 deletions(-) diff --git a/scripts/developer/libraries/utils.js b/scripts/developer/libraries/utils.js index f39f4d7913..0da9703c87 100644 --- a/scripts/developer/libraries/utils.js +++ b/scripts/developer/libraries/utils.js @@ -311,3 +311,6 @@ clamp = function(val, min, max){ return Math.max(min, Math.min(max, val)) } +easeIn = function(t) { + return Math.pow(t / 1, 5); +} diff --git a/scripts/system/audioMuteOverlay.js b/scripts/system/audioMuteOverlay.js index 458efad6d0..d08fafc88d 100644 --- a/scripts/system/audioMuteOverlay.js +++ b/scripts/system/audioMuteOverlay.js @@ -15,8 +15,10 @@ // (function() { // BEGIN LOCAL_SCOPE + Script.include(utilsPath); + var TWEEN_SPEED = 0.025; - var LERP_AMOUNT = 0.25; + var MIX_AMOUNT = 0.25; var overlayPosition = Vec3.ZERO; var tweenPosition = 0; @@ -47,14 +49,6 @@ } } - function lerp(a, b, val) { - return (1 - val) * a + val * b; - } - - function easeIn(t) { - return Math.pow(t / 1, 5); - } - function getOffsetPosition() { return Vec3.sum(Camera.position, Quat.getFront(Camera.orientation)); } @@ -88,19 +82,10 @@ endColor[color] = storedColor; } } - - // update position based on LERP_AMOUNT - var offsetPosition = getOffsetPosition(); - overlayPosition.x = lerp(overlayPosition.x, offsetPosition.x, LERP_AMOUNT); - overlayPosition.y = lerp(overlayPosition.y, offsetPosition.y, LERP_AMOUNT); - overlayPosition.z = lerp(overlayPosition.z, offsetPosition.z, LERP_AMOUNT); - + // mix previous position with new and mix colors + overlayPosition = Vec3.mix(overlayPosition, getOffsetPosition(), MIX_AMOUNT); Overlays.editOverlay(overlayID, { - color: { - red: lerp(startColor.red, endColor.red, easeIn(tweenPosition)), - green: lerp(startColor.green, endColor.green, easeIn(tweenPosition)), - blue: lerp(startColor.blue, endColor.blue, easeIn(tweenPosition)) - }, + color: colorMix(startColor, endColor, easeIn(tweenPosition)), position: overlayPosition, rotation: Camera.orientation }); From 41041c711f8752f96b7fb0c18a65ed9d1c9364ab Mon Sep 17 00:00:00 2001 From: Triplelexx Date: Wed, 15 Mar 2017 11:09:13 +0000 Subject: [PATCH 187/302] use utils.js --- scripts/system/audioMuteOverlay.js | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/system/audioMuteOverlay.js b/scripts/system/audioMuteOverlay.js index d08fafc88d..d7bb843561 100644 --- a/scripts/system/audioMuteOverlay.js +++ b/scripts/system/audioMuteOverlay.js @@ -15,6 +15,7 @@ // (function() { // BEGIN LOCAL_SCOPE + var utilsPath = Script.resolvePath('../developer/libraries/utils.js?v='+ Date.now()); Script.include(utilsPath); var TWEEN_SPEED = 0.025; From 5883bd39dc255fcdb28e282c52fdbb664d6f6ff0 Mon Sep 17 00:00:00 2001 From: Triplelexx Date: Wed, 15 Mar 2017 11:17:27 +0000 Subject: [PATCH 188/302] rename color to component --- scripts/system/audioMuteOverlay.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/scripts/system/audioMuteOverlay.js b/scripts/system/audioMuteOverlay.js index d7bb843561..473e06b556 100644 --- a/scripts/system/audioMuteOverlay.js +++ b/scripts/system/audioMuteOverlay.js @@ -77,10 +77,10 @@ } else { // after tween completion reset to zero and flip values to ping pong tweenPosition = 0; - for (var color in startColor) { - var storedColor = startColor[color]; - startColor[color] = endColor[color]; - endColor[color] = storedColor; + for (var component in startColor) { + var storedColor = startColor[component]; + startColor[component] = endColor[component]; + endColor[component] = storedColor; } } // mix previous position with new and mix colors From bfc51d8222f1bd85a5014c557f9bfdcf9322b07e Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 14 Mar 2017 18:10:44 -0700 Subject: [PATCH 189/302] use property not function for advanced movement controls --- .../controllers/toggleAdvancedMovementForHandControllers.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/system/controllers/toggleAdvancedMovementForHandControllers.js b/scripts/system/controllers/toggleAdvancedMovementForHandControllers.js index 5cf27a0ec5..8f8f5c2d3e 100644 --- a/scripts/system/controllers/toggleAdvancedMovementForHandControllers.js +++ b/scripts/system/controllers/toggleAdvancedMovementForHandControllers.js @@ -109,10 +109,10 @@ function menuItemEvent(menuItem) { if (menuItem == MENU_ITEM_NAME) { isChecked = Menu.isOptionChecked(MENU_ITEM_NAME); if (isChecked === true) { - MyAvatar.setUseAdvancedMovementControls(true); + MyAvatar.advancedMovementControls = true; disableMappings(); } else if (isChecked === false) { - MyAvatar.setUseAdvancedMovementControls(false); + MyAvatar.advancedMovementControls = false; enableMappings(); } } From 2d0e11e2d1a54e9b330889dc66d163534458fd2b Mon Sep 17 00:00:00 2001 From: Zach Pomerantz Date: Mon, 13 Mar 2017 19:22:33 -0400 Subject: [PATCH 190/302] add AudioNoiseGate to recording --- libraries/audio-client/src/AudioClient.cpp | 197 +++++++++------------ libraries/audio-client/src/AudioClient.h | 15 +- 2 files changed, 93 insertions(+), 119 deletions(-) diff --git a/libraries/audio-client/src/AudioClient.cpp b/libraries/audio-client/src/AudioClient.cpp index c32b5600d9..4f8f4c9766 100644 --- a/libraries/audio-client/src/AudioClient.cpp +++ b/libraries/audio-client/src/AudioClient.cpp @@ -184,7 +184,6 @@ AudioClient::AudioClient() : _outgoingAvatarAudioSequenceNumber(0), _audioOutputIODevice(_localInjectorsStream, _receivedAudioStream, this), _stats(&_receivedAudioStream), - _inputGate(), _positionGetter(DEFAULT_POSITION_GETTER), _orientationGetter(DEFAULT_ORIENTATION_GETTER) { // avoid putting a lock in the device callback @@ -971,14 +970,87 @@ void AudioClient::handleLocalEchoAndReverb(QByteArray& inputByteArray) { } } -void AudioClient::handleAudioInput() { +void AudioClient::handleAudioInput(QByteArray& audioBuffer) { + if (_muted) { + _lastInputLoudness = 0.0f; + _timeSinceLastClip = 0.0f; + } else { + int16_t* samples = reinterpret_cast(audioBuffer.data()); + int numSamples = audioBuffer.size() / sizeof(AudioConstants::SAMPLE_SIZE); + bool didClip = false; + + bool shouldRemoveDCOffset = !_isPlayingBackRecording && !_isStereoInput; + if (shouldRemoveDCOffset) { + _noiseGate.removeDCOffset(samples, numSamples); + } + + bool shouldNoiseGate = (_isPlayingBackRecording || !_isStereoInput) && _isNoiseGateEnabled; + if (shouldNoiseGate) { + _noiseGate.gateSamples(samples, numSamples); + _lastInputLoudness = _noiseGate.getLastLoudness(); + didClip = _noiseGate.clippedInLastBlock(); + } else { + float loudness = 0.0f; + for (int i = 0; i < numSamples; ++i) { + int16_t sample = std::abs(samples[i]); + loudness += (float)sample; + didClip = didClip || + (sample > (AudioConstants::MAX_SAMPLE_VALUE * AudioNoiseGate::CLIPPING_THRESHOLD)); + } + _lastInputLoudness = fabs(loudness / numSamples); + } + + if (didClip) { + _timeSinceLastClip = 0.0f; + } else if (_timeSinceLastClip >= 0.0f) { + _timeSinceLastClip += (float)numSamples / (float)AudioConstants::SAMPLE_RATE; + } + + emit inputReceived({ audioBuffer.data(), numSamples }); + + if (_noiseGate.openedInLastBlock()) { + emit noiseGateOpened(); + } else if (_noiseGate.closedInLastBlock()) { + emit noiseGateClosed(); + } + } + + // the codec needs a flush frame before sending silent packets, so + // do not send one if the gate closed in this block (eventually this can be crossfaded). + auto packetType = _shouldEchoToServer ? + PacketType::MicrophoneAudioWithEcho : PacketType::MicrophoneAudioNoEcho; + if (_lastInputLoudness == 0.0f && !_noiseGate.closedInLastBlock()) { + packetType = PacketType::SilentAudioFrame; + _silentOutbound.increment(); + } else { + _audioOutbound.increment(); + } + + Transform audioTransform; + audioTransform.setTranslation(_positionGetter()); + audioTransform.setRotation(_orientationGetter()); + + QByteArray encodedBuffer; + if (_encoder) { + _encoder->encode(audioBuffer, encodedBuffer); + } else { + encodedBuffer = audioBuffer; + } + + emitAudioPacket(encodedBuffer.data(), encodedBuffer.size(), _outgoingAvatarAudioSequenceNumber, + audioTransform, avatarBoundingBoxCorner, avatarBoundingBoxScale, + packetType, _selectedCodecName); + _stats.sentPacket(); +} + +void AudioClient::handleMicAudioInput() { if (!_inputDevice || _isPlayingBackRecording) { return; } // input samples required to produce exactly NETWORK_FRAME_SAMPLES of output - const int inputSamplesRequired = (_inputToNetworkResampler ? - _inputToNetworkResampler->getMinInput(AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL) : + const int inputSamplesRequired = (_inputToNetworkResampler ? + _inputToNetworkResampler->getMinInput(AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL) : AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL) * _inputFormat.channelCount(); const auto inputAudioSamples = std::unique_ptr(new int16_t[inputSamplesRequired]); @@ -1001,126 +1073,27 @@ void AudioClient::handleAudioInput() { static int16_t networkAudioSamples[AudioConstants::NETWORK_FRAME_SAMPLES_STEREO]; while (_inputRingBuffer.samplesAvailable() >= inputSamplesRequired) { - - if (!_muted) { - - - // Increment the time since the last clip - if (_timeSinceLastClip >= 0.0f) { - _timeSinceLastClip += (float)numNetworkSamples / (float)AudioConstants::SAMPLE_RATE; - } - + if (_muted) { + _inputRingBuffer.shiftReadPosition(inputSamplesRequired); + } else { _inputRingBuffer.readSamples(inputAudioSamples.get(), inputSamplesRequired); possibleResampling(_inputToNetworkResampler, inputAudioSamples.get(), networkAudioSamples, inputSamplesRequired, numNetworkSamples, _inputFormat.channelCount(), _desiredInputFormat.channelCount()); - - // Remove DC offset - if (!_isStereoInput) { - _inputGate.removeDCOffset(networkAudioSamples, numNetworkSamples); - } - - // only impose the noise gate and perform tone injection if we are sending mono audio - if (!_isStereoInput && _isNoiseGateEnabled) { - _inputGate.gateSamples(networkAudioSamples, numNetworkSamples); - - // if we performed the noise gate we can get values from it instead of enumerating the samples again - _lastInputLoudness = _inputGate.getLastLoudness(); - - if (_inputGate.clippedInLastBlock()) { - _timeSinceLastClip = 0.0f; - } - - } else { - float loudness = 0.0f; - - for (int i = 0; i < numNetworkSamples; i++) { - int thisSample = std::abs(networkAudioSamples[i]); - loudness += (float)thisSample; - - if (thisSample > (AudioConstants::MAX_SAMPLE_VALUE * AudioNoiseGate::CLIPPING_THRESHOLD)) { - _timeSinceLastClip = 0.0f; - } - } - - _lastInputLoudness = fabs(loudness / numNetworkSamples); - } - - emit inputReceived({ reinterpret_cast(networkAudioSamples), numNetworkBytes }); - - if (_inputGate.openedInLastBlock()) { - emit noiseGateOpened(); - } else if (_inputGate.closedInLastBlock()) { - emit noiseGateClosed(); - } - - } else { - // our input loudness is 0, since we're muted - _lastInputLoudness = 0; - _timeSinceLastClip = 0.0f; - - _inputRingBuffer.shiftReadPosition(inputSamplesRequired); } - - auto packetType = _shouldEchoToServer ? - PacketType::MicrophoneAudioWithEcho : PacketType::MicrophoneAudioNoEcho; - - // if the _inputGate closed in this last frame, then we don't actually want - // to send a silent packet, instead, we want to go ahead and encode and send - // the output from the input gate (eventually, this could be crossfaded) - // and allow the codec to properly encode down to silent/zero. If we still - // have _lastInputLoudness of 0 in our NEXT frame, we will send a silent packet - if (_lastInputLoudness == 0 && !_inputGate.closedInLastBlock()) { - packetType = PacketType::SilentAudioFrame; - _silentOutbound.increment(); - } else { - _micAudioOutbound.increment(); - } - - Transform audioTransform; - audioTransform.setTranslation(_positionGetter()); - audioTransform.setRotation(_orientationGetter()); - // FIXME find a way to properly handle both playback audio and user audio concurrently - - QByteArray decodedBuffer(reinterpret_cast(networkAudioSamples), numNetworkBytes); - QByteArray encodedBuffer; - if (_encoder) { - _encoder->encode(decodedBuffer, encodedBuffer); - } else { - encodedBuffer = decodedBuffer; - } - - emitAudioPacket(encodedBuffer.constData(), encodedBuffer.size(), _outgoingAvatarAudioSequenceNumber, - audioTransform, avatarBoundingBoxCorner, avatarBoundingBoxScale, - packetType, _selectedCodecName); - _stats.sentPacket(); - int bytesInInputRingBuffer = _inputRingBuffer.samplesAvailable() * AudioConstants::SAMPLE_SIZE; float msecsInInputRingBuffer = bytesInInputRingBuffer / (float)(_inputFormat.bytesForDuration(USECS_PER_MSEC)); _stats.updateInputMsUnplayed(msecsInInputRingBuffer); + + QByteArray audioBuffer(reinterpret_cast(networkAudioSamples), numNetworkBytes); + handleAudioInput(audioBuffer); } } -// FIXME - should this go through the noise gate and honor mute and echo? void AudioClient::handleRecordedAudioInput(const QByteArray& audio) { - Transform audioTransform; - audioTransform.setTranslation(_positionGetter()); - audioTransform.setRotation(_orientationGetter()); - - QByteArray encodedBuffer; - if (_encoder) { - _encoder->encode(audio, encodedBuffer); - } else { - encodedBuffer = audio; - } - - _micAudioOutbound.increment(); - - // FIXME check a flag to see if we should echo audio? - emitAudioPacket(encodedBuffer.data(), encodedBuffer.size(), _outgoingAvatarAudioSequenceNumber, - audioTransform, avatarBoundingBoxCorner, avatarBoundingBoxScale, - PacketType::MicrophoneAudioWithEcho, _selectedCodecName); + QByteArray audioBuffer(audio); + handleAudioInput(audioBuffer); } void AudioClient::prepareLocalAudioInjectors() { @@ -1434,7 +1407,7 @@ bool AudioClient::switchInputToAudioDevice(const QAudioDeviceInfo& inputDeviceIn lock.unlock(); if (_inputDevice) { - connect(_inputDevice, SIGNAL(readyRead()), this, SLOT(handleAudioInput())); + connect(_inputDevice, SIGNAL(readyRead()), this, SLOT(handleMicAudioInput())); supportedFormat = true; } else { qCDebug(audioclient) << "Error starting audio input -" << _audioInput->error(); diff --git a/libraries/audio-client/src/AudioClient.h b/libraries/audio-client/src/AudioClient.h index 7e9acc0586..5ed88aa63a 100644 --- a/libraries/audio-client/src/AudioClient.h +++ b/libraries/audio-client/src/AudioClient.h @@ -124,16 +124,16 @@ public: void selectAudioFormat(const QString& selectedCodecName); Q_INVOKABLE QString getSelectedAudioFormat() const { return _selectedCodecName; } - Q_INVOKABLE bool getNoiseGateOpen() const { return _inputGate.isOpen(); } - Q_INVOKABLE float getSilentOutboundPPS() const { return _silentOutbound.rate(); } - Q_INVOKABLE float getMicAudioOutboundPPS() const { return _micAudioOutbound.rate(); } + Q_INVOKABLE bool getNoiseGateOpen() const { return _noiseGate.isOpen(); } Q_INVOKABLE float getSilentInboundPPS() const { return _silentInbound.rate(); } Q_INVOKABLE float getAudioInboundPPS() const { return _audioInbound.rate(); } + Q_INVOKABLE float getSilentOutboundPPS() const { return _silentOutbound.rate(); } + Q_INVOKABLE float getMicAudioOutboundPPS() const { return _audioOutbound.rate(); } const MixedProcessedAudioStream& getReceivedAudioStream() const { return _receivedAudioStream; } MixedProcessedAudioStream& getReceivedAudioStream() { return _receivedAudioStream; } - float getLastInputLoudness() const { return glm::max(_lastInputLoudness - _inputGate.getMeasuredFloor(), 0.0f); } + float getLastInputLoudness() const { return glm::max(_lastInputLoudness - _noiseGate.getMeasuredFloor(), 0.0f); } float getTimeSinceLastClip() const { return _timeSinceLastClip; } float getAudioAverageInputLoudness() const { return _lastInputLoudness; } @@ -180,7 +180,7 @@ public slots: void handleMismatchAudioFormat(SharedNodePointer node, const QString& currentCodec, const QString& recievedCodec); void sendDownstreamAudioStatsPacket() { _stats.publish(); } - void handleAudioInput(); + void handleMicAudioInput(); void handleRecordedAudioInput(const QByteArray& audio); void reset(); void audioMixerKilled(); @@ -250,6 +250,7 @@ protected: private: void outputFormatChanged(); + void handleAudioInput(QByteArray& audioBuffer); bool mixLocalAudioInjectors(float* mixBuffer); float azimuthForSource(const glm::vec3& relativePosition); float gainForSource(float distance, float volume); @@ -371,7 +372,7 @@ private: AudioIOStats _stats; - AudioNoiseGate _inputGate; + AudioNoiseGate _noiseGate; AudioPositionGetter _positionGetter; AudioOrientationGetter _orientationGetter; @@ -395,7 +396,7 @@ private: QThread* _checkDevicesThread { nullptr }; RateCounter<> _silentOutbound; - RateCounter<> _micAudioOutbound; + RateCounter<> _audioOutbound; RateCounter<> _silentInbound; RateCounter<> _audioInbound; }; From 789bb7c8550d439748d894d54b7f8e896d319dce Mon Sep 17 00:00:00 2001 From: Zach Pomerantz Date: Tue, 14 Mar 2017 19:07:48 +0000 Subject: [PATCH 191/302] rename audio(Mic)OutboundPPS --- interface/resources/qml/Stats.qml | 2 +- interface/src/ui/Stats.cpp | 6 +++--- interface/src/ui/Stats.h | 4 ++-- libraries/audio-client/src/AudioClient.h | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/interface/resources/qml/Stats.qml b/interface/resources/qml/Stats.qml index 564c74b526..6f131d0473 100644 --- a/interface/resources/qml/Stats.qml +++ b/interface/resources/qml/Stats.qml @@ -198,7 +198,7 @@ Item { } StatText { visible: root.expanded; - text: "Audio Out Mic: " + root.audioMicOutboundPPS + " pps, " + + text: "Audio Out Mic: " + root.audioOutboundPPS + " pps, " + "Silent: " + root.audioSilentOutboundPPS + " pps"; } StatText { diff --git a/interface/src/ui/Stats.cpp b/interface/src/ui/Stats.cpp index 923d9f642d..e8cd5409f0 100644 --- a/interface/src/ui/Stats.cpp +++ b/interface/src/ui/Stats.cpp @@ -220,10 +220,10 @@ void Stats::updateStats(bool force) { STAT_UPDATE(audioMixerInPps, roundf(bandwidthRecorder->getAverageInputPacketsPerSecond(NodeType::AudioMixer))); STAT_UPDATE(audioMixerOutKbps, roundf(bandwidthRecorder->getAverageOutputKilobitsPerSecond(NodeType::AudioMixer))); STAT_UPDATE(audioMixerOutPps, roundf(bandwidthRecorder->getAverageOutputPacketsPerSecond(NodeType::AudioMixer))); - STAT_UPDATE(audioMicOutboundPPS, audioClient->getMicAudioOutboundPPS()); - STAT_UPDATE(audioSilentOutboundPPS, audioClient->getSilentOutboundPPS()); STAT_UPDATE(audioAudioInboundPPS, audioClient->getAudioInboundPPS()); STAT_UPDATE(audioSilentInboundPPS, audioClient->getSilentInboundPPS()); + STAT_UPDATE(audioOutboundPPS, audioClient->getAudioOutboundPPS()); + STAT_UPDATE(audioSilentOutboundPPS, audioClient->getSilentOutboundPPS()); } else { STAT_UPDATE(audioMixerKbps, -1); STAT_UPDATE(audioMixerPps, -1); @@ -231,7 +231,7 @@ void Stats::updateStats(bool force) { STAT_UPDATE(audioMixerInPps, -1); STAT_UPDATE(audioMixerOutKbps, -1); STAT_UPDATE(audioMixerOutPps, -1); - STAT_UPDATE(audioMicOutboundPPS, -1); + STAT_UPDATE(audioOutboundPPS, -1); STAT_UPDATE(audioSilentOutboundPPS, -1); STAT_UPDATE(audioAudioInboundPPS, -1); STAT_UPDATE(audioSilentInboundPPS, -1); diff --git a/interface/src/ui/Stats.h b/interface/src/ui/Stats.h index 0ce113e0a0..964c94cd4d 100644 --- a/interface/src/ui/Stats.h +++ b/interface/src/ui/Stats.h @@ -77,7 +77,7 @@ class Stats : public QQuickItem { STATS_PROPERTY(int, audioMixerOutPps, 0) STATS_PROPERTY(int, audioMixerKbps, 0) STATS_PROPERTY(int, audioMixerPps, 0) - STATS_PROPERTY(int, audioMicOutboundPPS, 0) + STATS_PROPERTY(int, audioOutboundPPS, 0) STATS_PROPERTY(int, audioSilentOutboundPPS, 0) STATS_PROPERTY(int, audioAudioInboundPPS, 0) STATS_PROPERTY(int, audioSilentInboundPPS, 0) @@ -198,7 +198,7 @@ signals: void audioMixerOutPpsChanged(); void audioMixerKbpsChanged(); void audioMixerPpsChanged(); - void audioMicOutboundPPSChanged(); + void audioOutboundPPSChanged(); void audioSilentOutboundPPSChanged(); void audioAudioInboundPPSChanged(); void audioSilentInboundPPSChanged(); diff --git a/libraries/audio-client/src/AudioClient.h b/libraries/audio-client/src/AudioClient.h index 5ed88aa63a..0d180c0f5c 100644 --- a/libraries/audio-client/src/AudioClient.h +++ b/libraries/audio-client/src/AudioClient.h @@ -128,7 +128,7 @@ public: Q_INVOKABLE float getSilentInboundPPS() const { return _silentInbound.rate(); } Q_INVOKABLE float getAudioInboundPPS() const { return _audioInbound.rate(); } Q_INVOKABLE float getSilentOutboundPPS() const { return _silentOutbound.rate(); } - Q_INVOKABLE float getMicAudioOutboundPPS() const { return _audioOutbound.rate(); } + Q_INVOKABLE float getAudioOutboundPPS() const { return _audioOutbound.rate(); } const MixedProcessedAudioStream& getReceivedAudioStream() const { return _receivedAudioStream; } MixedProcessedAudioStream& getReceivedAudioStream() { return _receivedAudioStream; } From d13752a7c6971e15bc2f4c377192a6adf1e1acd9 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Wed, 15 Mar 2017 10:34:43 -0700 Subject: [PATCH 192/302] use the correct property name for useAdvancedMovementControls --- .../controllers/toggleAdvancedMovementForHandControllers.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/system/controllers/toggleAdvancedMovementForHandControllers.js b/scripts/system/controllers/toggleAdvancedMovementForHandControllers.js index 8f8f5c2d3e..e6c9b0aee0 100644 --- a/scripts/system/controllers/toggleAdvancedMovementForHandControllers.js +++ b/scripts/system/controllers/toggleAdvancedMovementForHandControllers.js @@ -109,10 +109,10 @@ function menuItemEvent(menuItem) { if (menuItem == MENU_ITEM_NAME) { isChecked = Menu.isOptionChecked(MENU_ITEM_NAME); if (isChecked === true) { - MyAvatar.advancedMovementControls = true; + MyAvatar.useAdvancedMovementControls = true; disableMappings(); } else if (isChecked === false) { - MyAvatar.advancedMovementControls = false; + MyAvatar.useAdvancedMovementControls = false; enableMappings(); } } From 0c657a1d558bbd1d19e24932f9ee6e26cf02de6d Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Wed, 15 Mar 2017 10:57:12 -0700 Subject: [PATCH 193/302] add parameter to unload to remove entityID from map --- .../src/scripts/EntityScriptServer.cpp | 4 ++-- .../src/EntityTreeRenderer.cpp | 2 +- libraries/script-engine/src/ScriptEngine.cpp | 23 ++++++++----------- libraries/script-engine/src/ScriptEngine.h | 2 +- 4 files changed, 14 insertions(+), 17 deletions(-) diff --git a/assignment-client/src/scripts/EntityScriptServer.cpp b/assignment-client/src/scripts/EntityScriptServer.cpp index 47071b10b7..f674ab006c 100644 --- a/assignment-client/src/scripts/EntityScriptServer.cpp +++ b/assignment-client/src/scripts/EntityScriptServer.cpp @@ -455,13 +455,13 @@ void EntityScriptServer::addingEntity(const EntityItemID& entityID) { void EntityScriptServer::deletingEntity(const EntityItemID& entityID) { if (_entityViewer.getTree() && !_shuttingDown && _entitiesScriptEngine) { - _entitiesScriptEngine->unloadEntityScript(entityID); + _entitiesScriptEngine->unloadEntityScript(entityID, true); } } void EntityScriptServer::entityServerScriptChanging(const EntityItemID& entityID, const bool reload) { if (_entityViewer.getTree() && !_shuttingDown) { - _entitiesScriptEngine->unloadEntityScript(entityID); + _entitiesScriptEngine->unloadEntityScript(entityID, true); checkAndCallPreload(entityID, reload); } } diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.cpp b/libraries/entities-renderer/src/EntityTreeRenderer.cpp index 27e00b47c6..c6bd70a0cb 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.cpp +++ b/libraries/entities-renderer/src/EntityTreeRenderer.cpp @@ -940,7 +940,7 @@ void EntityTreeRenderer::mouseMoveEvent(QMouseEvent* event) { void EntityTreeRenderer::deletingEntity(const EntityItemID& entityID) { if (_tree && !_shuttingDown && _entitiesScriptEngine) { - _entitiesScriptEngine->unloadEntityScript(entityID); + _entitiesScriptEngine->unloadEntityScript(entityID, true); } forceRecheckEntities(); // reset our state to force checking our inside/outsideness of entities diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index 73a79f1bc6..e8a2b105db 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -541,16 +541,6 @@ void ScriptEngine::init() { auto entityScriptingInterface = DependencyManager::get(); entityScriptingInterface->init(); - connect(entityScriptingInterface.data(), &EntityScriptingInterface::deletingEntity, this, [this](const EntityItemID& entityID) { - if (_entityScripts.contains(entityID)) { - if (isEntityScriptRunning(entityID)) { - qCWarning(scriptengine) << "deletingEntity while entity script is still running!" << entityID; - } - _entityScripts.remove(entityID); - emit entityScriptDetailsUpdated(); - } - }); - // register various meta-types registerMetaTypes(this); @@ -1844,7 +1834,7 @@ void ScriptEngine::entityScriptContentAvailable(const EntityItemID& entityID, co processDeferredEntityLoads(entityScript, entityID); } -void ScriptEngine::unloadEntityScript(const EntityItemID& entityID) { +void ScriptEngine::unloadEntityScript(const EntityItemID& entityID, bool shouldRemoveFromMap) { if (QThread::currentThread() != thread()) { #ifdef THREAD_DEBUGGING qCDebug(scriptengine) << "*** WARNING *** ScriptEngine::unloadEntityScript() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "] " @@ -1852,7 +1842,8 @@ void ScriptEngine::unloadEntityScript(const EntityItemID& entityID) { #endif QMetaObject::invokeMethod(this, "unloadEntityScript", - Q_ARG(const EntityItemID&, entityID)); + Q_ARG(const EntityItemID&, entityID), + Q_ARG(bool, shouldRemoveFromMap)); return; } #ifdef THREAD_DEBUGGING @@ -1867,7 +1858,12 @@ void ScriptEngine::unloadEntityScript(const EntityItemID& entityID) { } else { qCDebug(scriptengine) << "unload called while !running" << entityID << oldDetails.status; } - if (oldDetails.status != EntityScriptStatus::UNLOADED) { + + if (shouldRemoveFromMap) { + // this was a deleted entity, we've been asked to remove it from the map + _entityScripts.remove(entityID); + emit entityScriptDetailsUpdated(); + } else if (oldDetails.status != EntityScriptStatus::UNLOADED) { EntityScriptDetails newDetails; newDetails.status = EntityScriptStatus::UNLOADED; newDetails.lastModified = QDateTime::currentMSecsSinceEpoch(); @@ -1875,6 +1871,7 @@ void ScriptEngine::unloadEntityScript(const EntityItemID& entityID) { newDetails.scriptText = oldDetails.scriptText; setEntityScriptDetails(entityID, newDetails); } + stopAllTimersForEntityScript(entityID); { // FIXME: shouldn't have to do this here, but currently something seems to be firing unloads moments after firing initial load requests diff --git a/libraries/script-engine/src/ScriptEngine.h b/libraries/script-engine/src/ScriptEngine.h index b988ccfe90..681499779c 100644 --- a/libraries/script-engine/src/ScriptEngine.h +++ b/libraries/script-engine/src/ScriptEngine.h @@ -171,7 +171,7 @@ public: return _entityScripts.contains(entityID) && _entityScripts[entityID].status == EntityScriptStatus::RUNNING; } Q_INVOKABLE void loadEntityScript(const EntityItemID& entityID, const QString& entityScript, bool forceRedownload); - Q_INVOKABLE void unloadEntityScript(const EntityItemID& entityID); // will call unload method + Q_INVOKABLE void unloadEntityScript(const EntityItemID& entityID, bool shouldRemoveFromMap = false); // will call unload method Q_INVOKABLE void unloadAllEntityScripts(); Q_INVOKABLE void callEntityScriptMethod(const EntityItemID& entityID, const QString& methodName, const QStringList& params = QStringList()) override; From ea2f1359bceb29a261a22487806aab0fdc0704a7 Mon Sep 17 00:00:00 2001 From: samcake Date: Wed, 15 Mar 2017 12:23:44 -0700 Subject: [PATCH 194/302] Expose rendering in wireframe mode --- libraries/render-utils/src/LightingModel.cpp | 10 ++++++++++ libraries/render-utils/src/LightingModel.h | 12 +++++++---- libraries/render-utils/src/LightingModel.slh | 9 ++++++--- .../render-utils/src/RenderDeferredTask.cpp | 20 ++++++++++++++++--- libraries/render/src/render/DrawTask.cpp | 12 +++++------ libraries/render/src/render/DrawTask.h | 4 ++-- libraries/render/src/render/ShapePipeline.h | 4 ++++ .../utilities/render/deferredLighting.qml | 3 ++- 8 files changed, 55 insertions(+), 19 deletions(-) diff --git a/libraries/render-utils/src/LightingModel.cpp b/libraries/render-utils/src/LightingModel.cpp index 47af83da36..bd321bad95 100644 --- a/libraries/render-utils/src/LightingModel.cpp +++ b/libraries/render-utils/src/LightingModel.cpp @@ -133,6 +133,7 @@ void LightingModel::setSpotLight(bool enable) { bool LightingModel::isSpotLightEnabled() const { return (bool)_parametersBuffer.get().enableSpotLight; } + void LightingModel::setShowLightContour(bool enable) { if (enable != isShowLightContourEnabled()) { _parametersBuffer.edit().showLightContour = (float)enable; @@ -142,6 +143,14 @@ bool LightingModel::isShowLightContourEnabled() const { return (bool)_parametersBuffer.get().showLightContour; } +void LightingModel::setWireframe(bool enable) { + if (enable != isWireframeEnabled()) { + _parametersBuffer.edit().enableWireframe = (float)enable; + } +} +bool LightingModel::isWireframeEnabled() const { + return (bool)_parametersBuffer.get().enableWireframe; +} MakeLightingModel::MakeLightingModel() { _lightingModel = std::make_shared(); } @@ -167,6 +176,7 @@ void MakeLightingModel::configure(const Config& config) { _lightingModel->setSpotLight(config.enableSpotLight); _lightingModel->setShowLightContour(config.showLightContour); + _lightingModel->setWireframe(config.enableWireframe); } void MakeLightingModel::run(const render::SceneContextPointer& sceneContext, const render::RenderContextPointer& renderContext, LightingModelPointer& lightingModel) { diff --git a/libraries/render-utils/src/LightingModel.h b/libraries/render-utils/src/LightingModel.h index 45514654f2..c1189d5160 100644 --- a/libraries/render-utils/src/LightingModel.h +++ b/libraries/render-utils/src/LightingModel.h @@ -64,6 +64,9 @@ public: void setShowLightContour(bool enable); bool isShowLightContourEnabled() const; + void setWireframe(bool enable); + bool isWireframeEnabled() const; + UniformBufferView getParametersBuffer() const { return _parametersBuffer; } protected: @@ -89,13 +92,12 @@ protected: float enablePointLight{ 1.0f }; float enableSpotLight{ 1.0f }; - float showLightContour{ 0.0f }; // false by default + float showLightContour { 0.0f }; // false by default float enableObscurance{ 1.0f }; float enableMaterialTexturing { 1.0f }; - - float spares{ 0.0f }; + float enableWireframe { 0.0f }; // false by default Parameters() {} }; @@ -129,6 +131,7 @@ class MakeLightingModelConfig : public render::Job::Config { Q_PROPERTY(bool enablePointLight MEMBER enablePointLight NOTIFY dirty) Q_PROPERTY(bool enableSpotLight MEMBER enableSpotLight NOTIFY dirty) + Q_PROPERTY(bool enableWireframe MEMBER enableWireframe NOTIFY dirty) Q_PROPERTY(bool showLightContour MEMBER showLightContour NOTIFY dirty) public: @@ -152,9 +155,10 @@ public: bool enablePointLight{ true }; bool enableSpotLight{ true }; - bool showLightContour { false }; // false by default + bool enableWireframe { false }; // false by default + signals: void dirty(); }; diff --git a/libraries/render-utils/src/LightingModel.slh b/libraries/render-utils/src/LightingModel.slh index 74285aa6a9..209a1f38d6 100644 --- a/libraries/render-utils/src/LightingModel.slh +++ b/libraries/render-utils/src/LightingModel.slh @@ -17,7 +17,7 @@ struct LightingModel { vec4 _UnlitEmissiveLightmapBackground; vec4 _ScatteringDiffuseSpecularAlbedo; vec4 _AmbientDirectionalPointSpot; - vec4 _ShowContourObscuranceSpare2; + vec4 _ShowContourObscuranceWireframe; }; uniform lightingModelBuffer{ @@ -37,7 +37,7 @@ float isBackgroundEnabled() { return lightingModel._UnlitEmissiveLightmapBackground.w; } float isObscuranceEnabled() { - return lightingModel._ShowContourObscuranceSpare2.y; + return lightingModel._ShowContourObscuranceWireframe.y; } float isScatteringEnabled() { @@ -67,9 +67,12 @@ float isSpotEnabled() { } float isShowLightContour() { - return lightingModel._ShowContourObscuranceSpare2.x; + return lightingModel._ShowContourObscuranceWireframe.x; } +float isWireframeEnabled() { + return lightingModel._ShowContourObscuranceWireframe.z; +} <@endfunc@> <$declareLightingModel()$> diff --git a/libraries/render-utils/src/RenderDeferredTask.cpp b/libraries/render-utils/src/RenderDeferredTask.cpp index 676d176cca..903cbf9367 100644 --- a/libraries/render-utils/src/RenderDeferredTask.cpp +++ b/libraries/render-utils/src/RenderDeferredTask.cpp @@ -259,7 +259,14 @@ void DrawDeferred::run(const SceneContextPointer& sceneContext, const RenderCont // Setup lighting model for all items; batch.setUniformBuffer(render::ShapePipeline::Slot::LIGHTING_MODEL, lightingModel->getParametersBuffer()); - renderShapes(sceneContext, renderContext, _shapePlumber, inItems, _maxDrawn); + // From the lighting model define a global shapKey ORED with individiual keys + ShapeKey::Builder keyBuilder; + if (lightingModel->isWireframeEnabled()) { + keyBuilder.withWireframe(); + } + ShapeKey globalKey = keyBuilder.build(); + + renderShapes(sceneContext, renderContext, _shapePlumber, inItems, _maxDrawn, globalKey); args->_batch = nullptr; }); @@ -295,10 +302,17 @@ void DrawStateSortDeferred::run(const SceneContextPointer& sceneContext, const R // Setup lighting model for all items; batch.setUniformBuffer(render::ShapePipeline::Slot::LIGHTING_MODEL, lightingModel->getParametersBuffer()); + // From the lighting model define a global shapKey ORED with individiual keys + ShapeKey::Builder keyBuilder; + if (lightingModel->isWireframeEnabled()) { + keyBuilder.withWireframe(); + } + ShapeKey globalKey = keyBuilder.build(); + if (_stateSort) { - renderStateSortShapes(sceneContext, renderContext, _shapePlumber, inItems, _maxDrawn); + renderStateSortShapes(sceneContext, renderContext, _shapePlumber, inItems, _maxDrawn, globalKey); } else { - renderShapes(sceneContext, renderContext, _shapePlumber, inItems, _maxDrawn); + renderShapes(sceneContext, renderContext, _shapePlumber, inItems, _maxDrawn, globalKey); } args->_batch = nullptr; }); diff --git a/libraries/render/src/render/DrawTask.cpp b/libraries/render/src/render/DrawTask.cpp index 2829c6f8e7..e8537e3452 100755 --- a/libraries/render/src/render/DrawTask.cpp +++ b/libraries/render/src/render/DrawTask.cpp @@ -39,9 +39,9 @@ void render::renderItems(const SceneContextPointer& sceneContext, const RenderCo } } -void renderShape(RenderArgs* args, const ShapePlumberPointer& shapeContext, const Item& item) { +void renderShape(RenderArgs* args, const ShapePlumberPointer& shapeContext, const Item& item, const ShapeKey& globalKey) { assert(item.getKey().isShape()); - const auto& key = item.getShapeKey(); + auto key = item.getShapeKey() | globalKey; if (key.isValid() && !key.hasOwnPipeline()) { args->_pipeline = shapeContext->pickPipeline(args, key); if (args->_pipeline) { @@ -56,7 +56,7 @@ void renderShape(RenderArgs* args, const ShapePlumberPointer& shapeContext, cons } void render::renderShapes(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, - const ShapePlumberPointer& shapeContext, const ItemBounds& inItems, int maxDrawnItems) { + const ShapePlumberPointer& shapeContext, const ItemBounds& inItems, int maxDrawnItems, const ShapeKey& globalKey) { auto& scene = sceneContext->_scene; RenderArgs* args = renderContext->args; @@ -66,12 +66,12 @@ void render::renderShapes(const SceneContextPointer& sceneContext, const RenderC } for (auto i = 0; i < numItemsToDraw; ++i) { auto& item = scene->getItem(inItems[i].id); - renderShape(args, shapeContext, item); + renderShape(args, shapeContext, item, globalKey); } } void render::renderStateSortShapes(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, - const ShapePlumberPointer& shapeContext, const ItemBounds& inItems, int maxDrawnItems) { + const ShapePlumberPointer& shapeContext, const ItemBounds& inItems, int maxDrawnItems, const ShapeKey& globalKey) { auto& scene = sceneContext->_scene; RenderArgs* args = renderContext->args; @@ -91,7 +91,7 @@ void render::renderStateSortShapes(const SceneContextPointer& sceneContext, cons { assert(item.getKey().isShape()); - const auto key = item.getShapeKey(); + auto key = item.getShapeKey() | globalKey; if (key.isValid() && !key.hasOwnPipeline()) { auto& bucket = sortedShapes[key]; if (bucket.empty()) { diff --git a/libraries/render/src/render/DrawTask.h b/libraries/render/src/render/DrawTask.h index 6e0e5ba10b..a9c5f3a4d8 100755 --- a/libraries/render/src/render/DrawTask.h +++ b/libraries/render/src/render/DrawTask.h @@ -17,8 +17,8 @@ namespace render { void renderItems(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemBounds& inItems, int maxDrawnItems = -1); -void renderShapes(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ShapePlumberPointer& shapeContext, const ItemBounds& inItems, int maxDrawnItems = -1); -void renderStateSortShapes(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ShapePlumberPointer& shapeContext, const ItemBounds& inItems, int maxDrawnItems = -1); +void renderShapes(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ShapePlumberPointer& shapeContext, const ItemBounds& inItems, int maxDrawnItems = -1, const ShapeKey& globalKey = ShapeKey()); +void renderStateSortShapes(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ShapePlumberPointer& shapeContext, const ItemBounds& inItems, int maxDrawnItems = -1, const ShapeKey& globalKey = ShapeKey()); class DrawLightConfig : public Job::Config { Q_OBJECT diff --git a/libraries/render/src/render/ShapePipeline.h b/libraries/render/src/render/ShapePipeline.h index 0c77a15184..7a0b9fe3f3 100644 --- a/libraries/render/src/render/ShapePipeline.h +++ b/libraries/render/src/render/ShapePipeline.h @@ -46,6 +46,10 @@ public: ShapeKey() : _flags{ 0 } {} ShapeKey(const Flags& flags) : _flags{flags} {} + friend ShapeKey operator&(const ShapeKey& _Left, const ShapeKey& _Right) { return ShapeKey(_Left._flags & _Right._flags); } + friend ShapeKey operator|(const ShapeKey& _Left, const ShapeKey& _Right) { return ShapeKey(_Left._flags | _Right._flags); } + friend ShapeKey operator^(const ShapeKey& _Left, const ShapeKey& _Right) { return ShapeKey(_Left._flags ^ _Right._flags); } + class Builder { public: Builder() {} diff --git a/scripts/developer/utilities/render/deferredLighting.qml b/scripts/developer/utilities/render/deferredLighting.qml index 99a9f258e3..c7ec8e1153 100644 --- a/scripts/developer/utilities/render/deferredLighting.qml +++ b/scripts/developer/utilities/render/deferredLighting.qml @@ -25,7 +25,7 @@ Column { "Lightmap:LightingModel:enableLightmap", "Background:LightingModel:enableBackground", "ssao:AmbientOcclusion:enabled", - "Textures:LightingModel:enableMaterialTexturing", + "Textures:LightingModel:enableMaterialTexturing" ] CheckBox { text: modelData.split(":")[0] @@ -45,6 +45,7 @@ Column { "Diffuse:LightingModel:enableDiffuse", "Specular:LightingModel:enableSpecular", "Albedo:LightingModel:enableAlbedo", + "Wireframe:LightingModel:enableWireframe" ] CheckBox { text: modelData.split(":")[0] From 3e68018a129a4debfeea14f856b58cdbcc54218a Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Wed, 15 Mar 2017 14:33:32 -0700 Subject: [PATCH 195/302] Fix node persmissions hash --- libraries/networking/src/NodePermissions.h | 40 ++++++++++++++++------ 1 file changed, 29 insertions(+), 11 deletions(-) diff --git a/libraries/networking/src/NodePermissions.h b/libraries/networking/src/NodePermissions.h index 5d2755f9b5..091eb18909 100644 --- a/libraries/networking/src/NodePermissions.h +++ b/libraries/networking/src/NodePermissions.h @@ -13,18 +13,31 @@ #define hifi_NodePermissions_h #include +#include #include #include #include #include - +#include +#include #include "GroupRank.h" class NodePermissions; using NodePermissionsPointer = std::shared_ptr; -using NodePermissionsKey = QPair; // name, rankID +using NodePermissionsKey = std::pair; // name, rankID using NodePermissionsKeyList = QList>; +namespace std { + template<> + struct hash { + size_t operator()(const NodePermissionsKey& key) const { + size_t result = qHash(key.first); + result <<= 32; + result |= qHash(key.second); + return result; + } + }; +} class NodePermissions { public: @@ -100,27 +113,32 @@ public: NodePermissionsMap() { } NodePermissionsPointer& operator[](const NodePermissionsKey& key) { NodePermissionsKey dataKey(key.first.toLower(), key.second); - if (!_data.contains(dataKey)) { + if (0 == _data.count(dataKey)) { _data[dataKey] = NodePermissionsPointer(new NodePermissions(key)); } return _data[dataKey]; } NodePermissionsPointer operator[](const NodePermissionsKey& key) const { - return _data.value(NodePermissionsKey(key.first.toLower(), key.second)); + NodePermissionsPointer result; + auto itr = _data.find(NodePermissionsKey(key.first.toLower(), key.second)); + if (_data.end() != itr) { + result = itr->second; + } + return result; } bool contains(const NodePermissionsKey& key) const { - return _data.contains(NodePermissionsKey(key.first.toLower(), key.second)); + return 0 != _data.count(NodePermissionsKey(key.first.toLower(), key.second)); } - bool contains(const QString& keyFirst, QUuid keySecond) const { - return _data.contains(NodePermissionsKey(keyFirst.toLower(), keySecond)); + bool contains(const QString& keyFirst, const QUuid& keySecond) const { + return 0 != _data.count(NodePermissionsKey(keyFirst.toLower(), keySecond)); } - QList keys() const { return _data.keys(); } - QHash get() { return _data; } + //QList keys() const { return _data.keys(); } + std::unordered_map get() { return _data; } void clear() { _data.clear(); } - void remove(const NodePermissionsKey& key) { _data.remove(key); } + void remove(const NodePermissionsKey& key) { _data.erase(key); } private: - QHash _data; + std::unordered_map _data; }; From f250c0d29888f3741fbf9c32edae066369894031 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Wed, 15 Mar 2017 14:41:13 -0700 Subject: [PATCH 196/302] simpler bindTransform() for ModelMeshPartPayload --- .../src/avatar/CauterizedMeshPartPayload.cpp | 52 +++++-------------- .../src/avatar/CauterizedMeshPartPayload.h | 7 +-- interface/src/avatar/CauterizedModel.cpp | 23 +++++++- .../render-utils/src/MeshPartPayload.cpp | 29 +++++------ libraries/render-utils/src/MeshPartPayload.h | 6 ++- libraries/render-utils/src/Model.cpp | 16 +++--- 6 files changed, 64 insertions(+), 69 deletions(-) diff --git a/interface/src/avatar/CauterizedMeshPartPayload.cpp b/interface/src/avatar/CauterizedMeshPartPayload.cpp index c8ec90dcee..72a98bd994 100644 --- a/interface/src/avatar/CauterizedMeshPartPayload.cpp +++ b/interface/src/avatar/CauterizedMeshPartPayload.cpp @@ -20,33 +20,11 @@ using namespace render; CauterizedMeshPartPayload::CauterizedMeshPartPayload(Model* model, int meshIndex, int partIndex, int shapeIndex, const Transform& transform, const Transform& offsetTransform) : ModelMeshPartPayload(model, meshIndex, partIndex, shapeIndex, transform, offsetTransform) {} -void CauterizedMeshPartPayload::updateTransformForSkinnedCauterizedMesh(const Transform& transform, - const QVector& clusterMatrices, - const QVector& cauterizedClusterMatrices) { - _transform = transform; - _cauterizedTransform = transform; - - if (clusterMatrices.size() > 0) { - _worldBound = AABox(); - for (auto& clusterMatrix : clusterMatrices) { - AABox clusterBound = _localBound; - clusterBound.transform(clusterMatrix); - _worldBound += clusterBound; - } - - _worldBound.transform(transform); - if (clusterMatrices.size() == 1) { - _transform = _transform.worldTransform(Transform(clusterMatrices[0])); - if (cauterizedClusterMatrices.size() != 0) { - _cauterizedTransform = _cauterizedTransform.worldTransform(Transform(cauterizedClusterMatrices[0])); - } else { - _cauterizedTransform = _transform; - } - } - } else { - _worldBound = _localBound; - _worldBound.transform(_drawTransform); - } +void CauterizedMeshPartPayload::updateTransformForCauterizedMesh( + const Transform& renderTransform, + const gpu::BufferPointer& buffer) { + _cauterizedTransform = renderTransform; + _cauterizedClusterBuffer = buffer; } void CauterizedMeshPartPayload::bindTransform(gpu::Batch& batch, const render::ShapePipeline::LocationsPointer locations, RenderArgs::RenderMode renderMode) const { @@ -55,20 +33,16 @@ void CauterizedMeshPartPayload::bindTransform(gpu::Batch& batch, const render::S SkeletonModel* skeleton = static_cast(_model); bool useCauterizedMesh = (renderMode != RenderArgs::RenderMode::SHADOW_RENDER_MODE) && skeleton->getEnableCauterization(); - if (state.clusterBuffer) { - if (useCauterizedMesh) { - const Model::MeshState& cState = skeleton->getCauterizeMeshState(_meshIndex); - batch.setUniformBuffer(ShapePipeline::Slot::BUFFER::SKINNING, cState.clusterBuffer); - } else { - batch.setUniformBuffer(ShapePipeline::Slot::BUFFER::SKINNING, state.clusterBuffer); + if (useCauterizedMesh) { + if (_cauterizedClusterBuffer) { + batch.setUniformBuffer(ShapePipeline::Slot::BUFFER::SKINNING, _cauterizedClusterBuffer); + } + batch.setModelTransform(_cauterizedTransform); + } else { + if (_clusterBuffer) { + batch.setUniformBuffer(ShapePipeline::Slot::BUFFER::SKINNING, _clusterBuffer); } batch.setModelTransform(_transform); - } else { - if (useCauterizedMesh) { - batch.setModelTransform(_cauterizedTransform); - } else { - batch.setModelTransform(_transform); - } } } diff --git a/interface/src/avatar/CauterizedMeshPartPayload.h b/interface/src/avatar/CauterizedMeshPartPayload.h index f4319ead6f..dc88e950c1 100644 --- a/interface/src/avatar/CauterizedMeshPartPayload.h +++ b/interface/src/avatar/CauterizedMeshPartPayload.h @@ -17,12 +17,13 @@ class CauterizedMeshPartPayload : public ModelMeshPartPayload { public: CauterizedMeshPartPayload(Model* model, int meshIndex, int partIndex, int shapeIndex, const Transform& transform, const Transform& offsetTransform); - void updateTransformForSkinnedCauterizedMesh(const Transform& transform, - const QVector& clusterMatrices, - const QVector& cauterizedClusterMatrices); + + void updateTransformForCauterizedMesh(const Transform& renderTransform, const gpu::BufferPointer& buffer); void bindTransform(gpu::Batch& batch, const render::ShapePipeline::LocationsPointer locations, RenderArgs::RenderMode renderMode) const override; + private: + gpu::BufferPointer _cauterizedClusterBuffer; Transform _cauterizedTransform; }; diff --git a/interface/src/avatar/CauterizedModel.cpp b/interface/src/avatar/CauterizedModel.cpp index 1ca87a498a..8ac14fcb6f 100644 --- a/interface/src/avatar/CauterizedModel.cpp +++ b/interface/src/avatar/CauterizedModel.cpp @@ -191,6 +191,9 @@ void CauterizedModel::updateRenderItems() { return; } + // lazy update of cluster matrices used for rendering. We need to update them here, so we can correctly update the bounding box. + self->updateClusterMatrices(); + render::ScenePointer scene = AbstractViewStateInterface::instance()->getMain3DScene(); Transform modelTransform; @@ -209,15 +212,31 @@ void CauterizedModel::updateRenderItems() { if (data._model && data._model->isLoaded()) { // Ensure the model geometry was not reset between frames if (deleteGeometryCounter == data._model->getGeometryCounter()) { - // lazy update of cluster matrices used for rendering. We need to update them here, so we can correctly update the bounding box. - data._model->updateClusterMatrices(); + // this stuff identical to what happens in regular Model + const Model::MeshState& state = data._model->getMeshState(data._meshIndex); + Transform renderTransform = modelTransform; + if (state.clusterMatrices.size() == 1) { + renderTransform = modelTransform.worldTransform(Transform(state.clusterMatrices[0])); + } + data.updateTransformForSkinnedMesh(renderTransform, modelTransform, state.clusterBuffer); + // this stuff for cauterized mesh + CauterizedModel* cModel = static_cast(data._model); + const Model::MeshState& cState = cModel->getCauterizeMeshState(data._meshIndex); + renderTransform = modelTransform; + if (cState.clusterMatrices.size() == 1) { + renderTransform = modelTransform.worldTransform(Transform(cState.clusterMatrices[0])); + } + data.updateTransformForCauterizedMesh(renderTransform, cState.clusterBuffer); + +/* // update the model transform and bounding box for this render item. const Model::MeshState& state = data._model->getMeshState(data._meshIndex); CauterizedModel* cModel = static_cast(data._model); assert(data._meshIndex < cModel->_cauterizeMeshStates.size()); const Model::MeshState& cState = cModel->_cauterizeMeshStates.at(data._meshIndex); data.updateTransformForSkinnedCauterizedMesh(modelTransform, state.clusterMatrices, cState.clusterMatrices); +*/ } } }); diff --git a/libraries/render-utils/src/MeshPartPayload.cpp b/libraries/render-utils/src/MeshPartPayload.cpp index 5b3d285b47..d4b28dbddb 100644 --- a/libraries/render-utils/src/MeshPartPayload.cpp +++ b/libraries/render-utils/src/MeshPartPayload.cpp @@ -372,19 +372,12 @@ void ModelMeshPartPayload::notifyLocationChanged() { } -void ModelMeshPartPayload::updateTransformForSkinnedMesh(const Transform& transform, const QVector& clusterMatrices) { - _transform = transform; - - if (clusterMatrices.size() > 0) { - _worldBound = _adjustedLocalBound; - _worldBound.transform(_transform); - if (clusterMatrices.size() == 1) { - _transform = _transform.worldTransform(Transform(clusterMatrices[0])); - } - } else { - _worldBound = _localBound; - _worldBound.transform(_transform); - } +void ModelMeshPartPayload::updateTransformForSkinnedMesh(const Transform& renderTransform, const Transform& boundTransform, + const gpu::BufferPointer& buffer) { + _transform = renderTransform; + _worldBound = _adjustedLocalBound; + _worldBound.transform(boundTransform); + _clusterBuffer = buffer; } ItemKey ModelMeshPartPayload::getKey() const { @@ -532,9 +525,8 @@ void ModelMeshPartPayload::bindMesh(gpu::Batch& batch) const { void ModelMeshPartPayload::bindTransform(gpu::Batch& batch, const ShapePipeline::LocationsPointer locations, RenderArgs::RenderMode renderMode) const { // Still relying on the raw data from the model - const Model::MeshState& state = _model->getMeshState(_meshIndex); - if (state.clusterBuffer) { - batch.setUniformBuffer(ShapePipeline::Slot::BUFFER::SKINNING, state.clusterBuffer); + if (_clusterBuffer) { + batch.setUniformBuffer(ShapePipeline::Slot::BUFFER::SKINNING, _clusterBuffer); } batch.setModelTransform(_transform); } @@ -590,8 +582,11 @@ void ModelMeshPartPayload::render(RenderArgs* args) const { auto locations = args->_pipeline->locations; assert(locations); - // Bind the model transform and the skinCLusterMatrices if needed + // update the clusterMatrices if necessary + // TODO: we want to remove this updateClusterMatrices() call in this context + // but I recall we tried this before and broke something. - Andrew _model->updateClusterMatrices(); + bindTransform(batch, locations, args->_renderMode); //Bind the index buffer and vertex buffer and Blend shapes if needed diff --git a/libraries/render-utils/src/MeshPartPayload.h b/libraries/render-utils/src/MeshPartPayload.h index c585c95025..ef74011c40 100644 --- a/libraries/render-utils/src/MeshPartPayload.h +++ b/libraries/render-utils/src/MeshPartPayload.h @@ -89,8 +89,9 @@ public: typedef Payload::DataPointer Pointer; void notifyLocationChanged() override; - void updateTransformForSkinnedMesh(const Transform& transform, - const QVector& clusterMatrices); + void updateTransformForSkinnedMesh(const Transform& renderTransform, + const Transform& boundTransform, + const gpu::BufferPointer& buffer); float computeFadeAlpha() const; @@ -108,6 +109,7 @@ public: void computeAdjustedLocalBound(const QVector& clusterMatrices); + gpu::BufferPointer _clusterBuffer; Model* _model; int _meshIndex; diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index 48c1d29b68..3cd0fc65f6 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -227,6 +227,10 @@ void Model::updateRenderItems() { return; } + // lazy update of cluster matrices used for rendering. + // We need to update them here, so we can correctly update the bounding box. + self->updateClusterMatrices(); + render::ScenePointer scene = AbstractViewStateInterface::instance()->getMain3DScene(); uint32_t deleteGeometryCounter = self->_deleteGeometryCounter; @@ -240,12 +244,12 @@ void Model::updateRenderItems() { Transform modelTransform = data._model->getTransform(); modelTransform.setScale(glm::vec3(1.0f)); - // lazy update of cluster matrices used for rendering. We need to update them here, so we can correctly update the bounding box. - data._model->updateClusterMatrices(); - - // update the model transform and bounding box for this render item. - const Model::MeshState& state = data._model->_meshStates.at(data._meshIndex); - data.updateTransformForSkinnedMesh(modelTransform, state.clusterMatrices); + const Model::MeshState& state = data._model->getMeshState(data._meshIndex); + Transform renderTransform = modelTransform; + if (state.clusterMatrices.size() == 1) { + renderTransform = modelTransform.worldTransform(Transform(state.clusterMatrices[0])); + } + data.updateTransformForSkinnedMesh(renderTransform, modelTransform, state.clusterBuffer); } } }); From b8ded015c6d085b8b1a022bb3e09d4d74d87e0e6 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Wed, 15 Mar 2017 14:37:43 -0700 Subject: [PATCH 197/302] Fix packetlist compilation on VS 2017 --- libraries/networking/src/udt/PacketQueue.cpp | 23 ++++++++++++-------- libraries/networking/src/udt/PacketQueue.h | 5 +++-- 2 files changed, 17 insertions(+), 11 deletions(-) diff --git a/libraries/networking/src/udt/PacketQueue.cpp b/libraries/networking/src/udt/PacketQueue.cpp index bb20982ca4..9560f2f187 100644 --- a/libraries/networking/src/udt/PacketQueue.cpp +++ b/libraries/networking/src/udt/PacketQueue.cpp @@ -15,6 +15,10 @@ using namespace udt; +PacketQueue::PacketQueue() { + _channels.emplace_back(new std::list()); +} + MessageNumber PacketQueue::getNextMessageNumber() { static const MessageNumber MAX_MESSAGE_NUMBER = MessageNumber(1) << MESSAGE_NUMBER_SIZE; _currentMessageNumber = (_currentMessageNumber + 1) % MAX_MESSAGE_NUMBER; @@ -24,7 +28,7 @@ MessageNumber PacketQueue::getNextMessageNumber() { bool PacketQueue::isEmpty() const { LockGuard locker(_packetsLock); // Only the main channel and it is empty - return (_channels.size() == 1) && _channels.front().empty(); + return (_channels.size() == 1) && _channels.front()->empty(); } PacketQueue::PacketPointer PacketQueue::takePacket() { @@ -34,19 +38,19 @@ PacketQueue::PacketPointer PacketQueue::takePacket() { } // Find next non empty channel - if (_channels[nextIndex()].empty()) { + if (_channels[nextIndex()]->empty()) { nextIndex(); } auto& channel = _channels[_currentIndex]; - Q_ASSERT(!channel.empty()); + Q_ASSERT(!channel->empty()); // Take front packet - auto packet = std::move(channel.front()); - channel.pop_front(); + auto packet = std::move(channel->front()); + channel->pop_front(); // Remove now empty channel (Don't remove the main channel) - if (channel.empty() && _currentIndex != 0) { - channel.swap(_channels.back()); + if (channel->empty() && _currentIndex != 0) { + channel->swap(*_channels.back()); _channels.pop_back(); --_currentIndex; } @@ -61,7 +65,7 @@ unsigned int PacketQueue::nextIndex() { void PacketQueue::queuePacket(PacketPointer packet) { LockGuard locker(_packetsLock); - _channels.front().push_back(std::move(packet)); + _channels.front()->push_back(std::move(packet)); } void PacketQueue::queuePacketList(PacketListPointer packetList) { @@ -70,5 +74,6 @@ void PacketQueue::queuePacketList(PacketListPointer packetList) { } LockGuard locker(_packetsLock); - _channels.push_back(std::move(packetList->_packets)); + _channels.emplace_back(new std::list()); + _channels.back()->swap(packetList->_packets); } diff --git a/libraries/networking/src/udt/PacketQueue.h b/libraries/networking/src/udt/PacketQueue.h index 69784fd8db..2b3d3a4b5b 100644 --- a/libraries/networking/src/udt/PacketQueue.h +++ b/libraries/networking/src/udt/PacketQueue.h @@ -30,10 +30,11 @@ class PacketQueue { using LockGuard = std::lock_guard; using PacketPointer = std::unique_ptr; using PacketListPointer = std::unique_ptr; - using Channel = std::list; + using Channel = std::unique_ptr>; using Channels = std::vector; public: + PacketQueue(); void queuePacket(PacketPointer packet); void queuePacketList(PacketListPointer packetList); @@ -49,7 +50,7 @@ private: MessageNumber _currentMessageNumber { 0 }; mutable Mutex _packetsLock; // Protects the packets to be sent. - Channels _channels = Channels(1); // One channel per packet list + Main channel + Channels _channels; // One channel per packet list + Main channel unsigned int _currentIndex { 0 }; }; From cf848d73e4fd0dce38d6aafda2772893259b663b Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Wed, 15 Mar 2017 16:03:36 -0700 Subject: [PATCH 198/302] don't updateClusterMatrices() in render() --- libraries/render-utils/src/MeshPartPayload.cpp | 5 ----- 1 file changed, 5 deletions(-) diff --git a/libraries/render-utils/src/MeshPartPayload.cpp b/libraries/render-utils/src/MeshPartPayload.cpp index d4b28dbddb..41a1bb4c74 100644 --- a/libraries/render-utils/src/MeshPartPayload.cpp +++ b/libraries/render-utils/src/MeshPartPayload.cpp @@ -582,11 +582,6 @@ void ModelMeshPartPayload::render(RenderArgs* args) const { auto locations = args->_pipeline->locations; assert(locations); - // update the clusterMatrices if necessary - // TODO: we want to remove this updateClusterMatrices() call in this context - // but I recall we tried this before and broke something. - Andrew - _model->updateClusterMatrices(); - bindTransform(batch, locations, args->_renderMode); //Bind the index buffer and vertex buffer and Blend shapes if needed From 2837ae91832e67a26ad0daef3eb4ae27b3f173c1 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Wed, 15 Mar 2017 16:13:15 -0700 Subject: [PATCH 199/302] More VS 2017/2015 fixes --- .../src/DomainServerSettingsManager.cpp | 32 +++++++------------ libraries/networking/src/NodePermissions.h | 12 +++++-- 2 files changed, 22 insertions(+), 22 deletions(-) diff --git a/domain-server/src/DomainServerSettingsManager.cpp b/domain-server/src/DomainServerSettingsManager.cpp index 661a6213b8..c3890d7ce5 100644 --- a/domain-server/src/DomainServerSettingsManager.cpp +++ b/domain-server/src/DomainServerSettingsManager.cpp @@ -246,10 +246,11 @@ void DomainServerSettingsManager::setupConfigMap(const QStringList& argumentList _agentPermissions[editorKey]->set(NodePermissions::Permission::canAdjustLocks); } - QList> permissionsSets; + QList> permissionsSets; permissionsSets << _standardAgentPermissions.get() << _agentPermissions.get(); foreach (auto permissionsSet, permissionsSets) { - foreach (NodePermissionsKey userKey, permissionsSet.keys()) { + for (auto entry : permissionsSet) { + const auto& userKey = entry.first; if (onlyEditorsAreRezzers) { if (permissionsSet[userKey]->can(NodePermissions::Permission::canAdjustLocks)) { permissionsSet[userKey]->set(NodePermissions::Permission::canRezPermanentEntities); @@ -1355,18 +1356,12 @@ QStringList DomainServerSettingsManager::getAllKnownGroupNames() { // extract all the group names from the group-permissions and group-forbiddens settings QSet result; - QHashIterator i(_groupPermissions.get()); - while (i.hasNext()) { - i.next(); - NodePermissionsKey key = i.key(); - result += key.first; + for (const auto& entry : _groupPermissions.get()) { + result += entry.first.first; } - QHashIterator j(_groupForbiddens.get()); - while (j.hasNext()) { - j.next(); - NodePermissionsKey key = j.key(); - result += key.first; + for (const auto& entry : _groupForbiddens.get()) { + result += entry.first.first; } return result.toList(); @@ -1377,20 +1372,17 @@ bool DomainServerSettingsManager::setGroupID(const QString& groupName, const QUu _groupIDs[groupName.toLower()] = groupID; _groupNames[groupID] = groupName; - QHashIterator i(_groupPermissions.get()); - while (i.hasNext()) { - i.next(); - NodePermissionsPointer perms = i.value(); + + for (const auto& entry : _groupPermissions.get()) { + auto& perms = entry.second; if (perms->getID().toLower() == groupName.toLower() && !perms->isGroup()) { changed = true; perms->setGroupID(groupID); } } - QHashIterator j(_groupForbiddens.get()); - while (j.hasNext()) { - j.next(); - NodePermissionsPointer perms = j.value(); + for (const auto& entry : _groupForbiddens.get()) { + auto& perms = entry.second; if (perms->getID().toLower() == groupName.toLower() && !perms->isGroup()) { changed = true; perms->setGroupID(groupID); diff --git a/libraries/networking/src/NodePermissions.h b/libraries/networking/src/NodePermissions.h index 091eb18909..6fa005e360 100644 --- a/libraries/networking/src/NodePermissions.h +++ b/libraries/networking/src/NodePermissions.h @@ -132,8 +132,16 @@ public: bool contains(const QString& keyFirst, const QUuid& keySecond) const { return 0 != _data.count(NodePermissionsKey(keyFirst.toLower(), keySecond)); } - //QList keys() const { return _data.keys(); } - std::unordered_map get() { return _data; } + + QList keys() const { + QList result; + for (const auto& entry : _data) { + result.push_back(entry.first); + } + return result; + } + + const std::unordered_map& get() { return _data; } void clear() { _data.clear(); } void remove(const NodePermissionsKey& key) { _data.erase(key); } From d3fa28d4bc25f484857079757756f87c752a7c75 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Wed, 15 Mar 2017 16:17:59 -0700 Subject: [PATCH 200/302] remove cruft and fix indentation --- .../src/avatar/CauterizedMeshPartPayload.cpp | 6 +-- interface/src/avatar/CauterizedModel.cpp | 51 ++++++++----------- libraries/render-utils/src/Model.cpp | 12 ++--- 3 files changed, 30 insertions(+), 39 deletions(-) diff --git a/interface/src/avatar/CauterizedMeshPartPayload.cpp b/interface/src/avatar/CauterizedMeshPartPayload.cpp index 72a98bd994..d60f334442 100644 --- a/interface/src/avatar/CauterizedMeshPartPayload.cpp +++ b/interface/src/avatar/CauterizedMeshPartPayload.cpp @@ -21,7 +21,7 @@ CauterizedMeshPartPayload::CauterizedMeshPartPayload(Model* model, int meshIndex : ModelMeshPartPayload(model, meshIndex, partIndex, shapeIndex, transform, offsetTransform) {} void CauterizedMeshPartPayload::updateTransformForCauterizedMesh( - const Transform& renderTransform, + const Transform& renderTransform, const gpu::BufferPointer& buffer) { _cauterizedTransform = renderTransform; _cauterizedClusterBuffer = buffer; @@ -33,12 +33,12 @@ void CauterizedMeshPartPayload::bindTransform(gpu::Batch& batch, const render::S SkeletonModel* skeleton = static_cast(_model); bool useCauterizedMesh = (renderMode != RenderArgs::RenderMode::SHADOW_RENDER_MODE) && skeleton->getEnableCauterization(); - if (useCauterizedMesh) { + if (useCauterizedMesh) { if (_cauterizedClusterBuffer) { batch.setUniformBuffer(ShapePipeline::Slot::BUFFER::SKINNING, _cauterizedClusterBuffer); } batch.setModelTransform(_cauterizedTransform); - } else { + } else { if (_clusterBuffer) { batch.setUniformBuffer(ShapePipeline::Slot::BUFFER::SKINNING, _clusterBuffer); } diff --git a/interface/src/avatar/CauterizedModel.cpp b/interface/src/avatar/CauterizedModel.cpp index 8ac14fcb6f..d8db83fbb7 100644 --- a/interface/src/avatar/CauterizedModel.cpp +++ b/interface/src/avatar/CauterizedModel.cpp @@ -26,8 +26,8 @@ CauterizedModel::~CauterizedModel() { } void CauterizedModel::deleteGeometry() { - Model::deleteGeometry(); - _cauterizeMeshStates.clear(); + Model::deleteGeometry(); + _cauterizeMeshStates.clear(); } bool CauterizedModel::updateGeometry() { @@ -41,7 +41,7 @@ bool CauterizedModel::updateGeometry() { _cauterizeMeshStates.append(state); } } - return needsFullUpdate; + return needsFullUpdate; } void CauterizedModel::createVisibleRenderItemSet() { @@ -86,13 +86,13 @@ void CauterizedModel::createVisibleRenderItemSet() { } } } else { - Model::createVisibleRenderItemSet(); + Model::createVisibleRenderItemSet(); } } void CauterizedModel::createCollisionRenderItemSet() { // Temporary HACK: use base class method for now - Model::createCollisionRenderItemSet(); + Model::createCollisionRenderItemSet(); } void CauterizedModel::updateClusterMatrices() { @@ -122,8 +122,8 @@ void CauterizedModel::updateClusterMatrices() { state.clusterBuffer->setSubData(0, state.clusterMatrices.size() * sizeof(glm::mat4), (const gpu::Byte*) state.clusterMatrices.constData()); } - } - } + } + } // as an optimization, don't build cautrizedClusterMatrices if the boneSet is empty. if (!_cauterizeBoneSet.empty()) { @@ -212,31 +212,22 @@ void CauterizedModel::updateRenderItems() { if (data._model && data._model->isLoaded()) { // Ensure the model geometry was not reset between frames if (deleteGeometryCounter == data._model->getGeometryCounter()) { - // this stuff identical to what happens in regular Model - const Model::MeshState& state = data._model->getMeshState(data._meshIndex); - Transform renderTransform = modelTransform; - if (state.clusterMatrices.size() == 1) { - renderTransform = modelTransform.worldTransform(Transform(state.clusterMatrices[0])); - } - data.updateTransformForSkinnedMesh(renderTransform, modelTransform, state.clusterBuffer); - - // this stuff for cauterized mesh - CauterizedModel* cModel = static_cast(data._model); - const Model::MeshState& cState = cModel->getCauterizeMeshState(data._meshIndex); - renderTransform = modelTransform; - if (cState.clusterMatrices.size() == 1) { - renderTransform = modelTransform.worldTransform(Transform(cState.clusterMatrices[0])); - } - data.updateTransformForCauterizedMesh(renderTransform, cState.clusterBuffer); - -/* - // update the model transform and bounding box for this render item. + // this stuff identical to what happens in regular Model const Model::MeshState& state = data._model->getMeshState(data._meshIndex); + Transform renderTransform = modelTransform; + if (state.clusterMatrices.size() == 1) { + renderTransform = modelTransform.worldTransform(Transform(state.clusterMatrices[0])); + } + data.updateTransformForSkinnedMesh(renderTransform, modelTransform, state.clusterBuffer); + + // this stuff for cauterized mesh CauterizedModel* cModel = static_cast(data._model); - assert(data._meshIndex < cModel->_cauterizeMeshStates.size()); - const Model::MeshState& cState = cModel->_cauterizeMeshStates.at(data._meshIndex); - data.updateTransformForSkinnedCauterizedMesh(modelTransform, state.clusterMatrices, cState.clusterMatrices); -*/ + const Model::MeshState& cState = cModel->getCauterizeMeshState(data._meshIndex); + renderTransform = modelTransform; + if (cState.clusterMatrices.size() == 1) { + renderTransform = modelTransform.worldTransform(Transform(cState.clusterMatrices[0])); + } + data.updateTransformForCauterizedMesh(renderTransform, cState.clusterBuffer); } } }); diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index 3cd0fc65f6..c584b0bc21 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -228,7 +228,7 @@ void Model::updateRenderItems() { } // lazy update of cluster matrices used for rendering. - // We need to update them here, so we can correctly update the bounding box. + // We need to update them here so we can correctly update the bounding box. self->updateClusterMatrices(); render::ScenePointer scene = AbstractViewStateInterface::instance()->getMain3DScene(); @@ -245,10 +245,10 @@ void Model::updateRenderItems() { modelTransform.setScale(glm::vec3(1.0f)); const Model::MeshState& state = data._model->getMeshState(data._meshIndex); - Transform renderTransform = modelTransform; - if (state.clusterMatrices.size() == 1) { - renderTransform = modelTransform.worldTransform(Transform(state.clusterMatrices[0])); - } + Transform renderTransform = modelTransform; + if (state.clusterMatrices.size() == 1) { + renderTransform = modelTransform.worldTransform(Transform(state.clusterMatrices[0])); + } data.updateTransformForSkinnedMesh(renderTransform, modelTransform, state.clusterBuffer); } } @@ -1052,7 +1052,7 @@ void Model::updateRig(float deltaTime, glm::mat4 parentTransform) { } void Model::computeMeshPartLocalBounds() { - for (auto& part : _modelMeshRenderItemsSet) { + for (auto& part : _modelMeshRenderItemsSet) { assert(part->_meshIndex < _modelMeshRenderItemsSet.size()); const Model::MeshState& state = _meshStates.at(part->_meshIndex); part->computeAdjustedLocalBound(state.clusterMatrices); From 361e03fc78786da67554d75cf7c81d9e7db02536 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Wed, 15 Mar 2017 16:57:31 -0700 Subject: [PATCH 201/302] remove unused variable --- interface/src/avatar/CauterizedMeshPartPayload.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/interface/src/avatar/CauterizedMeshPartPayload.cpp b/interface/src/avatar/CauterizedMeshPartPayload.cpp index d60f334442..c11f92083b 100644 --- a/interface/src/avatar/CauterizedMeshPartPayload.cpp +++ b/interface/src/avatar/CauterizedMeshPartPayload.cpp @@ -29,7 +29,6 @@ void CauterizedMeshPartPayload::updateTransformForCauterizedMesh( void CauterizedMeshPartPayload::bindTransform(gpu::Batch& batch, const render::ShapePipeline::LocationsPointer locations, RenderArgs::RenderMode renderMode) const { // Still relying on the raw data from the model - const Model::MeshState& state = _model->getMeshState(_meshIndex); SkeletonModel* skeleton = static_cast(_model); bool useCauterizedMesh = (renderMode != RenderArgs::RenderMode::SHADOW_RENDER_MODE) && skeleton->getEnableCauterization(); From 0399249a03eb75cd1a27725ef41a0460e052bfff Mon Sep 17 00:00:00 2001 From: samcake Date: Wed, 15 Mar 2017 16:58:31 -0700 Subject: [PATCH 202/302] Expeand the wireframe to the primitive entities and the polyvox --- .../src/RenderablePolyVoxEntityItem.cpp | 19 ++++++++++++++++--- .../src/RenderablePolyVoxEntityItem.h | 1 + .../src/RenderableShapeEntityItem.cpp | 13 +++++++++++-- .../render-utils/src/RenderDeferredTask.cpp | 5 +++++ libraries/render/src/render/ShapePipeline.h | 4 ++-- libraries/shared/src/RenderArgs.h | 1 + 6 files changed, 36 insertions(+), 7 deletions(-) diff --git a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp index 7359a548fc..95890b533a 100644 --- a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp @@ -53,6 +53,8 @@ #include "PhysicalEntitySimulation.h" gpu::PipelinePointer RenderablePolyVoxEntityItem::_pipeline = nullptr; +gpu::PipelinePointer RenderablePolyVoxEntityItem::_wireframePipeline = nullptr; + const float MARCHING_CUBE_COLLISION_HULL_OFFSET = 0.5; @@ -696,7 +698,7 @@ void RenderablePolyVoxEntityItem::render(RenderArgs* args) { !mesh->getIndexBuffer()._buffer) { return; } - + if (!_pipeline) { gpu::ShaderPointer vertexShader = gpu::Shader::createVertex(std::string(polyvox_vert)); gpu::ShaderPointer pixelShader = gpu::Shader::createPixel(std::string(polyvox_frag)); @@ -715,6 +717,13 @@ void RenderablePolyVoxEntityItem::render(RenderArgs* args) { state->setDepthTest(true, true, gpu::LESS_EQUAL); _pipeline = gpu::Pipeline::create(program, state); + + auto wireframeState = std::make_shared(); + wireframeState->setCullMode(gpu::State::CULL_BACK); + wireframeState->setDepthTest(true, true, gpu::LESS_EQUAL); + wireframeState->setFillMode(gpu::State::FILL_LINE); + + _wireframePipeline = gpu::Pipeline::create(program, wireframeState); } if (!_vertexFormat) { @@ -725,7 +734,11 @@ void RenderablePolyVoxEntityItem::render(RenderArgs* args) { } gpu::Batch& batch = *args->_batch; - batch.setPipeline(_pipeline); + + // Pick correct Pipeline + bool wireframe = (render::ShapeKey(args->_globalShapeKey).isWireframe()); + auto pipeline = (wireframe ? _wireframePipeline : _pipeline); + batch.setPipeline(pipeline); Transform transform(voxelToWorldMatrix()); batch.setModelTransform(transform); @@ -762,7 +775,7 @@ void RenderablePolyVoxEntityItem::render(RenderArgs* args) { batch.setResourceTexture(2, DependencyManager::get()->getWhiteTexture()); } - int voxelVolumeSizeLocation = _pipeline->getProgram()->getUniforms().findLocation("voxelVolumeSize"); + int voxelVolumeSizeLocation = pipeline->getProgram()->getUniforms().findLocation("voxelVolumeSize"); batch._glUniform3f(voxelVolumeSizeLocation, voxelVolumeSize.x, voxelVolumeSize.y, voxelVolumeSize.z); batch.drawIndexed(gpu::TRIANGLES, (gpu::uint32)mesh->getNumIndices(), 0); diff --git a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.h b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.h index 45842c2fb9..fee3d2df02 100644 --- a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.h +++ b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.h @@ -163,6 +163,7 @@ private: const int MATERIAL_GPU_SLOT = 3; render::ItemID _myItem{ render::Item::INVALID_ITEM_ID }; static gpu::PipelinePointer _pipeline; + static gpu::PipelinePointer _wireframePipeline; ShapeInfo _shapeInfo; diff --git a/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp b/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp index c3e097382c..1ad60bf7c6 100644 --- a/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp @@ -114,13 +114,22 @@ void RenderableShapeEntityItem::render(RenderArgs* args) { auto outColor = _procedural->getColor(color); outColor.a *= _procedural->isFading() ? Interpolate::calculateFadeRatio(_procedural->getFadeStartTime()) : 1.0f; batch._glColor4f(outColor.r, outColor.g, outColor.b, outColor.a); - DependencyManager::get()->renderShape(batch, MAPPING[_shape]); + if (render::ShapeKey(args->_globalShapeKey).isWireframe()) { + DependencyManager::get()->renderWireShape(batch, MAPPING[_shape]); + } else { + DependencyManager::get()->renderShape(batch, MAPPING[_shape]); + } } else { // FIXME, support instanced multi-shape rendering using multidraw indirect color.a *= _isFading ? Interpolate::calculateFadeRatio(_fadeStartTime) : 1.0f; auto geometryCache = DependencyManager::get(); auto pipeline = color.a < 1.0f ? geometryCache->getTransparentShapePipeline() : geometryCache->getOpaqueShapePipeline(); - geometryCache->renderSolidShapeInstance(batch, MAPPING[_shape], color, pipeline); + + if (render::ShapeKey(args->_globalShapeKey).isWireframe()) { + geometryCache->renderWireShapeInstance(batch, MAPPING[_shape], color, pipeline); + } else { + geometryCache->renderSolidShapeInstance(batch, MAPPING[_shape], color, pipeline); + } } static const auto triCount = DependencyManager::get()->getShapeTriangleCount(MAPPING[_shape]); diff --git a/libraries/render-utils/src/RenderDeferredTask.cpp b/libraries/render-utils/src/RenderDeferredTask.cpp index 903cbf9367..85a782968f 100644 --- a/libraries/render-utils/src/RenderDeferredTask.cpp +++ b/libraries/render-utils/src/RenderDeferredTask.cpp @@ -265,9 +265,12 @@ void DrawDeferred::run(const SceneContextPointer& sceneContext, const RenderCont keyBuilder.withWireframe(); } ShapeKey globalKey = keyBuilder.build(); + args->_globalShapeKey = globalKey._flags.to_ulong(); renderShapes(sceneContext, renderContext, _shapePlumber, inItems, _maxDrawn, globalKey); + args->_batch = nullptr; + args->_globalShapeKey = 0; }); config->setNumDrawn((int)inItems.size()); @@ -308,6 +311,7 @@ void DrawStateSortDeferred::run(const SceneContextPointer& sceneContext, const R keyBuilder.withWireframe(); } ShapeKey globalKey = keyBuilder.build(); + args->_globalShapeKey = globalKey._flags.to_ulong(); if (_stateSort) { renderStateSortShapes(sceneContext, renderContext, _shapePlumber, inItems, _maxDrawn, globalKey); @@ -315,6 +319,7 @@ void DrawStateSortDeferred::run(const SceneContextPointer& sceneContext, const R renderShapes(sceneContext, renderContext, _shapePlumber, inItems, _maxDrawn, globalKey); } args->_batch = nullptr; + args->_globalShapeKey = 0; }); config->setNumDrawn((int)inItems.size()); diff --git a/libraries/render/src/render/ShapePipeline.h b/libraries/render/src/render/ShapePipeline.h index 7a0b9fe3f3..73e8f82f24 100644 --- a/libraries/render/src/render/ShapePipeline.h +++ b/libraries/render/src/render/ShapePipeline.h @@ -148,7 +148,7 @@ public: bool isSkinned() const { return _flags[SKINNED]; } bool isDepthOnly() const { return _flags[DEPTH_ONLY]; } bool isDepthBiased() const { return _flags[DEPTH_BIAS]; } - bool isWireFrame() const { return _flags[WIREFRAME]; } + bool isWireframe() const { return _flags[WIREFRAME]; } bool isCullFace() const { return !_flags[NO_CULL_FACE]; } bool hasOwnPipeline() const { return _flags[OWN_PIPELINE]; } @@ -184,7 +184,7 @@ inline QDebug operator<<(QDebug debug, const ShapeKey& key) { << "isSkinned:" << key.isSkinned() << "isDepthOnly:" << key.isDepthOnly() << "isDepthBiased:" << key.isDepthBiased() - << "isWireFrame:" << key.isWireFrame() + << "isWireframe:" << key.isWireframe() << "isCullFace:" << key.isCullFace() << "]"; } diff --git a/libraries/shared/src/RenderArgs.h b/libraries/shared/src/RenderArgs.h index b2c05b0548..50722c0deb 100644 --- a/libraries/shared/src/RenderArgs.h +++ b/libraries/shared/src/RenderArgs.h @@ -122,6 +122,7 @@ public: gpu::Batch* _batch = nullptr; std::shared_ptr _whiteTexture; + uint32_t _globalShapeKey { 0 }; bool _enableTexturing { true }; RenderDetails _details; From 4367e3d036ddfa8359459b2d86acb06254727fdd Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Wed, 15 Mar 2017 18:13:10 -0700 Subject: [PATCH 203/302] PR comments --- domain-server/src/DomainServerSettingsManager.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/domain-server/src/DomainServerSettingsManager.cpp b/domain-server/src/DomainServerSettingsManager.cpp index c3890d7ce5..d6b57b450a 100644 --- a/domain-server/src/DomainServerSettingsManager.cpp +++ b/domain-server/src/DomainServerSettingsManager.cpp @@ -246,8 +246,10 @@ void DomainServerSettingsManager::setupConfigMap(const QStringList& argumentList _agentPermissions[editorKey]->set(NodePermissions::Permission::canAdjustLocks); } - QList> permissionsSets; - permissionsSets << _standardAgentPermissions.get() << _agentPermissions.get(); + std::list> permissionsSets{ + _standardAgentPermissions.get(), + _agentPermissions.get() + }; foreach (auto permissionsSet, permissionsSets) { for (auto entry : permissionsSet) { const auto& userKey = entry.first; @@ -301,7 +303,6 @@ void DomainServerSettingsManager::setupConfigMap(const QStringList& argumentList } QVariantMap& DomainServerSettingsManager::getDescriptorsMap() { - static const QString DESCRIPTORS{ "descriptors" }; auto& settingsMap = getSettingsMap(); From 60d10f3374c9bb7459570558f7052216e9fac5d9 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Wed, 15 Mar 2017 20:44:19 -0700 Subject: [PATCH 204/302] command-line utility for fetching an asset from a remote asset-server --- tools/CMakeLists.txt | 2 + tools/atp-get/CMakeLists.txt | 3 + tools/atp-get/src/ATPGetApp.cpp | 269 ++++++++++++++++++++++++++++++++ tools/atp-get/src/ATPGetApp.h | 52 ++++++ tools/atp-get/src/main.cpp | 31 ++++ 5 files changed, 357 insertions(+) create mode 100644 tools/atp-get/CMakeLists.txt create mode 100644 tools/atp-get/src/ATPGetApp.cpp create mode 100644 tools/atp-get/src/ATPGetApp.h create mode 100644 tools/atp-get/src/main.cpp diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt index a85a112bf5..16aa17cd7b 100644 --- a/tools/CMakeLists.txt +++ b/tools/CMakeLists.txt @@ -17,3 +17,5 @@ set_target_properties(ac-client PROPERTIES FOLDER "Tools") add_subdirectory(skeleton-dump) set_target_properties(skeleton-dump PROPERTIES FOLDER "Tools") +add_subdirectory(atp-get) +set_target_properties(skeleton-dump PROPERTIES FOLDER "Tools") diff --git a/tools/atp-get/CMakeLists.txt b/tools/atp-get/CMakeLists.txt new file mode 100644 index 0000000000..b1646dc023 --- /dev/null +++ b/tools/atp-get/CMakeLists.txt @@ -0,0 +1,3 @@ +set(TARGET_NAME atp-get) +setup_hifi_project(Core Widgets) +link_hifi_libraries(shared networking) diff --git a/tools/atp-get/src/ATPGetApp.cpp b/tools/atp-get/src/ATPGetApp.cpp new file mode 100644 index 0000000000..30054fffea --- /dev/null +++ b/tools/atp-get/src/ATPGetApp.cpp @@ -0,0 +1,269 @@ +// +// ATPGetApp.cpp +// tools/atp-get/src +// +// Created by Seth Alves on 2017-3-15 +// Copyright 2017 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ATPGetApp.h" + +ATPGetApp::ATPGetApp(int argc, char* argv[]) : + QCoreApplication(argc, argv) +{ + // parse command-line + QCommandLineParser parser; + parser.setApplicationDescription("High Fidelity ATP-Get"); + + const QCommandLineOption helpOption = parser.addHelpOption(); + + const QCommandLineOption verboseOutput("v", "verbose output"); + parser.addOption(verboseOutput); + + const QCommandLineOption domainAddressOption("d", "domain-server address", "127.0.0.1"); + parser.addOption(domainAddressOption); + + const QCommandLineOption cacheSTUNOption("s", "cache stun-server response"); + parser.addOption(cacheSTUNOption); + + const QCommandLineOption listenPortOption("listenPort", "listen port", QString::number(INVALID_PORT)); + parser.addOption(listenPortOption); + + + if (!parser.parse(QCoreApplication::arguments())) { + qCritical() << parser.errorText() << endl; + parser.showHelp(); + Q_UNREACHABLE(); + } + + if (parser.isSet(helpOption)) { + parser.showHelp(); + Q_UNREACHABLE(); + } + + _verbose = parser.isSet(verboseOutput); + if (!_verbose) { + QLoggingCategory::setFilterRules("qt.network.ssl.warning=false"); + + const_cast(&networking())->setEnabled(QtDebugMsg, false); + const_cast(&networking())->setEnabled(QtInfoMsg, false); + const_cast(&networking())->setEnabled(QtWarningMsg, false); + + const_cast(&shared())->setEnabled(QtDebugMsg, false); + const_cast(&shared())->setEnabled(QtInfoMsg, false); + const_cast(&shared())->setEnabled(QtWarningMsg, false); + } + + + QStringList filenames = parser.positionalArguments(); + if (filenames.empty() || filenames.size() > 2) { + qDebug() << "give remote url and optional local filename as arguments"; + parser.showHelp(); + Q_UNREACHABLE(); + } + + _url = QUrl(filenames[0]); + if (_url.scheme() != "atp") { + qDebug() << "url should start with atp:"; + parser.showHelp(); + Q_UNREACHABLE(); + } + + if (filenames.size() == 2) { + _localOutputFile = filenames[1]; + } + + QString domainServerAddress = "127.0.0.1:40103"; + if (parser.isSet(domainAddressOption)) { + domainServerAddress = parser.value(domainAddressOption); + } + + if (_verbose) { + qDebug() << "domain-server address is" << domainServerAddress; + } + + int listenPort = INVALID_PORT; + if (parser.isSet(listenPortOption)) { + listenPort = parser.value(listenPortOption).toInt(); + } + + Setting::init(); + DependencyManager::registerInheritance(); + + DependencyManager::set([&]{ return QString("Mozilla/5.0 (HighFidelityATPGet)"); }); + DependencyManager::set(); + DependencyManager::set(NodeType::Agent, listenPort); + + + auto nodeList = DependencyManager::get(); + + // start the nodeThread so its event loop is running + QThread* nodeThread = new QThread(this); + nodeThread->setObjectName("NodeList Thread"); + nodeThread->start(); + + // make sure the node thread is given highest priority + nodeThread->setPriority(QThread::TimeCriticalPriority); + + // setup a timer for domain-server check ins + QTimer* domainCheckInTimer = new QTimer(nodeList.data()); + connect(domainCheckInTimer, &QTimer::timeout, nodeList.data(), &NodeList::sendDomainServerCheckIn); + domainCheckInTimer->start(DOMAIN_SERVER_CHECK_IN_MSECS); + + // put the NodeList and datagram processing on the node thread + nodeList->moveToThread(nodeThread); + + const DomainHandler& domainHandler = nodeList->getDomainHandler(); + + connect(&domainHandler, SIGNAL(hostnameChanged(const QString&)), SLOT(domainChanged(const QString&))); + // connect(&domainHandler, SIGNAL(resetting()), SLOT(resettingDomain())); + // connect(&domainHandler, SIGNAL(disconnectedFromDomain()), SLOT(clearDomainOctreeDetails())); + connect(&domainHandler, &DomainHandler::domainConnectionRefused, this, &ATPGetApp::domainConnectionRefused); + + connect(nodeList.data(), &NodeList::nodeAdded, this, &ATPGetApp::nodeAdded); + connect(nodeList.data(), &NodeList::nodeKilled, this, &ATPGetApp::nodeKilled); + connect(nodeList.data(), &NodeList::nodeActivated, this, &ATPGetApp::nodeActivated); + // connect(nodeList.data(), &NodeList::uuidChanged, getMyAvatar(), &MyAvatar::setSessionUUID); + // connect(nodeList.data(), &NodeList::uuidChanged, this, &ATPGetApp::setSessionUUID); + connect(nodeList.data(), &NodeList::packetVersionMismatch, this, &ATPGetApp::notifyPacketVersionMismatch); + + nodeList->addSetOfNodeTypesToNodeInterestSet(NodeSet() << NodeType::AudioMixer << NodeType::AvatarMixer + << NodeType::EntityServer << NodeType::AssetServer << NodeType::MessagesMixer); + + DependencyManager::get()->handleLookupString(domainServerAddress, false); + + auto assetClient = DependencyManager::set(); + assetClient->init(); + + QTimer* doTimer = new QTimer(this); + doTimer->setSingleShot(true); + connect(doTimer, &QTimer::timeout, this, &ATPGetApp::timedOut); + doTimer->start(4000); +} + +ATPGetApp::~ATPGetApp() { +} + + +void ATPGetApp::domainConnectionRefused(const QString& reasonMessage, int reasonCodeInt, const QString& extraInfo) { + qDebug() << "domainConnectionRefused"; +} + +void ATPGetApp::domainChanged(const QString& domainHostname) { + if (_verbose) { + qDebug() << "domainChanged"; + } +} + +void ATPGetApp::nodeAdded(SharedNodePointer node) { + if (_verbose) { + qDebug() << "node added: " << node->getType(); + } +} + +void ATPGetApp::nodeActivated(SharedNodePointer node) { + if (node->getType() == NodeType::AssetServer) { + lookup(); + } +} + +void ATPGetApp::nodeKilled(SharedNodePointer node) { + qDebug() << "nodeKilled"; +} + +void ATPGetApp::timedOut() { + finish(1); +} + +void ATPGetApp::notifyPacketVersionMismatch() { + if (_verbose) { + qDebug() << "packet version mismatch"; + } + finish(1); +} + +void ATPGetApp::lookup() { + + auto path = _url.path(); + qDebug() << "path is " << path; + + auto request = DependencyManager::get()->createGetMappingRequest(path); + QObject::connect(request, &GetMappingRequest::finished, this, [=](GetMappingRequest* request) mutable { + auto result = request->getError(); + if (result == GetMappingRequest::NotFound) { + qDebug() << "not found"; + } else if (result == GetMappingRequest::NoError) { + qDebug() << "found, hash is " << request->getHash(); + download(request->getHash()); + } else { + qDebug() << "error -- " << request->getError() << " -- " << request->getErrorString(); + } + request->deleteLater(); + }); + request->start(); +} + +void ATPGetApp::download(AssetHash hash) { + auto assetClient = DependencyManager::get(); + auto assetRequest = new AssetRequest(hash); + + connect(assetRequest, &AssetRequest::finished, this, [this](AssetRequest* request) mutable { + Q_ASSERT(request->getState() == AssetRequest::Finished); + + if (request->getError() == AssetRequest::Error::NoError) { + QString data = QString::fromUtf8(request->getData()); + if (_localOutputFile == "") { + QTextStream cout(stdout); + cout << data; + } else { + QFile outputHandle(_localOutputFile); + if (outputHandle.open(QIODevice::ReadWrite)) { + QTextStream stream( &outputHandle ); + stream << data; + } else { + qDebug() << "couldn't open output file:" << _localOutputFile; + } + } + } + + request->deleteLater(); + finish(0); + }); + + assetRequest->start(); +} + +void ATPGetApp::finish(int exitCode) { + auto nodeList = DependencyManager::get(); + + // send the domain a disconnect packet, force stoppage of domain-server check-ins + nodeList->getDomainHandler().disconnect(); + nodeList->setIsShuttingDown(true); + + // tell the packet receiver we're shutting down, so it can drop packets + nodeList->getPacketReceiver().setShouldDropPackets(true); + + QThread* nodeThread = DependencyManager::get()->thread(); + // remove the NodeList from the DependencyManager + DependencyManager::destroy(); + // ask the node thread to quit and wait until it is done + nodeThread->quit(); + nodeThread->wait(); + + QCoreApplication::exit(exitCode); +} diff --git a/tools/atp-get/src/ATPGetApp.h b/tools/atp-get/src/ATPGetApp.h new file mode 100644 index 0000000000..5507d2aa62 --- /dev/null +++ b/tools/atp-get/src/ATPGetApp.h @@ -0,0 +1,52 @@ +// +// ATPGetApp.h +// tools/atp-get/src +// +// Created by Seth Alves on 2017-3-15 +// Copyright 2017 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + + +#ifndef hifi_ATPGetApp_h +#define hifi_ATPGetApp_h + +#include +#include +#include +#include +#include +#include +#include +#include + + +class ATPGetApp : public QCoreApplication { + Q_OBJECT +public: + ATPGetApp(int argc, char* argv[]); + ~ATPGetApp(); + +private slots: + void domainConnectionRefused(const QString& reasonMessage, int reasonCodeInt, const QString& extraInfo); + void domainChanged(const QString& domainHostname); + void nodeAdded(SharedNodePointer node); + void nodeActivated(SharedNodePointer node); + void nodeKilled(SharedNodePointer node); + void notifyPacketVersionMismatch(); + +private: + NodeList* _nodeList; + void timedOut(); + void lookup(); + void download(AssetHash hash); + void finish(int exitCode); + bool _verbose; + + QUrl _url; + QString _localOutputFile; +}; + +#endif // hifi_ATPGetApp_h diff --git a/tools/atp-get/src/main.cpp b/tools/atp-get/src/main.cpp new file mode 100644 index 0000000000..bddf30c666 --- /dev/null +++ b/tools/atp-get/src/main.cpp @@ -0,0 +1,31 @@ +// +// main.cpp +// tools/atp-get/src +// +// Created by Seth Alves on 2017-3-15 +// Copyright 2017 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 "ATPGetApp.h" + +using namespace std; + +int main(int argc, char * argv[]) { + QCoreApplication::setApplicationName(BuildInfo::AC_CLIENT_SERVER_NAME); + QCoreApplication::setOrganizationName(BuildInfo::MODIFIED_ORGANIZATION); + QCoreApplication::setOrganizationDomain(BuildInfo::ORGANIZATION_DOMAIN); + QCoreApplication::setApplicationVersion(BuildInfo::VERSION); + + ATPGetApp app(argc, argv); + + return app.exec(); +} From 438fb42da45cc429d82a33a0b15f54bd07599601 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Wed, 15 Mar 2017 20:54:08 -0700 Subject: [PATCH 205/302] oops --- tools/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt index 16aa17cd7b..8dc993e6fe 100644 --- a/tools/CMakeLists.txt +++ b/tools/CMakeLists.txt @@ -18,4 +18,4 @@ add_subdirectory(skeleton-dump) set_target_properties(skeleton-dump PROPERTIES FOLDER "Tools") add_subdirectory(atp-get) -set_target_properties(skeleton-dump PROPERTIES FOLDER "Tools") +set_target_properties(atp-get PROPERTIES FOLDER "Tools") From 52a571558cc974eedae95cc3f2f4908433c40c0e Mon Sep 17 00:00:00 2001 From: humbletim Date: Thu, 16 Mar 2017 01:23:23 -0400 Subject: [PATCH 206/302] * changes per CR feedback * revert JSON content-type workaround * add specific error check / advice for unanchored module ids * update unit tests --- .../entities/src/EntityScriptingInterface.cpp | 12 +- libraries/script-engine/src/ScriptEngine.cpp | 132 +++++------------- libraries/script-engine/src/ScriptEngine.h | 7 +- .../developer/libraries/jasmine/hifi-boot.js | 2 +- .../tests/unit_tests/moduleUnitTests.js | 40 ++++-- 5 files changed, 65 insertions(+), 128 deletions(-) diff --git a/libraries/entities/src/EntityScriptingInterface.cpp b/libraries/entities/src/EntityScriptingInterface.cpp index 54efa3d89f..08216b015a 100644 --- a/libraries/entities/src/EntityScriptingInterface.cpp +++ b/libraries/entities/src/EntityScriptingInterface.cpp @@ -686,24 +686,18 @@ bool EntityScriptingInterface::reloadServerScripts(QUuid entityID) { bool EntityPropertyMetadataRequest::script(EntityItemID entityID, QScriptValue handler) { using LocalScriptStatusRequest = QFutureWatcher; - LocalScriptStatusRequest *request = new LocalScriptStatusRequest; + LocalScriptStatusRequest* request = new LocalScriptStatusRequest; QObject::connect(request, &LocalScriptStatusRequest::finished, _engine, [=]() mutable { - auto engine = _engine; - if (!engine) { - // this is just to address any lingering doubts -- when _engine is destroyed, this connect gets broken automatically - qCDebug(entities) << __FUNCTION__ << " -- engine destroyed while inflight" << entityID; - return; - } auto details = request->result().toMap(); QScriptValue err, result; if (details.contains("isError")) { if (!details.contains("message")) { details["message"] = details["errorInfo"]; } - err = engine->makeError(engine->toScriptValue(details)); + err = _engine->makeError(_engine->toScriptValue(details)); } else { details["success"] = true; - result = engine->toScriptValue(details); + result = _engine->toScriptValue(details); } callScopedHandlerObject(handler, err, result); request->deleteLater(); diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index 615c385a52..d956deed2f 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -73,15 +73,11 @@ #include "MIDIEvent.h" -const QString ScriptEngine::_SETTINGS_ENABLE_EXTENDED_MODULE_COMPAT { - "com.highfidelity.experimental.enableExtendedModuleCompatbility" -}; - const QString ScriptEngine::_SETTINGS_ENABLE_EXTENDED_EXCEPTIONS { "com.highfidelity.experimental.enableExtendedJSExceptions" }; -static const int MAX_MODULE_ID_LENTGH { 4096 }; +static const int MAX_MODULE_ID_LENGTH { 4096 }; static const int MAX_DEBUG_VALUE_LENGTH { 80 }; static const QScriptEngine::QObjectWrapOptions DEFAULT_QOBJECT_WRAP_OPTIONS = @@ -96,7 +92,7 @@ int functionSignatureMetaID = qRegisterMetaTypeargumentCount(); i++) { if (i > 0) { @@ -345,11 +341,7 @@ void ScriptEngine::runInThread() { // The thread interface cannot live on itself, and we want to move this into the thread, so // the thread cannot have this as a parent. QThread* workerThread = new QThread(); -#ifdef Q_OS_LINUX - workerThread->setObjectName(QString("js:") + getFilename()); -#else - workerThread->setObjectName(QString("Script Thread:") + getFilename()); -#endif + workerThread->setObjectName(QString("js:") + getFilename().replace("about:","")); moveToThread(workerThread); // NOTE: If you connect any essential signals for proper shutdown or cleanup of @@ -550,7 +542,7 @@ static QScriptValue createScriptableResourcePrototype(QScriptEngine* engine) { void ScriptEngine::resetModuleCache(bool deleteScriptCache) { if (QThread::currentThread() != thread()) { - executeOnScriptThread([=](){ resetModuleCache(deleteScriptCache); }); + executeOnScriptThread([=]() { resetModuleCache(deleteScriptCache); }); return; } auto jsRequire = globalObject().property("Script").property("require"); @@ -1379,14 +1371,14 @@ QString ScriptEngine::_requireResolve(const QString& moduleId, const QString& re auto throwResolveError = [&](const QScriptValue& error) -> QString { raiseException(error); maybeEmitUncaughtException("require.resolve"); - return nullptr; + return QString(); }; // de-fuzz the input a little by restricting to rational sizes auto idLength = url.toString().length(); - if (idLength < 1 || idLength > MAX_MODULE_ID_LENTGH) { + if (idLength < 1 || idLength > MAX_MODULE_ID_LENGTH) { auto details = QString("rejecting invalid module id size (%1 chars [1,%2])") - .arg(idLength).arg(MAX_MODULE_ID_LENTGH); + .arg(idLength).arg(MAX_MODULE_ID_LENGTH); return throwResolveError(makeError(message.arg(details), "RangeError")); } @@ -1402,11 +1394,21 @@ QString ScriptEngine::_requireResolve(const QString& moduleId, const QString& re url = resolvePath(moduleId); } else { // check if the moduleId refers to a "system" module - QString defaultsPath = defaultScriptsLoc.path(); - QString systemModulePath = QString("%1/modules/%2.js").arg(defaultsPath).arg(moduleId); + QString systemPath = defaultScriptsLoc.path(); + QString systemModulePath = QString("%1/modules/%2.js").arg(systemPath).arg(moduleId); url = defaultScriptsLoc; url.setPath(systemModulePath); if (!QFileInfo(url.toLocalFile()).isFile()) { + if (!moduleId.contains("./")) { + // the user might be trying to refer to a relative file without anchoring it + // let's do them a favor and test for that case -- offering specific advice if detected + auto unanchoredUrl = resolvePath("./" + moduleId); + if (QFileInfo(unanchoredUrl.toLocalFile()).isFile()) { + auto msg = QString("relative module ids must be anchored; use './%1' instead") + .arg(moduleId); + return throwResolveError(makeError(message.arg(msg))); + } + } return throwResolveError(makeError(message.arg("system module not found"))); } } @@ -1469,7 +1471,7 @@ bool ScriptEngine::registerModuleWithParent(const QScriptValue& module, const QS if (children.isArray()) { auto key = module.property("id"); auto length = children.property("length").toInt32(); - for (int i=0; i < length; i++) { + for (int i = 0; i < length; i++) { if (children.property(i).property("id").strictlyEquals(key)) { qCDebug(scriptengine_module) << key.toString() << " updating parent.children[" << i << "] = module"; children.setProperty(i, module); @@ -1516,10 +1518,10 @@ QScriptValue ScriptEngine::newModule(const QString& modulePath, const QScriptVal } // synchronously fetch a module's source code using BatchLoader -QScriptValue ScriptEngine::fetchModuleSource(const QString& modulePath, const bool forceDownload) { +QVariantMap ScriptEngine::fetchModuleSource(const QString& modulePath, const bool forceDownload) { using UrlMap = QMap; auto scriptCache = DependencyManager::get(); - QScriptValue req = newObject(); + QVariantMap req; qCDebug(scriptengine_module) << "require.fetchModuleSource: " << QUrl(modulePath).fileName() << QThread::currentThread(); auto onload = [=, &req](const UrlMap& data, const UrlMap& _status) { @@ -1528,13 +1530,13 @@ QScriptValue ScriptEngine::fetchModuleSource(const QString& modulePath, const bo auto contents = data[url]; qCDebug(scriptengine_module) << "require.fetchModuleSource.onload: " << QUrl(url).fileName() << status << QThread::currentThread(); if (isStopping()) { - req.setProperty("status", "Stopped"); - req.setProperty("success", false); + req["status"] = "Stopped"; + req["success"] = false; } else { - req.setProperty("url", url); - req.setProperty("status", status); - req.setProperty("success", ScriptCache::isSuccessStatus(status)); - req.setProperty("contents", contents, READONLY_HIDDEN_PROP_FLAGS); + req["url"] = url; + req["status"] = status; + req["success"] = ScriptCache::isSuccessStatus(status); + req["contents"] = contents; } }; @@ -1661,17 +1663,17 @@ QScriptValue ScriptEngine::require(const QString& moduleId) { // download the module source auto req = fetchModuleSource(modulePath, invalidateCache); - if (!req.property("success").toBool()) { - auto error = QString("error retrieving script (%1)").arg(req.property("status").toString()); + if (!req.contains("success") || !req["success"].toBool()) { + auto error = QString("error retrieving script (%1)").arg(req["status"].toString()); return throwModuleError(modulePath, error); } #if DEBUG_JS_MODULES qCDebug(scriptengine_module) << "require.loaded: " << - QUrl(req.property("url").toString()).fileName() << req.property("status").toString(); + QUrl(req["url"].toString()).fileName() << req["status"].toString(); #endif - auto sourceCode = req.property("contents").toString(); + auto sourceCode = req["contents"].toString(); if (QUrl(modulePath).fileName().endsWith(".json", Qt::CaseInsensitive)) { module.setProperty("content-type", "application/json"); @@ -1679,28 +1681,6 @@ QScriptValue ScriptEngine::require(const QString& moduleId) { module.setProperty("content-type", "application/javascript"); } - { - // It seems that many JSON sources don't actually put .json in the URL... - // so for now as a workaround users wanting to indicate JSON parsing mode can - // do so by calling with a custom this context, eg: - // - // var ITEMS_URL = 'https://highfidelity.io/api/v1/marketplace/items'; - // var thisObject = { 'content-type': 'application/json' }; - // var items = Script.require.call(thisObject, ITEMS_URL + '?category=everything&sort=recent'); - - auto thisObject = currentContext()->thisObject(); - bool calledWithCustomThis = thisObject.isObject() && - !thisObject.strictlyEquals(globalObject()) && - !thisObject.toQObject(); - - if (calledWithCustomThis) { -#ifdef DEBUG_JS - _debugDump("this", thisObject); -#endif - applyUserOptions(module, thisObject); - } - } - // evaluate the module auto result = instantiateModule(module, sourceCode); @@ -1721,54 +1701,6 @@ QScriptValue ScriptEngine::require(const QString& moduleId) { return module.property("exports"); } -// User-configurable override options -void ScriptEngine::applyUserOptions(QScriptValue& module, QScriptValue& options) { - if (!IS_THREADSAFE_INVOCATION(thread(), __FUNCTION__)) { - return; - } - if (!options.isValid()) { - return; - } - // options['content-type'] === 'application/json' - // -- allows JSON modules to be used from URLs not ending in .json - if (options.property("content-type").isString()) { - module.setProperty("content-type", options.property("content-type")); - qCDebug(scriptengine_module) << "module['content-type'] =" << module.property("content-type").toString(); - } - - if (ScriptEngine::_enableExtendedModuleCompatbility.get()) { - auto closure = module.property("__closure__"); - - auto maybeSetToExports = [&](const QString& key) { - if (options.property(key).toString() == "exports") { - closure.setProperty(key, module.property("exports")); - qCDebug(scriptengine_module) << "module.closure[" << key << "] = exports"; - } - }; - - // options[{key}] = 'exports' - // several "agnostic" modules in the wild are just one step away from being compatible -- - // they just didn't know not to look specifically for this, self or global for attaching - // things onto. - maybeSetToExports("global"); - maybeSetToExports("self"); - maybeSetToExports("this"); - - // when options is an Object it will get used as the value of "this" during module evaluation - // (which is what one might expect when calling require.call(thisObject, ...)) - if (options.isObject()) { - closure.setProperty("this", options); - } - - // when options.global is an Object it'll get used as the global object (during evaluation only) - if (options.property("global").isObject()) { - closure.setProperty("global", options.property("global")); - qCDebug(scriptengine_module) << "module.closure['global'] = options.global"; - } - } - maybeEmitUncaughtException(__FUNCTION__); -} - // If a callback is specified, the included files will be loaded asynchronously and the callback will be called // when all of the files have finished loading. // If no callback is specified, the included files will be loaded synchronously and will block execution until diff --git a/libraries/script-engine/src/ScriptEngine.h b/libraries/script-engine/src/ScriptEngine.h index ef6f3b6896..a3f709eff1 100644 --- a/libraries/script-engine/src/ScriptEngine.h +++ b/libraries/script-engine/src/ScriptEngine.h @@ -167,7 +167,7 @@ public: QScriptValue currentModule(); bool registerModuleWithParent(const QScriptValue& module, const QScriptValue& parent); QScriptValue newModule(const QString& modulePath, const QScriptValue& parent = QScriptValue()); - QScriptValue fetchModuleSource(const QString& modulePath, const bool forceDownload = false); + QVariantMap fetchModuleSource(const QString& modulePath, const bool forceDownload = false); QScriptValue instantiateModule(const QScriptValue& module, const QString& sourceCode); Q_INVOKABLE QObject* setInterval(const QScriptValue& function, int intervalMS); @@ -308,7 +308,7 @@ protected: AssetScriptingInterface _assetScriptingInterface{ this }; - std::function _emitScriptUpdates{ [](){ return true; } }; + std::function _emitScriptUpdates{ []() { return true; } }; std::recursive_mutex _lock; @@ -317,10 +317,7 @@ protected: static const QString _SETTINGS_ENABLE_EXTENDED_MODULE_COMPAT; static const QString _SETTINGS_ENABLE_EXTENDED_EXCEPTIONS; - Setting::Handle _enableExtendedModuleCompatbility { _SETTINGS_ENABLE_EXTENDED_MODULE_COMPAT, false }; Setting::Handle _enableExtendedJSExceptions { _SETTINGS_ENABLE_EXTENDED_EXCEPTIONS, true }; - - void applyUserOptions(QScriptValue& module, QScriptValue& options); }; #endif // hifi_ScriptEngine_h diff --git a/scripts/developer/libraries/jasmine/hifi-boot.js b/scripts/developer/libraries/jasmine/hifi-boot.js index 8757550ae8..772dd8c17e 100644 --- a/scripts/developer/libraries/jasmine/hifi-boot.js +++ b/scripts/developer/libraries/jasmine/hifi-boot.js @@ -62,7 +62,7 @@ clearTimeout = Script.clearTimeout; clearInterval = Script.clearInterval; - var jasmine = jasmineRequire.core(jasmineRequire); + var jasmine = this.jasmine = jasmineRequire.core(jasmineRequire); var env = jasmine.getEnv(); diff --git a/scripts/developer/tests/unit_tests/moduleUnitTests.js b/scripts/developer/tests/unit_tests/moduleUnitTests.js index a9446d1d6e..6810dd8b6d 100644 --- a/scripts/developer/tests/unit_tests/moduleUnitTests.js +++ b/scripts/developer/tests/unit_tests/moduleUnitTests.js @@ -32,6 +32,18 @@ describe('require', function() { mod.exists; }).toThrowError(/Cannot find/); }); + it('should reject unanchored, existing filenames with advice', function() { + expect(function() { + var mod = require.resolve('moduleTests/example.json'); + mod.exists; + }).toThrowError(/use '.\/moduleTests\/example\.json'/); + }); + it('should reject unanchored, non-existing filenames', function() { + expect(function() { + var mod = require.resolve('asdfssdf/example.json'); + mod.exists; + }).toThrowError(/Cannot find.*system module not found/); + }); it('should reject non-existent filenames', function() { expect(function() { require.resolve('./404error.js'); @@ -67,19 +79,21 @@ describe('require', function() { var example = require('./moduleTests/example.json'); expect(example.name).toEqual('Example JSON Module'); }); - INTERFACE.describe('interface', function() { - NETWORK.describe('network', function() { - // xit('should import #content-type=application/json modules', function() { - // var results = require('https://jsonip.com#content-type=application/json'); - // expect(results.ip).toMatch(/^[.0-9]+$/); - // }); - it('should import content-type: application/json modules', function() { - var scope = { 'content-type': 'application/json' }; - var results = require.call(scope, 'https://jsonip.com'); - expect(results.ip).toMatch(/^[.0-9]+$/); - }); - }); - }); + // noet: support for loading JSON via content type workarounds reverted + // (leaving these tests intact in case ever revisited later) + // INTERFACE.describe('interface', function() { + // NETWORK.describe('network', function() { + // xit('should import #content-type=application/json modules', function() { + // var results = require('https://jsonip.com#content-type=application/json'); + // expect(results.ip).toMatch(/^[.0-9]+$/); + // }); + // xit('should import content-type: application/json modules', function() { + // var scope = { 'content-type': 'application/json' }; + // var results = require.call(scope, 'https://jsonip.com'); + // expect(results.ip).toMatch(/^[.0-9]+$/); + // }); + // }); + // }); }); From 366c90ef6acfb46e5f85998cc433ba2fce1a7639 Mon Sep 17 00:00:00 2001 From: humbletim Date: Thu, 16 Mar 2017 01:28:50 -0400 Subject: [PATCH 207/302] add Q_ASSERT to IS_THREADSAFE_INVOCATION --- libraries/shared/src/BaseScriptEngine.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/libraries/shared/src/BaseScriptEngine.cpp b/libraries/shared/src/BaseScriptEngine.cpp index d803e85ed6..c92d629b75 100644 --- a/libraries/shared/src/BaseScriptEngine.cpp +++ b/libraries/shared/src/BaseScriptEngine.cpp @@ -31,6 +31,7 @@ bool BaseScriptEngine::IS_THREADSAFE_INVOCATION(const QThread *thread, const QSt qCCritical(shared) << QString("Scripting::%1 @ %2 -- ignoring thread-unsafe call from %3") .arg(method).arg(thread ? thread->objectName() : "(!thread)").arg(QThread::currentThread()->objectName()); qCDebug(shared) << "(please resolve on the calling side by using invokeMethod, executeOnScriptThread, etc.)"; + Q_ASSERT(false); return false; } From e7a16d4294a26c724b5f30dabf504088227ed7c9 Mon Sep 17 00:00:00 2001 From: kunalgosar Date: Thu, 16 Mar 2017 10:53:35 -0700 Subject: [PATCH 208/302] code standard fixes --- interface/src/ui/BaseLogDialog.cpp | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/interface/src/ui/BaseLogDialog.cpp b/interface/src/ui/BaseLogDialog.cpp index c316206e7a..571d3ac403 100644 --- a/interface/src/ui/BaseLogDialog.cpp +++ b/interface/src/ui/BaseLogDialog.cpp @@ -29,6 +29,7 @@ const int SEARCH_BUTTON_WIDTH = 20; const int SEARCH_TOGGLE_BUTTON_WIDTH = 50; const int SEARCH_TEXT_WIDTH = 240; const int TIME_STAMP_LENGTH = 16; +const int FONT_WEIGHT = 75; const QColor HIGHLIGHT_COLOR = QColor("#3366CC"); const QColor BOLD_COLOR = QColor("#445c8c"); const QString BOLD_PATTERN = "\\[\\d*\\/.*:\\d*:\\d*\\]"; @@ -38,7 +39,7 @@ public: Highlighter(QTextDocument* parent = nullptr); void setBold(int indexToBold); QString keyword; - + protected: void highlightBlock(const QString& text) override; @@ -94,7 +95,7 @@ void BaseLogDialog::initControls() { _leftPad += SEARCH_TOGGLE_BUTTON_WIDTH + BUTTON_MARGIN; _searchPrevButton->show(); connect(_searchPrevButton, SIGNAL(clicked()), SLOT(toggleSearchPrev())); - + _searchNextButton = new QPushButton(this); _searchNextButton->setObjectName("searchNextButton"); _searchNextButton->setGeometry(_leftPad, ELEMENT_MARGIN, SEARCH_TOGGLE_BUTTON_WIDTH, ELEMENT_HEIGHT); @@ -134,7 +135,7 @@ void BaseLogDialog::handleSearchTextChanged(QString searchText) { if (searchText.isEmpty()) { return; } - + QTextCursor cursor = _logTextBox->textCursor(); if (cursor.hasSelection()) { QString selectedTerm = cursor.selectedText(); @@ -142,16 +143,16 @@ void BaseLogDialog::handleSearchTextChanged(QString searchText) { return; } } - + cursor.setPosition(0, QTextCursor::MoveAnchor); _logTextBox->setTextCursor(cursor); bool foundTerm = _logTextBox->find(searchText); - + if (!foundTerm) { cursor.movePosition(QTextCursor::End, QTextCursor::MoveAnchor); _logTextBox->setTextCursor(cursor); } - + _searchTerm = searchText; _highlighter->keyword = searchText; _highlighter->rehighlight(); @@ -195,10 +196,10 @@ void BaseLogDialog::updateSelection() { } Highlighter::Highlighter(QTextDocument* parent) : QSyntaxHighlighter(parent) { - boldFormat.setFontWeight(75); + boldFormat.setFontWeight(FONT_WEIGHT); boldFormat.setForeground(BOLD_COLOR); keywordFormat.setForeground(HIGHLIGHT_COLOR); - } +} void Highlighter::highlightBlock(const QString& text) { QRegExp expression(BOLD_PATTERN); @@ -206,11 +207,11 @@ void Highlighter::highlightBlock(const QString& text) { int index = text.indexOf(expression, 0); while (index >= 0) { - int length = expression.matchedLength(); - setFormat(index, length, boldFormat); - index = text.indexOf(expression, index + length); + int length = expression.matchedLength(); + setFormat(index, length, boldFormat); + index = text.indexOf(expression, index + length); } - + if (keyword.isNull() || keyword.isEmpty()) { return; } From 3b8c82491a74bfafdd6d230591e9230d8f883f93 Mon Sep 17 00:00:00 2001 From: Zach Pomerantz Date: Mon, 13 Mar 2017 19:27:00 -0400 Subject: [PATCH 209/302] add AudioNoiseGate to Agent --- assignment-client/src/Agent.cpp | 24 +++++++++++++++++++----- assignment-client/src/Agent.h | 2 ++ 2 files changed, 21 insertions(+), 5 deletions(-) diff --git a/assignment-client/src/Agent.cpp b/assignment-client/src/Agent.cpp index 3562975c4d..a0c80453e0 100644 --- a/assignment-client/src/Agent.cpp +++ b/assignment-client/src/Agent.cpp @@ -371,25 +371,39 @@ void Agent::executeScript() { using namespace recording; static const FrameType AUDIO_FRAME_TYPE = Frame::registerFrameType(AudioConstants::getAudioFrameName()); Frame::registerFrameHandler(AUDIO_FRAME_TYPE, [this, &scriptedAvatar](Frame::ConstPointer frame) { - const QByteArray& audio = frame->data; static quint16 audioSequenceNumber{ 0 }; - Transform audioTransform; + QByteArray audio(frame->data); + + if (_isNoiseGateEnabled) { + static int numSamples = AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL; + _noiseGate.gateSamples(reinterpret_cast(audio.data()), numSamples); + } + + computeLoudness(&audio, scriptedAvatar); + + // the codec needs a flush frame before sending silent packets, so + // do not send one if the gate closed in this block (eventually this can be crossfaded). + auto packetType = PacketType::MicrophoneAudioNoEcho; + if (scriptedAvatar->getAudioLoudness() == 0.0f && !_noiseGate.closedInLastBlock()) { + packetType = PacketType::SilentAudioFrame; + } + + Transform audioTransform; auto headOrientation = scriptedAvatar->getHeadOrientation(); audioTransform.setTranslation(scriptedAvatar->getPosition()); audioTransform.setRotation(headOrientation); - computeLoudness(&audio, scriptedAvatar); - QByteArray encodedBuffer; if (_encoder) { _encoder->encode(audio, encodedBuffer); } else { encodedBuffer = audio; } + AbstractAudioInterface::emitAudioPacket(encodedBuffer.data(), encodedBuffer.size(), audioSequenceNumber, audioTransform, scriptedAvatar->getPosition(), glm::vec3(0), - PacketType::MicrophoneAudioNoEcho, _selectedCodecName); + packetType, _selectedCodecName); }); auto avatarHashMap = DependencyManager::set(); diff --git a/assignment-client/src/Agent.h b/assignment-client/src/Agent.h index 6bece4e945..620ac8e047 100644 --- a/assignment-client/src/Agent.h +++ b/assignment-client/src/Agent.h @@ -29,6 +29,7 @@ #include +#include "AudioNoiseGate.h" #include "MixedAudioStream.h" #include "avatars/ScriptableAvatar.h" @@ -110,6 +111,7 @@ private: QTimer* _avatarIdentityTimer = nullptr; QHash _outgoingScriptAudioSequenceNumbers; + AudioNoiseGate _noiseGate; bool _isNoiseGateEnabled { false }; CodecPluginPointer _codec; From 0cee9ee172e6d864af514a386e2b72c7fba4af6d Mon Sep 17 00:00:00 2001 From: kunalgosar Date: Thu, 16 Mar 2017 11:57:46 -0700 Subject: [PATCH 210/302] font change --- interface/resources/styles/log_dialog.qss | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/interface/resources/styles/log_dialog.qss b/interface/resources/styles/log_dialog.qss index 9ce5aeccc2..d3ae4e0a00 100644 --- a/interface/resources/styles/log_dialog.qss +++ b/interface/resources/styles/log_dialog.qss @@ -1,6 +1,6 @@ QPlainTextEdit { - font-family: Courier New, Courier, Monotype; + font-family: Inconsolata, Consolas, Courier New, monospace; font-size: 16px; padding-left: 28px; padding-top: 7px; @@ -11,7 +11,7 @@ QPlainTextEdit { } QLineEdit { - font-family: Courier New, Courier, Monotype; + font-family: Inconsolata, Consolas, Courier New, monospace; padding-left: 7px; background-color: #CCCCCC; border-width: 0; From e265a0a1f88709bb201f0029bcb4c91e44edf422 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Thu, 16 Mar 2017 13:12:30 -0700 Subject: [PATCH 211/302] Fix possible crash in Menu::removeSeparator --- libraries/ui/src/ui/Menu.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/ui/src/ui/Menu.cpp b/libraries/ui/src/ui/Menu.cpp index f68fff0204..a793942056 100644 --- a/libraries/ui/src/ui/Menu.cpp +++ b/libraries/ui/src/ui/Menu.cpp @@ -470,8 +470,8 @@ void Menu::removeSeparator(const QString& menuName, const QString& separatorName if (menu) { int textAt = findPositionOfMenuItem(menu, separatorName); QList menuActions = menu->actions(); - QAction* separatorText = menuActions[textAt]; if (textAt > 0 && textAt < menuActions.size()) { + QAction* separatorText = menuActions[textAt]; QAction* separatorLine = menuActions[textAt - 1]; if (separatorLine) { if (separatorLine->isSeparator()) { From db956f4cc3a5e8a3e658de560e652d6ef941b358 Mon Sep 17 00:00:00 2001 From: Zach Pomerantz Date: Thu, 16 Mar 2017 18:06:18 -0400 Subject: [PATCH 212/302] use single frame buffer for local audio --- libraries/audio-client/src/AudioClient.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/audio-client/src/AudioClient.cpp b/libraries/audio-client/src/AudioClient.cpp index c32b5600d9..fa5d0d24d5 100644 --- a/libraries/audio-client/src/AudioClient.cpp +++ b/libraries/audio-client/src/AudioClient.cpp @@ -160,7 +160,7 @@ AudioClient::AudioClient() : _loopbackAudioOutput(NULL), _loopbackOutputDevice(NULL), _inputRingBuffer(0), - _localInjectorsStream(0), + _localInjectorsStream(0, 1), _receivedAudioStream(RECEIVED_AUDIO_STREAM_CAPACITY_FRAMES), _isStereoInput(false), _outputStarveDetectionStartTimeMsec(0), From 200550aba98205629014d268c24fb4d749b243ce Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Thu, 16 Mar 2017 15:22:34 -0700 Subject: [PATCH 213/302] ModelScriptingInterface::appendMeshes --- .../src/RenderablePolyVoxEntityItem.cpp | 1 - libraries/gpu/src/gpu/Buffer.h | 2 +- .../src/ModelScriptingInterface.cpp | 89 ++++++++++++++++++- .../src/ModelScriptingInterface.h | 5 ++ 4 files changed, 94 insertions(+), 3 deletions(-) diff --git a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp index 88a0d25b7f..dd62122633 100644 --- a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp @@ -1574,7 +1574,6 @@ void RenderablePolyVoxEntityItem::locationChanged(bool tellPhysics) { bool RenderablePolyVoxEntityItem::getMeshAsScriptValue(QScriptEngine *engine, QScriptValue& result) const { bool success = false; MeshProxy* meshProxy = nullptr; - model::MeshPointer mesh = nullptr; withReadLock([&] { if (_meshInitialized) { success = true; diff --git a/libraries/gpu/src/gpu/Buffer.h b/libraries/gpu/src/gpu/Buffer.h index 2507e8e0a6..290b84bef0 100644 --- a/libraries/gpu/src/gpu/Buffer.h +++ b/libraries/gpu/src/gpu/Buffer.h @@ -198,7 +198,7 @@ public: BufferView(const BufferPointer& buffer, Size offset, Size size, const Element& element = DEFAULT_ELEMENT); BufferView(const BufferPointer& buffer, Size offset, Size size, uint16 stride, const Element& element = DEFAULT_ELEMENT); - Size getNumElements() const { return _size / _element.getSize(); } + Size getNumElements() const { return (_size - _offset) / _stride; } //Template iterator with random access on the buffer sysmem template diff --git a/libraries/script-engine/src/ModelScriptingInterface.cpp b/libraries/script-engine/src/ModelScriptingInterface.cpp index 4ba82edf7d..b725548c96 100644 --- a/libraries/script-engine/src/ModelScriptingInterface.cpp +++ b/libraries/script-engine/src/ModelScriptingInterface.cpp @@ -12,11 +12,12 @@ #include #include #include +#include "ScriptEngine.h" #include "ModelScriptingInterface.h" #include "OBJWriter.h" - ModelScriptingInterface::ModelScriptingInterface(QObject* parent) : QObject(parent) { + _modelScriptEngine = qobject_cast(parent); } QScriptValue meshToScriptValue(QScriptEngine* engine, MeshProxy* const &in) { @@ -51,3 +52,89 @@ QString ModelScriptingInterface::meshToOBJ(MeshProxyList in) { return writeOBJToString(meshes); } + + +QScriptValue ModelScriptingInterface::appendMeshes(MeshProxyList in) { + model::MeshPointer result(new model::Mesh()); + int attributeTypeNormal = gpu::Stream::InputSlot::NORMAL; // libraries/gpu/src/gpu/Stream.h + + size_t totalVertexCount { 0 }; + size_t totalAttributeCount { 0 }; + size_t totalIndexCount { 0 }; + foreach (const MeshProxy* meshProxy, in) { + MeshPointer mesh = meshProxy->getMeshPointer(); + totalVertexCount += mesh->getNumVertices(); + totalAttributeCount += mesh->getNumAttributes(); + totalIndexCount += mesh->getNumIndices(); + } + + gpu::Resource::Size combinedVertexSize = totalVertexCount * sizeof(glm::vec3); + unsigned char* combinedVertexData = new unsigned char[combinedVertexSize]; + unsigned char* combinedVertexDataCursor = combinedVertexData; + + gpu::Resource::Size combinedNormalSize = totalVertexCount * sizeof(glm::vec3); + unsigned char* combinedNormalData = new unsigned char[combinedNormalSize]; + unsigned char* combinedNormalDataCursor = combinedNormalData; + + gpu::Resource::Size combinedIndexSize = totalVertexCount * sizeof(uint32_t); + unsigned char* combinedIndexData = new unsigned char[combinedIndexSize]; + unsigned char* combinedIndexDataCursor = combinedIndexData; + + uint32_t indexStartOffset { 0 }; + + foreach (const MeshProxy* meshProxy, in) { + MeshPointer mesh = meshProxy->getMeshPointer(); + + // vertex data + const gpu::BufferView& vertexBufferView = mesh->getVertexBuffer(); + gpu::BufferView::Index numVertices = (gpu::BufferView::Index)mesh->getNumVertices(); + for (gpu::BufferView::Index i = 0; i < numVertices; i ++) { + glm::vec3 pos = vertexBufferView.get(i); + memcpy(combinedVertexDataCursor, &pos, sizeof(pos)); + combinedVertexDataCursor += sizeof(pos); + } + + // normal data + const gpu::BufferView& normalsBufferView = mesh->getAttributeBuffer(attributeTypeNormal); + gpu::BufferView::Index numNormals = (gpu::BufferView::Index)mesh->getNumAttributes(); + for (gpu::BufferView::Index i = 0; i < numNormals; i ++) { + glm::vec3 normal = normalsBufferView.get(i); + memcpy(combinedNormalDataCursor, &normal, sizeof(normal)); + combinedNormalDataCursor += sizeof(normal); + } + // TODO -- other attributes + + // face data + const gpu::BufferView& indexBufferView = mesh->getIndexBuffer(); + gpu::BufferView::Index numIndexes = (gpu::BufferView::Index)mesh->getNumAttributes(); + for (gpu::BufferView::Index i = 0; i < numIndexes; i ++) { + uint32_t index = indexBufferView.get(i); + index += indexStartOffset; + memcpy(combinedIndexDataCursor, &index, sizeof(index)); + combinedIndexDataCursor += sizeof(index); + } + + indexStartOffset += numVertices; + } + + gpu::Element vertexElement = gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ); + gpu::Buffer* combinedVertexBuffer = new gpu::Buffer(combinedVertexSize, combinedVertexData); + gpu::BufferPointer combinedVertexBufferPointer(combinedVertexBuffer); + gpu::BufferView combinedVertexBufferView(combinedVertexBufferPointer, vertexElement); + result->setVertexBuffer(combinedVertexBufferView); + + gpu::Element normalElement = gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ); + gpu::Buffer* combinedNormalsBuffer = new gpu::Buffer(combinedNormalSize, combinedNormalData); + gpu::BufferPointer combinedNormalsBufferPointer(combinedNormalsBuffer); + gpu::BufferView combinedNormalsBufferView(combinedNormalsBufferPointer, normalElement); + result->addAttribute(attributeTypeNormal, combinedNormalsBufferView); + + gpu::Element indexElement = gpu::Element(gpu::SCALAR, gpu::UINT32, gpu::RAW); + gpu::Buffer* combinedIndexesBuffer = new gpu::Buffer(combinedIndexSize, combinedIndexData); + gpu::BufferPointer combinedIndexesBufferPointer(combinedIndexesBuffer); + gpu::BufferView combinedIndexesBufferView(combinedIndexesBufferPointer, indexElement); + result->setIndexBuffer(combinedIndexesBufferView); + + MeshProxy* resultProxy = new MeshProxy(result); + return meshToScriptValue(_modelScriptEngine, resultProxy); +} diff --git a/libraries/script-engine/src/ModelScriptingInterface.h b/libraries/script-engine/src/ModelScriptingInterface.h index 94b7338ae2..7ec8ab46ef 100644 --- a/libraries/script-engine/src/ModelScriptingInterface.h +++ b/libraries/script-engine/src/ModelScriptingInterface.h @@ -20,6 +20,7 @@ #include "MeshProxy.h" using MeshPointer = std::shared_ptr; +class ScriptEngine; class ModelScriptingInterface : public QObject { Q_OBJECT @@ -28,6 +29,10 @@ public: ModelScriptingInterface(QObject* parent); Q_INVOKABLE QString meshToOBJ(MeshProxyList in); + Q_INVOKABLE QScriptValue appendMeshes(MeshProxyList in); + +private: + ScriptEngine* _modelScriptEngine { nullptr }; }; QScriptValue meshToScriptValue(QScriptEngine* engine, MeshProxy* const &in); From 0e8d62c11e5cfa1421d8f28dd6137c6730ed4d31 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Thu, 16 Mar 2017 15:24:39 -0700 Subject: [PATCH 214/302] delete dead code --- libraries/fbx/src/OBJWriter.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/libraries/fbx/src/OBJWriter.cpp b/libraries/fbx/src/OBJWriter.cpp index 42b5774245..5ee04c5718 100644 --- a/libraries/fbx/src/OBJWriter.cpp +++ b/libraries/fbx/src/OBJWriter.cpp @@ -71,7 +71,6 @@ bool writeOBJToTextStream(QTextStream& out, QList meshes) { const gpu::BufferView& partBuffer = mesh->getPartBuffer(); const gpu::BufferView& indexBuffer = mesh->getIndexBuffer(); - // const gpu::BufferView& vertexBuffer = mesh->getVertexBuffer(); model::Index partCount = (model::Index)mesh->getNumParts(); for (int partIndex = 0; partIndex < partCount; partIndex++) { From 7517f224a84ee35cc77705722a860597b6fa0732 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Wed, 15 Mar 2017 13:49:50 -0700 Subject: [PATCH 215/302] Add MyAvatar drive keys capture capability --- interface/src/Application.cpp | 14 ++++++------- interface/src/avatar/MyAvatar.cpp | 34 +++++++++++++++---------------- interface/src/avatar/MyAvatar.h | 17 +++++++++++++--- 3 files changed, 37 insertions(+), 28 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 1bb4c64884..df9d10caa6 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -4383,16 +4383,16 @@ void Application::update(float deltaTime) { myAvatar->clearDriveKeys(); if (_myCamera.getMode() != CAMERA_MODE_INDEPENDENT) { if (!_controllerScriptingInterface->areActionsCaptured()) { - myAvatar->setDriveKeys(TRANSLATE_Z, -1.0f * userInputMapper->getActionState(controller::Action::TRANSLATE_Z)); - myAvatar->setDriveKeys(TRANSLATE_Y, userInputMapper->getActionState(controller::Action::TRANSLATE_Y)); - myAvatar->setDriveKeys(TRANSLATE_X, userInputMapper->getActionState(controller::Action::TRANSLATE_X)); + myAvatar->setDriveKey(TRANSLATE_Z, -1.0f * userInputMapper->getActionState(controller::Action::TRANSLATE_Z)); + myAvatar->setDriveKey(TRANSLATE_Y, userInputMapper->getActionState(controller::Action::TRANSLATE_Y)); + myAvatar->setDriveKey(TRANSLATE_X, userInputMapper->getActionState(controller::Action::TRANSLATE_X)); if (deltaTime > FLT_EPSILON) { - myAvatar->setDriveKeys(PITCH, -1.0f * userInputMapper->getActionState(controller::Action::PITCH)); - myAvatar->setDriveKeys(YAW, -1.0f * userInputMapper->getActionState(controller::Action::YAW)); - myAvatar->setDriveKeys(STEP_YAW, -1.0f * userInputMapper->getActionState(controller::Action::STEP_YAW)); + myAvatar->setDriveKey(PITCH, -1.0f * userInputMapper->getActionState(controller::Action::PITCH)); + myAvatar->setDriveKey(YAW, -1.0f * userInputMapper->getActionState(controller::Action::YAW)); + myAvatar->setDriveKey(STEP_YAW, -1.0f * userInputMapper->getActionState(controller::Action::STEP_YAW)); } } - myAvatar->setDriveKeys(ZOOM, userInputMapper->getActionState(controller::Action::TRANSLATE_CAMERA_Z)); + myAvatar->setDriveKey(ZOOM, userInputMapper->getActionState(controller::Action::TRANSLATE_CAMERA_Z)); } controller::Pose leftHandPose = userInputMapper->getPoseState(controller::Action::LEFT_HAND); diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 318608e3a8..c38d1ed607 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -119,9 +119,7 @@ MyAvatar::MyAvatar(RigPointer rig) : using namespace recording; _skeletonModel->flagAsCauterized(); - for (int i = 0; i < MAX_DRIVE_KEYS; i++) { - _driveKeys[i] = 0.0f; - } + clearDriveKeys(); // Necessary to select the correct slot using SlotType = void(MyAvatar::*)(const glm::vec3&, bool, const glm::quat&, bool); @@ -462,7 +460,7 @@ void MyAvatar::simulate(float deltaTime) { // When there are no step values, we zero out the last step pulse. // This allows a user to do faster snapping by tapping a control for (int i = STEP_TRANSLATE_X; !stepAction && i <= STEP_YAW; ++i) { - if (_driveKeys[i] != 0.0f) { + if (getDriveKey(i) != 0.0f) { stepAction = true; } } @@ -1655,7 +1653,7 @@ bool MyAvatar::shouldRenderHead(const RenderArgs* renderArgs) const { void MyAvatar::updateOrientation(float deltaTime) { // Smoothly rotate body with arrow keys - float targetSpeed = _driveKeys[YAW] * _yawSpeed; + float targetSpeed = getDriveKey(YAW) * _yawSpeed; if (targetSpeed != 0.0f) { const float ROTATION_RAMP_TIMESCALE = 0.1f; float blend = deltaTime / ROTATION_RAMP_TIMESCALE; @@ -1684,8 +1682,8 @@ void MyAvatar::updateOrientation(float deltaTime) { // Comfort Mode: If you press any of the left/right rotation drive keys or input, you'll // get an instantaneous 15 degree turn. If you keep holding the key down you'll get another // snap turn every half second. - if (_driveKeys[STEP_YAW] != 0.0f) { - totalBodyYaw += _driveKeys[STEP_YAW]; + if (getDriveKey(STEP_YAW) != 0.0f) { + totalBodyYaw += getDriveKey(STEP_YAW); } // use head/HMD orientation to turn while flying @@ -1722,7 +1720,7 @@ void MyAvatar::updateOrientation(float deltaTime) { // update body orientation by movement inputs setOrientation(getOrientation() * glm::quat(glm::radians(glm::vec3(0.0f, totalBodyYaw, 0.0f)))); - getHead()->setBasePitch(getHead()->getBasePitch() + _driveKeys[PITCH] * _pitchSpeed * deltaTime); + getHead()->setBasePitch(getHead()->getBasePitch() + getDriveKey(PITCH) * _pitchSpeed * deltaTime); if (qApp->isHMDMode()) { glm::quat orientation = glm::quat_cast(getSensorToWorldMatrix()) * getHMDSensorOrientation(); @@ -1756,14 +1754,14 @@ void MyAvatar::updateActionMotor(float deltaTime) { } // compute action input - glm::vec3 front = (_driveKeys[TRANSLATE_Z]) * IDENTITY_FRONT; - glm::vec3 right = (_driveKeys[TRANSLATE_X]) * IDENTITY_RIGHT; + glm::vec3 front = (getDriveKey(TRANSLATE_Z)) * IDENTITY_FRONT; + glm::vec3 right = (getDriveKey(TRANSLATE_X)) * IDENTITY_RIGHT; glm::vec3 direction = front + right; CharacterController::State state = _characterController.getState(); if (state == CharacterController::State::Hover) { // we're flying --> support vertical motion - glm::vec3 up = (_driveKeys[TRANSLATE_Y]) * IDENTITY_UP; + glm::vec3 up = (getDriveKey(TRANSLATE_Y)) * IDENTITY_UP; direction += up; } @@ -1802,7 +1800,7 @@ void MyAvatar::updateActionMotor(float deltaTime) { _actionMotorVelocity = MAX_WALKING_SPEED * direction; } - float boomChange = _driveKeys[ZOOM]; + float boomChange = getDriveKey(ZOOM); _boomLength += 2.0f * _boomLength * boomChange + boomChange * boomChange; _boomLength = glm::clamp(_boomLength, ZOOM_MIN, ZOOM_MAX); } @@ -1833,11 +1831,11 @@ void MyAvatar::updatePosition(float deltaTime) { } // capture the head rotation, in sensor space, when the user first indicates they would like to move/fly. - if (!_hoverReferenceCameraFacingIsCaptured && (fabs(_driveKeys[TRANSLATE_Z]) > 0.1f || fabs(_driveKeys[TRANSLATE_X]) > 0.1f)) { + if (!_hoverReferenceCameraFacingIsCaptured && (fabs(getDriveKey(TRANSLATE_Z)) > 0.1f || fabs(getDriveKey(TRANSLATE_X)) > 0.1f)) { _hoverReferenceCameraFacingIsCaptured = true; // transform the camera facing vector into sensor space. _hoverReferenceCameraFacing = transformVectorFast(glm::inverse(_sensorToWorldMatrix), getHead()->getCameraOrientation() * Vectors::UNIT_Z); - } else if (_hoverReferenceCameraFacingIsCaptured && (fabs(_driveKeys[TRANSLATE_Z]) <= 0.1f && fabs(_driveKeys[TRANSLATE_X]) <= 0.1f)) { + } else if (_hoverReferenceCameraFacingIsCaptured && (fabs(getDriveKey(TRANSLATE_Z)) <= 0.1f && fabs(getDriveKey(TRANSLATE_X)) <= 0.1f)) { _hoverReferenceCameraFacingIsCaptured = false; } } @@ -2094,12 +2092,12 @@ bool MyAvatar::getCharacterControllerEnabled() { void MyAvatar::clearDriveKeys() { for (int i = 0; i < MAX_DRIVE_KEYS; ++i) { - _driveKeys[i] = 0.0f; + setDriveKey(i, 0.0f); } } void MyAvatar::relayDriveKeysToCharacterController() { - if (_driveKeys[TRANSLATE_Y] > 0.0f) { + if (getDriveKey(TRANSLATE_Y) > 0.0f) { _characterController.jump(); } } @@ -2382,7 +2380,7 @@ bool MyAvatar::didTeleport() { } bool MyAvatar::hasDriveInput() const { - return fabsf(_driveKeys[TRANSLATE_X]) > 0.0f || fabsf(_driveKeys[TRANSLATE_Y]) > 0.0f || fabsf(_driveKeys[TRANSLATE_Z]) > 0.0f; + return fabsf(getDriveKey(TRANSLATE_X)) > 0.0f || fabsf(getDriveKey(TRANSLATE_Y)) > 0.0f || fabsf(getDriveKey(TRANSLATE_Z)) > 0.0f; } void MyAvatar::setAway(bool value) { @@ -2498,7 +2496,7 @@ bool MyAvatar::pinJoint(int index, const glm::vec3& position, const glm::quat& o return false; } - setPosition(position); + slamPosition(position); setOrientation(orientation); _rig->setMaxHipsOffsetLength(0.05f); diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index 00923e78cc..731ba89299 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -12,6 +12,8 @@ #ifndef hifi_MyAvatar_h #define hifi_MyAvatar_h +#include + #include #include @@ -87,6 +89,8 @@ class MyAvatar : public Avatar { Q_PROPERTY(bool hmdLeanRecenterEnabled READ getHMDLeanRecenterEnabled WRITE setHMDLeanRecenterEnabled) Q_PROPERTY(bool characterControllerEnabled READ getCharacterControllerEnabled WRITE setCharacterControllerEnabled) + Q_ENUMS(DriveKeys) + public: explicit MyAvatar(RigPointer rig); ~MyAvatar(); @@ -180,9 +184,15 @@ public: // Set what driving keys are being pressed to control thrust levels void clearDriveKeys(); - void setDriveKeys(int key, float val) { _driveKeys[key] = val; }; + void setDriveKey(int key, float val) { _driveKeys[key] = val; }; + float getDriveKey(int key) const { return isDriveKeyCaptured(key) ? 0.0f : _driveKeys[key]; }; + Q_INVOKABLE float getRawDriveKey(int key) const { return _driveKeys[key]; }; void relayDriveKeysToCharacterController(); + Q_INVOKABLE void captureDriveKey(int key) { _capturedDriveKeys.set(key); } + Q_INVOKABLE void releaseDriveKey(int key) { _capturedDriveKeys.reset(key); } + Q_INVOKABLE bool isDriveKeyCaptured(int key) const { return _capturedDriveKeys.test(key); } + eyeContactTarget getEyeContactTarget(); Q_INVOKABLE glm::vec3 getTrackedHeadPosition() const { return _trackedHeadPosition; } @@ -352,7 +362,6 @@ private: virtual bool shouldRenderHead(const RenderArgs* renderArgs) const override; void setShouldRenderLocally(bool shouldRender) { _shouldRender = shouldRender; setEnableMeshVisible(shouldRender); } bool getShouldRenderLocally() const { return _shouldRender; } - bool getDriveKeys(int key) { return _driveKeys[key] != 0.0f; }; bool isMyAvatar() const override { return true; } virtual int parseDataFromBuffer(const QByteArray& buffer) override; virtual glm::vec3 getSkeletonPosition() const override; @@ -388,7 +397,9 @@ private: void clampScaleChangeToDomainLimits(float desiredScale); glm::mat4 computeCameraRelativeHandControllerMatrix(const glm::mat4& controllerSensorMatrix) const; - float _driveKeys[MAX_DRIVE_KEYS]; + std::array _driveKeys; + std::bitset _capturedDriveKeys; + bool _wasPushing; bool _isPushing; bool _isBeingPushed; From 35dd45ddf9f3829bb2707555b5d7805006b7cd81 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Wed, 15 Mar 2017 17:39:38 -0700 Subject: [PATCH 216/302] Capture drive keys while avatar is seated --- scripts/tutorials/entity_scripts/sit.js | 99 +++++++++++++------------ 1 file changed, 50 insertions(+), 49 deletions(-) diff --git a/scripts/tutorials/entity_scripts/sit.js b/scripts/tutorials/entity_scripts/sit.js index ffce196572..42cf7685ad 100644 --- a/scripts/tutorials/entity_scripts/sit.js +++ b/scripts/tutorials/entity_scripts/sit.js @@ -6,26 +6,28 @@ var ANIMATION_FPS = 30; var ANIMATION_FIRST_FRAME = 1; var ANIMATION_LAST_FRAME = 10; - var RELEASE_KEYS = ['w', 'a', 's', 'd', 'UP', 'LEFT', 'DOWN', 'RIGHT']; var RELEASE_TIME = 500; // ms var RELEASE_DISTANCE = 0.2; // meters var MAX_IK_ERROR = 30; + var IK_SETTLE_TIME = 250; // ms var DESKTOP_UI_CHECK_INTERVAL = 100; var DESKTOP_MAX_DISTANCE = 5; - var SIT_DELAY = 25 - var MAX_RESET_DISTANCE = 0.5 + var SIT_DELAY = 25; + var MAX_RESET_DISTANCE = 0.5; // meters + var OVERRIDEN_DRIVE_KEYS = [0, 1, 2, 4, 5, 6]; this.entityID = null; - this.timers = {}; this.animStateHandlerID = null; this.interval = null; + this.sitDownTimestamp = null; + this.lastTimeNoDriveKeys = null; this.preload = function(entityID) { this.entityID = entityID; } this.unload = function() { if (Settings.getValue(SETTING_KEY) === this.entityID) { - this.sitUp(); + this.standUp(); } if (this.interval !== null) { Script.clearInterval(this.interval); @@ -96,6 +98,10 @@ print("Someone is already sitting in that chair."); return; } + print("Sitting down (" + this.entityID + ")"); + + this.sitDownTimestamp = Date.now(); + this.lastTimeNoDriveKeys = this.sitDownTimestamp; var previousValue = Settings.getValue(SETTING_KEY); Settings.setValue(SETTING_KEY, this.entityID); @@ -118,20 +124,17 @@ return { headType: 0 }; }, ["headType"]); Script.update.connect(this, this.update); - Controller.keyPressEvent.connect(this, this.keyPressed); - Controller.keyReleaseEvent.connect(this, this.keyReleased); - for (var i in RELEASE_KEYS) { - Controller.captureKeyEvents({ text: RELEASE_KEYS[i] }); + for (var i in OVERRIDEN_DRIVE_KEYS) { + MyAvatar.captureDriveKey(OVERRIDEN_DRIVE_KEYS[i]); } } - this.sitUp = function() { + this.standUp = function() { + print("Standing up (" + this.entityID + ")"); MyAvatar.removeAnimationStateHandler(this.animStateHandlerID); Script.update.disconnect(this, this.update); - Controller.keyPressEvent.disconnect(this, this.keyPressed); - Controller.keyReleaseEvent.disconnect(this, this.keyReleased); - for (var i in RELEASE_KEYS) { - Controller.releaseKeyEvents({ text: RELEASE_KEYS[i] }); + for (var i in OVERRIDEN_DRIVE_KEYS) { + MyAvatar.releaseDriveKey(OVERRIDEN_DRIVE_KEYS[i]); } this.setSeatUser(null); @@ -156,6 +159,7 @@ } } + // function called by teleport.js if it detects the appropriate userData this.sit = function () { this.sitDown(); } @@ -207,7 +211,36 @@ var properties = Entities.getEntityProperties(this.entityID); var avatarDistance = Vec3.distance(MyAvatar.position, properties.position); var ikError = MyAvatar.getIKErrorOnLastSolve(); - if (avatarDistance > RELEASE_DISTANCE || ikError > MAX_IK_ERROR) { + var now = Date.now(); + var shouldStandUp = false; + + // Check if a drive key is pressed + var hasActiveDriveKey = false; + for (var i in OVERRIDEN_DRIVE_KEYS) { + if (MyAvatar.getRawDriveKey(OVERRIDEN_DRIVE_KEYS[i]) != 0.0) { + hasActiveDriveKey = true; + break; + } + } + + // Only standup if user has been pushing a drive key for RELEASE_TIME + if (hasActiveDriveKey) { + var elapsed = now - this.lastTimeNoDriveKeys; + shouldStandUp = elapsed > RELEASE_TIME; + } else { + this.lastTimeNoDriveKeys = Date.now(); + } + + // Allow some time for the IK to settle + if (ikError > MAX_IK_ERROR) { + var elapsed = now - this.sitDownTimestamp; + if (elapsed > IK_SETTLE_TIME) { + shouldStandUp = true; + } + } + + + if (shouldStandUp || avatarDistance > RELEASE_DISTANCE) { print("IK error: " + ikError + ", distance from chair: " + avatarDistance); // Move avatar in front of the chair to avoid getting stuck in collision hulls @@ -215,45 +248,13 @@ var offset = { x: 0, y: 1.0, z: -0.5 - properties.dimensions.z * properties.registrationPoint.z }; var position = Vec3.sum(properties.position, Vec3.multiplyQbyV(properties.rotation, offset)); MyAvatar.position = position; + print("Moving Avatar in front of the chair.") } - this.sitUp(); + this.standUp(); } } } - this.keyPressed = function(event) { - if (isInEditMode()) { - return; - } - - if (RELEASE_KEYS.indexOf(event.text) !== -1) { - var that = this; - this.timers[event.text] = Script.setTimeout(function() { - delete that.timers[event.text]; - - var properties = Entities.getEntityProperties(that.entityID); - var avatarDistance = Vec3.distance(MyAvatar.position, properties.position); - - // Move avatar in front of the chair to avoid getting stuck in collision hulls - if (avatarDistance < MAX_RESET_DISTANCE) { - var offset = { x: 0, y: 1.0, z: -0.5 - properties.dimensions.z * properties.registrationPoint.z }; - var position = Vec3.sum(properties.position, Vec3.multiplyQbyV(properties.rotation, offset)); - MyAvatar.position = position; - } - - that.sitUp(); - }, RELEASE_TIME); - } - } - this.keyReleased = function(event) { - if (RELEASE_KEYS.indexOf(event.text) !== -1) { - if (this.timers[event.text]) { - Script.clearTimeout(this.timers[event.text]); - delete this.timers[event.text]; - } - } - } - this.canSitDesktop = function() { var properties = Entities.getEntityProperties(this.entityID, ["position"]); var distanceFromSeat = Vec3.distance(MyAvatar.position, properties.position); From 3e9474e8784408673f2461886463facfe9c98026 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Thu, 16 Mar 2017 14:30:33 -0700 Subject: [PATCH 217/302] CR --- interface/src/avatar/MyAvatar.h | 14 ++++++-------- scripts/tutorials/entity_scripts/sit.js | 18 ++++++++---------- 2 files changed, 14 insertions(+), 18 deletions(-) diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index 731ba89299..14142726ea 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -89,8 +89,6 @@ class MyAvatar : public Avatar { Q_PROPERTY(bool hmdLeanRecenterEnabled READ getHMDLeanRecenterEnabled WRITE setHMDLeanRecenterEnabled) Q_PROPERTY(bool characterControllerEnabled READ getCharacterControllerEnabled WRITE setCharacterControllerEnabled) - Q_ENUMS(DriveKeys) - public: explicit MyAvatar(RigPointer rig); ~MyAvatar(); @@ -185,13 +183,13 @@ public: // Set what driving keys are being pressed to control thrust levels void clearDriveKeys(); void setDriveKey(int key, float val) { _driveKeys[key] = val; }; - float getDriveKey(int key) const { return isDriveKeyCaptured(key) ? 0.0f : _driveKeys[key]; }; + float getDriveKey(int key) const { return isDriveKeyDisabled(key) ? 0.0f : _driveKeys[key]; }; Q_INVOKABLE float getRawDriveKey(int key) const { return _driveKeys[key]; }; void relayDriveKeysToCharacterController(); - Q_INVOKABLE void captureDriveKey(int key) { _capturedDriveKeys.set(key); } - Q_INVOKABLE void releaseDriveKey(int key) { _capturedDriveKeys.reset(key); } - Q_INVOKABLE bool isDriveKeyCaptured(int key) const { return _capturedDriveKeys.test(key); } + Q_INVOKABLE void disableDriveKey(int key) { _disabledDriveKeys.set(key); } + Q_INVOKABLE void enableDriveKey(int key) { _disabledDriveKeys.reset(key); } + Q_INVOKABLE bool isDriveKeyDisabled(int key) const { return _disabledDriveKeys.test(key); } eyeContactTarget getEyeContactTarget(); @@ -397,8 +395,8 @@ private: void clampScaleChangeToDomainLimits(float desiredScale); glm::mat4 computeCameraRelativeHandControllerMatrix(const glm::mat4& controllerSensorMatrix) const; - std::array _driveKeys; - std::bitset _capturedDriveKeys; + float _driveKeys[MAX_DRIVE_KEYS]; + std::bitset _disabledDriveKeys; bool _wasPushing; bool _isPushing; diff --git a/scripts/tutorials/entity_scripts/sit.js b/scripts/tutorials/entity_scripts/sit.js index 42cf7685ad..5fff9877b0 100644 --- a/scripts/tutorials/entity_scripts/sit.js +++ b/scripts/tutorials/entity_scripts/sit.js @@ -19,7 +19,7 @@ this.entityID = null; this.animStateHandlerID = null; this.interval = null; - this.sitDownTimestamp = null; + this.sitDownSettlePeriod = null; this.lastTimeNoDriveKeys = null; this.preload = function(entityID) { @@ -100,8 +100,9 @@ } print("Sitting down (" + this.entityID + ")"); - this.sitDownTimestamp = Date.now(); - this.lastTimeNoDriveKeys = this.sitDownTimestamp; + var now = Date.now(); + this.sitDownSettlePeriod = now + IK_SETTLE_TIME; + this.lastTimeNoDriveKeys = now; var previousValue = Settings.getValue(SETTING_KEY); Settings.setValue(SETTING_KEY, this.entityID); @@ -125,7 +126,7 @@ }, ["headType"]); Script.update.connect(this, this.update); for (var i in OVERRIDEN_DRIVE_KEYS) { - MyAvatar.captureDriveKey(OVERRIDEN_DRIVE_KEYS[i]); + MyAvatar.disableDriveKey(OVERRIDEN_DRIVE_KEYS[i]); } } @@ -134,7 +135,7 @@ MyAvatar.removeAnimationStateHandler(this.animStateHandlerID); Script.update.disconnect(this, this.update); for (var i in OVERRIDEN_DRIVE_KEYS) { - MyAvatar.releaseDriveKey(OVERRIDEN_DRIVE_KEYS[i]); + MyAvatar.enableDriveKey(OVERRIDEN_DRIVE_KEYS[i]); } this.setSeatUser(null); @@ -232,11 +233,8 @@ } // Allow some time for the IK to settle - if (ikError > MAX_IK_ERROR) { - var elapsed = now - this.sitDownTimestamp; - if (elapsed > IK_SETTLE_TIME) { - shouldStandUp = true; - } + if (ikError > MAX_IK_ERROR && now > this.sitDownSettlePeriod) { + shouldStandUp = true; } From 42316e713d9330e33d6cf3da4d08d3264cad442a Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Thu, 16 Mar 2017 16:24:52 -0700 Subject: [PATCH 218/302] CR drive keys --- interface/src/avatar/MyAvatar.cpp | 68 ++++++++++++++++++++++++- interface/src/avatar/MyAvatar.h | 16 +++--- scripts/tutorials/entity_scripts/sit.js | 8 +-- 3 files changed, 77 insertions(+), 15 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index c38d1ed607..cc40c8eab2 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -2091,8 +2091,27 @@ bool MyAvatar::getCharacterControllerEnabled() { } void MyAvatar::clearDriveKeys() { - for (int i = 0; i < MAX_DRIVE_KEYS; ++i) { - setDriveKey(i, 0.0f); + _driveKeys.fill(0.0f); +} + +void MyAvatar::setDriveKey(int key, float val) { + try { + _driveKeys.at(key) = val; + } catch (const std::exception& exception) { + qCCritical(interfaceapp) << Q_FUNC_INFO << ": Index out of bounds"; + } +} + +float MyAvatar::getDriveKey(int key) const { + return isDriveKeyDisabled(key) ? 0.0f : getRawDriveKey(key); +} + +float MyAvatar::getRawDriveKey(int key) const { + try { + return _driveKeys.at(key); + } catch (const std::exception& exception) { + qCCritical(interfaceapp) << Q_FUNC_INFO << ": Index out of bounds"; + return 0.0f; } } @@ -2102,6 +2121,51 @@ void MyAvatar::relayDriveKeysToCharacterController() { } } +void MyAvatar::disableDriveKey(int key) { + try { + _disabledDriveKeys.set(key); + } catch (const std::exception& exception) { + qCCritical(interfaceapp) << Q_FUNC_INFO << ": Index out of bounds"; + } +} + +void MyAvatar::enableDriveKey(int key) { + try { + _disabledDriveKeys.reset(key); + } catch (const std::exception& exception) { + qCCritical(interfaceapp) << Q_FUNC_INFO << ": Index out of bounds"; + } +} + +void MyAvatar::disableDriveKeys(std::vector key) { + try { + std::for_each(std::begin(key), std::end(key), [&](int val){ + _disabledDriveKeys.set(val); + }); + } catch (const std::exception& exception) { + qCCritical(interfaceapp) << Q_FUNC_INFO << ": Index out of bounds"; + } +} + +void MyAvatar::enableDriveKeys(std::vector key) { + try { + std::for_each(std::begin(key), std::end(key), [&](int val) { + _disabledDriveKeys.reset(val); + }); + } catch (const std::exception& exception) { + qCCritical(interfaceapp) << Q_FUNC_INFO << ": Index out of bounds"; + } +} + +bool MyAvatar::isDriveKeyDisabled(int key) const { + try { + return _disabledDriveKeys.test(key); + } catch (const std::exception& exception) { + qCCritical(interfaceapp) << Q_FUNC_INFO << ": Index out of bounds"; + return false; + } +} + glm::vec3 MyAvatar::getWorldBodyPosition() const { return transformPoint(_sensorToWorldMatrix, extractTranslation(_bodySensorMatrix)); } diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index 14142726ea..cae445c226 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -182,14 +182,16 @@ public: // Set what driving keys are being pressed to control thrust levels void clearDriveKeys(); - void setDriveKey(int key, float val) { _driveKeys[key] = val; }; - float getDriveKey(int key) const { return isDriveKeyDisabled(key) ? 0.0f : _driveKeys[key]; }; - Q_INVOKABLE float getRawDriveKey(int key) const { return _driveKeys[key]; }; + void setDriveKey(int key, float val); + float getDriveKey(int key) const; + Q_INVOKABLE float getRawDriveKey(int key) const; void relayDriveKeysToCharacterController(); - Q_INVOKABLE void disableDriveKey(int key) { _disabledDriveKeys.set(key); } - Q_INVOKABLE void enableDriveKey(int key) { _disabledDriveKeys.reset(key); } - Q_INVOKABLE bool isDriveKeyDisabled(int key) const { return _disabledDriveKeys.test(key); } + Q_INVOKABLE void disableDriveKey(int key); + Q_INVOKABLE void enableDriveKey(int key); + Q_INVOKABLE void disableDriveKeys(std::vector key); + Q_INVOKABLE void enableDriveKeys(std::vector key); + Q_INVOKABLE bool isDriveKeyDisabled(int key) const; eyeContactTarget getEyeContactTarget(); @@ -395,7 +397,7 @@ private: void clampScaleChangeToDomainLimits(float desiredScale); glm::mat4 computeCameraRelativeHandControllerMatrix(const glm::mat4& controllerSensorMatrix) const; - float _driveKeys[MAX_DRIVE_KEYS]; + std::array _driveKeys; std::bitset _disabledDriveKeys; bool _wasPushing; diff --git a/scripts/tutorials/entity_scripts/sit.js b/scripts/tutorials/entity_scripts/sit.js index 5fff9877b0..420a77e2b8 100644 --- a/scripts/tutorials/entity_scripts/sit.js +++ b/scripts/tutorials/entity_scripts/sit.js @@ -125,18 +125,14 @@ return { headType: 0 }; }, ["headType"]); Script.update.connect(this, this.update); - for (var i in OVERRIDEN_DRIVE_KEYS) { - MyAvatar.disableDriveKey(OVERRIDEN_DRIVE_KEYS[i]); - } + MyAvatar.disableDriveKey(OVERRIDEN_DRIVE_KEYS); } this.standUp = function() { print("Standing up (" + this.entityID + ")"); MyAvatar.removeAnimationStateHandler(this.animStateHandlerID); Script.update.disconnect(this, this.update); - for (var i in OVERRIDEN_DRIVE_KEYS) { - MyAvatar.enableDriveKey(OVERRIDEN_DRIVE_KEYS[i]); - } + MyAvatar.enableDriveKey(OVERRIDEN_DRIVE_KEYS); this.setSeatUser(null); if (Settings.getValue(SETTING_KEY) === this.entityID) { From dc71f502f0c4d2bbabafa87f28b490c32925b655 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Fri, 17 Mar 2017 13:09:06 +1300 Subject: [PATCH 219/302] Delete Developer > Network > RAM Caches Size menu item and dialog --- interface/src/Menu.cpp | 2 - interface/src/Menu.h | 1 - interface/src/ui/CachesSizeDialog.cpp | 84 --------------------------- interface/src/ui/CachesSizeDialog.h | 45 -------------- interface/src/ui/DialogsManager.cpp | 11 ---- interface/src/ui/DialogsManager.h | 3 - 6 files changed, 146 deletions(-) delete mode 100644 interface/src/ui/CachesSizeDialog.cpp delete mode 100644 interface/src/ui/CachesSizeDialog.h diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index beacbaccab..8e124d27c7 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -560,8 +560,6 @@ Menu::Menu() { false, &UserActivityLogger::getInstance(), SLOT(disable(bool))); - addActionToQMenuAndActionHash(networkMenu, MenuOption::CachesSize, 0, - dialogsManager.data(), SLOT(cachesSizeDialog())); addActionToQMenuAndActionHash(networkMenu, MenuOption::DiskCacheEditor, 0, dialogsManager.data(), SLOT(toggleDiskCacheEditor())); addActionToQMenuAndActionHash(networkMenu, MenuOption::ShowDSConnectTable, 0, diff --git a/interface/src/Menu.h b/interface/src/Menu.h index c806ffa9ee..e0ac340edc 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -52,7 +52,6 @@ namespace MenuOption { const QString BinaryEyelidControl = "Binary Eyelid Control"; const QString BookmarkLocation = "Bookmark Location"; const QString Bookmarks = "Bookmarks"; - const QString CachesSize = "RAM Caches Size"; const QString CalibrateCamera = "Calibrate Camera"; const QString CameraEntityMode = "Entity Mode"; const QString CenterPlayerInView = "Center Player In View"; diff --git a/interface/src/ui/CachesSizeDialog.cpp b/interface/src/ui/CachesSizeDialog.cpp deleted file mode 100644 index 935a6d126e..0000000000 --- a/interface/src/ui/CachesSizeDialog.cpp +++ /dev/null @@ -1,84 +0,0 @@ -// -// CachesSizeDialog.cpp -// -// -// Created by Clement on 1/12/15. -// Copyright 2015 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#include -#include -#include - -#include -#include -#include -#include -#include - -#include "CachesSizeDialog.h" - - -QDoubleSpinBox* createDoubleSpinBox(QWidget* parent) { - QDoubleSpinBox* box = new QDoubleSpinBox(parent); - box->setDecimals(0); - box->setRange(MIN_UNUSED_MAX_SIZE / BYTES_PER_MEGABYTES, MAX_UNUSED_MAX_SIZE / BYTES_PER_MEGABYTES); - - return box; -} - -CachesSizeDialog::CachesSizeDialog(QWidget* parent) : - QDialog(parent, Qt::Window | Qt::WindowCloseButtonHint) -{ - setWindowTitle("Caches Size"); - - // Create layouter - QFormLayout* form = new QFormLayout(this); - setLayout(form); - - form->addRow("Animations cache size (MB):", _animations = createDoubleSpinBox(this)); - form->addRow("Geometries cache size (MB):", _geometries = createDoubleSpinBox(this)); - form->addRow("Sounds cache size (MB):", _sounds = createDoubleSpinBox(this)); - form->addRow("Textures cache size (MB):", _textures = createDoubleSpinBox(this)); - - resetClicked(true); - - // Add a button to reset - QPushButton* confirmButton = new QPushButton("Confirm", this); - QPushButton* resetButton = new QPushButton("Reset", this); - form->addRow(confirmButton, resetButton); - connect(confirmButton, SIGNAL(clicked(bool)), this, SLOT(confirmClicked(bool))); - connect(resetButton, SIGNAL(clicked(bool)), this, SLOT(resetClicked(bool))); -} - -void CachesSizeDialog::confirmClicked(bool checked) { - DependencyManager::get()->setUnusedResourceCacheSize(_animations->value() * BYTES_PER_MEGABYTES); - DependencyManager::get()->setUnusedResourceCacheSize(_geometries->value() * BYTES_PER_MEGABYTES); - DependencyManager::get()->setUnusedResourceCacheSize(_sounds->value() * BYTES_PER_MEGABYTES); - // Disabling the texture cache because it's a liability in cases where we're overcommiting GPU memory -#if 0 - DependencyManager::get()->setUnusedResourceCacheSize(_textures->value() * BYTES_PER_MEGABYTES); -#endif - - QDialog::close(); -} - -void CachesSizeDialog::resetClicked(bool checked) { - _animations->setValue(DependencyManager::get()->getUnusedResourceCacheSize() / BYTES_PER_MEGABYTES); - _geometries->setValue(DependencyManager::get()->getUnusedResourceCacheSize() / BYTES_PER_MEGABYTES); - _sounds->setValue(DependencyManager::get()->getUnusedResourceCacheSize() / BYTES_PER_MEGABYTES); - _textures->setValue(DependencyManager::get()->getUnusedResourceCacheSize() / BYTES_PER_MEGABYTES); -} - -void CachesSizeDialog::reject() { - // Just regularly close upon ESC - QDialog::close(); -} - -void CachesSizeDialog::closeEvent(QCloseEvent* event) { - QDialog::closeEvent(event); - emit closed(); -} diff --git a/interface/src/ui/CachesSizeDialog.h b/interface/src/ui/CachesSizeDialog.h deleted file mode 100644 index 025d0f2bac..0000000000 --- a/interface/src/ui/CachesSizeDialog.h +++ /dev/null @@ -1,45 +0,0 @@ -// -// CachesSizeDialog.h -// -// -// Created by Clement on 1/12/15. -// Copyright 2015 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#ifndef hifi_CachesSizeDialog_h -#define hifi_CachesSizeDialog_h - -#include - -class QDoubleSpinBox; - -class CachesSizeDialog : public QDialog { - Q_OBJECT -public: - // Sets up the UI - CachesSizeDialog(QWidget* parent); - -signals: - void closed(); - -public slots: - void reject() override; - void confirmClicked(bool checked); - void resetClicked(bool checked); - -protected: - // Emits a 'closed' signal when this dialog is closed. - void closeEvent(QCloseEvent* event) override; - -private: - QDoubleSpinBox* _animations = nullptr; - QDoubleSpinBox* _geometries = nullptr; - QDoubleSpinBox* _scripts = nullptr; - QDoubleSpinBox* _sounds = nullptr; - QDoubleSpinBox* _textures = nullptr; -}; - -#endif // hifi_CachesSizeDialog_h diff --git a/interface/src/ui/DialogsManager.cpp b/interface/src/ui/DialogsManager.cpp index 3252fef4f0..20b9f6b173 100644 --- a/interface/src/ui/DialogsManager.cpp +++ b/interface/src/ui/DialogsManager.cpp @@ -19,7 +19,6 @@ #include #include "AddressBarDialog.h" -#include "CachesSizeDialog.h" #include "ConnectionFailureDialog.h" #include "DiskCacheEditor.h" #include "DomainConnectionDialog.h" @@ -97,16 +96,6 @@ void DialogsManager::octreeStatsDetails() { _octreeStatsDialog->raise(); } -void DialogsManager::cachesSizeDialog() { - if (!_cachesSizeDialog) { - maybeCreateDialog(_cachesSizeDialog); - - connect(_cachesSizeDialog, SIGNAL(closed()), _cachesSizeDialog, SLOT(deleteLater())); - _cachesSizeDialog->show(); - } - _cachesSizeDialog->raise(); -} - void DialogsManager::lodTools() { if (!_lodToolsDialog) { maybeCreateDialog(_lodToolsDialog); diff --git a/interface/src/ui/DialogsManager.h b/interface/src/ui/DialogsManager.h index 54aef38984..4e3b785679 100644 --- a/interface/src/ui/DialogsManager.h +++ b/interface/src/ui/DialogsManager.h @@ -21,7 +21,6 @@ class AnimationsDialog; class AttachmentsDialog; -class CachesSizeDialog; class DiskCacheEditor; class LodToolsDialog; class OctreeStatsDialog; @@ -50,7 +49,6 @@ public slots: void toggleLoginDialog(); void showLoginDialog(); void octreeStatsDetails(); - void cachesSizeDialog(); void lodTools(); void hmdTools(bool showTools); void showScriptEditor(); @@ -76,7 +74,6 @@ private: QPointer _animationsDialog; QPointer _attachmentsDialog; - QPointer _cachesSizeDialog; QPointer _diskCacheEditor; QPointer _ircInfoBox; QPointer _hmdToolsDialog; From bbad6a0eb7cb1c6ee32f2ea77dbed6dc7564bc71 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Thu, 16 Mar 2017 17:26:07 -0700 Subject: [PATCH 220/302] mesh-append works, mesh-transform doesn't? --- .../entities/src/EntityScriptingInterface.cpp | 13 +++ .../entities/src/EntityScriptingInterface.h | 1 + .../src/ModelScriptingInterface.cpp | 104 +++++++++++++++++- .../src/ModelScriptingInterface.h | 1 + 4 files changed, 114 insertions(+), 5 deletions(-) diff --git a/libraries/entities/src/EntityScriptingInterface.cpp b/libraries/entities/src/EntityScriptingInterface.cpp index ae34024bbc..7eae5b2dc4 100644 --- a/libraries/entities/src/EntityScriptingInterface.cpp +++ b/libraries/entities/src/EntityScriptingInterface.cpp @@ -1550,3 +1550,16 @@ bool EntityScriptingInterface::AABoxIntersectsCapsule(const glm::vec3& low, cons AABox aaBox(low, dimensions); return aaBox.findCapsulePenetration(start, end, radius, penetration); } + +glm::mat4 EntityScriptingInterface::getEntityTransform(const QUuid& entityID) { + glm::mat4 result; + if (_entityTree) { + _entityTree->withReadLock([&] { + EntityItemPointer entity = _entityTree->findEntityByEntityItemID(EntityItemID(entityID)); + if (entity) { + result = entity->getEntityToWorldMatrix(); + } + }); + } + return result; +} diff --git a/libraries/entities/src/EntityScriptingInterface.h b/libraries/entities/src/EntityScriptingInterface.h index b0c137ef77..be34317cd5 100644 --- a/libraries/entities/src/EntityScriptingInterface.h +++ b/libraries/entities/src/EntityScriptingInterface.h @@ -294,6 +294,7 @@ public slots: Q_INVOKABLE bool AABoxIntersectsCapsule(const glm::vec3& low, const glm::vec3& dimensions, const glm::vec3& start, const glm::vec3& end, float radius); + Q_INVOKABLE glm::mat4 getEntityTransform(const QUuid& entityID); signals: void collisionWithEntity(const EntityItemID& idA, const EntityItemID& idB, const Collision& collision); diff --git a/libraries/script-engine/src/ModelScriptingInterface.cpp b/libraries/script-engine/src/ModelScriptingInterface.cpp index b725548c96..86a651b6b2 100644 --- a/libraries/script-engine/src/ModelScriptingInterface.cpp +++ b/libraries/script-engine/src/ModelScriptingInterface.cpp @@ -55,7 +55,6 @@ QString ModelScriptingInterface::meshToOBJ(MeshProxyList in) { QScriptValue ModelScriptingInterface::appendMeshes(MeshProxyList in) { - model::MeshPointer result(new model::Mesh()); int attributeTypeNormal = gpu::Stream::InputSlot::NORMAL; // libraries/gpu/src/gpu/Stream.h size_t totalVertexCount { 0 }; @@ -72,11 +71,11 @@ QScriptValue ModelScriptingInterface::appendMeshes(MeshProxyList in) { unsigned char* combinedVertexData = new unsigned char[combinedVertexSize]; unsigned char* combinedVertexDataCursor = combinedVertexData; - gpu::Resource::Size combinedNormalSize = totalVertexCount * sizeof(glm::vec3); + gpu::Resource::Size combinedNormalSize = totalAttributeCount * sizeof(glm::vec3); unsigned char* combinedNormalData = new unsigned char[combinedNormalSize]; unsigned char* combinedNormalDataCursor = combinedNormalData; - gpu::Resource::Size combinedIndexSize = totalVertexCount * sizeof(uint32_t); + gpu::Resource::Size combinedIndexSize = totalIndexCount * sizeof(uint32_t); unsigned char* combinedIndexData = new unsigned char[combinedIndexSize]; unsigned char* combinedIndexDataCursor = combinedIndexData; @@ -96,7 +95,7 @@ QScriptValue ModelScriptingInterface::appendMeshes(MeshProxyList in) { // normal data const gpu::BufferView& normalsBufferView = mesh->getAttributeBuffer(attributeTypeNormal); - gpu::BufferView::Index numNormals = (gpu::BufferView::Index)mesh->getNumAttributes(); + gpu::BufferView::Index numNormals = (gpu::BufferView::Index)mesh->getNumAttributes(); for (gpu::BufferView::Index i = 0; i < numNormals; i ++) { glm::vec3 normal = normalsBufferView.get(i); memcpy(combinedNormalDataCursor, &normal, sizeof(normal)); @@ -106,7 +105,7 @@ QScriptValue ModelScriptingInterface::appendMeshes(MeshProxyList in) { // face data const gpu::BufferView& indexBufferView = mesh->getIndexBuffer(); - gpu::BufferView::Index numIndexes = (gpu::BufferView::Index)mesh->getNumAttributes(); + gpu::BufferView::Index numIndexes = (gpu::BufferView::Index)mesh->getNumIndices(); for (gpu::BufferView::Index i = 0; i < numIndexes; i ++) { uint32_t index = indexBufferView.get(i); index += indexStartOffset; @@ -117,6 +116,8 @@ QScriptValue ModelScriptingInterface::appendMeshes(MeshProxyList in) { indexStartOffset += numVertices; } + model::MeshPointer result(new model::Mesh()); + gpu::Element vertexElement = gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ); gpu::Buffer* combinedVertexBuffer = new gpu::Buffer(combinedVertexSize, combinedVertexData); gpu::BufferPointer combinedVertexBufferPointer(combinedVertexBuffer); @@ -135,6 +136,99 @@ QScriptValue ModelScriptingInterface::appendMeshes(MeshProxyList in) { gpu::BufferView combinedIndexesBufferView(combinedIndexesBufferPointer, indexElement); result->setIndexBuffer(combinedIndexesBufferView); + + std::vector parts; + parts.emplace_back(model::Mesh::Part((model::Index)0, // startIndex + (model::Index)result->getNumIndices(), // numIndices + (model::Index)0, // baseVertex + model::Mesh::TRIANGLES)); // topology + result->setPartBuffer(gpu::BufferView(new gpu::Buffer(parts.size() * sizeof(model::Mesh::Part), + (gpu::Byte*) parts.data()), gpu::Element::PART_DRAWCALL)); + + + MeshProxy* resultProxy = new MeshProxy(result); + return meshToScriptValue(_modelScriptEngine, resultProxy); +} + + +QScriptValue ModelScriptingInterface::transformMesh(glm::mat4 transform, MeshProxy* meshProxy) { + int attributeTypeNormal = gpu::Stream::InputSlot::NORMAL; // libraries/gpu/src/gpu/Stream.h + + MeshPointer mesh = meshProxy->getMeshPointer(); + + gpu::Resource::Size vertexSize = mesh->getNumVertices() * sizeof(glm::vec3); + unsigned char* resultVertexData = new unsigned char[vertexSize]; + unsigned char* vertexDataCursor = resultVertexData; + + gpu::Resource::Size normalSize = mesh->getNumAttributes() * sizeof(glm::vec3); + unsigned char* resultNormalData = new unsigned char[normalSize]; + unsigned char* normalDataCursor = resultNormalData; + + gpu::Resource::Size indexSize = mesh->getNumIndices() * sizeof(uint32_t); + unsigned char* resultIndexData = new unsigned char[indexSize]; + unsigned char* indexDataCursor = resultIndexData; + + // vertex data + const gpu::BufferView& vertexBufferView = mesh->getVertexBuffer(); + gpu::BufferView::Index numVertices = (gpu::BufferView::Index)mesh->getNumVertices(); + for (gpu::BufferView::Index i = 0; i < numVertices; i ++) { + glm::vec3 pos = vertexBufferView.get(i); + pos = glm::vec3(transform * glm::vec4(pos, 0.0f)); + memcpy(vertexDataCursor, &pos, sizeof(pos)); + vertexDataCursor += sizeof(pos); + } + + // normal data + const gpu::BufferView& normalsBufferView = mesh->getAttributeBuffer(attributeTypeNormal); + gpu::BufferView::Index numNormals = (gpu::BufferView::Index)mesh->getNumAttributes(); + for (gpu::BufferView::Index i = 0; i < numNormals; i ++) { + glm::vec3 normal = normalsBufferView.get(i); + normal = glm::vec3(transform * glm::vec4(normal, 0.0f)); + memcpy(normalDataCursor, &normal, sizeof(normal)); + normalDataCursor += sizeof(normal); + } + // TODO -- other attributes + + // face data + const gpu::BufferView& indexBufferView = mesh->getIndexBuffer(); + gpu::BufferView::Index numIndexes = (gpu::BufferView::Index)mesh->getNumIndices(); + for (gpu::BufferView::Index i = 0; i < numIndexes; i ++) { + uint32_t index = indexBufferView.get(i); + memcpy(indexDataCursor, &index, sizeof(index)); + indexDataCursor += sizeof(index); + } + + model::MeshPointer result(new model::Mesh()); + + gpu::Element vertexElement = gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ); + gpu::Buffer* resultVertexBuffer = new gpu::Buffer(vertexSize, resultVertexData); + gpu::BufferPointer resultVertexBufferPointer(resultVertexBuffer); + gpu::BufferView resultVertexBufferView(resultVertexBufferPointer, vertexElement); + result->setVertexBuffer(resultVertexBufferView); + + gpu::Element normalElement = gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ); + gpu::Buffer* resultNormalsBuffer = new gpu::Buffer(normalSize, resultNormalData); + gpu::BufferPointer resultNormalsBufferPointer(resultNormalsBuffer); + gpu::BufferView resultNormalsBufferView(resultNormalsBufferPointer, normalElement); + result->addAttribute(attributeTypeNormal, resultNormalsBufferView); + + gpu::Element indexElement = gpu::Element(gpu::SCALAR, gpu::UINT32, gpu::RAW); + gpu::Buffer* resultIndexesBuffer = new gpu::Buffer(indexSize, resultIndexData); + gpu::BufferPointer resultIndexesBufferPointer(resultIndexesBuffer); + gpu::BufferView resultIndexesBufferView(resultIndexesBufferPointer, indexElement); + result->setIndexBuffer(resultIndexesBufferView); + + + std::vector parts; + parts.emplace_back(model::Mesh::Part((model::Index)0, // startIndex + (model::Index)result->getNumIndices(), // numIndices + (model::Index)0, // baseVertex + model::Mesh::TRIANGLES)); // topology + result->setPartBuffer(gpu::BufferView(new gpu::Buffer(parts.size() * sizeof(model::Mesh::Part), + (gpu::Byte*) parts.data()), gpu::Element::PART_DRAWCALL)); + + + MeshProxy* resultProxy = new MeshProxy(result); return meshToScriptValue(_modelScriptEngine, resultProxy); } diff --git a/libraries/script-engine/src/ModelScriptingInterface.h b/libraries/script-engine/src/ModelScriptingInterface.h index 7ec8ab46ef..14789943e3 100644 --- a/libraries/script-engine/src/ModelScriptingInterface.h +++ b/libraries/script-engine/src/ModelScriptingInterface.h @@ -30,6 +30,7 @@ public: Q_INVOKABLE QString meshToOBJ(MeshProxyList in); Q_INVOKABLE QScriptValue appendMeshes(MeshProxyList in); + Q_INVOKABLE QScriptValue transformMesh(glm::mat4 transform, MeshProxy* meshProxy); private: ScriptEngine* _modelScriptEngine { nullptr }; From e21d7d9edf7c39693988bb4418cae3edfa85daf0 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Fri, 17 Mar 2017 13:34:11 +1300 Subject: [PATCH 221/302] Add Developer > Network > Clear Disk Cache menu action --- interface/src/Menu.cpp | 2 ++ interface/src/Menu.h | 1 + 2 files changed, 3 insertions(+) diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index beacbaccab..765d0f4a09 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -554,6 +554,8 @@ Menu::Menu() { "NetworkingPreferencesDialog"); }); addActionToQMenuAndActionHash(networkMenu, MenuOption::ReloadContent, 0, qApp, SLOT(reloadResourceCaches())); + addActionToQMenuAndActionHash(networkMenu, MenuOption::ClearDiskCache, 0, + DependencyManager::get().data(), SLOT(clearCache())); addCheckableActionToQMenuAndActionHash(networkMenu, MenuOption::DisableActivityLogger, 0, diff --git a/interface/src/Menu.h b/interface/src/Menu.h index c806ffa9ee..2755e11dd4 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -57,6 +57,7 @@ namespace MenuOption { const QString CameraEntityMode = "Entity Mode"; const QString CenterPlayerInView = "Center Player In View"; const QString Chat = "Chat..."; + const QString ClearDiskCache = "Clear Disk Cache"; const QString Collisions = "Collisions"; const QString Connexion = "Activate 3D Connexion Devices"; const QString Console = "Console..."; From 9155cc63345fc205200cffffe4db72912a79650d Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Thu, 16 Mar 2017 17:44:56 -0700 Subject: [PATCH 222/302] Fix windows warnings --- interface/src/avatar/MyAvatar.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index cc40c8eab2..eb77779691 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -2097,7 +2097,7 @@ void MyAvatar::clearDriveKeys() { void MyAvatar::setDriveKey(int key, float val) { try { _driveKeys.at(key) = val; - } catch (const std::exception& exception) { + } catch (const std::exception&) { qCCritical(interfaceapp) << Q_FUNC_INFO << ": Index out of bounds"; } } @@ -2109,7 +2109,7 @@ float MyAvatar::getDriveKey(int key) const { float MyAvatar::getRawDriveKey(int key) const { try { return _driveKeys.at(key); - } catch (const std::exception& exception) { + } catch (const std::exception&) { qCCritical(interfaceapp) << Q_FUNC_INFO << ": Index out of bounds"; return 0.0f; } @@ -2124,7 +2124,7 @@ void MyAvatar::relayDriveKeysToCharacterController() { void MyAvatar::disableDriveKey(int key) { try { _disabledDriveKeys.set(key); - } catch (const std::exception& exception) { + } catch (const std::exception&) { qCCritical(interfaceapp) << Q_FUNC_INFO << ": Index out of bounds"; } } @@ -2132,7 +2132,7 @@ void MyAvatar::disableDriveKey(int key) { void MyAvatar::enableDriveKey(int key) { try { _disabledDriveKeys.reset(key); - } catch (const std::exception& exception) { + } catch (const std::exception&) { qCCritical(interfaceapp) << Q_FUNC_INFO << ": Index out of bounds"; } } @@ -2142,7 +2142,7 @@ void MyAvatar::disableDriveKeys(std::vector key) { std::for_each(std::begin(key), std::end(key), [&](int val){ _disabledDriveKeys.set(val); }); - } catch (const std::exception& exception) { + } catch (const std::exception&) { qCCritical(interfaceapp) << Q_FUNC_INFO << ": Index out of bounds"; } } @@ -2152,7 +2152,7 @@ void MyAvatar::enableDriveKeys(std::vector key) { std::for_each(std::begin(key), std::end(key), [&](int val) { _disabledDriveKeys.reset(val); }); - } catch (const std::exception& exception) { + } catch (const std::exception&) { qCCritical(interfaceapp) << Q_FUNC_INFO << ": Index out of bounds"; } } @@ -2160,7 +2160,7 @@ void MyAvatar::enableDriveKeys(std::vector key) { bool MyAvatar::isDriveKeyDisabled(int key) const { try { return _disabledDriveKeys.test(key); - } catch (const std::exception& exception) { + } catch (const std::exception&) { qCCritical(interfaceapp) << Q_FUNC_INFO << ": Index out of bounds"; return false; } From 5a1ce4bb05292c80d0453486ff5ba17c563ca40b Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Thu, 16 Mar 2017 17:46:47 -0700 Subject: [PATCH 223/302] CR --- interface/src/avatar/MyAvatar.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index eb77779691..7b478b3726 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -2162,7 +2162,7 @@ bool MyAvatar::isDriveKeyDisabled(int key) const { return _disabledDriveKeys.test(key); } catch (const std::exception&) { qCCritical(interfaceapp) << Q_FUNC_INFO << ": Index out of bounds"; - return false; + return true; } } From 939b4f16129c9d81f5b2ae145933cbf145fbb8f9 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Fri, 17 Mar 2017 13:49:09 +1300 Subject: [PATCH 224/302] Remove Developer > Network > Disk Cache Editor menu item and dialog --- interface/src/Menu.cpp | 2 - interface/src/Menu.h | 1 - interface/src/ui/DialogsManager.cpp | 6 -- interface/src/ui/DialogsManager.h | 3 - interface/src/ui/DiskCacheEditor.cpp | 146 --------------------------- interface/src/ui/DiskCacheEditor.h | 49 --------- 6 files changed, 207 deletions(-) delete mode 100644 interface/src/ui/DiskCacheEditor.cpp delete mode 100644 interface/src/ui/DiskCacheEditor.h diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 765d0f4a09..9afd2d6472 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -564,8 +564,6 @@ Menu::Menu() { SLOT(disable(bool))); addActionToQMenuAndActionHash(networkMenu, MenuOption::CachesSize, 0, dialogsManager.data(), SLOT(cachesSizeDialog())); - addActionToQMenuAndActionHash(networkMenu, MenuOption::DiskCacheEditor, 0, - dialogsManager.data(), SLOT(toggleDiskCacheEditor())); addActionToQMenuAndActionHash(networkMenu, MenuOption::ShowDSConnectTable, 0, dialogsManager.data(), SLOT(showDomainConnectionDialog())); diff --git a/interface/src/Menu.h b/interface/src/Menu.h index 2755e11dd4..367abe935a 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -84,7 +84,6 @@ namespace MenuOption { const QString DisableActivityLogger = "Disable Activity Logger"; const QString DisableEyelidAdjustment = "Disable Eyelid Adjustment"; const QString DisableLightEntities = "Disable Light Entities"; - const QString DiskCacheEditor = "Disk Cache Editor"; const QString DisplayCrashOptions = "Display Crash Options"; const QString DisplayHandTargets = "Show Hand Targets"; const QString DisplayModelBounds = "Display Model Bounds"; diff --git a/interface/src/ui/DialogsManager.cpp b/interface/src/ui/DialogsManager.cpp index 3252fef4f0..ac8b943aa8 100644 --- a/interface/src/ui/DialogsManager.cpp +++ b/interface/src/ui/DialogsManager.cpp @@ -21,7 +21,6 @@ #include "AddressBarDialog.h" #include "CachesSizeDialog.h" #include "ConnectionFailureDialog.h" -#include "DiskCacheEditor.h" #include "DomainConnectionDialog.h" #include "HMDToolsDialog.h" #include "LodToolsDialog.h" @@ -67,11 +66,6 @@ void DialogsManager::setDomainConnectionFailureVisibility(bool visible) { } } -void DialogsManager::toggleDiskCacheEditor() { - maybeCreateDialog(_diskCacheEditor); - _diskCacheEditor->toggle(); -} - void DialogsManager::toggleLoginDialog() { LoginDialog::toggleAction(); } diff --git a/interface/src/ui/DialogsManager.h b/interface/src/ui/DialogsManager.h index 54aef38984..bb144ba99b 100644 --- a/interface/src/ui/DialogsManager.h +++ b/interface/src/ui/DialogsManager.h @@ -22,7 +22,6 @@ class AnimationsDialog; class AttachmentsDialog; class CachesSizeDialog; -class DiskCacheEditor; class LodToolsDialog; class OctreeStatsDialog; class ScriptEditorWindow; @@ -46,7 +45,6 @@ public slots: void showAddressBar(); void showFeed(); void setDomainConnectionFailureVisibility(bool visible); - void toggleDiskCacheEditor(); void toggleLoginDialog(); void showLoginDialog(); void octreeStatsDetails(); @@ -77,7 +75,6 @@ private: QPointer _animationsDialog; QPointer _attachmentsDialog; QPointer _cachesSizeDialog; - QPointer _diskCacheEditor; QPointer _ircInfoBox; QPointer _hmdToolsDialog; QPointer _lodToolsDialog; diff --git a/interface/src/ui/DiskCacheEditor.cpp b/interface/src/ui/DiskCacheEditor.cpp deleted file mode 100644 index 1a7be8642b..0000000000 --- a/interface/src/ui/DiskCacheEditor.cpp +++ /dev/null @@ -1,146 +0,0 @@ -// -// DiskCacheEditor.cpp -// -// -// Created by Clement on 3/4/15. -// Copyright 2015 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#include "DiskCacheEditor.h" - -#include -#include -#include -#include -#include -#include -#include - -#include - -#include "OffscreenUi.h" - -DiskCacheEditor::DiskCacheEditor(QWidget* parent) : QObject(parent) { -} - -QWindow* DiskCacheEditor::windowHandle() { - return (_dialog) ? _dialog->windowHandle() : nullptr; -} - -void DiskCacheEditor::toggle() { - if (!_dialog) { - makeDialog(); - } - - if (!_dialog->isActiveWindow()) { - _dialog->show(); - _dialog->raise(); - _dialog->activateWindow(); - } else { - _dialog->close(); - } -} - -void DiskCacheEditor::makeDialog() { - _dialog = new QDialog(static_cast(parent())); - Q_CHECK_PTR(_dialog); - _dialog->setAttribute(Qt::WA_DeleteOnClose); - _dialog->setWindowTitle("Disk Cache Editor"); - - QGridLayout* layout = new QGridLayout(_dialog); - Q_CHECK_PTR(layout); - _dialog->setLayout(layout); - - - QLabel* path = new QLabel("Path : ", _dialog); - Q_CHECK_PTR(path); - path->setAlignment(Qt::AlignRight); - layout->addWidget(path, 0, 0); - - QLabel* size = new QLabel("Current Size : ", _dialog); - Q_CHECK_PTR(size); - size->setAlignment(Qt::AlignRight); - layout->addWidget(size, 1, 0); - - QLabel* maxSize = new QLabel("Max Size : ", _dialog); - Q_CHECK_PTR(maxSize); - maxSize->setAlignment(Qt::AlignRight); - layout->addWidget(maxSize, 2, 0); - - - _path = new QLabel(_dialog); - Q_CHECK_PTR(_path); - _path->setAlignment(Qt::AlignLeft); - layout->addWidget(_path, 0, 1, 1, 3); - - _size = new QLabel(_dialog); - Q_CHECK_PTR(_size); - _size->setAlignment(Qt::AlignLeft); - layout->addWidget(_size, 1, 1, 1, 3); - - _maxSize = new QLabel(_dialog); - Q_CHECK_PTR(_maxSize); - _maxSize->setAlignment(Qt::AlignLeft); - layout->addWidget(_maxSize, 2, 1, 1, 3); - - refresh(); - - - static const int REFRESH_INTERVAL = 100; // msec - _refreshTimer = new QTimer(_dialog); - _refreshTimer->setInterval(REFRESH_INTERVAL); // Qt::CoarseTimer acceptable, no need for real time accuracy - _refreshTimer->setSingleShot(false); - QObject::connect(_refreshTimer.data(), &QTimer::timeout, this, &DiskCacheEditor::refresh); - _refreshTimer->start(); - - QPushButton* clearCacheButton = new QPushButton(_dialog); - Q_CHECK_PTR(clearCacheButton); - clearCacheButton->setText("Clear"); - clearCacheButton->setToolTip("Erases the entire content of the disk cache."); - connect(clearCacheButton, SIGNAL(clicked()), SLOT(clear())); - layout->addWidget(clearCacheButton, 3, 3); -} - -void DiskCacheEditor::refresh() { - DependencyManager::get()->cacheInfoRequest(this, "cacheInfoCallback"); -} - -void DiskCacheEditor::cacheInfoCallback(QString cacheDirectory, qint64 cacheSize, qint64 maximumCacheSize) { - static const auto stringify = [](qint64 number) { - static const QStringList UNITS = QStringList() << "B" << "KB" << "MB" << "GB"; - static const qint64 CHUNK = 1024; - QString unit; - int i = 0; - for (i = 0; i < 4; ++i) { - if (number / CHUNK > 0) { - number /= CHUNK; - } else { - break; - } - } - return QString("%0 %1").arg(number).arg(UNITS[i]); - }; - - if (_path) { - _path->setText(cacheDirectory); - } - if (_size) { - _size->setText(stringify(cacheSize)); - } - if (_maxSize) { - _maxSize->setText(stringify(maximumCacheSize)); - } -} - -void DiskCacheEditor::clear() { - auto buttonClicked = OffscreenUi::question(_dialog, "Clearing disk cache", - "You are about to erase all the content of the disk cache, " - "are you sure you want to do that?", - QMessageBox::Ok | QMessageBox::Cancel); - if (buttonClicked == QMessageBox::Ok) { - DependencyManager::get()->clearCache(); - } -} diff --git a/interface/src/ui/DiskCacheEditor.h b/interface/src/ui/DiskCacheEditor.h deleted file mode 100644 index 3f8fa1a883..0000000000 --- a/interface/src/ui/DiskCacheEditor.h +++ /dev/null @@ -1,49 +0,0 @@ -// -// DiskCacheEditor.h -// -// -// Created by Clement on 3/4/15. -// Copyright 2015 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#ifndef hifi_DiskCacheEditor_h -#define hifi_DiskCacheEditor_h - -#include -#include - -class QDialog; -class QLabel; -class QWindow; -class QTimer; - -class DiskCacheEditor : public QObject { - Q_OBJECT - -public: - DiskCacheEditor(QWidget* parent = nullptr); - - QWindow* windowHandle(); - -public slots: - void toggle(); - -private slots: - void refresh(); - void cacheInfoCallback(QString cacheDirectory, qint64 cacheSize, qint64 maximumCacheSize); - void clear(); - -private: - void makeDialog(); - - QPointer _dialog; - QPointer _path; - QPointer _size; - QPointer _maxSize; - QPointer _refreshTimer; -}; - -#endif // hifi_DiskCacheEditor_h \ No newline at end of file From 698791295a6f2b60893619f1555a498084759e45 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Thu, 16 Mar 2017 18:37:58 -0700 Subject: [PATCH 225/302] Expose DriveKeys enum to JS --- interface/src/Application.cpp | 17 ++++--- interface/src/avatar/MyAvatar.cpp | 59 +++++++++++++------------ interface/src/avatar/MyAvatar.h | 48 +++++++++++--------- scripts/tutorials/entity_scripts/sit.js | 17 +++++-- 4 files changed, 79 insertions(+), 62 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index df9d10caa6..8cd5a9564a 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -4383,16 +4383,16 @@ void Application::update(float deltaTime) { myAvatar->clearDriveKeys(); if (_myCamera.getMode() != CAMERA_MODE_INDEPENDENT) { if (!_controllerScriptingInterface->areActionsCaptured()) { - myAvatar->setDriveKey(TRANSLATE_Z, -1.0f * userInputMapper->getActionState(controller::Action::TRANSLATE_Z)); - myAvatar->setDriveKey(TRANSLATE_Y, userInputMapper->getActionState(controller::Action::TRANSLATE_Y)); - myAvatar->setDriveKey(TRANSLATE_X, userInputMapper->getActionState(controller::Action::TRANSLATE_X)); + myAvatar->setDriveKey(MyAvatar::TRANSLATE_Z, -1.0f * userInputMapper->getActionState(controller::Action::TRANSLATE_Z)); + myAvatar->setDriveKey(MyAvatar::TRANSLATE_Y, userInputMapper->getActionState(controller::Action::TRANSLATE_Y)); + myAvatar->setDriveKey(MyAvatar::TRANSLATE_X, userInputMapper->getActionState(controller::Action::TRANSLATE_X)); if (deltaTime > FLT_EPSILON) { - myAvatar->setDriveKey(PITCH, -1.0f * userInputMapper->getActionState(controller::Action::PITCH)); - myAvatar->setDriveKey(YAW, -1.0f * userInputMapper->getActionState(controller::Action::YAW)); - myAvatar->setDriveKey(STEP_YAW, -1.0f * userInputMapper->getActionState(controller::Action::STEP_YAW)); + myAvatar->setDriveKey(MyAvatar::PITCH, -1.0f * userInputMapper->getActionState(controller::Action::PITCH)); + myAvatar->setDriveKey(MyAvatar::YAW, -1.0f * userInputMapper->getActionState(controller::Action::YAW)); + myAvatar->setDriveKey(MyAvatar::STEP_YAW, -1.0f * userInputMapper->getActionState(controller::Action::STEP_YAW)); } } - myAvatar->setDriveKey(ZOOM, userInputMapper->getActionState(controller::Action::TRANSLATE_CAMERA_Z)); + myAvatar->setDriveKey(MyAvatar::ZOOM, userInputMapper->getActionState(controller::Action::TRANSLATE_CAMERA_Z)); } controller::Pose leftHandPose = userInputMapper->getPoseState(controller::Action::LEFT_HAND); @@ -5503,8 +5503,7 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEngine* scri scriptEngine->registerGlobalObject("Rates", new RatesScriptingInterface(this)); // hook our avatar and avatar hash map object into this script engine - scriptEngine->registerGlobalObject("MyAvatar", getMyAvatar().get()); - qScriptRegisterMetaType(scriptEngine, audioListenModeToScriptValue, audioListenModeFromScriptValue); + getMyAvatar()->registerMetaTypes(scriptEngine); scriptEngine->registerGlobalObject("AvatarList", DependencyManager::get().data()); diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 7b478b3726..401378fff1 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -228,6 +228,21 @@ MyAvatar::~MyAvatar() { _lookAtTargetAvatar.reset(); } +void MyAvatar::registerMetaTypes(QScriptEngine* engine) { + QScriptValue value = engine->newQObject(this, QScriptEngine::QtOwnership, QScriptEngine::ExcludeDeleteLater | QScriptEngine::ExcludeChildObjects); + engine->globalObject().setProperty("MyAvatar", value); + + QScriptValue driveKeys = engine->newObject(); + auto metaEnum = QMetaEnum::fromType(); + for (int i = 0; i < MAX_DRIVE_KEYS; ++i) { + driveKeys.setProperty(metaEnum.key(i), metaEnum.value(i)); + } + engine->globalObject().setProperty("DriveKeys", driveKeys); + + qScriptRegisterMetaType(engine, audioListenModeToScriptValue, audioListenModeFromScriptValue); + qScriptRegisterMetaType(engine, driveKeysToScriptValue, driveKeysFromScriptValue); +} + void MyAvatar::setOrientationVar(const QVariant& newOrientationVar) { Avatar::setOrientation(quatFromVariant(newOrientationVar)); } @@ -460,7 +475,7 @@ void MyAvatar::simulate(float deltaTime) { // When there are no step values, we zero out the last step pulse. // This allows a user to do faster snapping by tapping a control for (int i = STEP_TRANSLATE_X; !stepAction && i <= STEP_YAW; ++i) { - if (getDriveKey(i) != 0.0f) { + if (getDriveKey((DriveKeys)i) != 0.0f) { stepAction = true; } } @@ -2094,7 +2109,7 @@ void MyAvatar::clearDriveKeys() { _driveKeys.fill(0.0f); } -void MyAvatar::setDriveKey(int key, float val) { +void MyAvatar::setDriveKey(DriveKeys key, float val) { try { _driveKeys.at(key) = val; } catch (const std::exception&) { @@ -2102,11 +2117,11 @@ void MyAvatar::setDriveKey(int key, float val) { } } -float MyAvatar::getDriveKey(int key) const { +float MyAvatar::getDriveKey(DriveKeys key) const { return isDriveKeyDisabled(key) ? 0.0f : getRawDriveKey(key); } -float MyAvatar::getRawDriveKey(int key) const { +float MyAvatar::getRawDriveKey(DriveKeys key) const { try { return _driveKeys.at(key); } catch (const std::exception&) { @@ -2121,7 +2136,7 @@ void MyAvatar::relayDriveKeysToCharacterController() { } } -void MyAvatar::disableDriveKey(int key) { +void MyAvatar::disableDriveKey(DriveKeys key) { try { _disabledDriveKeys.set(key); } catch (const std::exception&) { @@ -2129,7 +2144,7 @@ void MyAvatar::disableDriveKey(int key) { } } -void MyAvatar::enableDriveKey(int key) { +void MyAvatar::enableDriveKey(DriveKeys key) { try { _disabledDriveKeys.reset(key); } catch (const std::exception&) { @@ -2137,27 +2152,7 @@ void MyAvatar::enableDriveKey(int key) { } } -void MyAvatar::disableDriveKeys(std::vector key) { - try { - std::for_each(std::begin(key), std::end(key), [&](int val){ - _disabledDriveKeys.set(val); - }); - } catch (const std::exception&) { - qCCritical(interfaceapp) << Q_FUNC_INFO << ": Index out of bounds"; - } -} - -void MyAvatar::enableDriveKeys(std::vector key) { - try { - std::for_each(std::begin(key), std::end(key), [&](int val) { - _disabledDriveKeys.reset(val); - }); - } catch (const std::exception&) { - qCCritical(interfaceapp) << Q_FUNC_INFO << ": Index out of bounds"; - } -} - -bool MyAvatar::isDriveKeyDisabled(int key) const { +bool MyAvatar::isDriveKeyDisabled(DriveKeys key) const { try { return _disabledDriveKeys.test(key); } catch (const std::exception&) { @@ -2251,7 +2246,15 @@ QScriptValue audioListenModeToScriptValue(QScriptEngine* engine, const AudioList } void audioListenModeFromScriptValue(const QScriptValue& object, AudioListenerMode& audioListenerMode) { - audioListenerMode = (AudioListenerMode)object.toUInt16(); + audioListenerMode = static_cast(object.toUInt16()); +} + +QScriptValue driveKeysToScriptValue(QScriptEngine* engine, const MyAvatar::DriveKeys& driveKeys) { + return driveKeys; +} + +void driveKeysFromScriptValue(const QScriptValue& object, MyAvatar::DriveKeys& driveKeys) { + driveKeys = static_cast(object.toUInt16()); } diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index cae445c226..9bcec1134f 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -31,20 +31,6 @@ class AvatarActionHold; class ModelItemID; -enum DriveKeys { - TRANSLATE_X = 0, - TRANSLATE_Y, - TRANSLATE_Z, - YAW, - STEP_TRANSLATE_X, - STEP_TRANSLATE_Y, - STEP_TRANSLATE_Z, - STEP_YAW, - PITCH, - ZOOM, - MAX_DRIVE_KEYS -}; - enum eyeContactTarget { LEFT_EYE, RIGHT_EYE, @@ -90,9 +76,26 @@ class MyAvatar : public Avatar { Q_PROPERTY(bool characterControllerEnabled READ getCharacterControllerEnabled WRITE setCharacterControllerEnabled) public: + enum DriveKeys { + TRANSLATE_X = 0, + TRANSLATE_Y, + TRANSLATE_Z, + YAW, + STEP_TRANSLATE_X, + STEP_TRANSLATE_Y, + STEP_TRANSLATE_Z, + STEP_YAW, + PITCH, + ZOOM, + MAX_DRIVE_KEYS + }; + Q_ENUM(DriveKeys) + explicit MyAvatar(RigPointer rig); ~MyAvatar(); + void registerMetaTypes(QScriptEngine* engine); + virtual void simulateAttachments(float deltaTime) override; AudioListenerMode getAudioListenerModeHead() const { return FROM_HEAD; } @@ -182,16 +185,14 @@ public: // Set what driving keys are being pressed to control thrust levels void clearDriveKeys(); - void setDriveKey(int key, float val); - float getDriveKey(int key) const; - Q_INVOKABLE float getRawDriveKey(int key) const; + void setDriveKey(DriveKeys key, float val); + float getDriveKey(DriveKeys key) const; + Q_INVOKABLE float getRawDriveKey(DriveKeys key) const; void relayDriveKeysToCharacterController(); - Q_INVOKABLE void disableDriveKey(int key); - Q_INVOKABLE void enableDriveKey(int key); - Q_INVOKABLE void disableDriveKeys(std::vector key); - Q_INVOKABLE void enableDriveKeys(std::vector key); - Q_INVOKABLE bool isDriveKeyDisabled(int key) const; + Q_INVOKABLE void disableDriveKey(DriveKeys key); + Q_INVOKABLE void enableDriveKey(DriveKeys key); + Q_INVOKABLE bool isDriveKeyDisabled(DriveKeys key) const; eyeContactTarget getEyeContactTarget(); @@ -552,4 +553,7 @@ private: QScriptValue audioListenModeToScriptValue(QScriptEngine* engine, const AudioListenerMode& audioListenerMode); void audioListenModeFromScriptValue(const QScriptValue& object, AudioListenerMode& audioListenerMode); +QScriptValue driveKeysToScriptValue(QScriptEngine* engine, const MyAvatar::DriveKeys& driveKeys); +void driveKeysFromScriptValue(const QScriptValue& object, MyAvatar::DriveKeys& driveKeys); + #endif // hifi_MyAvatar_h diff --git a/scripts/tutorials/entity_scripts/sit.js b/scripts/tutorials/entity_scripts/sit.js index 420a77e2b8..82afdc8974 100644 --- a/scripts/tutorials/entity_scripts/sit.js +++ b/scripts/tutorials/entity_scripts/sit.js @@ -14,7 +14,14 @@ var DESKTOP_MAX_DISTANCE = 5; var SIT_DELAY = 25; var MAX_RESET_DISTANCE = 0.5; // meters - var OVERRIDEN_DRIVE_KEYS = [0, 1, 2, 4, 5, 6]; + var OVERRIDEN_DRIVE_KEYS = [ + DriveKeys.TRANSLATE_X, + DriveKeys.TRANSLATE_Y, + DriveKeys.TRANSLATE_Z, + DriveKeys.STEP_TRANSLATE_X, + DriveKeys.STEP_TRANSLATE_Y, + DriveKeys.STEP_TRANSLATE_Z, + ]; this.entityID = null; this.animStateHandlerID = null; @@ -125,14 +132,18 @@ return { headType: 0 }; }, ["headType"]); Script.update.connect(this, this.update); - MyAvatar.disableDriveKey(OVERRIDEN_DRIVE_KEYS); + for (var i in OVERRIDEN_DRIVE_KEYS) { + MyAvatar.disableDriveKey(OVERRIDEN_DRIVE_KEYS[i]); + } } this.standUp = function() { print("Standing up (" + this.entityID + ")"); MyAvatar.removeAnimationStateHandler(this.animStateHandlerID); Script.update.disconnect(this, this.update); - MyAvatar.enableDriveKey(OVERRIDEN_DRIVE_KEYS); + for (var i in OVERRIDEN_DRIVE_KEYS) { + MyAvatar.enableDriveKey(OVERRIDEN_DRIVE_KEYS[i]); + } this.setSeatUser(null); if (Settings.getValue(SETTING_KEY) === this.entityID) { From 813e3a1bcaa147db3f236373f58287390a736cb8 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Thu, 16 Mar 2017 20:47:56 -0700 Subject: [PATCH 226/302] Fix issue with scaling prior to KTX serialization --- libraries/fbx/src/FBXReader.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/libraries/fbx/src/FBXReader.cpp b/libraries/fbx/src/FBXReader.cpp index fcaef90527..52a51daac3 100644 --- a/libraries/fbx/src/FBXReader.cpp +++ b/libraries/fbx/src/FBXReader.cpp @@ -1468,6 +1468,9 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS // Create the Material Library consolidateFBXMaterials(mapping); + // We can't scale allow the scaling of a given image to different sizes, because the hash used for the KTX cache is based on the original image + // Allowing scaling of the same image to different sizes would cause different KTX files to target the same cache key +#if 0 // HACK: until we get proper LOD management we're going to cap model textures // according to how many unique textures the model uses: // 1 - 8 textures --> 2048 @@ -1481,6 +1484,7 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS int numTextures = uniqueTextures.size(); const int MAX_NUM_TEXTURES_AT_MAX_RESOLUTION = 8; int maxWidth = sqrt(MAX_NUM_PIXELS_FOR_FBX_TEXTURE); + if (numTextures > MAX_NUM_TEXTURES_AT_MAX_RESOLUTION) { int numTextureThreshold = MAX_NUM_TEXTURES_AT_MAX_RESOLUTION; const int MIN_MIP_TEXTURE_WIDTH = 64; @@ -1494,7 +1498,7 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS material.setMaxNumPixelsPerTexture(maxWidth * maxWidth); } } - +#endif geometry.materials = _fbxMaterials; // see if any materials have texture children From eba6c8de5e64721c95083d15a0245d0074ef8147 Mon Sep 17 00:00:00 2001 From: Triplelexx Date: Fri, 17 Mar 2017 17:37:19 +0000 Subject: [PATCH 227/302] remove About Interface from File menu --- interface/resources/html/img/devices.png | Bin 7492 -> 0 bytes interface/resources/html/img/models.png | Bin 8664 -> 0 bytes interface/resources/html/img/move.png | Bin 6121 -> 0 bytes interface/resources/html/img/run-script.png | Bin 4873 -> 0 bytes interface/resources/html/img/talk.png | Bin 2611 -> 0 bytes interface/resources/html/img/write-script.png | Bin 2006 -> 0 bytes .../resources/html/interface-welcome.html | 187 ------------------ interface/src/Application.cpp | 5 - interface/src/Application.h | 1 - interface/src/Menu.cpp | 3 - interface/src/Menu.h | 1 - 11 files changed, 197 deletions(-) delete mode 100644 interface/resources/html/img/devices.png delete mode 100644 interface/resources/html/img/models.png delete mode 100644 interface/resources/html/img/move.png delete mode 100644 interface/resources/html/img/run-script.png delete mode 100644 interface/resources/html/img/talk.png delete mode 100644 interface/resources/html/img/write-script.png delete mode 100644 interface/resources/html/interface-welcome.html diff --git a/interface/resources/html/img/devices.png b/interface/resources/html/img/devices.png deleted file mode 100644 index fc4231e96e25732a0659c911e7c15ded5b54911b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7492 zcmchb=QkUU!^K0jVkgv|iCJ2E)fNf0XVoaG_TGE8Qi8U2X-N=bD{5Db8b#IKloYjv zT2U0g^ZgT^H_z*P&b@E$J)d)KqLG0X4J8{T005xTegroG07zf}00}AZ4gdg1K3)C; z003A65f*`_KF)z5_Wn))bw{7)PCVLP_AX8)PWFyreuGX*0075^HeB5-bYTyz@A>q} zc|wiGU!eztleTiTo9&Xv%w->6n+2^WettGN7I=%4$q$*?RY5_Zg!HN3*IxVRT3=qT z-A5{en}6BQZegj{C+x{WwnMIO%}jZ_V&>+uO~pqZGxBU{i94M~z9Pm~ON|tkL_nhe4vGl>maKpN^ch-AXrn#w!>8D4t zZnO#+C7K_^&>yf}6@j-KF%2AaDMCs|DaR1jh;MfJ#&$b%PJPHZ&(780Kyeu~7sZEI zvkkxS@3UM)w$i21(l8YsSgc8?(-@sU>wb2+ZScKQ@j-98Py#_^`>bV@+{7WaRijW2 z1cuRbu%E=?aJU(j&dPkc$^?(r)j!cSOTL||!^b3G(oC3C_KzLFVW#mearN~L_oFu? zIxBjj9SKMM$@AU`$nd1m%*+d}VY|z-^R z|Mk|lD3w$;jps&CQWVI2R6(-pwg(FI4Diu*U)wQ9nh>dIoj|9<4$CZq|;nr8h1zS=>7x{|49O|K$9I=Z9+<&H-?zgxxvNlX~*dJrwlJy?QR9talftP&$;yyMih`p4t{{jSf6J#VkW>oqMAl|#4ih07x@e;S-g&dZ zJ@tX-LuL>fCk1f>Z;(#uJaaiB+)ZFmV%v`|BSc1HTqUPukibFQxAVbj99v8ZnLSxx z{ZHRbYBe@!pkM>jzPCS%J)9{G!p95CruA{WpGuQ~`ytjA0OZ+0{Xy^nO~(SuCDrnv zfmDAu*2lhP=J{vc0T^o{`{g~2=nd@H#2H|pfaXx_fyHi=iL~aCL$?iD8?FHmtJ`KJk zG`rkf9mIg4)QkmavDFMimBH)lvrcB#S7=i(@K4_|>frkFKujfDnRtD}?(-$B{rY0J z&!KH;Mh1(&iMkFy19NKLhppw`{Bt3$Bycmqrkr6@i4P#c?qpknYG}oUlOQruv)&LD zy!lDH%FtShDyGru!Ifv#?8ORQOxe~Gm}kqZ^%`~JQ)IA-Zn#2u*1nK_69Ip79ddXB zM*+n(NfanB{`b5z^4Ayf*THq?X?XdqGd@E^JgZF0HfoG{l}HSRAUHh}|4`Zeh1I1= z@^%4l&-uI6POR(=r18!tG6;UsNT5B9_&j0#USDc2kco*&ff$EIqY>FUmGCmR?K+}i zw0^@wYkG#l!oWFX=%l`!liW|=9*is;H+&*ln&=(b`oi>H z?a0CK@UW+chwf(NrJ(BhpvS!T=AeXNXMBc(99~xJX^XFX%+G;W3;))*Z_4nhxi_Lv zy~oH75I*MSY88372x@F?vrkj#_kW=lV@6(mIitDe zpLAYKdxnLbZ?y(&CRsgN`okUi>&0GS0@uWw!u4n`#Q1Nq=)>gwot+A8->mx5JJ1tT zXAg}NF_x^QEteh7^IvgvTNa7boEn2LeQtA$8V42!8#mFfkF!5Bc?GAN`zRKu%+`;p zR4x-%Bha16$;o}uf0quTn>SBS+Oca6{cjmZB|1*4ePm*ebMae4Qs?+Dc|)~5@68y% z7qAAQuL`DjIjtJXlC?~VIVC)&u1nYWN!Z+v#)kF4(T=1~{frAHR$dTdldh*GJqY+= z=i~6pQj^Vou88}W-=B1t{M^bSUSG_1igLwVALqNM{{2cv95AYhIr=^L1Ba&z(45vo zs>~RaCF=;L;3qweZ@UG|ZP##WEzOyMINt-ZzG37@Dy|G)?mVI7rBFHkAw`UsFE=gy z)Y!O`l%188wKuNCy>uL@ZJ3MgK0Q6%TkT7Jbw9{^(ZhA9=qmz&Egn$bwBl54z^VpS zeL0=6e)W$}MK|~0^X|vhaZPTuQ&;?AUCa7A>m&yUxe7UaWW9VaSjQ-BZYaF~dD2ZK zcGp^!VkbbDzQNUaG{8x$@2s8vWPFqlzI5L3nI$ zoC~GH_PS$LaqT)WSGh)%%GaWdw#MNR8$aG-dBRyfhBSv_Fk%X513v>|FqS&K5EFB{ zGp#&vf`#d((FJTkx;xzJMPVMvp9bLc4N$@yG;OTTnX>bk_v=Vix3lWy{HP`ycoqIQ zFfWItFT3;KdfIgkuIJ#(@knWO$!uT^-eeexlSb8w(W6ajmVtdK^wn{%g#08kcii#B zrYoGq!S;8D4`<5}2Xj5ECyFq+BtLPI3SJyD;(fTy8}*UkMUid!L5p8eJn4C&1I_s8 zHdoz-1!-+#g8A9c0K?!#uV%t_AM%Qqkx<`u&=Fy{m`|)G9yWT+n%oZ;`|M5rnwmA~ z>MgP)s&t^%E^{_HYoV;Zn48IMr@gg-6a0ya*q_u;UAX*RhELsQE*;8wKhD&Sz`m#z zyf&s1dA1ZnMdFSxu^n|AUn)D%(R(!kQ||8ZX1Zu~ycc${E~TPOc>Uw=TVTJIU||f2 z$YXQ1YDFSCWZ4p~m_hs6aHIJ9SprmrK=mectYiGf&2F9saVg$((MjQ^PY)(yH1Pj6 z`)($pQ&DL8=l>LOc&vGswZ+-eMRT+~$Zssy2CRMm*qI}_1FYq%u z@!Pfjyi3m-d>C*NZg3&n@NHZxDuSCt_NV6}yMTJ`pXn&u>Tk_pYa|M&)ny{@vG&rx zmI$+Umr)mZ_rlRtH^iI;tkRHA7DZAK<>%nAcJb*J8*Y`aW6N<)4Vn0?vNZ8*IU;2w zjoz5OlBO*KXzDH|O`&In!OA3gNt{*i&OuzEvxq^1dM}UXHaB-lh~IRxMbF1qhEU=p znUt4=m-o*tRhr0N;{TwtegNzyAjg{5babYB`^8JG{vH z;{!1G8#UqO^s{4y1)Z0PV-0sh*dDq?rCH`j*iz^h*(v1S;KOti8tE}5=d z_+Ffm#J0t^FlX+u*UO8g>FH_1!G(xnA%OrjWK?aC_8uwD)|V&*ZAjKGGN5NkQ8K0} zENqy}2@3yt%$2K5Z>IBfVE;e-oxTMJrEi__g*UoHpS9ta7+AyYe7^bCmhtYz;an<0 zifn5Yrp(Jm?paf5p1!KnLT0>`GQZe1|IoG^p;U=E#hlFr(tDoM@7Wky9w5r=3h$o( z9?aaNd{KlW!OwA$Nu;b=N}YEt`+1JiKbYq?1UP%9>48p2yfYQ2PAXZ z4SHuH)*C_XP(+mNL6lFw9fiTNVx;%2Eh_&m}tz zwB|_$@@fcH>sRCaX>-Jyc5I689X-#_4A?2FtvInbLYuu*hVJ$HcY*BlGg%&`l{!{% zt*ILq3g%4IO)YA^(Th6`-8{&ilK+-zVGP`Ti;Vjq2-Qzxj+huOuMPfe*~nmJ+`v~x zo8rdnJG(f%CskL{`eAdvl7D~AKyTBs8nIQ{oB2Hz zn*Cx|!aEb9-Wk`sKsltB5=9|Qe}7H6pG|Md3fbg+r@eQjbijPhn>QMkaz|$2Ios5T zfN|6sJ6>pme~PV_-rSN?pgy(W&xX++wEL0CbCEm&ep>Fz-KoC^U9$@J;PP&e#=?6a zJu~B-BgR3p?IjENe=8vnZ6m^K3{VW(-DRB1^J?-CH!u@fNG1Pk`?JnA-BM)BV*wP%haplz~oGME@|T8j3#XhbF0U=F>6_% zW(N9#ll8*NcuD^K>*W-zPNU+x2kM1>-##|CWa$10^xQNjz{^OkWZRiuO&;2tVz=ch z>P6HPoRY@ILhXjHu?7r@ddBDlIvM^Mafp@s75X5u6qNP5boQR<%jG=e#a@DgQjI%5 z3_(9L=k3G_Vvf%BMj01cw^xg7XD$&v88E`IDUf`72iz5QvPgT{c=cm}uop6PUt*zA~W4!+N_bd^npr+%|R;J5yGb(U}@sh%$GFCnEpuC?^XG@3=|5g&X z?*zn|Zlfl~KPl?POC;ZoWO-6!+bEW=yYjRG;l4L2^@vmiY>tA|byJaV{Bti0{mar$QA^HPL*j0yDo?=+M*DnPMjG;+gcjFO3p!uS;$_tV>_hhyy+=A3f4W zdcj%bb<(O<@(z1Xzb;VT68=H=S(oa*jU*d}_OGvudw zgFHzv?N+?hCf9ERLutkb_ui&!8z3QCai3ZE)rQ|L8AdaGJ&C}luM(Q^wm^wkX)!j^ z9rfz~VS4S>V~NBm3&9A~c)+We$)RxAFp2xaa1 z^(0J(w{N=zWUpr7(6TTOV=?ul=McDOH}_7Zl4h(XB7;n3_eeU1JlID;R%)1IQ^d2B zi4oe}B_4(x^A@=G1}fOer#W6FY9w;{r)VI0+pmWDmhF}ji=JA*U3)NN$)N%7@?a6-m2C29!A276F&h+)lZA5VEs-Mrz&u;L$(l_=i~&f!c3+1Qw212YbUEHG~Bv?$PO5W?|}-s(EkqhI)QS`)GPR~(q>E-uxT`cbuufM_YzFp_~dOGY=cS@ zv&MRtHFNygT{6q-$eyG5Acj3YX}P^Ki?aMHZa?ZU4(&bf_Rn_3>!uf29!sKuUQ=(I zl6(XRksjMDcUta9mHSmePY(V-cwe}YC2A&HxNj6unJ~x8B81!NrAe*{J~0|E`Jb`& za_G33+$-EJ4z+~~!V;kZ;EVBejFcvhH0$ zvF$C^EFgE-@3i>*Wq=+-VrAm`a>PNk4xh0W64RB7@*Ro1+O>;;W;L*MqbNJi+7FbC zoy=G~;9C?N;E`O#{gxtJf3%YjAOrOW6S4317|qqs!kErR-up&wIn+{6yh!+&l_zmW zi!1DRRt18^+RP$BV!}A_&sTD8rK|n61IZ=I%P-N9{dfVX~2 zW{dYmiuS!@^YQlHKaX~EE6-X;1Qh|r{I6izN>2{)yWg7P_y~7r91RSR*4EZuSeVd@ zAQ886E2ISOn^^ma$zjd~99(D66QraY_=DF>pm_55^{3}*-O;DhPm*g4^WVUxkr7~(D_8N}`aYYI|L;e^H&ha?0e4%q_#pBN}v>0eU059Qj z`fwHu^}$ec&_}b1q5ZgN8lyZRf$V z(_jS@s6fGcTvbakl@7{7a-|oEkqMcxgr9TLw z7AdWf41DllB4k+;y$~J>|7^h$ZGpQb!MpR6U{Lon_ zmsJd!Ni7y3sgytTN2!hCndi$2Zayf7DbgT*7ej5hE*bg$6I<*d6qYrN95cq+2YIg$ ztwg6E)&VaEjO?%T{aHWO4gumbv|fA-Ob5{z+=Cg$(`{PIEmliJN@PP|WGo{L7lN6e zSa-WbA6m2h1zHZyfHiGQz?C&Sm!UF#Ov1hk! z?izn@f;5PiJ%T%==kBS*wtIFSYRzdmhV~zIlG*B#3bS_+lZTdhr2Q3dciwGpnCKM^YR(#XZItJB#K*stD1 z2E}mI^O@Bx>iE7~!1s)TPuP-)Rgh-JUod3S8v={12o>&akT3GQ112>a@Q}kt9?LpO z9r~r(0E|9_&IAVs&m4=%+z)z%66Y!tACoq6X)!OIh z=Pieu&zvnEEx9))!Q6?mV2D~7c-6g0v2s#x!pY}a{Xa8M19e-yPIh}kOAkg936KER z5wJb=PMSzs1sxDXO~q52h67WvAVpD|nWXf~lFtovq-qm;dwbGHgnzX# zcRBXch`k3;Sgb( ppolieo?-#G(+ve1e7m6%2Xur8Bb=O5)BpegKpSBI{|I~b@_)VVBlZ9Q diff --git a/interface/resources/html/img/models.png b/interface/resources/html/img/models.png deleted file mode 100644 index b09c36011d540759da5326ed70bda97592e9636b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8664 zcmc(c=Q|q?z_k++V#nSjR@GK*?UA5X)hKEfvG?9ZklNH9MJZ}iQhV>ccWczFtxDDA zxqi?4{15NPbFOpl^Wj8kzEUC~WFQ0p03<5P3fcew5D5SP_91ux0AL~YWjO!_?@}CwG%5*|J#)=hJGJde(nViANFk7W$7D z3`;#tKRO_8 z_VnrOh9JpmS_!{^fcF$HR1Pvn5K0p)zIn~kw%97(YC?tf6oaKtT0rF>nRs2{jG@td zAA%=9Or<%SmF05Nh!02rTt_kPY(Dnt;i=-ccNNN^n^=HlI_~rfQF#Dd$2$EI^UIWX zUF^_8QJ~4#Nk0)P2T7gg97k5O!O_qs-JIj9S`~v6AR%+b|4@eMVr0DT#vxlt=q)UW zt3nw9!wm0J*nB}uE$bD=qI#k+0AvY;fE$fHgor|q0|Z5;dPl`z*#Z)_T(kRafe^UI zv-bj!S~gB5GIzT8Jfl&XXo1-ohHU6Ol&Xs6_g9f!JT&wPQ7EX>?XQ9(-6uIw046~^ z)QeKR{@~!TRw_OY1m4(N2Tf&>95r1!Rsjit{xiI$=#PoO#{Cab6j{HhetD;vz2r{!|DaqB ze21);ovzUTlY_q!4iv5;fI?C+&i}irt5?z1ixWPuXy~dq+^r{thK8yc>#9F0)!@F* z?Gd0unLd$&ba7T?OU+oJI$ekG!%Gfvp-3pp`${(uXK|b?&9sQ{5KRm1er;DXoPva@Gb-Pjsfgqn9fntM< zP2Ie=pN-IJOa>u0QO|l3?q4l|dqfta@uA4am9ndbt!@1sh4cQ$O*;&?A@Vy4Z>ox? z{8D~*w_Bp2s*uj2+WSRAxx$^dfq$oz4l`{&A@CK`O?rXHXt*5Y3ogtcmR%uKR9inV^cslOpQq`S>+$44FFY#BKgOO zi_t`%`-Fl)vTGCrG(gwp-5AYbAMyXteYN+?p=#NK42Nm!$X>5T{?|+_WYBpEbWdwe z9O|x%^A*3-O_T@?=?8p>gT9Fh6Y)Ukv=|b)VzZ!x{hi{yym6i0M7H#bKc;3Tv`dV=?fN z_1y_#QuZ@bPmfi&*pVZOQB5N4v_O71;QES5UD>GKaW5@!6o9$Li{M-t}t z*EKx>Nc~{fsAof1zOpgfX(fi%gQ^w*K|a0=-L&bJ}??&=rzQBm@bd{i~ha-)-}N@UnxKtlrtO>Gf#T zqjH_>A7RzR^QIJuOE1+8<0#^%?NiVdVL|`C<<6l9Ld=vY*$g_KC3AlK7;S-z!7~b$ zSljh3U!E^e$zO}KfBfbsNCiD}I`Bm;9r?ie!&-%=S^_w)?7WbfCsxqoRmBV`-J&*9 z3#{2c@gtzJ=Sa<;D%)1(AI8%_ais1 zxC3J%X!Pin)-SESreXiR-ncB|7>lLP@;pU)|1*hm-o{b9?@J(kr(dFzEyGr(vV{Td6}ylc(-coE*t=L%eZH zNZ}aex+qEIyyYzeKz`c_eFD<5WOwhdPw(48NHD{O*cKnkhB#erFPz?G}|CDh0 z_bVG}Oh{h09OQsZmDdLvzHIj_mt2os#}L|m9ftKTo`Vs8nS~88@Q^?hhj{gaHIMZy zbiKBRsUzU8^Qlz5vL87GY{v0OOlqau01%Mqs@(g(#q76M=mz1{v`KbqT)07S>s09s zucOoE6Js-oDony9tif*e_)VT|fsHC5lI&mtc|l!v+#Yzto-T$4Khz z#uEMQ#U%q@$A%(Ve^zT-4R+f1?5JT1j<>h9H&a`yfaos}Y`@;Welk<2fXx9W=oK zk$yG?lOm!IeRJhGtV|NM98w~gY2GQH*OS6jlRd>{L!g% zqAdQq6$eHp!h2KS=KZcqimpN)xVjT^ro=KMjX4z{pMCvv)ZJz@Pg=3Sn$vnQpGP>!N9 zmVTaqaxaGWP4m~cjgyxvS7(RQruWF7_EULMe%H00F=ixbFHcJVO*`KYj8l1-o&?)_ zd#mWbkCSxnXcMjt4&Gd~PIgjKAMAxGY>9o^Vi><26J42IpL8I6^M&@DI#9(a`oS?U z5UR;HK3(7+MG>S+!>W;n0#_Apyt^EFG@E^uZd(6v)$%hZc=x!)s<(MQ4P^`!*mR2` zmA!yZ@ft_39;bYf#)yggt^du@7b3wn5VCVW#`2sKv(dbRdO`*4 z-p&2aJC%t8vk=fX$j@1qW%u!mC!2glleRI|uo8~7slIkk9@2b?)7=bu$#!(=+BGaK zwtFY^&`z8OrK)S1Fb(?C=N8T_Q_+sI6^h!NnUHZXxxYR8b7y*E$eG-bY=|iuPV7`o z+C3zvX3;g?p?P*6s1$6r{3knq5#IIfj7BkIi_e5^*S^JS>C=!ocPXB25Te$d=5sA!IbQdw zdcaK>S=z&hfe}1^yREwr9$B-KsN^m=_E3VKy?z1aN0X^{U@?>ea*b z)r1J9tQ&G(1kmR3zvh>Gmb&FuU{BJ zyM29qoY_Z2cIe!`=WKH|743yQMqjcEVTI?he<*$~d5O;G&gy{45ze6>=^*?6*!aam zg!7aa*uwaZD_`;C1-v(``2_g_GED{-?{ZWnW+)U}+c;g_uUdao1jCQ%>2|?yhe^Ai z$V7fVAp(%EKqO(0Tvdk%YDK(r78)e{03W$>-8cuQ>vIEXG@etv5`X_M?_|QTcNo8m&hwo?D|G220O};_uVkzOP`c?#=Emg_`hNz zTK$WH4=1(qZWS_F+u8XAzLIZ+-cl?)eWRci*`5N$p0;=CjQp0=Y_%k3P8}xT7 zifLs|i*>}2tJt8H*VZgzg!=XqR5fXZ?kuI|23aED4w#kyyZoV9g|c-^%!i? zT~|g8{tOt$tzQ5eIDfAxX!liP4Y=B6&SX0&npA+-0a)!KcXL9Yo5N|VWIfz>Y4-_#{KWYT?X z_y9rj6+z##ACo#<@OPwH5((A$Y#YrTXzT5*H}c#6SEh|We|(8faJKuMhRl`aS3rg4 z?-Q==AOjZHWb)-OxCMtsxklDVR_X>)SQygB+EPRi^ocshVl^XeAi`UpR6K-yjSqhy z+c&p&U+srrR=pTG)sR=wzpEctj{)W(u)i5P#mKarG|a%%czN!bt6vNTWe63hwmiU; zuG6A&;`~Q-N>x!`(34a|@l`ud0*u9HGUsqGRM_2EV{P0%`(tb`&iq*nzyabEsR(5<&?>)O~996VkWg33SBd0(PxQs#aD*NmCL7f zn{17*7QhCU)6eVJ7ARxz*2!1`Vx`}04N3sc=-7pS8_^Lv7%}OCUg149%VNNAsmSdT zpOLvLr~e$cm~8Z}X$*P(s7fi|s4*eJVaCuQQDsRnG-3XN+b*YG3~$hy{)M+RSGQ2Q z&nGK7*HXysGUbh2C4z1$(9z$(8occqhh5|L^_RaM~g1&bq$&J}&wD5G={i z;^Tt7zU8w`KeuxDn4zuc=LI(A0d!P5`5yfU5sJDgVed?WSIiyITYyupFZNlRK4o}K zd$Yh&CfNb*&=;Q^k#g*nU-c1Dd3yblN8MRhsIHaff;)+y?y%wmt#$l9(YEu#!7MR2qAji>P& z9VVd#lW}cO_eWY}kSiM3F~WHjBDum^Qk;b(sd(016{Tr!9Vo>v$KTbh;NM@f_-Cq6 zbAsDaLHzaPnA7C3=Qv&~p}7q(BHdT*nP6ju`%|r=8WwXtXf*%h*U{7pqBd9vjK*#4 zWiN1}VZ!fi>jcTXIT&_}R~C=?BbJsxj7_*<@{tyXU{VH(f1UeDRg)|PD%U!v!!=v4 z+jL3={J_RonYG7HAc~7ud}r~^mZZH{W--cje*&3Jporcl^0%_(UTue&ml8E|>G>bD zIi1R3DLcA1hg)W&1)6r~m@?*Qw}Mu3w#gq0c+b?^4RHkgJ~`78V7jTK8P`!vz~1^7 z{kzk#4K#L8QR`b`lH;dV>Qsc^z|3k1yI<9NJbK7v+ zYK{YOZ3gcX!81yN=rZm~tCK{f+7F2kO)1*y+6224(OxWZtX%q(a^9xo!=I&}^*&BN zWd;c)pyRX{mV*!#DnbR^%682&*<%?S^(y@}!`w>)0WgfB;&WxXfAT>T)q0!N0BOC& z@3%Hfl?~PsFW3}(x368`+vP|eW1kF-?+^J3js`)$+x{@tJ4+*=d|^@uIxI^DU8JYT ztFiItFYB)|J!cp~c44UTfcu>A$xWi9a#qGY3Xu&;-)ma<@($_&hT_ zTW|qE-mHMKm(iIn7v*fX?+_WT(n91=T0vvix26X4SAPmV{IJ@MluD|$FjPRiw`8Wt zmY}jd=WH#aA>sxk0PyY3~bCug|ki3{+v@7CA~2Ys$`!RIeQ9ChB;rkBq6OMt^DvW~pRz0M&1 z{21xI;As%AwF(J|?Jj6hr5Px4SZ=dW+EMy>vwHgP{=RyM9>&KIiDwY_dEujHt_NOj z7izsJ>c{+NZR4FxG=4^vOYAmPbvc9zU;MWJ;^@1`?OF(~<3v33s7@+9CJ}90Hka;- z-^0CR?1*Uga*E4EfK6ux_Oomlt-1|gz6M5Os{eTS(VvlGw?2x}Q>=_JM;A4<6`?hm zp|#<*3q$*Am!d|alHE(zL-kQ4Jb(r%nJ`c;Gjv^D2fzj{WN&H(rWrWv)eT^tXNyvK zrGCSKcu*WY4s>&yGEpz2(KGFiDC)k^5r#AA{-+i1iq7g8|$HbFp#FYK^7 znoFKX4^vNJrdxZh9fWtW_2jiC!z(IHT5P%~EK};wwS%5!AqtO-MXcIWbB(oM!Gpv_;0I$)X#JbHX{KUKr8b_e7>tBh!(OCQ$I}f-`y6 zwtis$j>qNE=wR!mv)22U>iFjJ`tP~CY{?>@(yiQNQpXHgmQPp{jC{N*Y%S!m&9f79 ziLv7YB!(FMX0)72oFKTgB1TRPQ92$~kj%{To}B!ikag(@*waSEl;Y^+_B>60re>u4 zyqw!*8V4$l)H0ke<=KSb_Tk*RgFf$GQd{rhlq;$C_R=I1=$VE(5b$$kanu;hcwHT# zhVC3QB1cF_&%S%>&$;Urg(Nutpih<+L!1n!o4yilKTREKRYUi=s*%+sv6w@(mFqKm zx96692BZPg*4x}^`v&wjJ5CK_cOzuMi{M*T93-#@Ow$^*%8{PQlJN4s>e;P*bLzM| z$zvwiv#}-1km`T#8iD*Zt&dvnotEN*J$|pQ2Ygi5OFNSUbRQs;p~fW!!BFU0c}HIKKRmmeD1|FXq9F$AnVA$??D&rsY+_5Y6Rk5x$4AxqciiCBN$=gR*u37dco# zH4HI1Kdn!C6QR?`M z=LA}yY4^RNT|vMFPtPXCN?-Z;dfk;l^(U1nv?+-kx3j0fz&kyHNI?OCPXT}C-cZu= zJnPz?^EKfws!4Fft2!%^Ui{g7Ha`BL{o&54a{}VxB0vEF6UV7zU-5iVcHBUI;c9IC zSUF2GVxH@flgAydjLV^74CTSjYb>hbl*jjR3UOD2dxc#kDdkiY( z3m->}yI#Lab9>FBUJjTeVL24{(dsp*$!*3BZHpZ#Cy!L8VeiDbq%S_tQMHSWTZB=C z*@eP~3?-ol`=|Dp{5Nx%$BFnXyG-;9D0HsYM51S49UV{w5y|18E+_R&atS?B&KwyR z@w;i~aCOP0;h3JzOsu1a?tAyS)4_dTVASqVB|T}6u(j`4H?iUgu#@5G$I-cU7kqK_`=!2|G+S&BK!5lniU9{fM1*l+Ho%{9n8-J&(wu|QA_IFo{ z7c9P=FD81!&63Smemt$q)EGCV`SJ1efwkvux**K-!GCjbauP>zyDyAHF*jvMJ8#pr zc#1KV#!rsD)oJI#lS2?vg!j2oQ(wq`Zo>&I7b{KE(i%qqL%q~fG7%10nfT3G=S{9; z<2ZLB`NCCD1iWET(?%X`%Y5pc(edS1o{}mrAr8ytMRY}nu*uZVZaMDdr02&D*7@&) z-7{$cy}ql2E|$#k1m(Cq`sr`pe7_wLoLK?}xx{m-yc77QI4HE}o(P6<;cPtZxi+7w za=Gd2>l+Hb>k~|S>wkCtDlww)s#IgftiY3DI7U9nOGd8NH+X4OmxJb%HTS7OjIe+B zXIEvGe78LTZy~Mun3b|WK2xy=Yxg_O@Ty3UlFzO%)SZx#cqs1HHH!exi554c5C2oI z{-B9E)*aBRZ$W(!;m?W7ipzGQ)uduw{tGy7t!x5-mg6c^uyQRVWhmFD^PgymLEf8D zr$Hi72lP~HL$C)StYxN1=9#Do^@6AmG`jDGwEv|zdT`#I{1Kpc(kmnUHxjR3aGQ7TNOmi{&EX-=ZcvNxSJ$jUGZP**&kB@u9Lk7Jss~#9*;^6SPPVP-xb;i1}Ms8tpF`MJ9#=nTf)AWaF^dFzp3C z9K}V=&66?roqM_B69M33QYh03E1^SMR}-q=EsGCEM09C^cbYdqC4jlVx*i<{%}fPS zV4$@!lr8-4B0wq-10oUjs8?O4tw*muF~jGi->hgnmshH}U%O6QWJ}RApMvR_??l#P zGAXAay;2KgLyZ|qYXwLcmCAgwI5BA_DGx)qK^3b&P248Ga1pO9d6I3zPzhcGVnq?S zh_C{)#i=nLL_pqUfVwmAdn`uiED51QYAV>EMe-^a#x~nDMeYU7L1||kZ_P0@oz>l> zma(Z6eo?3}kZ5+UBP zmxo(>Ghw@=kWhOPWH_NP6f{SNnSL6f2tjB%AsYEsS&=?+P@Ol>;lSwmp_yVWJ=V~? z>yVSZ;!pcL$8JO>I0#KkoN(6mECThL?DD$ zEktD|T30NjDg;&{@FPE{3#iQ4xgF(k;h{#p6byru(L%t)1el&~2_~qs2neCcZ){OY zUZ3_#Ui>}LAU_6H;tivsS&OWO;Dd>?c6lJ7_L~g60`6p?-A_2USvC`Dr>vl$IXnzb zxFx|SD@HIqH&!ajOCAzxkBbD#VjM%wB%E83eI~5D~e+L_VFTFZISLD8wSHPa01z?^dyhI^`{FNdA_u$=wo|DHGtm zHSnq6CJRekm_}T0Ec1p{p5k;+o2FF>e>k&&#bB-!3-cpC(MDJNAXl~TpWirl)#0yS zzxEuvg{*s48aL|~32B^*WsFkBen1&H9jp$o9&OFckUcd$ZE+61_Nv^h_Dg9H6uwT( z35n}cbGjrZCQki~Y4JOy39QnuaG*i^-@9~Ix^X{uLw7KIW5FX!zplY;ZKP*^qRK>J z=V$BA!QFGAYXVYICQI*Fg{czqOTvt0a2bf_(_zo{YSC$QeKcgIDh0VNp~`Q|EoVp@ zwqzry?ENk;PWKDuebPn=kQB-7x&dEMtp}fTb6R~4Z_g(^oDsv|-ePKX%f*^nbvM2p zEeKP4A8k&lvYnjn;|u0nzOG5zAa37HMN~kK{Lh5w#c{dD=O=sS2Wz9nR!&X=SH?ij>F$l@4bqS8CeRw(<%$XJD9N=*PspJ zpUu7uukzy0YuQdasijWXu=a;<`}_Otyeq(oBDa(N^ao5Pf3MDWh9{~_n&7#N#n;f_ z44AJP%o%B2od_{d+L1<=P6>dyVw<92>b&o@mOe}>(F(_v zjM@Lay0rAbs=eNl)6SSP-C_L&=3jqWGB1rY9zUJj3F8kLI6HsjcQWL0+xPU(a`?8! z;O^d1uaRqh5QM&t%t}kM65Xp>ub#HDOLQ+0vMn2*H5CtWUmq*2*K2}jhhj$Ax;uTd zJRc<*jCJ0c{#ZYu5%>P4uijlT3DSHyHI1s5d|f`3Y!qQMTINn5>91tIr;B(WZ4)-h zhz&XT$F76p{@D(l|g|90{TOhNhP`EiXLJrd}IQHJaBH zSXRf3RJ>!YE-5i9?+4!WaH`{KMf_~KCC}2>X0F=g&5n<@W@bZ*j+Oq#V)x>tuG%Qs zuo^u{qE#gy*>f!A_dRb%3fa0_>%chO1~6(>8|x3sM2XN~Y*Clol|%ZFU*6ee2AwpA zL*t_b56Mt^VU3r8gN@RfM36yZgt5v*-DT?HM9=*AD56!XO6FnkU8U zHI2yiC#mIMX!z`1TxlfUw)rGUe{El@{y~T{e7haXEu)Q!0SM8n*2=S+&3+|p-)`E} z7Vy+CI48S~9W&{qjP4Lf+iZIO+cK*-#CS-tPBuDVSg=A!0Ch-fEzUs`#)U#62E3nQ zhSm%`^o3FtZ&SSKiOtJwvJKs`9#du)nOrSW*Ox4Cy9prOV3mCJ50r7WTS7GA6?6_C z7BLlq0EqP3A?6H<@qy`ZIvhXx0@3%wL!Pju(MdG4%TxYPj`-Ocs6GTBVS(>czkVHA zHSY3>d3aMAY*!IU%O?5|lG;L)o#R^Q@rB$KtKpM$_TpY^m6)_q=ulC+?!~hQ6d=;8 zZ~0p)Q&H<08YBeY^fbS{HF;IJ^};SgSUt5~#wX?pA(A3t=*6w~)_S&I|V+@!~x4H1Hhr@R{B0Amd!Qr$&MmP}J4c zUC&0bq>e0F1n%#al>9ZU5aetr{=ygwAt|!0k7{{eX=Yx7&D>%iec z&C7p)mGl;P)Dq$J;d@(KTkeYK7ktuWefz7ei^a*wN!5LUnZcd12#w5?kz}vA_lAE+ zv5*3&Da3d>gQ`y1G%>gl>@~T45ZpZYyzgF)6FD3MBBStR?req!ZpZvw=M%%en1CZg zH72-)1BApT-55tXt691?_64|O7Ss5BRx%g{0#u!Uvt!@uP%sR1T2rfe`CpCpUD2XP z)LhyA6p|XLIVG{*LSnbF^VLo=3Y(z7E4HlXM1t?QiMnMK%&!LqWU->b3Y_VjT`_yz zO3%8|ZNJ8tfObRcnkfg)&@jGzTbD03EL!gMJ@9RY58pRwh=_<9Z&o=}>k1pZ#jw~5 zI|s$ZoE&mkGBYu4yc#!%|e!Mz#KHDZPQ7-mVv=5!Gw2t_+%HH{M34{OGUlq^nTDtc^2fhCU;y*mia< z~PUn=HH}Vl{&%8BId%uh(JVBAw(hnvjg6;A2$45rDbcO4>Lx%4J3_g z(*Ax+Ug|j-QIq>s1#uB3sKD@ee4$C;rSLogZ7;(v=J+~?@8LwH(JK*gMMYZ2pDh(k ztld>&0vgdPqEuxSRCy?4#)Tn4p=ih_i;+8)`zdna;&gv_AOC$xfutdRA8+8m{(4mj zwlmvs-Kg0|m|m(|aqy+hSDdR=38k~u&F-b&eThsnp(NCcUPcWr-C>gl?)pxlG~}j9 zt^o>b^9`=}#n5N5#rhSE^h+sHPWKf*NLV_Z>@I}!{_O96oOMflF^z8yPf3&vZ_B11^aR*H$9ReJ>|9l2#PX1)wJ$_6wppO~au<;M~Zhx2M1 z+V~~pj|Gs?+*h1_Yr0?qYjZ4A*{eXM;EMGbD_SF>WK_?Qz_BowAgH%j0$-45lPrcn zY}mhaN=b|_si&8hmnZi`wV>59bKU5gLG{oG*|ndQ1!dL5RJ~`KNNwa8DL;Xokc(~Q z5WOZ^zEV9?-|~0-GHB0|-!+s^yja;NCC+2DJGP?n;|7`m&HN74$CQ7d{&Q5xk-ODE zE(|8ddpO?^OH8^Iu>$*N&Qz;5J`VNuNE5v*k6Cev4dK0keP3)hAyGl3M_J`&4Xpwl8PL z6ID6lc^OJtP;V7!sq&t9TJh}#L8xWI!1JxT@TW)us|jY*Q~!5? zrX0#K;VVOu-emaU+S=Olr64Hf2HmqU6q*qscYeI%=)+s4rnxNaMld8@9xb~c-k3%d z&zNeKG_G$TCWCQerc-YeY^X|nba2)6PJAqA>DQU(t`eABP!ciN{>{~$nbb7lhsE12 zZMDoM*%c~yM^1yZeSO;st#`0)GlR;gQv`_Q7nr0?PrBh538jE^7^mQ-OtwUtIy}|Z zeMxDMTwx>nV@nFH{jrvX)}kMeZsSK-C<%H}=fkI?UY032ccpc-iW+wc=c> zQW?0Me8fASM#82+=qxwGHp{T3<&19uI48t)gAj)PzNj0BtYg^=bq!7AHC;Hk^rC=?2W6*pBW!jt~gS*h2pK z-O5f|ClR+*CN^A*ij3Kz>l~&c9?rm5H&`AeMmd9MtE(ZE9P!*?Dzn;^Q)y>D=a%fQ z@u&IEY|K&D5FXg^(-Tpo#Pk}*)8+n2+O4}8{=So@cI(*`LY9MU5_9*k@2}xqrymF+ z?%H5Pc3i(bn79K++~s+wl7kB$3?6;cWs2(faHDblVU6w^uxPO`aV~Toywzv956^=@ zHf$!9%>c81t|%)56-BlmLn2Yb{PJYU-0yP^W;YpVqwnV)rf!0i@92z)BQ%UWq{Q4; z5H~=3f6VvjasR+&IX87r?{U&rCnW_1MS2AJi*gQwn^eySmRCnKRhl<1M5;9-Kj;%Kilc!c=ANFO zvx39rxJym3l*sYILj&yi3Lf7eNQ(xCbO;c*@rmH4Di=37qZ*3`3*gCFBz}@L3OG7waAX1b-DBhV>qT}f@V*e#l~jz# z)KtRULc)TWpDY$3P;NXZ-y=J>?b(LavC=2QE&k_*Gp(6QEEdp^0vP|3G8VJ3Z%x_Y zck1G{(x1NC8A1Iopcb0<#&(B@iij{Y2mYOZYm^;L*svfG3*|`$TSd<1eo9YIU!8CE z-CLS#@+|zGE2Yrc*;%=TSLCULkgzX!V!oLM8L?J|d&ZcZ`Q{lf)yjVXbgwX!_m2h` z_!JAVvix}QbLey4r->>P$I~1{{%n4i6Zh?8Wg&5Krn%NYsesm7t^`O}(4Ej3(tNsR zwV{i}`DR}cGJZ?C04&66*f?)@u{Bc{gTK7Iv`nMgZE0sj!g#(QOO@|vb2?yWjrdBp z*Vorm+?sm5S~sbXFdlH=Z>eIz*eA_tzqOIKDVV!PRyds!Eo4223=)?JypnLp)jXWk zjf`WFx@FWFAW2{U?u#(mHG14=`e(#8T2@#(SaEesv?50aXL@ufvEzEMI(*`NbA=R+ z2EK%?oP4mZ)c9c{_W5(N#bp4hTh}KmNBp0Va1Xhl#F{*&tq*+H)&_O ztzwmmL&`1NM>L8t1fS7_lke8w4K#jplPMZjrk4f@vgBN&=yAr4bMpIGo8bgtdcvR- zO@s!2(XO3h2H<5eTtRYhHla?~FV*#0Zv@}Q%f~FiPqb;|8 z!7cOHB%c^U1CB#kd1M?%W|T9-UoX}3gsn=5dEiHjWcp$w{qVBV#<^EwKrEDZ@aEor zuCdH(`6?9-I2sGJiT@_d#Idkqf%XQOIVeD!BTc*lMSz9c#5TL>lK!lZ=+7L2%0hZN zArPTgMDtXl{i_7N48UrJ3O&)(T;tSFPAmj4 z3Nn8QARf(&gb06oh8+Ti`Ln-h(I7D6%=f3M0L3`Mavce?`c7w4xof8Vns}o??h$rg z1`Flg5HwWH%2&UWap`ko`X>z%w)*%#5llTRKkJuOWS{~T%5xReX7pxrsXMLb7o;90 zWy1p9QTb)>T*3iQ8I>N;x!>PD;8+VeW(y@*xjv@#ts9p@elx@K_Cbs1X!`o zbk=4LmCB+3t2Xk3KgJ(R1fc0Up7!krbPxbC#XZA5BoctM)8??$bywHFTqc2kpZk4Q zdj_xyDe!Dk>N`L*0H_0Qsp`u|i%Tdz16W0oLTm34`C4YUU#cC101|~Zb=36gtnYY) zCIZ??^PR(94WH3@ER!*SlmK=W46CdxC*CM?4ts(n0Ya{Kym9ZDPqrP2TmX@TK|_dq z)r2ioLwZOS*v*z4XfcIp>|=FdoUwJ$iY|df!0u0YQC9v6xjm;|$VoWg7$B_?E3iz# z84xJH@xwy%!A#+sV+d#qL_Y4)U=zJ*@9;@Cg=;tB-15S!Wj{pDw diff --git a/interface/resources/html/img/run-script.png b/interface/resources/html/img/run-script.png deleted file mode 100644 index 941b8ee9f13664fc2b2ec51a75273ba3e553d3a5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4873 zcmcK8c{tQ>zrgV?q0!GWLkk-FGG(jyg&5hUg)+9nWM78L*s>=}j9nB+un6J+0DzWuyA%KbLEmeZzGj{-z5x!8(14D!rz2WS-`&9#ZHjhq4)X3o z!vTP=R3D*pCvbE*R}%vr6pHSQ<&g3XxyH^{wH@UC!>xW2H;-voM z3h|)4jo9HWC5cTvJyjG`L+g)JNtWJfOe^clAh;x}4Z(h2Q*_h_Jm~!{;!UuDLc|k` zp&mD&5xeKQHNL53sY&Px#IRX)h1wY{%a2%sJ1=!WAgwhu2+(!DKfVoruoP(XnH?KaM>Ki@gK|@D(9lGBm95(ku8y-BI5)xsZK5>su1 z>a9$bT;1_1D=N`|SNAo6oR{b1ynjS&s+67kjJ%HG0&pGGj&uW$exv)d85SbcJ^DG4 z@=__(BkiWI`Y^udWc~N)h>)O>NzWnIiD2OhxJ9t)=AldWvpTxW1bI#kNP&phzn7P* zqPUC-&Xx6?fUSTt3lqIAfYbT!cgp3Gu9}G!MNf>KIK1jEdFAT67rl;OJY(|Z06_pR9Ae;FfVRJcM|euweT5=bG$caQb; z^|gFc3s@cVfvG|lFH~JJRJ3jQS;hO=^9JAGZc=siWn>~yydmB_eD>c&UP?tWFh2+#EW~WQUxFq!a};IG=5a8 z8y#q$V3`<)tqN7M#1i z1MugSA=6hio`Pb2p=)ZnuT?CNTJH2A_ajZw_cDm{v$1R?Q7rv8Zn%E+`*I$dsJMad zqP8T$V=r=cPsQnZ1c%1@S)@ji263V3#8rezdRAi4Zl>|$Zp1#LwS~I3Jd>>T{iC|0 z`IDW+!4||R3Pw5JJBQfHJy8Hg3mjT%qh%H4p0iOR?^4|2y@Zd5JPCp_tJ7+Ig}04e zM;Gj*C9zJ38IA*DL^r&$GMV5Y;_%?0Pj#{e6X3P3OXNBQLyyH5h~DTjvKV_~l=4Ub z(#rXgXODW7N$^7I2*0}rLkJe~%zA048VkgKMopRU7_lNiH*f{{xmXf)mix)j)BP@3 zK3Dex3!ZvRhbDt}yO3Yu@bPqUFmS`cG017A>*YSmI!6rJ*Zy&d(At5?hOp0Rinbbbqznt7A=Ti(HH8`0JqfxVbc@S@6lX1{iWgsfZ+aSaLh zW?AvurT1WqKFXcS$;rXC-V)blI__`UK~={{QXYc4eH65W4%8_o5e$IE#s5Eb{wMSy z>k{Iz_^7C;^q?B&^d!_MH}kP3smP_4SX(;+OWH}66ckU?i=g#VoJjJQ*QX4Z1HJ?V zW18i>3Et4ozP8Pd4t`CPopp{juL;QO3w+FNUdJkZr1!s&4G*+&@kv%2QcQ{kcbM|L zm*mk2J~=1s4K@5dLA{7F=jIBSe>z4!c1B<(7UW=UEx{%k$L@`>mj^q*eL*cZ9d)2* z1^z$?@Ve_>d|?UhpnVEb!k~3|Qgy!@mxrqM~3eRtJ53eQ(9dp3+Pb zC$p&JnPBLNn3*}e9daUsF+SUysT}#E`Ebu|bN=(IBH9x7AhLd3q-vw%tfy4i(E;P- zGtsbp?{z2uIX$5C$nEU&yPAvNf5JqC`KDq-L)E}9^^}5-4j9KJn0reVx2~1AYaMJ4 zwD=bYP;)i#PZ;9?$HaA2R6Qr)>l5t>XE<+Ti#MXghjQogpZM}E``1(T`)@ebKbkXG z{eg_Q2gAKQs@*LJVG(PEI0x;D@CzSy-7w>k7DS&4%>T$j%>Qdr#8E>ffY|nOa#@UeizND;dvrdm!3fixH?ic7Rt!PpgI4tZmDIoij zhdl=-VLIYjq?VCHCl40GP+p+(k%PAPUcycNE@apdjIw?{ysgInExX*M!m4*3(Tbaq3Gxt zKtf(d;e0Z$5BaCN+?!wWoR1~s49xbM=dRmokSl!pTvE4QOV8Ii&Mpp?cJXTMd&%Ql z-Wpbyg?MEO8w=rm=;@Wn$5vN#M@t|1(^Ac*>&OHJS>j~(bHAa(^8Ahuw~U2$3#FkG z-|&<@c(2Iiq7G49V^h=N_AjlYeM%QZ8>^SzxwvIU_S3$-J4<(2}eqV^aJ z`x{?Z%_>Y4^pJ?Ac<_dUN{Q~IR{b4Lp$)YK8#M%KneE)$aw+Tdd{ru@CaaDA?b6Xev1e%vj;6O(JG7KmweW=B)>8|u)CYKF-{NBG;_7HhBwvF7N@6Qy5&CiHw$i8c_IU~7C~yW;5ziL zZS+c0KaG~4_=Qfs!%LpiiX7OXOv2dlJ0q8(S3QLY`N~uEOPo(EA0*$6$hvcvWFp#$ z1wAx##~ZGWB6K%g(O*%hs#F<74#dF!x-8usS*DlXjzp9v8(Tbvlh+QYO{%%GIf$+r z_i%%;VDi_Y$w6Y+%mv%;i+Gyb;fJNVPgf;sgIkv6 zszVH}Yi|P2DJpWr$INC_E40}Nx$0L~_{Ld3r#Roxw!dOVK#5M|H7#qyCJ__!DgwepN<*1*9Yh2 zAOvmCj1?zj#bYwuGjbxn{8XEx8%1Y)uG0-}l!3kp6+CN-_RzI4T7JbZ;c$BPJwH9u zi6MGB?W{#DR~El?EEoyiVbmUKaOax9n!><)6!ci%s9sgmRiERvWRtiR*W5#{!AnVU zIIf+>$>6m{9h&yAbb-KMf9swM9^*6u($Vh8M*EbeaLx;Se%C4CqP^Z7zXuw^^N)?_ zOg>+y6HbUrJD$<(vEYYygknN0Q4B?sjzX5tLG#L8)SV;!a1=v_hkEl^V%8ZD3sgl; zUfqiDq8wv-SGCs7cBGVA&1;|~CW*b*vp!9UG3vRO*6p#{^4H9Wl*ThQvh|>q z1t7*>^b?%k?BJNY1hm0gciP*VEJUafJ|OmzQt74|sbXfN@E0$P6>)IaC1o(gyBaRI{5 z88~$YSxMZArhy;;0uLD_81p@`xAVwG=kePzXw6X4Tzr``+Qju}XXpCuuXpf_u>CM|4;wt`CoT;bwtRi$N>O= zaKfKC2LKW<003N21^|GuJ1)Ni0Dz8h@QLw^BFDr9MUw!#kf>mimeb{+P|`V4P)Gu; zhlB%wy`xTN?7ZS9KY#e(q3WxWI#aAaU3JXoWE#o=P(;b+v~VaZsnLKf;Mbxa#{gSU zTC%oAXdiU8TI-7FZmB~Xc>J3^cV+P@S5nGi_-4Y%C-tM>zAlfBUJEt=RJiBu0T4<> zO8(zqOGq-6Zg}$H!w1(y_F2CZ*FI^6FT03_yIyYh^A8MLB*w?bW79zBhb(_ba>oxDwR%6Wfg zI|`;7n%hKwrx=@yS$2g~ol3CmCW5f`9Z2 z`mk?c;-S8eR?^?C^j)rVqumy@NGSA!3C_~>35q^7z9T8c>ajMxAjHyEcJ2}5vYwUJ z25g8@wCSrgVCT8b1}JU=HyxnTe+t%>+H6h^>j)^BdMn+ywbpo1jL1NuvdgK9>dlvN z7z$R9LJXNH=@4F@;BmHHhgUk1-DY1L$%GkTW#5bVx?f$0hBLI{#XXCQCS8h3Q4A^d z<;f`Y^sK*pMjn}4sh@^wE;nK1?NTDQU#hzh$A7EI)XOQgUY&-)@Vvv!`oje&f7FF$ z$wzN}yHwXWYpU_&oqCNZ#KHMf;hvShekBMkBl<0kWfNp-vb)r7HU8v4?d9bd3?yY2 zapWkwj;O=1ieU*{{i)(8`JBMz#)FraL!3^c+F2YC(BJT8+YJdbRa>VH_HpWQ`P`_s zWT%>0Y~w@^i=EIBj(xJ@2tor{fojS=Yjln!e?|C8J?KW?M%QO-0n%TdA5xWW+rm462 zY{Tamat{b8FcF>Cdr0T+EG;Het~#VR4LnMFQZVoaekfK8%Ii zfe9pwaRX6xy3BsUM8#^>t__U8LQYpA_#AXAQrLf%oWa}rtV+*g4$4eNr#$e5V0`(V zfv|6cCgt#Ag*Qw@oY3SDJx|Wy-oJ?|kA}@wIjb-}qo9Kpl;O)Ns@Gjny>DS)EZX8{ z;=!l<$kk4yjZOOF!sMZ{<@+#HR!25k;w)W>%lx$4bSJ6l5`&({HOhL2>ErzD!MudC z`fsba8r^@weyw1hHsOFdg~mW-i79^3G9lh!-|Nx6rYbbw-z@F$9AGNEQZq%t+&{7F z!*|%*L{7mz7=MF&>TV~tQ`X-7f`~!AuOieRJU1!wq}jBxPY@$^o_G-B@7i?FUM)Fo z(cA39`IvubBqJ{l4BP@V6#Guk_hnP-9JJr|{+!Lj*?hnK2vVy36T8qTrEJX|6dHyo z3|>`7k2d>xG$e+LZ)%t*{iFbUqg&N%Py37>WZ9cYV(1m7X*u5Bo`YGod!fEVtz2t? zRS(?FVYPaqD%|7|KO?KhZ*30YN4_7ca0iP5)RYA=m5G8Z0$7x!xtVh9OBQ2~lI zSrcQZGUkt>@*28xRs+h>HXxbjWGRZCdpH(*_N~Hc!=pyB%g{&Q)_P>{UYv`7rFkBlpl`KpZ1#5X z$^s%?Hl|jOqh5QK{*b(ml7z!R0Fp++f5%bo33~t@kGX6Q^wGW+MWD-ga~)k)_4V~U zg1^`}dDlCURq|?@{gx@L>QacN5S!$nkm4|xL#VoOcj>E310qQYwdKb=y=`lhXs~i2 zyWGrt9{-*o8kv#NiElh39Uvoc?5EfagAnO)W2&e1p1HLLI^zQx%iu15==V*+XpEz#n7g;dqd1!{24lZS$JzF&Ml}Y}^J{N$yEsSm+ zASXFh#am(KE%w`1tOoYh+3US*FgW5hrj@Br6|7X0)`}AhzKl`ot#b{@bi-rjTSS-X zP@m&8O=Fv9Ae7Z^O7_saWO~nPYEJkV1wYel^Oy`K*Pd_H>U3Gv8l>2PzE+HMwe~`& z^X7c3qRGA4Sck3HGkFBsi?S)ZY|Mg#8I8m=vTmk^p|g?IHzyu>0L~ZxZkPYbY(a#C z79D`Wf6h<~GNrLD0=@s?Q%G%Y!#YzcECO_dL97jZS|~1&mmOLwr;Sm88}c*FJDam+ zjp~n|fBnjR7K%^ReFL@;Qp6p3009wntpVrCf}KMiCO|HIvpy;TbGa8(6(3$Ve@*qq zJvhsDWxe*am%?_Kh7@u3PNxhp)& zGlKJ1*G4diHKvDqD~tDT__tH1-6aT2lxx<^b16OB3){z*^F_nN#qj0%0|<8PM=iOb zLZqv-+1<{ugjk=sA`WSC<;!Q(!zt)~p}IynkE+dpa w8mdJ$F@o>^$TljjB#j>>_U`-F!(BO3fT`8&WfDTw1ONbVa&SFU2?wVA2MC*sc>n+a diff --git a/interface/resources/html/img/write-script.png b/interface/resources/html/img/write-script.png deleted file mode 100644 index dae97e59b14bae50013b583cd9cb3b5bdf6ad464..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2006 zcmb`HX*Ao38pi)h(Ww^WC~;|$Yo=Oy6}h!H%2>+{#aNnJBh9EKC6-vKrnL4lqlR8f z+CxLd)*4blsO1QWsHU;S(vqMgmZY&S_c%TCW#-;9=gheuo-gltK0NP--!H=jVRz~) zwXXmGIOSk(?Fs;Z5CDKBS!n`!Um!t4q*Wpv@1Fw zC@!KOZ4Ll3cn51scl-o@sspQp0s1~_21*qu6P+5Qx8)(@64A${yN;nQG-&a!vK46|YxiCC+UCW# z?78c1pDn_!(s`qA{2QK}Osh^O6w)&nq|Y#8K%!6GZ2QSKTb`%$ z3fZ>{+WbzhbKE8?t5!s}?(f#PZ^l`Cy89ua>7VGH@y;Zlu%JU%-FpS2``0zP)S?M~ zYMcE+3MG7Uj=OMBeYTuBMD$bP8*mLO*t0C(S41!!jdI!=H4CS}wHew%@uOnH0Z!6q zos_8@nng~FU`KAV#e8;03_6sDa8IZg`}c(Z%?KG9_pr}Qda$=!#5pMW2uHg4cCb2Rtjhd*rqmR@Ph0qaA_#**$5H#z#5Q?MJ=)Ym$FZY9?% zS|;<*8MQ}Nr2o58>dbfJE24Op%Qi~X%CMlTv`)|WljBXHaM;DiJZ4^&B916Q8tH`B z-RQPdyR4W#97+6;nfFACb8g?Ma&$S41a8QYlLaXlK!T2Pm6n`mNlVU?fJ$0PA0<^z z7IYK^1HETh;ov5s#Q3_pMbh3JFLrfW89PH2b8Kmi@!LWkWApCXhc|bgSl5s0FiXum zOH6P?>2-|Y#)3HGd^xH%B$d%Dc>UAX!Tv6x z))ph)9&g0hbj*MckS!%+&ZVZ86Ki7DK>eB|4uq?38_etv!)W7hRZ-DI{NAdSd0U7JdT(r6szh|#Qp-YgZ1M|+?autgHT9)N;V z*=+WZE+#tuUAJQfygZURO})$U1FYaQ#g_IR^PIiC=Z=LB^;7cX?RSkMaK*8%=h7iu z$&u;eD}s+&3PSZCR;TJP(Ld&?*l!q;$yn^<>-AoQo`O)_u}9EtJ04EOEg&L9^MlNm z5eV1hasGwKu@{1_2Pb>;wVWSXVm4G{EX(7_nALToi@caH&RYn`95W@l$;-i zzxg}V%;`a%N@yzc?8*kM#E35mz2wCZcn2UTS7|4!+U+>gA*tq?>zzglk-I){VpB1! zv@IRN)o`N7;Ek}O`C$`Ii$&tzr`v{YKBS2?pR^Js(v*Jz`me?trK~fp%{Z8;)?)BvYwS>KaG-Vg!;)==52-4{=z4A@{ucqO zr$jL4JyU49Nq10&i%vjed#)3d+T8q2FnBT)9piD=py#!GLdYRlBx)gCVJAACE-2@9 z_|1WtF~PR8^M)a_FACc|?wxgSmv$j-rr)dWLl4yybP%YO0g=3!ZBa!thC}mtHPA*# zi(Yc}ks^)!S2T~f{lcyUH02Assd8V~RWSGh_g^XeM*g3w;SU8j - - - - - Welcome to Interface - - - - - -
-
-

Move around

- Move around -

- Move around with WASD & fly
- up or down with E & C.
- Cmnd/Ctrl+G will send you
- home. Hitting Enter will let you
- teleport to a user or location. -

-
-
-

Listen & talk

- Talk -

- Use your best headphones
- and microphone for high
- fidelity audio. -

-
-
-

Connect devices

- Connect devices -

- Have an Oculus Rift, a Razer
- Hydra, or a PrimeSense 3D
- camera? We support them all. -

-
-
-

Run a script

- Run a script -

- Cmnd/Cntrl+J will launch a
- Running Scripts dialog to help
- manage your scripts and search
- for new ones to run. -

-
-
-

Script something

- Write a script -

- Write a script; we're always
- adding new features.
- Cmnd/Cntrl+J will launch a
- Running Scripts dialog to help
- manage your scripts. -

-
-
-

Import models

- Import models -

- Use the edit.js script to
- add FBX models in-world. You
- can use grids and fine tune
- placement-related parameters
- with ease. -

-
-
-
-

Read the docs

-

- We are writing documentation on
- just about everything. Please,
- devour all we've written and make
- suggestions where necessary.
- Documentation is always at
- docs.highfidelity.com -

-
-
-
- - - - - diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 1bb4c64884..6d779c81bc 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -224,7 +224,6 @@ static const float MIRROR_FIELD_OF_VIEW = 30.0f; static const quint64 TOO_LONG_SINCE_LAST_SEND_DOWNSTREAM_AUDIO_STATS = 1 * USECS_PER_SECOND; -static const QString INFO_WELCOME_PATH = "html/interface-welcome.html"; static const QString INFO_EDIT_ENTITIES_PATH = "html/edit-commands.html"; static const QString INFO_HELP_PATH = "html/help.html"; @@ -2381,10 +2380,6 @@ void Application::setSettingConstrainToolbarPosition(bool setting) { DependencyManager::get()->setConstrainToolbarToCenterX(setting); } -void Application::aboutApp() { - InfoView::show(INFO_WELCOME_PATH); -} - void Application::showHelp() { static const QString HAND_CONTROLLER_NAME_VIVE = "vive"; static const QString HAND_CONTROLLER_NAME_OCULUS_TOUCH = "oculus"; diff --git a/interface/src/Application.h b/interface/src/Application.h index 98080783a6..73b2c399e6 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -368,7 +368,6 @@ public slots: void calibrateEyeTracker5Points(); #endif - void aboutApp(); static void showHelp(); void cycleCamera(); diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 8e124d27c7..0e22c05787 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -74,9 +74,6 @@ Menu::Menu() { // File > Help addActionToQMenuAndActionHash(fileMenu, MenuOption::Help, 0, qApp, SLOT(showHelp())); - // File > About - addActionToQMenuAndActionHash(fileMenu, MenuOption::AboutApp, 0, qApp, SLOT(aboutApp()), QAction::AboutRole); - // File > Quit addActionToQMenuAndActionHash(fileMenu, MenuOption::Quit, Qt::CTRL | Qt::Key_Q, qApp, SLOT(quit()), QAction::QuitRole); diff --git a/interface/src/Menu.h b/interface/src/Menu.h index e0ac340edc..a51419aa73 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -26,7 +26,6 @@ public: }; namespace MenuOption { - const QString AboutApp = "About Interface"; const QString AddRemoveFriends = "Add/Remove Friends..."; const QString AddressBar = "Show Address Bar"; const QString Animations = "Animations..."; From f1e81c34a323ed992773861e0459b3514968a103 Mon Sep 17 00:00:00 2001 From: trent Date: Fri, 17 Mar 2017 14:34:17 -0400 Subject: [PATCH 228/302] Fixed the texture assignment for the OBJ loader (it wasn't doing anything with the spceular texture) --- libraries/fbx/src/OBJReader.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/libraries/fbx/src/OBJReader.cpp b/libraries/fbx/src/OBJReader.cpp index 73cf7a520e..2660ecede5 100644 --- a/libraries/fbx/src/OBJReader.cpp +++ b/libraries/fbx/src/OBJReader.cpp @@ -267,7 +267,7 @@ void OBJReader::parseMaterialLibrary(QIODevice* device) { } if (token == "map_Kd") { currentMaterial.diffuseTextureFilename = filename; - } else { + } else if( token == "map_Ks" ) { currentMaterial.specularTextureFilename = filename; } } @@ -611,6 +611,9 @@ FBXGeometry* OBJReader::readOBJ(QByteArray& model, const QVariantHash& mapping, if (!objMaterial.diffuseTextureFilename.isEmpty()) { fbxMaterial.albedoTexture.filename = objMaterial.diffuseTextureFilename; } + if (!objMaterial.specularTextureFilename.isEmpty()) { + fbxMaterial.specularTexture.filename = objMaterial.specularTextureFilename; + } modelMaterial->setEmissive(fbxMaterial.emissiveColor); modelMaterial->setAlbedo(fbxMaterial.diffuseColor); From a68146e7d9e3e4d899de607751b7e5c4d9f057f1 Mon Sep 17 00:00:00 2001 From: Zach Pomerantz Date: Fri, 17 Mar 2017 15:07:02 -0400 Subject: [PATCH 229/302] mv audio output init to output thread --- libraries/audio-client/src/AudioClient.cpp | 45 ++++++++++++++-------- libraries/audio-client/src/AudioClient.h | 1 + 2 files changed, 31 insertions(+), 15 deletions(-) diff --git a/libraries/audio-client/src/AudioClient.cpp b/libraries/audio-client/src/AudioClient.cpp index fa5d0d24d5..854697d809 100644 --- a/libraries/audio-client/src/AudioClient.cpp +++ b/libraries/audio-client/src/AudioClient.cpp @@ -1540,12 +1540,39 @@ bool AudioClient::switchOutputToAudioDevice(const QAudioDeviceInfo& outputDevice // setup our general output device for audio-mixer audio _audioOutput = new QAudioOutput(outputDeviceInfo, _outputFormat, this); - int osDefaultBufferSize = _audioOutput->bufferSize(); int deviceChannelCount = _outputFormat.channelCount(); - int deviceFrameSize = (AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL * deviceChannelCount * _outputFormat.sampleRate()) / _desiredOutputFormat.sampleRate(); - int requestedSize = _sessionOutputBufferSizeFrames * deviceFrameSize * AudioConstants::SAMPLE_SIZE; + int frameSize = (AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL * deviceChannelCount * _outputFormat.sampleRate()) / _desiredOutputFormat.sampleRate(); + int requestedSize = _sessionOutputBufferSizeFrames * frameSize * AudioConstants::SAMPLE_SIZE; _audioOutput->setBufferSize(requestedSize); + // initialize mix buffers on the _audioOutput thread to avoid races + connect(_audioOutput, &QAudioOutput::stateChanged, [&, frameSize, requestedSize](QAudio::State state) { + if (state == QAudio::ActiveState) { + // restrict device callback to _outputPeriod samples + _outputPeriod = (_audioOutput->periodSize() / AudioConstants::SAMPLE_SIZE) * 2; + _outputMixBuffer = new float[_outputPeriod]; + _outputScratchBuffer = new int16_t[_outputPeriod]; + + // size local output mix buffer based on resampled network frame size + _networkPeriod = _localToOutputResampler->getMaxOutput(AudioConstants::NETWORK_FRAME_SAMPLES_STEREO); + _localOutputMixBuffer = new float[_networkPeriod]; + int localPeriod = _outputPeriod * 2; + _localInjectorsStream.resizeForFrameSize(localPeriod); + + int bufferSize = _audioOutput->bufferSize(); + int bufferSamples = bufferSize / AudioConstants::SAMPLE_SIZE; + int bufferFrames = bufferSamples / (float)frameSize; + qCDebug(audioclient) << "frame (samples):" << frameSize; + qCDebug(audioclient) << "buffer (frames):" << bufferFrames; + qCDebug(audioclient) << "buffer (samples):" << bufferSamples; + qCDebug(audioclient) << "buffer (bytes):" << bufferSize; + qCDebug(audioclient) << "requested (bytes):" << requestedSize; + qCDebug(audioclient) << "period (samples):" << _outputPeriod; + qCDebug(audioclient) << "local buffer (samples):" << localPeriod; + + disconnect(_audioOutput, &QAudioOutput::stateChanged, 0, 0); + } + }); connect(_audioOutput, &QAudioOutput::notify, this, &AudioClient::outputNotify); _audioOutputIODevice.start(); @@ -1555,18 +1582,6 @@ bool AudioClient::switchOutputToAudioDevice(const QAudioDeviceInfo& outputDevice _audioOutput->start(&_audioOutputIODevice); lock.unlock(); - int periodSampleSize = _audioOutput->periodSize() / AudioConstants::SAMPLE_SIZE; - // device callback is not restricted to periodSampleSize, so double the mix/scratch buffer sizes - _outputPeriod = periodSampleSize * 2; - _outputMixBuffer = new float[_outputPeriod]; - _outputScratchBuffer = new int16_t[_outputPeriod]; - _localOutputMixBuffer = new float[_outputPeriod]; - _localInjectorsStream.resizeForFrameSize(_outputPeriod * 2); - - qCDebug(audioclient) << "Output Buffer capacity in frames: " << _audioOutput->bufferSize() / AudioConstants::SAMPLE_SIZE / (float)deviceFrameSize << - "requested bytes:" << requestedSize << "actual bytes:" << _audioOutput->bufferSize() << - "os default:" << osDefaultBufferSize << "period size:" << _audioOutput->periodSize(); - // setup a loopback audio output device _loopbackAudioOutput = new QAudioOutput(outputDeviceInfo, _outputFormat, this); diff --git a/libraries/audio-client/src/AudioClient.h b/libraries/audio-client/src/AudioClient.h index 7e9acc0586..820e8ac260 100644 --- a/libraries/audio-client/src/AudioClient.h +++ b/libraries/audio-client/src/AudioClient.h @@ -339,6 +339,7 @@ private: int16_t _networkScratchBuffer[AudioConstants::NETWORK_FRAME_SAMPLES_AMBISONIC]; // for local audio (used by audio injectors thread) + int _networkPeriod { 0 }; float _localMixBuffer[AudioConstants::NETWORK_FRAME_SAMPLES_STEREO]; int16_t _localScratchBuffer[AudioConstants::NETWORK_FRAME_SAMPLES_AMBISONIC]; float* _localOutputMixBuffer { NULL }; From 2302b157f2abd4004112db4682b440e97ecef3d7 Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Fri, 17 Mar 2017 21:06:50 +0100 Subject: [PATCH 230/302] Boppo script for welcome area (will be in marketplace later) --- .../marketplace/boppo/boppoClownEntity.js | 80 ++++ .../marketplace/boppo/boppoServer.js | 299 ++++++++++++ .../marketplace/boppo/clownGloveDispenser.js | 154 +++++++ .../marketplace/boppo/createElBoppo.js | 426 ++++++++++++++++++ .../marketplace/boppo/lookAtEntity.js | 98 ++++ 5 files changed, 1057 insertions(+) create mode 100644 unpublishedScripts/marketplace/boppo/boppoClownEntity.js create mode 100644 unpublishedScripts/marketplace/boppo/boppoServer.js create mode 100644 unpublishedScripts/marketplace/boppo/clownGloveDispenser.js create mode 100644 unpublishedScripts/marketplace/boppo/createElBoppo.js create mode 100644 unpublishedScripts/marketplace/boppo/lookAtEntity.js diff --git a/unpublishedScripts/marketplace/boppo/boppoClownEntity.js b/unpublishedScripts/marketplace/boppo/boppoClownEntity.js new file mode 100644 index 0000000000..ef01e47f6f --- /dev/null +++ b/unpublishedScripts/marketplace/boppo/boppoClownEntity.js @@ -0,0 +1,80 @@ +// +// boppoClownEntity.js +// +// Created by Thijs Wenker on 3/15/17. +// Copyright 2017 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 +// + +/* globals BoppoClownEntity:true LookAtTarget */ + +(function() { + var SFX_PREFIX = 'http://hifi-content.s3-us-west-1.amazonaws.com/caitlyn/production/elBoppo/sfx/'; + var CHANNEL_PREFIX = 'io.highfidelity.boppo_server_'; + var PUNCH_SOUNDS = [ + 'punch_1.wav', + 'punch_2.wav' + ]; + var PUNCH_COOLDOWN = 300; + + Script.include(Script.resolvePath('lookAtEntity.js?v2')); + + BoppoClownEntity = function() { + var _this, + _entityID, + _boppoUserData, + _lookAtTarget, + _punchSounds = [], + _lastPlayedPunch = {}; + + var getOwnBoppoUserData = function() { + try { + return JSON.parse(Entities.getEntityProperties(_entityID, ['userData']).userData).Boppo; + } catch (e) { + // e + } + return {}; + }; + + BoppoClownEntity = function () { + _this = this; + PUNCH_SOUNDS.forEach(function(punch) { + _punchSounds.push(SoundCache.getSound(SFX_PREFIX + punch)); + }); + }; + + BoppoClownEntity.prototype = { + preload: function(entityID) { + _entityID = entityID; + _boppoUserData = getOwnBoppoUserData(); + _lookAtTarget = new LookAtTarget(_entityID); + }, + collisionWithEntity: function(boppoEntity, collidingEntity, collisionInfo) { + if (collisionInfo.type === 0 && + Entities.getEntityProperties(collidingEntity, ['name']).name.indexOf('Boxing Glove ') === 0) { + + if (_lastPlayedPunch[collidingEntity] === undefined || + Date.now() - _lastPlayedPunch[collidingEntity] > PUNCH_COOLDOWN) { + + // If boxing glove detected here: + Messages.sendMessage(CHANNEL_PREFIX + _boppoUserData.gameParentID, 'hit'); + + _lookAtTarget.lookAtByAction(); + var randomPunchIndex = Math.floor(Math.random() * _punchSounds.length); + Audio.playSound(_punchSounds[randomPunchIndex], { + position: collisionInfo.contactPoint + }); + _lastPlayedPunch[collidingEntity] = Date.now(); + } + } + } + + }; + + return new BoppoClownEntity(); + }; + + return new BoppoClownEntity(); +}); diff --git a/unpublishedScripts/marketplace/boppo/boppoServer.js b/unpublishedScripts/marketplace/boppo/boppoServer.js new file mode 100644 index 0000000000..ebe938c043 --- /dev/null +++ b/unpublishedScripts/marketplace/boppo/boppoServer.js @@ -0,0 +1,299 @@ +// +// boppoServer.js +// +// Created by Thijs Wenker on 3/15/17. +// Copyright 2017 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 +// + +/* globals BoppoServer:true */ + +(function() { + var SFX_PREFIX = 'http://hifi-content.s3-us-west-1.amazonaws.com/caitlyn/production/elBoppo/sfx/'; + var CLOWN_LAUGHS = [ + 'clown_laugh_1.wav', + 'clown_laugh_2.wav', + 'clown_laugh_3.wav', + 'clown_laugh_4.wav' + ]; + var TICK_TOCK_SOUND = 'ticktock%20-%20tock.wav'; + var BOXING_RING_BELL_START = 'boxingRingBell.wav'; + var BOXING_RING_BELL_END = 'boxingRingBell-end.wav'; + var BOPPO_MUSIC = 'boppoMusic.wav'; + var CHANNEL_PREFIX = 'io.highfidelity.boppo_server_'; + var MESSAGE_HIT = 'hit'; + var MESSAGE_ENTER_ZONE = 'enter-zone'; + var MESSAGE_UNLOAD_FIX = 'unload-fix'; + // don't set the search radius too high, it might remove boppo's from other nearby instances + var BOPPO_SEARCH_RADIUS = 4.0; + + var MILLISECONDS_PER_SECOND = 1000; + // Make sure the entities are loaded at startup (TODO: more solid fix) + var LOAD_TIMEOUT = 5000; + var SECONDS_PER_MINUTE = 60; + var DEFAULT_PLAYTIME = 30; // seconds + var BASE_TEN = 10; + var TICK_TOCK_FROM = 3; // seconds + var COOLDOWN_TIME_MS = MILLISECONDS_PER_SECOND * 3; + + BoppoServer = function() { + var _this, + _isInitialized = false, + _clownLaughs = [], + _musicInjector, + _music, + _laughingInjector, + _tickTockSound, + _boxingBellRingStart, + _boxingBellRingEnd, + _entityID, + _boppoClownID, + _channel, + _boppoEntities, + _isGameRunning, + _updateInterval, + _timeLeft, + _hits, + _coolDown; + + var getOwnBoppoUserData = function() { + try { + return JSON.parse(Entities.getEntityProperties(_entityID, ['userData']).userData).Boppo; + } catch (e) { + // e + } + return {}; + }; + + var updateBoppoEntities = function() { + Entities.getChildrenIDs(_entityID).forEach(function(entityID) { + try { + var userData = JSON.parse(Entities.getEntityProperties(entityID, ['userData']).userData); + if (userData.Boppo.type !== undefined) { + _boppoEntities[userData.Boppo.type] = entityID; + } + } catch (e) { + // e + } + }); + }; + + var clearUntrackedBoppos = function() { + var position = Entities.getEntityProperties(_entityID, ['position']).position; + Entities.findEntities(position, BOPPO_SEARCH_RADIUS).forEach(function(entityID) { + try { + if (JSON.parse(Entities.getEntityProperties(entityID, ['userData']).userData).Boppo.type === 'boppo') { + Entities.deleteEntity(entityID); + } + } catch (e) { + // e + } + }); + }; + + var updateTimerDisplay = function() { + if (_boppoEntities['timer']) { + var secondsString = _timeLeft % SECONDS_PER_MINUTE; + if (secondsString < BASE_TEN) { + secondsString = '0' + secondsString; + } + var minutesString = Math.floor(_timeLeft / SECONDS_PER_MINUTE); + Entities.editEntity(_boppoEntities['timer'], { + text: minutesString + ':' + secondsString + }); + } + }; + + var updateScoreDisplay = function() { + if (_boppoEntities['score']) { + Entities.editEntity(_boppoEntities['score'], { + text: 'SCORE: ' + _hits + }); + } + }; + + var playSoundAtBoxingRing = function(sound, properties) { + var _properties = properties ? properties : {}; + _properties['position'] = Entities.getEntityProperties(_entityID, ['position']).position; + // play beep + return Audio.playSound(sound, _properties); + }; + + var onUpdate = function() { + _timeLeft--; + + if (_timeLeft > 0 && _timeLeft <= TICK_TOCK_FROM) { + // play beep + playSoundAtBoxingRing(_tickTockSound); + } + if (_timeLeft === 0) { + if (_musicInjector !== undefined && _musicInjector.isPlaying()) { + _musicInjector.stop(); + _musicInjector = undefined; + } + playSoundAtBoxingRing(_boxingBellRingEnd); + _isGameRunning = false; + Script.clearInterval(_updateInterval); + _updateInterval = null; + _coolDown = true; + Script.setTimeout(function() { + _coolDown = false; + _this.resetBoppo(); + }, COOLDOWN_TIME_MS); + } + updateTimerDisplay(); + }; + + var onMessage = function(channel, message, sender) { + if (channel === _channel) { + if (message === MESSAGE_HIT) { + _this.hit(); + } else if (message === MESSAGE_ENTER_ZONE && !_isGameRunning) { + _this.resetBoppo(); + } else if (message === MESSAGE_UNLOAD_FIX && _isInitialized) { + _this.unload(); + } + } + }; + + BoppoServer = function () { + _this = this; + _hits = 0; + _boppoClownID = null; + _coolDown = false; + CLOWN_LAUGHS.forEach(function(clownLaugh) { + _clownLaughs.push(SoundCache.getSound(SFX_PREFIX + clownLaugh)); + }); + _tickTockSound = SoundCache.getSound(SFX_PREFIX + TICK_TOCK_SOUND); + _boxingBellRingStart = SoundCache.getSound(SFX_PREFIX + BOXING_RING_BELL_START); + _boxingBellRingEnd = SoundCache.getSound(SFX_PREFIX + BOXING_RING_BELL_END); + _music = SoundCache.getSound(SFX_PREFIX + BOPPO_MUSIC); + _boppoEntities = {}; + }; + + BoppoServer.prototype = { + preload: function(entityID) { + _entityID = entityID; + _channel = CHANNEL_PREFIX + entityID; + + Messages.sendLocalMessage(_channel, MESSAGE_UNLOAD_FIX); + Script.setTimeout(function() { + clearUntrackedBoppos(); + updateBoppoEntities(); + Messages.subscribe(_channel); + Messages.messageReceived.connect(onMessage); + _this.resetBoppo(); + _isInitialized = true; + }, LOAD_TIMEOUT); + }, + resetBoppo: function() { + if (_boppoClownID !== null) { + print('deleting boppo: ' + _boppoClownID); + Entities.deleteEntity(_boppoClownID); + } + var boppoBaseProperties = Entities.getEntityProperties(_entityID, ['position', 'rotation']); + _boppoClownID = Entities.addEntity({ + angularDamping: 0.0, + collisionSoundURL: 'http://hifi-content.s3.amazonaws.com/caitlyn/production/elBoppo/51460__andre-rocha-nascimento__basket-ball-01-bounce.wav', + collisionsWillMove: true, + compoundShapeURL: 'http://hifi-content.s3.amazonaws.com/caitlyn/production/elBoppo/bopo_phys.obj', + damping: 1.0, + density: 10000, + dimensions: { + x: 1.2668079137802124, + y: 2.0568051338195801, + z: 0.88563752174377441 + }, + dynamic: 1.0, + friction: 1.0, + gravity: { + x: 0, + y: -25, + z: 0 + }, + modelURL: 'http://hifi-content.s3.amazonaws.com/caitlyn/production/elBoppo/elBoppo3_VR.fbx', + name: 'El Boppo the Punching Bag Clown', + registrationPoint: { + x: 0.5, + y: 0, + z: 0.3 + }, + restitution: 0.99, + rotation: boppoBaseProperties.rotation, + position: Vec3.sum(boppoBaseProperties.position, + Vec3.multiplyQbyV(boppoBaseProperties.rotation, { + x: 0.08666179329156876, + y: -1.5698202848434448, + z: 0.1847127377986908 + })), + script: Script.resolvePath('boppoClownEntity.js'), + shapeType: 'compound', + type: 'Model', + userData: JSON.stringify({ + lookAt: { + targetID: _boppoEntities['lookAtThis'], + disablePitch: true, + disableYaw: false, + disableRoll: true, + clearDisabledAxis: true, + rotationOffset: { x: 0.0, y: 180.0, z: 0.0} + }, + Boppo: { + type: 'boppo', + gameParentID: _entityID + }, + grabbableKey: { + grabbable: false + } + }) + }); + updateBoppoEntities(); + _boppoEntities['boppo'] = _boppoClownID; + }, + laugh: function() { + if (_laughingInjector !== undefined && _laughingInjector.isPlaying()) { + return; + } + var randomLaughIndex = Math.floor(Math.random() * _clownLaughs.length); + _laughingInjector = Audio.playSound(_clownLaughs[randomLaughIndex], { + position: Entities.getEntityProperties(_boppoClownID, ['position']).position + }); + }, + hit: function() { + if (_coolDown) { + return; + } + if (!_isGameRunning) { + var boxingRingBoppoData = getOwnBoppoUserData(); + _updateInterval = Script.setInterval(onUpdate, MILLISECONDS_PER_SECOND); + _timeLeft = boxingRingBoppoData.playTimeSeconds ? parseInt(boxingRingBoppoData.playTimeSeconds) : + DEFAULT_PLAYTIME; + _isGameRunning = true; + _hits = 0; + playSoundAtBoxingRing(_boxingBellRingStart); + _musicInjector = playSoundAtBoxingRing(_music, {loop: true, volume: 0.6}); + } + _hits++; + updateTimerDisplay(); + updateScoreDisplay(); + _this.laugh(); + }, + unload: function() { + print('unload called'); + if (_updateInterval) { + Script.clearInterval(_updateInterval); + } + Messages.messageReceived.connect(onMessage); + Messages.unsubscribe(_channel); + Entities.deleteEntity(_boppoClownID); + print('endOfUnload'); + } + }; + + return new BoppoServer(); + }; + + return new BoppoServer(); +}); diff --git a/unpublishedScripts/marketplace/boppo/clownGloveDispenser.js b/unpublishedScripts/marketplace/boppo/clownGloveDispenser.js new file mode 100644 index 0000000000..42675d609a --- /dev/null +++ b/unpublishedScripts/marketplace/boppo/clownGloveDispenser.js @@ -0,0 +1,154 @@ +// +// clownGloveDispenser.js +// +// Created by Thijs Wenker on 8/2/16. +// Copyright 2016 High Fidelity, Inc. +// +// Based on examples/winterSmashUp/targetPractice/shooterPlatform.js +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +(function() { + var _this = this; + + var CHANNEL_PREFIX = 'io.highfidelity.boppo_server_'; + + var leftBoxingGlove = undefined; + var rightBoxingGlove = undefined; + + var inZone = false; + + var wearGloves = function() { + leftBoxingGlove = Entities.addEntity({ + position: MyAvatar.position, + collisionsWillMove: true, + dimensions: { + x: 0.24890634417533875, + y: 0.28214839100837708, + z: 0.21127720177173615 + }, + dynamic: true, + gravity: { + x: 0, + y: -9.8, + z: 0 + }, + modelURL: "http://hifi-content.s3.amazonaws.com/caitlyn/production/elBoppo/LFT_glove_VR3.fbx", + name: "Boxing Glove - Left", + registrationPoint: { + x: 0.5, + y: 0, + z: 0.5 + }, + shapeType: "simple-hull", + type: "Model", + userData: JSON.stringify({ + grabbableKey: { + invertSolidWhileHeld: true + }, + wearable: { + joints: { + LeftHand: [ + {x: 0, y: 0.0, z: 0.02 }, + Quat.fromVec3Degrees({x: 0, y: 0, z: 0}) + ] + } + } + }) + }); + Messages.sendLocalMessage('Hifi-Hand-Grab', JSON.stringify({hand: 'left', entityID: leftBoxingGlove})); + // Allows teleporting while glove is wielded + Messages.sendLocalMessage('Hifi-Teleport-Ignore-Add', leftBoxingGlove); + + rightBoxingGlove = Entities.addEntity({ + position: MyAvatar.position, + collisionsWillMove: true, + dimensions: { + x: 0.24890634417533875, + y: 0.28214839100837708, + z: 0.21127720177173615 + }, + dynamic: true, + gravity: { + x: 0, + y: -9.8, + z: 0 + }, + modelURL: "http://hifi-content.s3.amazonaws.com/caitlyn/production/elBoppo/RT_glove_VR2.fbx", + name: "Boxing Glove - Right", + registrationPoint: { + x: 0.5, + y: 0, + z: 0.5 + }, + shapeType: "simple-hull", + type: "Model", + userData: JSON.stringify({ + grabbableKey: { + invertSolidWhileHeld: true + }, + wearable: { + joints: { + RightHand: [ + {x: 0, y: 0.0, z: 0.02 }, + Quat.fromVec3Degrees({x: 0, y: 0, z: 0}) + ] + } + } + }) + }); + Messages.sendLocalMessage('Hifi-Hand-Grab', JSON.stringify({hand: 'right', entityID: rightBoxingGlove})); + // Allows teleporting while glove is wielded + Messages.sendLocalMessage('Hifi-Teleport-Ignore-Add', rightBoxingGlove); + }; + + var cleanUpGloves = function() { + if (leftBoxingGlove !== undefined) { + Entities.deleteEntity(leftBoxingGlove); + leftBoxingGlove = undefined; + } + if (rightBoxingGlove !== undefined) { + Entities.deleteEntity(rightBoxingGlove); + rightBoxingGlove = undefined; + } + }; + + var wearGlovesIfHMD = function() { + // cleanup your old gloves if they're still there (unlikely) + cleanUpGloves(); + if (HMD.active) { + wearGloves(); + } + }; + + _this.preload = function(entityID) { + HMD.displayModeChanged.connect(function() { + if (inZone) { + wearGlovesIfHMD(); + } + }); + }; + + _this.unload = function() { + cleanUpGloves(); + }; + + _this.enterEntity = function(entityID) { + inZone = true; + print('entered boxing glove dispenser entity'); + wearGlovesIfHMD(); + + // Reset boppo if game is not running: + var parentID = Entities.getEntityProperties(entityID, ['parentID']).parentID; + Messages.sendMessage(CHANNEL_PREFIX + parentID, 'enter-zone'); + }; + + _this.leaveEntity = function(entityID) { + inZone = false; + cleanUpGloves(); + }; + + _this.unload = _this.leaveEntity; +}); diff --git a/unpublishedScripts/marketplace/boppo/createElBoppo.js b/unpublishedScripts/marketplace/boppo/createElBoppo.js new file mode 100644 index 0000000000..6bef9bc0fd --- /dev/null +++ b/unpublishedScripts/marketplace/boppo/createElBoppo.js @@ -0,0 +1,426 @@ +// +// createElBoppo.js +// +// Created by Thijs Wenker on 3/17/17. +// Copyright 2017 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 +// + +/* globals SCRIPT_IMPORT_PROPERTIES */ + +var MODELS_PATH = 'http://hifi-content.s3.amazonaws.com/DomainContent/Welcome%20Area/production/models/boxingRing/'; +var SCRIPT_PATH = this.isCleanupAndSpawnScript ? + 'http://s3-us-west-1.amazonaws.com/hifi-content/thoys/dev/2017/elBoppo/' : + Script.resolvePath(''); +var WANT_CLEANUP_ON_SCRIPT_ENDING = false; + +var getCreatePosition = function() { + // can either return position defined by resetScript or avatar position + if (this.isCleanupAndSpawnScript) { + return SCRIPT_IMPORT_PROPERTIES.rootPosition; + } + return Vec3.sum(MyAvatar.position, {x: 1, z: -2}); +}; + +var boxingRing = Entities.addEntity({ + dimensions: { + x: 4.0584001541137695, + y: 4.0418000221252441, + z: 3.0490000247955322 + }, + modelURL: MODELS_PATH + 'assembled/boppoBoxingRingAssembly.fbx', + name: 'Boxing Ring Assembly', + rotation: { + w: 0.9996337890625, + x: -1.52587890625e-05, + y: -0.026230275630950928, + z: -4.57763671875e-05 + }, + position: getCreatePosition(), + scriptTimestamp: 1489612158459, + serverScripts: SCRIPT_PATH + 'boppoServer.js', + shapeType: 'static-mesh', + type: 'Model', + userData: JSON.stringify({ + Boppo: { + type: 'boxingring', + playTimeSeconds: 15 + } + }) +}); + +var boppoEntities = [ + { + dimensions: { + x: 0.36947935819625854, + y: 0.25536194443702698, + z: 0.059455446898937225 + }, + modelURL: MODELS_PATH + 'boxingGameSign/boppoSignFrame.fbx', + parentID: boxingRing, + localPosition: { + x: -1.0251024961471558, + y: 0.51661628484725952, + z: -1.1176263093948364 + }, + rotation: { + w: 0.996856689453125, + x: 0.013321161270141602, + y: 0.0024566650390625, + z: 0.078049898147583008 + }, + shapeType: 'box', + type: 'Model' + }, + { + dimensions: { + x: 0.33255371451377869, + y: 0.1812121719121933, + z: 0.0099999997764825821 + }, + lineHeight: 0.125, + name: 'Boxing Ring - High Score Board', + parentID: boxingRing, + localPosition: { + x: -1.0239436626434326, + y: 0.52212876081466675, + z: -1.0971509218215942 + }, + rotation: { + w: 0.9876401424407959, + x: 0.013046503067016602, + y: 0.0012359619140625, + z: 0.15605401992797852 + }, + text: '0:00', + textColor: { + blue: 0, + green: 0, + red: 255 + }, + type: 'Text', + userData: JSON.stringify({ + Boppo: { + type: 'timer' + } + }) + }, + { + dimensions: { + x: 0.50491130352020264, + y: 0.13274604082107544, + z: 0.0099999997764825821 + }, + lineHeight: 0.090000003576278687, + name: 'Boxing Ring - Score Board', + parentID: boxingRing, + localPosition: { + x: -0.77596306800842285, + y: 0.37797555327415466, + z: -1.0910623073577881 + }, + rotation: { + w: 0.9518122673034668, + x: 0.004237703513354063, + y: -0.0010041374480351806, + z: 0.30455198884010315 + }, + text: 'SCORE: 0', + textColor: { + blue: 0, + green: 0, + red: 255 + }, + type: 'Text', + userData: JSON.stringify({ + Boppo: { + type: 'score' + } + }) + }, + { + dimensions: { + x: 0.58153259754180908, + y: 0.1884911060333252, + z: 0.059455446898937225 + }, + modelURL: MODELS_PATH + 'boxingGameSign/boppoSignFrame.fbx', + parentID: boxingRing, + localPosition: { + x: -0.78200173377990723, + y: 0.35684797167778015, + z: -1.108180046081543 + }, + rotation: { + w: 0.97814905643463135, + x: 0.0040436983108520508, + y: -0.0005645751953125, + z: 0.20778214931488037 + }, + shapeType: 'box', + type: 'Model' + }, + { + dimensions: { + x: 4.1867804527282715, + y: 3.5065803527832031, + z: 5.6845207214355469 + }, + name: 'El Boppo the Clown boxing area & glove maker', + parentID: boxingRing, + localPosition: { + x: -0.012308252975344658, + y: 0.054641719907522202, + z: 0.98782551288604736 + }, + rotation: { + w: 1, + x: -1.52587890625e-05, + y: -1.52587890625e-05, + z: -1.52587890625e-05 + }, + script: SCRIPT_PATH + 'clownGloveDispenser.js', + shapeType: 'box', + type: 'Zone', + visible: false + }, + { + color: { + blue: 255, + green: 5, + red: 255 + }, + dimensions: { + x: 0.20000000298023224, + y: 0.20000000298023224, + z: 0.20000000298023224 + }, + name: 'LookAtBox', + parentID: boxingRing, + localPosition: { + x: -0.1772226095199585, + y: -1.7072629928588867, + z: 1.3122396469116211 + }, + rotation: { + w: 0.999969482421875, + x: 1.52587890625e-05, + y: 0.0043793916702270508, + z: 1.52587890625e-05 + }, + shape: 'Cube', + type: 'Box', + userData: JSON.stringify({ + Boppo: { + type: 'lookAtThis' + } + }) + }, + { + color: { + blue: 209, + green: 157, + red: 209 + }, + dimensions: { + x: 1.6913000345230103, + y: 1.2124500274658203, + z: 0.2572999894618988 + }, + name: 'boppoBackBoard', + parentID: boxingRing, + localPosition: { + x: -0.19500596821308136, + y: -1.1044719219207764, + z: -0.55993378162384033 + }, + rotation: { + w: 0.9807126522064209, + x: -0.19511711597442627, + y: 0.0085297822952270508, + z: 0.0016937255859375 + }, + shape: 'Cube', + type: 'Box', + visible: false + }, + { + color: { + blue: 0, + green: 0, + red: 255 + }, + dimensions: { + x: 1.8155574798583984, + y: 0.92306196689605713, + z: 0.51203572750091553 + }, + name: 'boppoBackBoard', + parentID: boxingRing, + localPosition: { + x: -0.11036647111177444, + y: -0.051978692412376404, + z: -0.79054081439971924 + }, + rotation: { + w: 0.9807431697845459, + x: 0.19505608081817627, + y: 0.0085602998733520508, + z: -0.0017547607421875 + }, + shape: 'Cube', + type: 'Box', + visible: false + }, + { + color: { + blue: 209, + green: 157, + red: 209 + }, + dimensions: { + x: 1.9941408634185791, + y: 1.2124500274658203, + z: 0.2572999894618988 + }, + name: 'boppoBackBoard', + localPosition: { + x: 0.69560068845748901, + y: -1.3840068578720093, + z: 0.059689953923225403 + }, + rotation: { + w: 0.73458456993103027, + x: -0.24113833904266357, + y: -0.56545358896255493, + z: -0.28734266757965088 + }, + shape: 'Cube', + type: 'Box', + visible: false + }, + { + color: { + blue: 82, + green: 82, + red: 82 + }, + dimensions: { + x: 8.3777303695678711, + y: 0.87573593854904175, + z: 7.9759469032287598 + }, + parentID: boxingRing, + localPosition: { + x: -0.38302639126777649, + y: -2.121284008026123, + z: 0.3699878454208374 + }, + rotation: { + w: 0.70711839199066162, + x: -7.62939453125e-05, + y: 0.70705735683441162, + z: -1.52587890625e-05 + }, + shape: 'Triangle', + type: 'Shape' + }, + { + color: { + blue: 209, + green: 157, + red: 209 + }, + dimensions: { + x: 1.889795184135437, + y: 0.86068248748779297, + z: 0.2572999894618988 + }, + name: 'boppoBackBoard', + parentID: boxingRing, + localPosition: { + x: -0.95167744159698486, + y: -1.4756947755813599, + z: -0.042313352227210999 + }, + rotation: { + w: 0.74004733562469482, + x: -0.24461740255355835, + y: 0.56044864654541016, + z: 0.27998781204223633 + }, + shape: 'Cube', + type: 'Box', + visible: false + }, + { + color: { + blue: 0, + green: 0, + red: 255 + }, + dimensions: { + x: 4.0720257759094238, + y: 0.50657749176025391, + z: 1.4769613742828369 + }, + name: 'boppo-stepsRamp', + parentID: boxingRing, + localPosition: { + x: -0.002939039608463645, + y: -1.9770187139511108, + z: 2.2165381908416748 + }, + rotation: { + w: 0.99252307415008545, + x: 0.12184333801269531, + y: -1.52587890625e-05, + z: -1.52587890625e-05 + }, + shape: 'Cube', + type: 'Box', + visible: false + }, + { + color: { + blue: 150, + green: 150, + red: 150 + }, + cutoff: 90, + dimensions: { + x: 5.2220535278320312, + y: 5.2220535278320312, + z: 5.2220535278320312 + }, + falloffRadius: 2, + intensity: 15, + name: 'boxing ring light', + parentID: boxingRing, + localPosition: { + x: -1.4094564914703369, + y: -0.36021926999092102, + z: 0.81797939538955688 + }, + rotation: { + w: 0.9807431697845459, + x: 1.52587890625e-05, + y: -0.19520866870880127, + z: -1.52587890625e-05 + }, + type: 'Light' + } +]; + +boppoEntities.forEach(function(entityProperties) { + entityProperties['parentID'] = boxingRing; + Entities.addEntity(entityProperties); +}); + +if (WANT_CLEANUP_ON_SCRIPT_ENDING) { + Script.scriptEnding.connect(function() { + Entities.deleteEntity(boxingRing); + }); +} diff --git a/unpublishedScripts/marketplace/boppo/lookAtEntity.js b/unpublishedScripts/marketplace/boppo/lookAtEntity.js new file mode 100644 index 0000000000..ba072814f2 --- /dev/null +++ b/unpublishedScripts/marketplace/boppo/lookAtEntity.js @@ -0,0 +1,98 @@ +// +// lookAtTarget.js +// +// Created by Thijs Wenker on 3/15/17. +// Copyright 2017 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 +// + +/* globals LookAtTarget:true */ + +LookAtTarget = function(sourceEntityID) { + /* private variables */ + var _this, + _options, + _sourceEntityID, + _sourceEntityProperties, + REQUIRED_PROPERTIES = ['position', 'rotation', 'userData'], + LOOK_AT_TAG = 'lookAtTarget'; + + LookAtTarget = function(sourceEntityID) { + _this = this; + _sourceEntityID = sourceEntityID; + _this.updateOptions(); + }; + + /* private functions */ + var updateEntitySourceProperties = function() { + _sourceEntityProperties = Entities.getEntityProperties(_sourceEntityID, REQUIRED_PROPERTIES); + }; + + var getUpdatedActionProperties = function() { + return { + targetRotation: _this.getLookAtRotation(), + angularTimeScale: 0.1, + ttl: 10 + }; + }; + + var getNewActionProperties = function() { + var newActionProperties = getUpdatedActionProperties(); + newActionProperties.tag = LOOK_AT_TAG; + return newActionProperties; + }; + + LookAtTarget.prototype = { + /* public functions */ + updateOptions: function() { + updateEntitySourceProperties(); + _options = JSON.parse(_sourceEntityProperties.userData).lookAt; + }, + getTargetPosition: function() { + return Entities.getEntityProperties(_options.targetID).position; + }, + getLookAtRotation: function() { + _this.updateOptions(); + + var newRotation = Quat.lookAt(_sourceEntityProperties.position, _this.getTargetPosition(), Vec3.UP); + if (_options.rotationOffset !== undefined) { + newRotation = Quat.multiply(newRotation, Quat.fromVec3Degrees(_options.rotationOffset)); + } + if (_options.disablePitch || _options.disableYaw || _options.disablePitch) { + var disabledAxis = _options.clearDisabledAxis ? Vec3.ZERO : + Quat.safeEulerAngles(_sourceEntityProperties.rotation); + var newEulers = Quat.safeEulerAngles(newRotation); + newRotation = Quat.fromVec3Degrees({ + x: _options.disablePitch ? disabledAxis.x : newEulers.x, + y: _options.disableYaw ? disabledAxis.y : newEulers.y, + z: _options.disableRoll ? disabledAxis.z : newEulers.z + }); + } + return newRotation; + }, + lookAtDirectly: function() { + Entities.editEntity(_sourceEntityID, {rotation: _this.getLookAtRotation()}); + }, + lookAtByAction: function() { + var actionIDs = Entities.getActionIDs(_sourceEntityID); + var actionFound = false; + actionIDs.forEach(function(actionID) { + if (actionFound) { + return; + } + var actionArguments = Entities.getActionArguments(_sourceEntityID, actionID); + if (actionArguments.tag === LOOK_AT_TAG) { + actionFound = true; + Entities.updateAction(_sourceEntityID, actionID, getUpdatedActionProperties()); + } + }); + if (!actionFound) { + Entities.addAction('spring', _sourceEntityID, getNewActionProperties()); + } + } + }; + + return new LookAtTarget(sourceEntityID); +}; From febe25511b12fc1830448a3cca1790653bdf7a86 Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Fri, 17 Mar 2017 21:10:06 +0100 Subject: [PATCH 231/302] teleport.js - ignore entity support (for boxing gloves) --- scripts/system/controllers/teleport.js | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/scripts/system/controllers/teleport.js b/scripts/system/controllers/teleport.js index c058f046db..e56562a18a 100644 --- a/scripts/system/controllers/teleport.js +++ b/scripts/system/controllers/teleport.js @@ -85,6 +85,7 @@ function Trigger(hand) { } var coolInTimeout = null; +var ignoredEntities = []; var TELEPORTER_STATES = { IDLE: 'idle', @@ -239,11 +240,11 @@ function Teleporter() { // We might hit an invisible entity that is not a seat, so we need to do a second pass. // * In the second pass we pick against visible entities only. // - var intersection = Entities.findRayIntersection(pickRay, true, [], [this.targetEntity], false, true); + var intersection = Entities.findRayIntersection(pickRay, true, ignoredEntities, [this.targetEntity], false, true); var teleportLocationType = getTeleportTargetType(intersection); if (teleportLocationType === TARGET.INVISIBLE) { - intersection = Entities.findRayIntersection(pickRay, true, [], [this.targetEntity], true, true); + intersection = Entities.findRayIntersection(pickRay, true, ignoredEntities, [this.targetEntity], true, true); teleportLocationType = getTeleportTargetType(intersection); } @@ -513,7 +514,7 @@ function cleanup() { Script.scriptEnding.connect(cleanup); var isDisabled = false; -var handleHandMessages = function(channel, message, sender) { +var handleTeleportMessages = function(channel, message, sender) { var data; if (sender === MyAvatar.sessionUUID) { if (channel === 'Hifi-Teleport-Disabler') { @@ -529,12 +530,20 @@ var handleHandMessages = function(channel, message, sender) { if (message === 'none') { isDisabled = false; } - + } else if (channel === 'Hifi-Teleport-Ignore-Add' && !Uuid.isNull(message) && ignoredEntities.indexOf(message) === -1) { + ignoredEntities.push(message); + } else if (channel === 'Hifi-Teleport-Ignore-Remove' && !Uuid.isNull(message)) { + var removeIndex = ignoredEntities.indexOf(message); + if (removeIndex > -1) { + ignoredEntities.splice(removeIndex, 1); + } } } } Messages.subscribe('Hifi-Teleport-Disabler'); -Messages.messageReceived.connect(handleHandMessages); +Messages.subscribe('Hifi-Teleport-Ignore-Add'); +Messages.subscribe('Hifi-Teleport-Ignore-Remove'); +Messages.messageReceived.connect(handleTeleportMessages); }()); // END LOCAL_SCOPE From bdabc21d57a65a218961fa42c050da147c79b3e2 Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Sat, 18 Mar 2017 00:12:13 +0100 Subject: [PATCH 232/302] fix path issue --- .../marketplace/boppo/createElBoppo.js | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/unpublishedScripts/marketplace/boppo/createElBoppo.js b/unpublishedScripts/marketplace/boppo/createElBoppo.js index 6bef9bc0fd..362527e5f5 100644 --- a/unpublishedScripts/marketplace/boppo/createElBoppo.js +++ b/unpublishedScripts/marketplace/boppo/createElBoppo.js @@ -11,11 +11,15 @@ /* globals SCRIPT_IMPORT_PROPERTIES */ var MODELS_PATH = 'http://hifi-content.s3.amazonaws.com/DomainContent/Welcome%20Area/production/models/boxingRing/'; -var SCRIPT_PATH = this.isCleanupAndSpawnScript ? - 'http://s3-us-west-1.amazonaws.com/hifi-content/thoys/dev/2017/elBoppo/' : - Script.resolvePath(''); var WANT_CLEANUP_ON_SCRIPT_ENDING = false; +var getScriptPath = function(localPath) { + if (this.isCleanupAndSpawnScript) { + return 'http://s3-us-west-1.amazonaws.com/hifi-content/thoys/dev/2017/elBoppo/' + localPath; + } + return Script.resolvePath(localPath); +}; + var getCreatePosition = function() { // can either return position defined by resetScript or avatar position if (this.isCleanupAndSpawnScript) { @@ -40,7 +44,7 @@ var boxingRing = Entities.addEntity({ }, position: getCreatePosition(), scriptTimestamp: 1489612158459, - serverScripts: SCRIPT_PATH + 'boppoServer.js', + serverScripts: getScriptPath('boppoServer.js'), shapeType: 'static-mesh', type: 'Model', userData: JSON.stringify({ @@ -181,7 +185,7 @@ var boppoEntities = [ y: -1.52587890625e-05, z: -1.52587890625e-05 }, - script: SCRIPT_PATH + 'clownGloveDispenser.js', + script: getScriptPath('clownGloveDispenser.js'), shapeType: 'box', type: 'Zone', visible: false From 38bb48e72c41ad30bd825f2aa48f6ff89110aa74 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Sat, 18 Mar 2017 12:13:13 +1300 Subject: [PATCH 233/302] Disable Developer > Assets > ATP Asset Migration menu item --- interface/src/Menu.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 8e124d27c7..02b8f5de78 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -417,6 +417,8 @@ Menu::Menu() { } // Developer > Assets >>> + // Menu item is not currently needed but code should be kept in case it proves useful again at some stage. + /* MenuWrapper* assetDeveloperMenu = developerMenu->addMenu("Assets"); auto& atpMigrator = ATPAssetMigrator::getInstance(); atpMigrator.setDialogParent(this); @@ -424,6 +426,7 @@ Menu::Menu() { addActionToQMenuAndActionHash(assetDeveloperMenu, MenuOption::AssetMigration, 0, &atpMigrator, SLOT(loadEntityServerFile())); + */ // Developer > Avatar >>> MenuWrapper* avatarDebugMenu = developerMenu->addMenu("Avatar"); From b3603ce8b3331b1e3ad3c37270c74c61ca833713 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Fri, 17 Mar 2017 16:52:56 -0700 Subject: [PATCH 234/302] Entities.voxelsToMesh, Model.transformMesh, Model.appendMeshes, Model.meshToOBJ appear to work now --- .../src/RenderablePolyVoxEntityItem.cpp | 29 ++- .../src/RenderablePolyVoxEntityItem.h | 3 +- .../entities/src/EntityScriptingInterface.cpp | 23 ++- .../entities/src/EntityScriptingInterface.h | 8 + libraries/entities/src/PolyVoxEntityItem.cpp | 2 +- libraries/entities/src/PolyVoxEntityItem.h | 2 +- libraries/model/src/model/Geometry.cpp | 84 ++++++++- libraries/model/src/model/Geometry.h | 10 +- .../src/ModelScriptingInterface.cpp | 169 ++++++++++-------- 9 files changed, 238 insertions(+), 92 deletions(-) diff --git a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp index dd62122633..6f9310a36e 100644 --- a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp @@ -664,11 +664,8 @@ void RenderablePolyVoxEntityItem::setZTextureURL(QString zTextureURL) { } } -void RenderablePolyVoxEntityItem::render(RenderArgs* args) { - PerformanceTimer perfTimer("RenderablePolyVoxEntityItem::render"); - assert(getType() == EntityTypes::PolyVox); - Q_ASSERT(args->_batch); +bool RenderablePolyVoxEntityItem::updateDependents() { bool voxelDataDirty; bool volDataDirty; withWriteLock([&] { @@ -686,6 +683,17 @@ void RenderablePolyVoxEntityItem::render(RenderArgs* args) { recomputeMesh(); } + return !volDataDirty; +} + + +void RenderablePolyVoxEntityItem::render(RenderArgs* args) { + PerformanceTimer perfTimer("RenderablePolyVoxEntityItem::render"); + assert(getType() == EntityTypes::PolyVox); + Q_ASSERT(args->_batch); + + updateDependents(); + model::MeshPointer mesh; glm::vec3 voxelVolumeSize; withReadLock([&] { @@ -1571,13 +1579,22 @@ void RenderablePolyVoxEntityItem::locationChanged(bool tellPhysics) { scene->enqueuePendingChanges(pendingChanges); } -bool RenderablePolyVoxEntityItem::getMeshAsScriptValue(QScriptEngine *engine, QScriptValue& result) const { +bool RenderablePolyVoxEntityItem::getMeshAsScriptValue(QScriptEngine *engine, QScriptValue& result) { + if (!updateDependents()) { + return false; + } + bool success = false; MeshProxy* meshProxy = nullptr; + glm::mat4 transform = voxelToLocalMatrix(); withReadLock([&] { if (_meshInitialized) { success = true; - meshProxy = new MeshProxy(_mesh); + // the mesh will be in voxel-space. transform it into object-space + meshProxy = new MeshProxy( + _mesh->map([=](glm::vec3 position){ return glm::vec3(transform * glm::vec4(position, 1.0f)); }, + [=](glm::vec3 normal){ return glm::vec3(transform * glm::vec4(normal, 0.0f)); }, + [](uint32_t index){ return index; })); } }); result = meshToScriptValue(engine, meshProxy); diff --git a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.h b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.h index 7fe45f8d99..5eaf93e3ae 100644 --- a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.h +++ b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.h @@ -133,7 +133,7 @@ public: QByteArray volDataToArray(quint16 voxelXSize, quint16 voxelYSize, quint16 voxelZSize) const; void setMesh(model::MeshPointer mesh); - bool getMeshAsScriptValue(QScriptEngine *engine, QScriptValue& result) const override; + bool getMeshAsScriptValue(QScriptEngine *engine, QScriptValue& result) override; void setCollisionPoints(ShapeInfo::PointCollection points, AABox box); PolyVox::SimpleVolume* getVolData() { return _volData; } @@ -192,6 +192,7 @@ private: void cacheNeighbors(); void copyUpperEdgesFromNeighbors(); void bonkNeighbors(); + bool updateDependents(); }; bool inUserBounds(const PolyVox::SimpleVolume* vol, PolyVoxEntityItem::PolyVoxSurfaceStyle surfaceStyle, diff --git a/libraries/entities/src/EntityScriptingInterface.cpp b/libraries/entities/src/EntityScriptingInterface.cpp index 7eae5b2dc4..37e9e940b1 100644 --- a/libraries/entities/src/EntityScriptingInterface.cpp +++ b/libraries/entities/src/EntityScriptingInterface.cpp @@ -8,6 +8,10 @@ // 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 "EntityScriptingInterface.h" #include "EntityItemID.h" @@ -926,13 +930,16 @@ bool EntityScriptingInterface::setVoxelsInCuboid(QUuid entityID, const glm::vec3 void EntityScriptingInterface::voxelsToMesh(QUuid entityID, QScriptValue callback) { PROFILE_RANGE(script_entities, __FUNCTION__); - polyVoxWorker(entityID, [callback](PolyVoxEntityItem& polyVoxEntity) mutable { - QScriptValue mesh; - polyVoxEntity.getMeshAsScriptValue(callback.engine(), mesh); - QScriptValueList args { mesh }; - callback.call(QScriptValue(), args); + bool success; + QScriptValue mesh; + + polyVoxWorker(entityID, [&](PolyVoxEntityItem& polyVoxEntity) mutable { + success = polyVoxEntity.getMeshAsScriptValue(callback.engine(), mesh); return true; }); + + QScriptValueList args { mesh, success }; + callback.call(QScriptValue(), args); } bool EntityScriptingInterface::setAllPoints(QUuid entityID, const QVector& points) { @@ -1557,7 +1564,11 @@ glm::mat4 EntityScriptingInterface::getEntityTransform(const QUuid& entityID) { _entityTree->withReadLock([&] { EntityItemPointer entity = _entityTree->findEntityByEntityItemID(EntityItemID(entityID)); if (entity) { - result = entity->getEntityToWorldMatrix(); + glm::mat4 translation = glm::translate(entity->getPosition()); + glm::mat4 rotation = glm::mat4_cast(entity->getRotation()); + glm::mat4 registration = glm::translate(ENTITY_ITEM_DEFAULT_REGISTRATION_POINT - + entity->getRegistrationPoint()); + result = translation * rotation * registration; } }); } diff --git a/libraries/entities/src/EntityScriptingInterface.h b/libraries/entities/src/EntityScriptingInterface.h index be34317cd5..fa727aaa7a 100644 --- a/libraries/entities/src/EntityScriptingInterface.h +++ b/libraries/entities/src/EntityScriptingInterface.h @@ -294,6 +294,14 @@ public slots: Q_INVOKABLE bool AABoxIntersectsCapsule(const glm::vec3& low, const glm::vec3& dimensions, const glm::vec3& start, const glm::vec3& end, float radius); + + /**jsdoc + * Returns object to world transform, excluding scale + * + * @function Entities.getEntityTransform + * @param {EntityID} entityID The ID of the entity whose transform is to be returned + * @return {Mat4} Entity's object to world transform, excluding scale + */ Q_INVOKABLE glm::mat4 getEntityTransform(const QUuid& entityID); signals: diff --git a/libraries/entities/src/PolyVoxEntityItem.cpp b/libraries/entities/src/PolyVoxEntityItem.cpp index a827a9903d..90344d6c4b 100644 --- a/libraries/entities/src/PolyVoxEntityItem.cpp +++ b/libraries/entities/src/PolyVoxEntityItem.cpp @@ -243,6 +243,6 @@ const QByteArray PolyVoxEntityItem::getVoxelData() const { return voxelDataCopy; } -bool PolyVoxEntityItem::getMeshAsScriptValue(QScriptEngine *engine, QScriptValue& result) const { +bool PolyVoxEntityItem::getMeshAsScriptValue(QScriptEngine *engine, QScriptValue& result) { return false; } diff --git a/libraries/entities/src/PolyVoxEntityItem.h b/libraries/entities/src/PolyVoxEntityItem.h index ae722b2add..943d273452 100644 --- a/libraries/entities/src/PolyVoxEntityItem.h +++ b/libraries/entities/src/PolyVoxEntityItem.h @@ -133,7 +133,7 @@ class PolyVoxEntityItem : public EntityItem { void setVoxelDataDirty(bool value) { withWriteLock([&] { _voxelDataDirty = value; }); } virtual void recomputeMesh() {}; - virtual bool getMeshAsScriptValue(QScriptEngine *engine, QScriptValue& result) const; + virtual bool getMeshAsScriptValue(QScriptEngine *engine, QScriptValue& result); protected: glm::vec3 _voxelVolumeSize; // this is always 3 bytes diff --git a/libraries/model/src/model/Geometry.cpp b/libraries/model/src/model/Geometry.cpp index 2bb6cfa436..4e5134a7d7 100755 --- a/libraries/model/src/model/Geometry.cpp +++ b/libraries/model/src/model/Geometry.cpp @@ -117,7 +117,7 @@ Box Mesh::evalPartsBound(int partStart, int partEnd) const { auto partItEnd = _partBuffer.cbegin() + partEnd; for (;part != partItEnd; part++) { - + Box partBound; auto index = _indexBuffer.cbegin() + (*part)._startIndex; auto endIndex = index + (*part)._numIndices; @@ -134,6 +134,87 @@ Box Mesh::evalPartsBound(int partStart, int partEnd) const { return totalBound; } + +model::MeshPointer Mesh::map(std::function vertexFunc, + std::function normalFunc, + std::function indexFunc) { + int attributeTypeNormal = gpu::Stream::InputSlot::NORMAL; // libraries/gpu/src/gpu/Stream.h + + gpu::Resource::Size vertexSize = getNumVertices() * sizeof(glm::vec3); + unsigned char* resultVertexData = new unsigned char[vertexSize]; + unsigned char* vertexDataCursor = resultVertexData; + + gpu::Resource::Size normalSize = getNumAttributes() * sizeof(glm::vec3); + unsigned char* resultNormalData = new unsigned char[normalSize]; + unsigned char* normalDataCursor = resultNormalData; + + gpu::Resource::Size indexSize = getNumIndices() * sizeof(uint32_t); + unsigned char* resultIndexData = new unsigned char[indexSize]; + unsigned char* indexDataCursor = resultIndexData; + + // vertex data + const gpu::BufferView& vertexBufferView = getVertexBuffer(); + gpu::BufferView::Index numVertices = (gpu::BufferView::Index)getNumVertices(); + for (gpu::BufferView::Index i = 0; i < numVertices; i ++) { + glm::vec3 pos = vertexFunc(vertexBufferView.get(i)); + memcpy(vertexDataCursor, &pos, sizeof(pos)); + vertexDataCursor += sizeof(pos); + } + + // normal data + const gpu::BufferView& normalsBufferView = getAttributeBuffer(attributeTypeNormal); + gpu::BufferView::Index numNormals = (gpu::BufferView::Index)getNumAttributes(); + for (gpu::BufferView::Index i = 0; i < numNormals; i ++) { + glm::vec3 normal = normalFunc(normalsBufferView.get(i)); + memcpy(normalDataCursor, &normal, sizeof(normal)); + normalDataCursor += sizeof(normal); + } + // TODO -- other attributes + + // face data + const gpu::BufferView& indexBufferView = getIndexBuffer(); + gpu::BufferView::Index numIndexes = (gpu::BufferView::Index)getNumIndices(); + for (gpu::BufferView::Index i = 0; i < numIndexes; i ++) { + uint32_t index = indexFunc(indexBufferView.get(i)); + memcpy(indexDataCursor, &index, sizeof(index)); + indexDataCursor += sizeof(index); + } + + model::MeshPointer result(new model::Mesh()); + + gpu::Element vertexElement = gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ); + gpu::Buffer* resultVertexBuffer = new gpu::Buffer(vertexSize, resultVertexData); + gpu::BufferPointer resultVertexBufferPointer(resultVertexBuffer); + gpu::BufferView resultVertexBufferView(resultVertexBufferPointer, vertexElement); + result->setVertexBuffer(resultVertexBufferView); + + gpu::Element normalElement = gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ); + gpu::Buffer* resultNormalsBuffer = new gpu::Buffer(normalSize, resultNormalData); + gpu::BufferPointer resultNormalsBufferPointer(resultNormalsBuffer); + gpu::BufferView resultNormalsBufferView(resultNormalsBufferPointer, normalElement); + result->addAttribute(attributeTypeNormal, resultNormalsBufferView); + + gpu::Element indexElement = gpu::Element(gpu::SCALAR, gpu::UINT32, gpu::RAW); + gpu::Buffer* resultIndexesBuffer = new gpu::Buffer(indexSize, resultIndexData); + gpu::BufferPointer resultIndexesBufferPointer(resultIndexesBuffer); + gpu::BufferView resultIndexesBufferView(resultIndexesBufferPointer, indexElement); + result->setIndexBuffer(resultIndexesBufferView); + + + // TODO -- shouldn't assume just one part + + std::vector parts; + parts.emplace_back(model::Mesh::Part((model::Index)0, // startIndex + (model::Index)result->getNumIndices(), // numIndices + (model::Index)0, // baseVertex + model::Mesh::TRIANGLES)); // topology + result->setPartBuffer(gpu::BufferView(new gpu::Buffer(parts.size() * sizeof(model::Mesh::Part), + (gpu::Byte*) parts.data()), gpu::Element::PART_DRAWCALL)); + + return result; +} + + Geometry::Geometry() { } @@ -148,4 +229,3 @@ Geometry::~Geometry() { void Geometry::setMesh(const MeshPointer& mesh) { _mesh = mesh; } - diff --git a/libraries/model/src/model/Geometry.h b/libraries/model/src/model/Geometry.h index 4256f0be03..9a20861ea7 100755 --- a/libraries/model/src/model/Geometry.h +++ b/libraries/model/src/model/Geometry.h @@ -25,6 +25,10 @@ typedef AABox Box; typedef std::vector< Box > Boxes; typedef glm::vec3 Vec3; +class Mesh; +using MeshPointer = std::shared_ptr< Mesh >; + + class Mesh { public: const static Index PRIMITIVE_RESTART_INDEX = -1; @@ -114,6 +118,11 @@ public: static gpu::Primitive topologyToPrimitive(Topology topo) { return static_cast(topo); } + // create a copy of this mesh after passing its vertices, normals, and indexes though the provided functions + MeshPointer map(std::function vertexFunc, + std::function normalFunc, + std::function indexFunc); + protected: gpu::Stream::FormatPointer _vertexFormat; @@ -130,7 +139,6 @@ protected: void evalVertexStream(); }; -using MeshPointer = std::shared_ptr< Mesh >; class Geometry { diff --git a/libraries/script-engine/src/ModelScriptingInterface.cpp b/libraries/script-engine/src/ModelScriptingInterface.cpp index 86a651b6b2..71b82a2d60 100644 --- a/libraries/script-engine/src/ModelScriptingInterface.cpp +++ b/libraries/script-engine/src/ModelScriptingInterface.cpp @@ -151,84 +151,105 @@ QScriptValue ModelScriptingInterface::appendMeshes(MeshProxyList in) { } +// QScriptValue ModelScriptingInterface::transformMesh(glm::mat4 transform, MeshProxy* meshProxy) { +// int attributeTypeNormal = gpu::Stream::InputSlot::NORMAL; // libraries/gpu/src/gpu/Stream.h + +// if (!meshProxy) { +// return QScriptValue(false); +// } + +// MeshPointer mesh = meshProxy->getMeshPointer(); + +// gpu::Resource::Size vertexSize = mesh->getNumVertices() * sizeof(glm::vec3); +// unsigned char* resultVertexData = new unsigned char[vertexSize]; +// unsigned char* vertexDataCursor = resultVertexData; + +// gpu::Resource::Size normalSize = mesh->getNumAttributes() * sizeof(glm::vec3); +// unsigned char* resultNormalData = new unsigned char[normalSize]; +// unsigned char* normalDataCursor = resultNormalData; + +// gpu::Resource::Size indexSize = mesh->getNumIndices() * sizeof(uint32_t); +// unsigned char* resultIndexData = new unsigned char[indexSize]; +// unsigned char* indexDataCursor = resultIndexData; + +// // vertex data +// const gpu::BufferView& vertexBufferView = mesh->getVertexBuffer(); +// gpu::BufferView::Index numVertices = (gpu::BufferView::Index)mesh->getNumVertices(); +// for (gpu::BufferView::Index i = 0; i < numVertices; i ++) { +// glm::vec3 pos = vertexBufferView.get(i); +// pos = glm::vec3(transform * glm::vec4(pos, 1.0f)); +// memcpy(vertexDataCursor, &pos, sizeof(pos)); +// vertexDataCursor += sizeof(pos); +// } + +// // normal data +// const gpu::BufferView& normalsBufferView = mesh->getAttributeBuffer(attributeTypeNormal); +// gpu::BufferView::Index numNormals = (gpu::BufferView::Index)mesh->getNumAttributes(); +// for (gpu::BufferView::Index i = 0; i < numNormals; i ++) { +// glm::vec3 normal = normalsBufferView.get(i); +// normal = glm::vec3(transform * glm::vec4(normal, 0.0f)); +// memcpy(normalDataCursor, &normal, sizeof(normal)); +// normalDataCursor += sizeof(normal); +// } +// // TODO -- other attributes + +// // face data +// const gpu::BufferView& indexBufferView = mesh->getIndexBuffer(); +// gpu::BufferView::Index numIndexes = (gpu::BufferView::Index)mesh->getNumIndices(); +// for (gpu::BufferView::Index i = 0; i < numIndexes; i ++) { +// uint32_t index = indexBufferView.get(i); +// memcpy(indexDataCursor, &index, sizeof(index)); +// indexDataCursor += sizeof(index); +// } + +// model::MeshPointer result(new model::Mesh()); + +// gpu::Element vertexElement = gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ); +// gpu::Buffer* resultVertexBuffer = new gpu::Buffer(vertexSize, resultVertexData); +// gpu::BufferPointer resultVertexBufferPointer(resultVertexBuffer); +// gpu::BufferView resultVertexBufferView(resultVertexBufferPointer, vertexElement); +// result->setVertexBuffer(resultVertexBufferView); + +// gpu::Element normalElement = gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ); +// gpu::Buffer* resultNormalsBuffer = new gpu::Buffer(normalSize, resultNormalData); +// gpu::BufferPointer resultNormalsBufferPointer(resultNormalsBuffer); +// gpu::BufferView resultNormalsBufferView(resultNormalsBufferPointer, normalElement); +// result->addAttribute(attributeTypeNormal, resultNormalsBufferView); + +// gpu::Element indexElement = gpu::Element(gpu::SCALAR, gpu::UINT32, gpu::RAW); +// gpu::Buffer* resultIndexesBuffer = new gpu::Buffer(indexSize, resultIndexData); +// gpu::BufferPointer resultIndexesBufferPointer(resultIndexesBuffer); +// gpu::BufferView resultIndexesBufferView(resultIndexesBufferPointer, indexElement); +// result->setIndexBuffer(resultIndexesBufferView); + + +// std::vector parts; +// parts.emplace_back(model::Mesh::Part((model::Index)0, // startIndex +// (model::Index)result->getNumIndices(), // numIndices +// (model::Index)0, // baseVertex +// model::Mesh::TRIANGLES)); // topology +// result->setPartBuffer(gpu::BufferView(new gpu::Buffer(parts.size() * sizeof(model::Mesh::Part), +// (gpu::Byte*) parts.data()), gpu::Element::PART_DRAWCALL)); + + + +// MeshProxy* resultProxy = new MeshProxy(result); +// return meshToScriptValue(_modelScriptEngine, resultProxy); +// } + + QScriptValue ModelScriptingInterface::transformMesh(glm::mat4 transform, MeshProxy* meshProxy) { - int attributeTypeNormal = gpu::Stream::InputSlot::NORMAL; // libraries/gpu/src/gpu/Stream.h - + if (!meshProxy) { + return QScriptValue(false); + } MeshPointer mesh = meshProxy->getMeshPointer(); - - gpu::Resource::Size vertexSize = mesh->getNumVertices() * sizeof(glm::vec3); - unsigned char* resultVertexData = new unsigned char[vertexSize]; - unsigned char* vertexDataCursor = resultVertexData; - - gpu::Resource::Size normalSize = mesh->getNumAttributes() * sizeof(glm::vec3); - unsigned char* resultNormalData = new unsigned char[normalSize]; - unsigned char* normalDataCursor = resultNormalData; - - gpu::Resource::Size indexSize = mesh->getNumIndices() * sizeof(uint32_t); - unsigned char* resultIndexData = new unsigned char[indexSize]; - unsigned char* indexDataCursor = resultIndexData; - - // vertex data - const gpu::BufferView& vertexBufferView = mesh->getVertexBuffer(); - gpu::BufferView::Index numVertices = (gpu::BufferView::Index)mesh->getNumVertices(); - for (gpu::BufferView::Index i = 0; i < numVertices; i ++) { - glm::vec3 pos = vertexBufferView.get(i); - pos = glm::vec3(transform * glm::vec4(pos, 0.0f)); - memcpy(vertexDataCursor, &pos, sizeof(pos)); - vertexDataCursor += sizeof(pos); + if (!mesh) { + return QScriptValue(false); } - // normal data - const gpu::BufferView& normalsBufferView = mesh->getAttributeBuffer(attributeTypeNormal); - gpu::BufferView::Index numNormals = (gpu::BufferView::Index)mesh->getNumAttributes(); - for (gpu::BufferView::Index i = 0; i < numNormals; i ++) { - glm::vec3 normal = normalsBufferView.get(i); - normal = glm::vec3(transform * glm::vec4(normal, 0.0f)); - memcpy(normalDataCursor, &normal, sizeof(normal)); - normalDataCursor += sizeof(normal); - } - // TODO -- other attributes - - // face data - const gpu::BufferView& indexBufferView = mesh->getIndexBuffer(); - gpu::BufferView::Index numIndexes = (gpu::BufferView::Index)mesh->getNumIndices(); - for (gpu::BufferView::Index i = 0; i < numIndexes; i ++) { - uint32_t index = indexBufferView.get(i); - memcpy(indexDataCursor, &index, sizeof(index)); - indexDataCursor += sizeof(index); - } - - model::MeshPointer result(new model::Mesh()); - - gpu::Element vertexElement = gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ); - gpu::Buffer* resultVertexBuffer = new gpu::Buffer(vertexSize, resultVertexData); - gpu::BufferPointer resultVertexBufferPointer(resultVertexBuffer); - gpu::BufferView resultVertexBufferView(resultVertexBufferPointer, vertexElement); - result->setVertexBuffer(resultVertexBufferView); - - gpu::Element normalElement = gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ); - gpu::Buffer* resultNormalsBuffer = new gpu::Buffer(normalSize, resultNormalData); - gpu::BufferPointer resultNormalsBufferPointer(resultNormalsBuffer); - gpu::BufferView resultNormalsBufferView(resultNormalsBufferPointer, normalElement); - result->addAttribute(attributeTypeNormal, resultNormalsBufferView); - - gpu::Element indexElement = gpu::Element(gpu::SCALAR, gpu::UINT32, gpu::RAW); - gpu::Buffer* resultIndexesBuffer = new gpu::Buffer(indexSize, resultIndexData); - gpu::BufferPointer resultIndexesBufferPointer(resultIndexesBuffer); - gpu::BufferView resultIndexesBufferView(resultIndexesBufferPointer, indexElement); - result->setIndexBuffer(resultIndexesBufferView); - - - std::vector parts; - parts.emplace_back(model::Mesh::Part((model::Index)0, // startIndex - (model::Index)result->getNumIndices(), // numIndices - (model::Index)0, // baseVertex - model::Mesh::TRIANGLES)); // topology - result->setPartBuffer(gpu::BufferView(new gpu::Buffer(parts.size() * sizeof(model::Mesh::Part), - (gpu::Byte*) parts.data()), gpu::Element::PART_DRAWCALL)); - - - + model::MeshPointer result = mesh->map([&](glm::vec3 position){ return glm::vec3(transform * glm::vec4(position, 1.0f)); }, + [&](glm::vec3 normal){ return glm::vec3(transform * glm::vec4(normal, 0.0f)); }, + [&](uint32_t index){ return index; }); MeshProxy* resultProxy = new MeshProxy(result); return meshToScriptValue(_modelScriptEngine, resultProxy); } From ab7d82edb6ba5f9a32770afd8cc941f639f6230c Mon Sep 17 00:00:00 2001 From: David Rowe Date: Sat, 18 Mar 2017 12:56:34 +1300 Subject: [PATCH 235/302] Use #ifdef instead of comment --- interface/src/Menu.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 02b8f5de78..b1d97e6467 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -418,7 +418,8 @@ Menu::Menu() { // Developer > Assets >>> // Menu item is not currently needed but code should be kept in case it proves useful again at some stage. - /* +//#define WANT_ASSET_MIGRATION +#ifdef WANT_ASSET_MIGRATION MenuWrapper* assetDeveloperMenu = developerMenu->addMenu("Assets"); auto& atpMigrator = ATPAssetMigrator::getInstance(); atpMigrator.setDialogParent(this); @@ -426,7 +427,7 @@ Menu::Menu() { addActionToQMenuAndActionHash(assetDeveloperMenu, MenuOption::AssetMigration, 0, &atpMigrator, SLOT(loadEntityServerFile())); - */ +#endif // Developer > Avatar >>> MenuWrapper* avatarDebugMenu = developerMenu->addMenu("Avatar"); From 535d4f5deaa8fa24ca43f03e85536641004018e1 Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Sat, 18 Mar 2017 00:57:50 +0100 Subject: [PATCH 236/302] fixup links --- unpublishedScripts/marketplace/boppo/boppoClownEntity.js | 2 +- unpublishedScripts/marketplace/boppo/boppoServer.js | 8 ++++---- .../marketplace/boppo/clownGloveDispenser.js | 4 ++-- unpublishedScripts/marketplace/boppo/createElBoppo.js | 4 ++-- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/unpublishedScripts/marketplace/boppo/boppoClownEntity.js b/unpublishedScripts/marketplace/boppo/boppoClownEntity.js index ef01e47f6f..cf0e157a18 100644 --- a/unpublishedScripts/marketplace/boppo/boppoClownEntity.js +++ b/unpublishedScripts/marketplace/boppo/boppoClownEntity.js @@ -11,7 +11,7 @@ /* globals BoppoClownEntity:true LookAtTarget */ (function() { - var SFX_PREFIX = 'http://hifi-content.s3-us-west-1.amazonaws.com/caitlyn/production/elBoppo/sfx/'; + var SFX_PREFIX = 'https://hifi-content.s3-us-west-1.amazonaws.com/caitlyn/production/elBoppo/sfx/'; var CHANNEL_PREFIX = 'io.highfidelity.boppo_server_'; var PUNCH_SOUNDS = [ 'punch_1.wav', diff --git a/unpublishedScripts/marketplace/boppo/boppoServer.js b/unpublishedScripts/marketplace/boppo/boppoServer.js index ebe938c043..1abc5a135c 100644 --- a/unpublishedScripts/marketplace/boppo/boppoServer.js +++ b/unpublishedScripts/marketplace/boppo/boppoServer.js @@ -11,7 +11,7 @@ /* globals BoppoServer:true */ (function() { - var SFX_PREFIX = 'http://hifi-content.s3-us-west-1.amazonaws.com/caitlyn/production/elBoppo/sfx/'; + var SFX_PREFIX = 'https://hifi-content.s3-us-west-1.amazonaws.com/caitlyn/production/elBoppo/sfx/'; var CLOWN_LAUGHS = [ 'clown_laugh_1.wav', 'clown_laugh_2.wav', @@ -196,9 +196,9 @@ var boppoBaseProperties = Entities.getEntityProperties(_entityID, ['position', 'rotation']); _boppoClownID = Entities.addEntity({ angularDamping: 0.0, - collisionSoundURL: 'http://hifi-content.s3.amazonaws.com/caitlyn/production/elBoppo/51460__andre-rocha-nascimento__basket-ball-01-bounce.wav', + collisionSoundURL: 'https://hifi-content.s3.amazonaws.com/caitlyn/production/elBoppo/51460__andre-rocha-nascimento__basket-ball-01-bounce.wav', collisionsWillMove: true, - compoundShapeURL: 'http://hifi-content.s3.amazonaws.com/caitlyn/production/elBoppo/bopo_phys.obj', + compoundShapeURL: 'https://hifi-content.s3.amazonaws.com/caitlyn/production/elBoppo/bopo_phys.obj', damping: 1.0, density: 10000, dimensions: { @@ -213,7 +213,7 @@ y: -25, z: 0 }, - modelURL: 'http://hifi-content.s3.amazonaws.com/caitlyn/production/elBoppo/elBoppo3_VR.fbx', + modelURL: 'https://hifi-content.s3.amazonaws.com/caitlyn/production/elBoppo/elBoppo3_VR.fbx', name: 'El Boppo the Punching Bag Clown', registrationPoint: { x: 0.5, diff --git a/unpublishedScripts/marketplace/boppo/clownGloveDispenser.js b/unpublishedScripts/marketplace/boppo/clownGloveDispenser.js index 42675d609a..cd0a0c0614 100644 --- a/unpublishedScripts/marketplace/boppo/clownGloveDispenser.js +++ b/unpublishedScripts/marketplace/boppo/clownGloveDispenser.js @@ -35,7 +35,7 @@ y: -9.8, z: 0 }, - modelURL: "http://hifi-content.s3.amazonaws.com/caitlyn/production/elBoppo/LFT_glove_VR3.fbx", + modelURL: "https://hifi-content.s3.amazonaws.com/caitlyn/production/elBoppo/LFT_glove_VR3.fbx", name: "Boxing Glove - Left", registrationPoint: { x: 0.5, @@ -76,7 +76,7 @@ y: -9.8, z: 0 }, - modelURL: "http://hifi-content.s3.amazonaws.com/caitlyn/production/elBoppo/RT_glove_VR2.fbx", + modelURL: "https://hifi-content.s3.amazonaws.com/caitlyn/production/elBoppo/RT_glove_VR2.fbx", name: "Boxing Glove - Right", registrationPoint: { x: 0.5, diff --git a/unpublishedScripts/marketplace/boppo/createElBoppo.js b/unpublishedScripts/marketplace/boppo/createElBoppo.js index 362527e5f5..4df6a2acda 100644 --- a/unpublishedScripts/marketplace/boppo/createElBoppo.js +++ b/unpublishedScripts/marketplace/boppo/createElBoppo.js @@ -10,12 +10,12 @@ /* globals SCRIPT_IMPORT_PROPERTIES */ -var MODELS_PATH = 'http://hifi-content.s3.amazonaws.com/DomainContent/Welcome%20Area/production/models/boxingRing/'; +var MODELS_PATH = 'https://hifi-content.s3.amazonaws.com/DomainContent/Welcome%20Area/production/models/boxingRing/'; var WANT_CLEANUP_ON_SCRIPT_ENDING = false; var getScriptPath = function(localPath) { if (this.isCleanupAndSpawnScript) { - return 'http://s3-us-west-1.amazonaws.com/hifi-content/thoys/dev/2017/elBoppo/' + localPath; + return 'https://hifi-content.s3.amazonaws.com/DomainContent/Welcome%20Area/Scripts/boppo/' + localPath; } return Script.resolvePath(localPath); }; From a8740614e23db29b5e6c7a67ea18e0e9c8533e6e Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Sat, 18 Mar 2017 01:22:06 +0100 Subject: [PATCH 237/302] CR Fixes --- scripts/system/controllers/teleport.js | 4 ++-- unpublishedScripts/marketplace/boppo/boppoClownEntity.js | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/scripts/system/controllers/teleport.js b/scripts/system/controllers/teleport.js index e56562a18a..1c6c9af272 100644 --- a/scripts/system/controllers/teleport.js +++ b/scripts/system/controllers/teleport.js @@ -240,11 +240,11 @@ function Teleporter() { // We might hit an invisible entity that is not a seat, so we need to do a second pass. // * In the second pass we pick against visible entities only. // - var intersection = Entities.findRayIntersection(pickRay, true, ignoredEntities, [this.targetEntity], false, true); + var intersection = Entities.findRayIntersection(pickRay, true, [], [this.targetEntity].concat(ignoredEntities), false, true); var teleportLocationType = getTeleportTargetType(intersection); if (teleportLocationType === TARGET.INVISIBLE) { - intersection = Entities.findRayIntersection(pickRay, true, ignoredEntities, [this.targetEntity], true, true); + intersection = Entities.findRayIntersection(pickRay, true, [], [this.targetEntity].concat(ignoredEntities), true, true); teleportLocationType = getTeleportTargetType(intersection); } diff --git a/unpublishedScripts/marketplace/boppo/boppoClownEntity.js b/unpublishedScripts/marketplace/boppo/boppoClownEntity.js index cf0e157a18..2fa4441e52 100644 --- a/unpublishedScripts/marketplace/boppo/boppoClownEntity.js +++ b/unpublishedScripts/marketplace/boppo/boppoClownEntity.js @@ -19,7 +19,7 @@ ]; var PUNCH_COOLDOWN = 300; - Script.include(Script.resolvePath('lookAtEntity.js?v2')); + Script.include('lookAtEntity.js'); BoppoClownEntity = function() { var _this, From a9b1a3866587ecb01a33577faa1a1d9ad92e0485 Mon Sep 17 00:00:00 2001 From: Triplelexx Date: Sat, 18 Mar 2017 00:53:13 +0000 Subject: [PATCH 238/302] remove Mini Mirror from View menu Avatar inputs only contains the audio meter now. --- interface/resources/qml/AvatarInputs.qml | 57 +------ interface/src/Application.cpp | 153 +++++------------- interface/src/Application.h | 4 - interface/src/Menu.cpp | 3 - interface/src/Menu.h | 1 - interface/src/ui/ApplicationOverlay.cpp | 43 ----- interface/src/ui/ApplicationOverlay.h | 3 - interface/src/ui/AvatarInputs.cpp | 20 --- interface/src/ui/AvatarInputs.h | 6 - .../render-utils/src/FramebufferCache.cpp | 12 -- libraries/render-utils/src/FramebufferCache.h | 5 - 11 files changed, 41 insertions(+), 266 deletions(-) diff --git a/interface/resources/qml/AvatarInputs.qml b/interface/resources/qml/AvatarInputs.qml index 384504aaa0..28f3c0c7b9 100644 --- a/interface/resources/qml/AvatarInputs.qml +++ b/interface/resources/qml/AvatarInputs.qml @@ -15,12 +15,11 @@ import Qt.labs.settings 1.0 Hifi.AvatarInputs { id: root objectName: "AvatarInputs" - width: mirrorWidth - height: controls.height + mirror.height + width: rootWidth + height: controls.height x: 10; y: 5 - readonly property int mirrorHeight: 215 - readonly property int mirrorWidth: 265 + readonly property int rootWidth: 265 readonly property int iconSize: 24 readonly property int iconPadding: 5 @@ -39,61 +38,15 @@ Hifi.AvatarInputs { anchors.fill: parent } - Item { - id: mirror - width: root.mirrorWidth - height: root.mirrorVisible ? root.mirrorHeight : 0 - visible: root.mirrorVisible - anchors.left: parent.left - clip: true - - Image { - id: closeMirror - visible: hover.containsMouse - width: root.iconSize - height: root.iconSize - anchors.top: parent.top - anchors.topMargin: root.iconPadding - anchors.left: parent.left - anchors.leftMargin: root.iconPadding - source: "../images/close.svg" - MouseArea { - anchors.fill: parent - onClicked: { - root.closeMirror(); - } - } - } - - Image { - id: zoomIn - visible: hover.containsMouse - width: root.iconSize - height: root.iconSize - anchors.bottom: parent.bottom - anchors.bottomMargin: root.iconPadding - anchors.left: parent.left - anchors.leftMargin: root.iconPadding - source: root.mirrorZoomed ? "../images/minus.svg" : "../images/plus.svg" - MouseArea { - anchors.fill: parent - onClicked: { - root.toggleZoom(); - } - } - } - } - Item { id: controls - width: root.mirrorWidth + width: root.rootWidth height: 44 visible: root.showAudioTools - anchors.top: mirror.bottom Rectangle { anchors.fill: parent - color: root.mirrorVisible ? (root.audioClipping ? "red" : "#696969") : "#00000000" + color: "#00000000" Item { id: audioMeter diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 8cd5a9564a..d6516db8f0 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -213,14 +213,7 @@ static const QString FBX_EXTENSION = ".fbx"; static const QString OBJ_EXTENSION = ".obj"; static const QString AVA_JSON_EXTENSION = ".ava.json"; -static const int MIRROR_VIEW_TOP_PADDING = 5; -static const int MIRROR_VIEW_LEFT_PADDING = 10; -static const int MIRROR_VIEW_WIDTH = 265; -static const int MIRROR_VIEW_HEIGHT = 215; static const float MIRROR_FULLSCREEN_DISTANCE = 0.389f; -static const float MIRROR_REARVIEW_DISTANCE = 0.722f; -static const float MIRROR_REARVIEW_BODY_DISTANCE = 2.56f; -static const float MIRROR_FIELD_OF_VIEW = 30.0f; static const quint64 TOO_LONG_SINCE_LAST_SEND_DOWNSTREAM_AUDIO_STATS = 1 * USECS_PER_SECOND; @@ -565,7 +558,6 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo _entityClipboardRenderer(false, this, this), _entityClipboard(new EntityTree()), _lastQueriedTime(usecTimestampNow()), - _mirrorViewRect(QRect(MIRROR_VIEW_LEFT_PADDING, MIRROR_VIEW_TOP_PADDING, MIRROR_VIEW_WIDTH, MIRROR_VIEW_HEIGHT)), _previousScriptLocation("LastScriptLocation", DESKTOP_LOCATION), _fieldOfView("fieldOfView", DEFAULT_FIELD_OF_VIEW_DEGREES), _hmdTabletScale("hmdTabletScale", DEFAULT_HMD_TABLET_SCALE_PERCENT), @@ -2119,21 +2111,6 @@ void Application::paintGL() { batch.resetStages(); }); - auto inputs = AvatarInputs::getInstance(); - if (inputs->mirrorVisible()) { - PerformanceTimer perfTimer("Mirror"); - - renderArgs._renderMode = RenderArgs::MIRROR_RENDER_MODE; - renderArgs._blitFramebuffer = DependencyManager::get()->getSelfieFramebuffer(); - - _mirrorViewRect.moveTo(inputs->x(), inputs->y()); - - renderRearViewMirror(&renderArgs, _mirrorViewRect, inputs->mirrorZoomed()); - - renderArgs._blitFramebuffer.reset(); - renderArgs._renderMode = RenderArgs::DEFAULT_RENDER_MODE; - } - { PerformanceTimer perfTimer("renderOverlay"); // NOTE: There is no batch associated with this renderArgs @@ -2887,49 +2864,45 @@ void Application::keyPressEvent(QKeyEvent* event) { #endif case Qt::Key_H: - if (isShifted) { - Menu::getInstance()->triggerOption(MenuOption::MiniMirror); - } else { - // whenever switching to/from full screen mirror from the keyboard, remember - // the state you were in before full screen mirror, and return to that. - auto previousMode = _myCamera.getMode(); - if (previousMode != CAMERA_MODE_MIRROR) { - switch (previousMode) { - case CAMERA_MODE_FIRST_PERSON: - _returnFromFullScreenMirrorTo = MenuOption::FirstPerson; - break; - case CAMERA_MODE_THIRD_PERSON: - _returnFromFullScreenMirrorTo = MenuOption::ThirdPerson; - break; - - // FIXME - it's not clear that these modes make sense to return to... - case CAMERA_MODE_INDEPENDENT: - _returnFromFullScreenMirrorTo = MenuOption::IndependentMode; - break; - case CAMERA_MODE_ENTITY: - _returnFromFullScreenMirrorTo = MenuOption::CameraEntityMode; - break; - - default: - _returnFromFullScreenMirrorTo = MenuOption::ThirdPerson; - break; - } - } - - bool isMirrorChecked = Menu::getInstance()->isOptionChecked(MenuOption::FullscreenMirror); - Menu::getInstance()->setIsOptionChecked(MenuOption::FullscreenMirror, !isMirrorChecked); - if (isMirrorChecked) { - - // if we got here without coming in from a non-Full Screen mirror case, then our - // _returnFromFullScreenMirrorTo is unknown. In that case we'll go to the old - // behavior of returning to ThirdPerson - if (_returnFromFullScreenMirrorTo.isEmpty()) { + // whenever switching to/from full screen mirror from the keyboard, remember + // the state you were in before full screen mirror, and return to that. + auto previousMode = _myCamera.getMode(); + if (previousMode != CAMERA_MODE_MIRROR) { + switch (previousMode) { + case CAMERA_MODE_FIRST_PERSON: + _returnFromFullScreenMirrorTo = MenuOption::FirstPerson; + break; + case CAMERA_MODE_THIRD_PERSON: _returnFromFullScreenMirrorTo = MenuOption::ThirdPerson; - } - Menu::getInstance()->setIsOptionChecked(_returnFromFullScreenMirrorTo, true); + break; + + // FIXME - it's not clear that these modes make sense to return to... + case CAMERA_MODE_INDEPENDENT: + _returnFromFullScreenMirrorTo = MenuOption::IndependentMode; + break; + case CAMERA_MODE_ENTITY: + _returnFromFullScreenMirrorTo = MenuOption::CameraEntityMode; + break; + + default: + _returnFromFullScreenMirrorTo = MenuOption::ThirdPerson; + break; } - cameraMenuChanged(); } + + bool isMirrorChecked = Menu::getInstance()->isOptionChecked(MenuOption::FullscreenMirror); + Menu::getInstance()->setIsOptionChecked(MenuOption::FullscreenMirror, !isMirrorChecked); + if (isMirrorChecked) { + + // if we got here without coming in from a non-Full Screen mirror case, then our + // _returnFromFullScreenMirrorTo is unknown. In that case we'll go to the old + // behavior of returning to ThirdPerson + if (_returnFromFullScreenMirrorTo.isEmpty()) { + _returnFromFullScreenMirrorTo = MenuOption::ThirdPerson; + } + Menu::getInstance()->setIsOptionChecked(_returnFromFullScreenMirrorTo, true); + } + cameraMenuChanged(); break; case Qt::Key_P: { if (!(isShifted || isMeta || isOption)) { @@ -3845,8 +3818,6 @@ void Application::init() { DependencyManager::get()->init(); _myCamera.setMode(CAMERA_MODE_FIRST_PERSON); - _mirrorCamera.setMode(CAMERA_MODE_MIRROR); - _timerStart.start(); _lastTimeUpdated.start(); @@ -5122,58 +5093,6 @@ void Application::displaySide(RenderArgs* renderArgs, Camera& theCamera, bool se activeRenderingThread = nullptr; } -void Application::renderRearViewMirror(RenderArgs* renderArgs, const QRect& region, bool isZoomed) { - auto originalViewport = renderArgs->_viewport; - // Grab current viewport to reset it at the end - - float aspect = (float)region.width() / region.height(); - float fov = MIRROR_FIELD_OF_VIEW; - - auto myAvatar = getMyAvatar(); - - // bool eyeRelativeCamera = false; - if (!isZoomed) { - _mirrorCamera.setPosition(myAvatar->getChestPosition() + - myAvatar->getOrientation() * glm::vec3(0.0f, 0.0f, -1.0f) * MIRROR_REARVIEW_BODY_DISTANCE * myAvatar->getScale()); - - } else { // HEAD zoom level - // FIXME note that the positioning of the camera relative to the avatar can suffer limited - // precision as the user's position moves further away from the origin. Thus at - // /1e7,1e7,1e7 (well outside the buildable volume) the mirror camera veers and sways - // wildly as you rotate your avatar because the floating point values are becoming - // larger, squeezing out the available digits of precision you have available at the - // human scale for camera positioning. - - // Previously there was a hack to correct this using the mechanism of repositioning - // the avatar at the origin of the world for the purposes of rendering the mirror, - // but it resulted in failing to render the avatar's head model in the mirror view - // when in first person mode. Presumably this was because of some missed culling logic - // that was not accounted for in the hack. - - // This was removed in commit 71e59cfa88c6563749594e25494102fe01db38e9 but could be further - // investigated in order to adapt the technique while fixing the head rendering issue, - // but the complexity of the hack suggests that a better approach - _mirrorCamera.setPosition(myAvatar->getDefaultEyePosition() + - myAvatar->getOrientation() * glm::vec3(0.0f, 0.0f, -1.0f) * MIRROR_REARVIEW_DISTANCE * myAvatar->getScale()); - } - _mirrorCamera.setProjection(glm::perspective(glm::radians(fov), aspect, DEFAULT_NEAR_CLIP, DEFAULT_FAR_CLIP)); - _mirrorCamera.setOrientation(myAvatar->getWorldAlignedOrientation() * glm::quat(glm::vec3(0.0f, PI, 0.0f))); - - - // set the bounds of rear mirror view - // the region is in device independent coordinates; must convert to device - float ratio = (float)QApplication::desktop()->windowHandle()->devicePixelRatio() * getRenderResolutionScale(); - int width = region.width() * ratio; - int height = region.height() * ratio; - gpu::Vec4i viewport = gpu::Vec4i(0, 0, width, height); - renderArgs->_viewport = viewport; - - // render rear mirror view - displaySide(renderArgs, _mirrorCamera, true); - - renderArgs->_viewport = originalViewport; -} - void Application::resetSensors(bool andReload) { DependencyManager::get()->reset(); DependencyManager::get()->reset(); diff --git a/interface/src/Application.h b/interface/src/Application.h index 98080783a6..da815db4cd 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -276,8 +276,6 @@ public: virtual void pushPostUpdateLambda(void* key, std::function func) override; - const QRect& getMirrorViewRect() const { return _mirrorViewRect; } - void updateMyAvatarLookAtPosition(); float getAvatarSimrate() const { return _avatarSimCounter.rate(); } @@ -557,8 +555,6 @@ private: int _avatarSimsPerSecondReport {0}; quint64 _lastAvatarSimsPerSecondUpdate {0}; Camera _myCamera; // My view onto the world - Camera _mirrorCamera; // Camera for mirror view - QRect _mirrorViewRect; Setting::Handle _previousScriptLocation; Setting::Handle _fieldOfView; diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 8e124d27c7..893b23839d 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -249,9 +249,6 @@ Menu::Menu() { viewMenu->addSeparator(); - // View > Mini Mirror - addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::MiniMirror, 0, false); - // View > Center Player In View addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::CenterPlayerInView, 0, true, qApp, SLOT(rotationModeChanged()), diff --git a/interface/src/Menu.h b/interface/src/Menu.h index e0ac340edc..4a990254ad 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -123,7 +123,6 @@ namespace MenuOption { const QString LogExtraTimings = "Log Extra Timing Details"; const QString LowVelocityFilter = "Low Velocity Filter"; const QString MeshVisible = "Draw Mesh"; - const QString MiniMirror = "Mini Mirror"; const QString MuteAudio = "Mute Microphone"; const QString MuteEnvironment = "Mute Environment"; const QString MuteFaceTracking = "Mute Face Tracking"; diff --git a/interface/src/ui/ApplicationOverlay.cpp b/interface/src/ui/ApplicationOverlay.cpp index 364dff52a3..b9d7fadc97 100644 --- a/interface/src/ui/ApplicationOverlay.cpp +++ b/interface/src/ui/ApplicationOverlay.cpp @@ -13,7 +13,6 @@ #include #include -#include #include #include #include @@ -42,7 +41,6 @@ ApplicationOverlay::ApplicationOverlay() _domainStatusBorder = geometryCache->allocateID(); _magnifierBorder = geometryCache->allocateID(); _qmlGeometryId = geometryCache->allocateID(); - _rearViewGeometryId = geometryCache->allocateID(); } ApplicationOverlay::~ApplicationOverlay() { @@ -51,7 +49,6 @@ ApplicationOverlay::~ApplicationOverlay() { geometryCache->releaseID(_domainStatusBorder); geometryCache->releaseID(_magnifierBorder); geometryCache->releaseID(_qmlGeometryId); - geometryCache->releaseID(_rearViewGeometryId); } } @@ -86,7 +83,6 @@ void ApplicationOverlay::renderOverlay(RenderArgs* renderArgs) { // Now render the overlay components together into a single texture renderDomainConnectionStatusBorder(renderArgs); // renders the connected domain line renderAudioScope(renderArgs); // audio scope in the very back - NOTE: this is the debug audio scope, not the VU meter - renderRearView(renderArgs); // renders the mirror view selfie renderOverlays(renderArgs); // renders Scripts Overlay and AudioScope renderQmlUi(renderArgs); // renders a unit quad with the QML UI texture, and the text overlays from scripts renderStatsAndLogs(renderArgs); // currently renders nothing @@ -163,45 +159,6 @@ void ApplicationOverlay::renderOverlays(RenderArgs* renderArgs) { qApp->getOverlays().renderHUD(renderArgs); } -void ApplicationOverlay::renderRearViewToFbo(RenderArgs* renderArgs) { -} - -void ApplicationOverlay::renderRearView(RenderArgs* renderArgs) { - if (!qApp->isHMDMode() && Menu::getInstance()->isOptionChecked(MenuOption::MiniMirror) && - !Menu::getInstance()->isOptionChecked(MenuOption::FullscreenMirror)) { - gpu::Batch& batch = *renderArgs->_batch; - - auto geometryCache = DependencyManager::get(); - - auto framebuffer = DependencyManager::get(); - auto selfieTexture = framebuffer->getSelfieFramebuffer()->getRenderBuffer(0); - - int width = renderArgs->_viewport.z; - int height = renderArgs->_viewport.w; - mat4 legacyProjection = glm::ortho(0, width, height, 0, ORTHO_NEAR_CLIP, ORTHO_FAR_CLIP); - batch.setProjectionTransform(legacyProjection); - batch.setModelTransform(Transform()); - batch.resetViewTransform(); - - float screenRatio = ((float)qApp->getDevicePixelRatio()); - float renderRatio = ((float)qApp->getRenderResolutionScale()); - - auto viewport = qApp->getMirrorViewRect(); - glm::vec2 bottomLeft(viewport.left(), viewport.top() + viewport.height()); - glm::vec2 topRight(viewport.left() + viewport.width(), viewport.top()); - bottomLeft *= screenRatio; - topRight *= screenRatio; - glm::vec2 texCoordMinCorner(0.0f, 0.0f); - glm::vec2 texCoordMaxCorner(viewport.width() * renderRatio / float(selfieTexture->getWidth()), viewport.height() * renderRatio / float(selfieTexture->getHeight())); - - batch.setResourceTexture(0, selfieTexture); - float alpha = DependencyManager::get()->getDesktop()->property("unpinnedAlpha").toFloat(); - geometryCache->renderQuad(batch, bottomLeft, topRight, texCoordMinCorner, texCoordMaxCorner, glm::vec4(1.0f, 1.0f, 1.0f, alpha), _rearViewGeometryId); - - batch.setResourceTexture(0, renderArgs->_whiteTexture); - } -} - void ApplicationOverlay::renderStatsAndLogs(RenderArgs* renderArgs) { // Display stats and log text onscreen diff --git a/interface/src/ui/ApplicationOverlay.h b/interface/src/ui/ApplicationOverlay.h index 7ace5ee885..af4d8779d4 100644 --- a/interface/src/ui/ApplicationOverlay.h +++ b/interface/src/ui/ApplicationOverlay.h @@ -31,8 +31,6 @@ public: private: void renderStatsAndLogs(RenderArgs* renderArgs); void renderDomainConnectionStatusBorder(RenderArgs* renderArgs); - void renderRearViewToFbo(RenderArgs* renderArgs); - void renderRearView(RenderArgs* renderArgs); void renderQmlUi(RenderArgs* renderArgs); void renderAudioScope(RenderArgs* renderArgs); void renderOverlays(RenderArgs* renderArgs); @@ -51,7 +49,6 @@ private: gpu::TexturePointer _overlayColorTexture; gpu::FramebufferPointer _overlayFramebuffer; int _qmlGeometryId { 0 }; - int _rearViewGeometryId { 0 }; }; #endif // hifi_ApplicationOverlay_h diff --git a/interface/src/ui/AvatarInputs.cpp b/interface/src/ui/AvatarInputs.cpp index b09289c78a..944be4bf9e 100644 --- a/interface/src/ui/AvatarInputs.cpp +++ b/interface/src/ui/AvatarInputs.cpp @@ -20,10 +20,6 @@ HIFI_QML_DEF(AvatarInputs) static AvatarInputs* INSTANCE{ nullptr }; -static const char SETTINGS_GROUP_NAME[] = "Rear View Tools"; -static const char ZOOM_LEVEL_SETTINGS[] = "ZoomLevel"; - -static Setting::Handle rearViewZoomLevel(QStringList() << SETTINGS_GROUP_NAME << ZOOM_LEVEL_SETTINGS, 0); AvatarInputs* AvatarInputs::getInstance() { if (!INSTANCE) { @@ -36,8 +32,6 @@ AvatarInputs* AvatarInputs::getInstance() { AvatarInputs::AvatarInputs(QQuickItem* parent) : QQuickItem(parent) { INSTANCE = this; - int zoomSetting = rearViewZoomLevel.get(); - _mirrorZoomed = zoomSetting == 0; } #define AI_UPDATE(name, src) \ @@ -62,8 +56,6 @@ void AvatarInputs::update() { if (!Menu::getInstance()) { return; } - AI_UPDATE(mirrorVisible, Menu::getInstance()->isOptionChecked(MenuOption::MiniMirror) && !qApp->isHMDMode() - && !Menu::getInstance()->isOptionChecked(MenuOption::FullscreenMirror)); AI_UPDATE(cameraEnabled, !Menu::getInstance()->isOptionChecked(MenuOption::NoFaceTracking)); AI_UPDATE(cameraMuted, Menu::getInstance()->isOptionChecked(MenuOption::MuteFaceTracking)); AI_UPDATE(isHMD, qApp->isHMDMode()); @@ -122,15 +114,3 @@ void AvatarInputs::toggleAudioMute() { void AvatarInputs::resetSensors() { qApp->resetSensors(); } - -void AvatarInputs::toggleZoom() { - _mirrorZoomed = !_mirrorZoomed; - rearViewZoomLevel.set(_mirrorZoomed ? 0 : 1); - emit mirrorZoomedChanged(); -} - -void AvatarInputs::closeMirror() { - if (Menu::getInstance()->isOptionChecked(MenuOption::MiniMirror)) { - Menu::getInstance()->triggerOption(MenuOption::MiniMirror); - } -} diff --git a/interface/src/ui/AvatarInputs.h b/interface/src/ui/AvatarInputs.h index 85570ecd3c..5535469445 100644 --- a/interface/src/ui/AvatarInputs.h +++ b/interface/src/ui/AvatarInputs.h @@ -28,8 +28,6 @@ class AvatarInputs : public QQuickItem { AI_PROPERTY(bool, audioMuted, false) AI_PROPERTY(bool, audioClipping, false) AI_PROPERTY(float, audioLevel, 0) - AI_PROPERTY(bool, mirrorVisible, false) - AI_PROPERTY(bool, mirrorZoomed, true) AI_PROPERTY(bool, isHMD, false) AI_PROPERTY(bool, showAudioTools, true) @@ -44,8 +42,6 @@ signals: void audioMutedChanged(); void audioClippingChanged(); void audioLevelChanged(); - void mirrorVisibleChanged(); - void mirrorZoomedChanged(); void isHMDChanged(); void showAudioToolsChanged(); @@ -53,8 +49,6 @@ protected: Q_INVOKABLE void resetSensors(); Q_INVOKABLE void toggleCameraMute(); Q_INVOKABLE void toggleAudioMute(); - Q_INVOKABLE void toggleZoom(); - Q_INVOKABLE void closeMirror(); private: float _trailingAudioLoudness{ 0 }; diff --git a/libraries/render-utils/src/FramebufferCache.cpp b/libraries/render-utils/src/FramebufferCache.cpp index 27429595b4..31b345fa9f 100644 --- a/libraries/render-utils/src/FramebufferCache.cpp +++ b/libraries/render-utils/src/FramebufferCache.cpp @@ -21,7 +21,6 @@ void FramebufferCache::setFrameBufferSize(QSize frameBufferSize) { //If the size changed, we need to delete our FBOs if (_frameBufferSize != frameBufferSize) { _frameBufferSize = frameBufferSize; - _selfieFramebuffer.reset(); { std::unique_lock lock(_mutex); _cachedFramebuffers.clear(); @@ -36,10 +35,6 @@ void FramebufferCache::createPrimaryFramebuffer() { auto defaultSampler = gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_POINT); - _selfieFramebuffer = gpu::FramebufferPointer(gpu::Framebuffer::create("selfie")); - auto tex = gpu::TexturePointer(gpu::Texture::create2D(colorFormat, width * 0.5, height * 0.5, defaultSampler)); - _selfieFramebuffer->setRenderBuffer(0, tex); - auto smoothSampler = gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR); } @@ -60,10 +55,3 @@ void FramebufferCache::releaseFramebuffer(const gpu::FramebufferPointer& framebu _cachedFramebuffers.push_back(framebuffer); } } - -gpu::FramebufferPointer FramebufferCache::getSelfieFramebuffer() { - if (!_selfieFramebuffer) { - createPrimaryFramebuffer(); - } - return _selfieFramebuffer; -} diff --git a/libraries/render-utils/src/FramebufferCache.h b/libraries/render-utils/src/FramebufferCache.h index f74d224a61..8065357615 100644 --- a/libraries/render-utils/src/FramebufferCache.h +++ b/libraries/render-utils/src/FramebufferCache.h @@ -27,9 +27,6 @@ public: void setFrameBufferSize(QSize frameBufferSize); const QSize& getFrameBufferSize() const { return _frameBufferSize; } - /// Returns the framebuffer object used to render selfie maps; - gpu::FramebufferPointer getSelfieFramebuffer(); - /// Returns a free framebuffer with a single color attachment for temp or intra-frame operations gpu::FramebufferPointer getFramebuffer(); @@ -42,8 +39,6 @@ private: gpu::FramebufferPointer _shadowFramebuffer; - gpu::FramebufferPointer _selfieFramebuffer; - QSize _frameBufferSize{ 100, 100 }; std::mutex _mutex; From 8b8f9d7b1a5bf72cdabcc880a3602b19a7c9cdae Mon Sep 17 00:00:00 2001 From: samcake Date: Fri, 17 Mar 2017 18:26:48 -0700 Subject: [PATCH 239/302] Fix a problem of the ambient lighting diffuse beeing too sstrong, we found a nice balance together with Alan --- libraries/render-utils/src/LightAmbient.slh | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/libraries/render-utils/src/LightAmbient.slh b/libraries/render-utils/src/LightAmbient.slh index 15e23015cb..a4e3764830 100644 --- a/libraries/render-utils/src/LightAmbient.slh +++ b/libraries/render-utils/src/LightAmbient.slh @@ -77,6 +77,16 @@ void evalLightingAmbient(out vec3 diffuse, out vec3 specular, LightAmbient ambie // Diffuse from ambient diffuse = (1.0 - metallic) * sphericalHarmonics_evalSphericalLight(getLightAmbientSphere(ambient), normal).xyz; + if (gl_FragCoord.x > 1000) { + vec3 ambientFresnel = vec3 (1.0) - fresnelSchlickAmbient(fresnel, eyeDir, normal, 1.0 - roughness); + diffuse *= ambientFresnel * (1.0 / 3.14); + + + if (gl_FragCoord.x > 2000) { + diffuse *= obscurance; + } + } + // Specular highlight from ambient specular = evalAmbientSpecularIrradiance(ambient, eyeDir, normal, roughness, fresnel) * obscurance * getLightAmbientIntensity(ambient); From 8e086a8fc8b20c78f185b0065e8446ea8dfffcd7 Mon Sep 17 00:00:00 2001 From: Triplelexx Date: Sat, 18 Mar 2017 03:51:55 +0000 Subject: [PATCH 240/302] fix build error --- interface/src/Application.cpp | 34 ++++++++++++++++++---------------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index d6516db8f0..4a8ffe702f 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -2863,30 +2863,30 @@ void Application::keyPressEvent(QKeyEvent* event) { break; #endif - case Qt::Key_H: + case Qt::Key_H: { // whenever switching to/from full screen mirror from the keyboard, remember // the state you were in before full screen mirror, and return to that. auto previousMode = _myCamera.getMode(); if (previousMode != CAMERA_MODE_MIRROR) { switch (previousMode) { - case CAMERA_MODE_FIRST_PERSON: - _returnFromFullScreenMirrorTo = MenuOption::FirstPerson; - break; - case CAMERA_MODE_THIRD_PERSON: - _returnFromFullScreenMirrorTo = MenuOption::ThirdPerson; - break; + case CAMERA_MODE_FIRST_PERSON: + _returnFromFullScreenMirrorTo = MenuOption::FirstPerson; + break; + case CAMERA_MODE_THIRD_PERSON: + _returnFromFullScreenMirrorTo = MenuOption::ThirdPerson; + break; // FIXME - it's not clear that these modes make sense to return to... - case CAMERA_MODE_INDEPENDENT: - _returnFromFullScreenMirrorTo = MenuOption::IndependentMode; - break; - case CAMERA_MODE_ENTITY: - _returnFromFullScreenMirrorTo = MenuOption::CameraEntityMode; - break; + case CAMERA_MODE_INDEPENDENT: + _returnFromFullScreenMirrorTo = MenuOption::IndependentMode; + break; + case CAMERA_MODE_ENTITY: + _returnFromFullScreenMirrorTo = MenuOption::CameraEntityMode; + break; - default: - _returnFromFullScreenMirrorTo = MenuOption::ThirdPerson; - break; + default: + _returnFromFullScreenMirrorTo = MenuOption::ThirdPerson; + break; } } @@ -2904,6 +2904,8 @@ void Application::keyPressEvent(QKeyEvent* event) { } cameraMenuChanged(); break; + } + case Qt::Key_P: { if (!(isShifted || isMeta || isOption)) { bool isFirstPersonChecked = Menu::getInstance()->isOptionChecked(MenuOption::FirstPerson); From 8b845658e2418cf147bb43f88a338bc696ff467a Mon Sep 17 00:00:00 2001 From: Triplelexx Date: Sat, 18 Mar 2017 05:00:05 +0000 Subject: [PATCH 241/302] remove redundant declarations --- libraries/render-utils/src/FramebufferCache.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/libraries/render-utils/src/FramebufferCache.cpp b/libraries/render-utils/src/FramebufferCache.cpp index 31b345fa9f..72b3c2ceb4 100644 --- a/libraries/render-utils/src/FramebufferCache.cpp +++ b/libraries/render-utils/src/FramebufferCache.cpp @@ -29,10 +29,6 @@ void FramebufferCache::setFrameBufferSize(QSize frameBufferSize) { } void FramebufferCache::createPrimaryFramebuffer() { - auto colorFormat = gpu::Element::COLOR_SRGBA_32; - auto width = _frameBufferSize.width(); - auto height = _frameBufferSize.height(); - auto defaultSampler = gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_POINT); auto smoothSampler = gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR); From 488ed081ddcd9b14e7e5b75470df3aaeba65e4cc Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Sat, 18 Mar 2017 10:18:04 -0700 Subject: [PATCH 242/302] Mesh::forEach and Mesh::map --- libraries/model/src/model/Geometry.cpp | 47 ++++-- libraries/model/src/model/Geometry.h | 4 + .../src/ModelScriptingInterface.cpp | 135 +++--------------- 3 files changed, 59 insertions(+), 127 deletions(-) diff --git a/libraries/model/src/model/Geometry.cpp b/libraries/model/src/model/Geometry.cpp index 4e5134a7d7..d511ae6cb9 100755 --- a/libraries/model/src/model/Geometry.cpp +++ b/libraries/model/src/model/Geometry.cpp @@ -140,19 +140,11 @@ model::MeshPointer Mesh::map(std::function vertexFunc, std::function indexFunc) { int attributeTypeNormal = gpu::Stream::InputSlot::NORMAL; // libraries/gpu/src/gpu/Stream.h + // vertex data gpu::Resource::Size vertexSize = getNumVertices() * sizeof(glm::vec3); unsigned char* resultVertexData = new unsigned char[vertexSize]; unsigned char* vertexDataCursor = resultVertexData; - gpu::Resource::Size normalSize = getNumAttributes() * sizeof(glm::vec3); - unsigned char* resultNormalData = new unsigned char[normalSize]; - unsigned char* normalDataCursor = resultNormalData; - - gpu::Resource::Size indexSize = getNumIndices() * sizeof(uint32_t); - unsigned char* resultIndexData = new unsigned char[indexSize]; - unsigned char* indexDataCursor = resultIndexData; - - // vertex data const gpu::BufferView& vertexBufferView = getVertexBuffer(); gpu::BufferView::Index numVertices = (gpu::BufferView::Index)getNumVertices(); for (gpu::BufferView::Index i = 0; i < numVertices; i ++) { @@ -162,6 +154,10 @@ model::MeshPointer Mesh::map(std::function vertexFunc, } // normal data + gpu::Resource::Size normalSize = getNumAttributes() * sizeof(glm::vec3); + unsigned char* resultNormalData = new unsigned char[normalSize]; + unsigned char* normalDataCursor = resultNormalData; + const gpu::BufferView& normalsBufferView = getAttributeBuffer(attributeTypeNormal); gpu::BufferView::Index numNormals = (gpu::BufferView::Index)getNumAttributes(); for (gpu::BufferView::Index i = 0; i < numNormals; i ++) { @@ -172,6 +168,10 @@ model::MeshPointer Mesh::map(std::function vertexFunc, // TODO -- other attributes // face data + gpu::Resource::Size indexSize = getNumIndices() * sizeof(uint32_t); + unsigned char* resultIndexData = new unsigned char[indexSize]; + unsigned char* indexDataCursor = resultIndexData; + const gpu::BufferView& indexBufferView = getIndexBuffer(); gpu::BufferView::Index numIndexes = (gpu::BufferView::Index)getNumIndices(); for (gpu::BufferView::Index i = 0; i < numIndexes; i ++) { @@ -215,6 +215,35 @@ model::MeshPointer Mesh::map(std::function vertexFunc, } +void Mesh::forEach(std::function vertexFunc, + std::function normalFunc, + std::function indexFunc) { + int attributeTypeNormal = gpu::Stream::InputSlot::NORMAL; // libraries/gpu/src/gpu/Stream.h + + // vertex data + const gpu::BufferView& vertexBufferView = getVertexBuffer(); + gpu::BufferView::Index numVertices = (gpu::BufferView::Index)getNumVertices(); + for (gpu::BufferView::Index i = 0; i < numVertices; i ++) { + vertexFunc(vertexBufferView.get(i)); + } + + // normal data + const gpu::BufferView& normalsBufferView = getAttributeBuffer(attributeTypeNormal); + gpu::BufferView::Index numNormals = (gpu::BufferView::Index)getNumAttributes(); + for (gpu::BufferView::Index i = 0; i < numNormals; i ++) { + normalFunc(normalsBufferView.get(i)); + } + // TODO -- other attributes + + // face data + const gpu::BufferView& indexBufferView = getIndexBuffer(); + gpu::BufferView::Index numIndexes = (gpu::BufferView::Index)getNumIndices(); + for (gpu::BufferView::Index i = 0; i < numIndexes; i ++) { + indexFunc(indexBufferView.get(i)); + } +} + + Geometry::Geometry() { } diff --git a/libraries/model/src/model/Geometry.h b/libraries/model/src/model/Geometry.h index 9a20861ea7..7ba3e83407 100755 --- a/libraries/model/src/model/Geometry.h +++ b/libraries/model/src/model/Geometry.h @@ -123,6 +123,10 @@ public: std::function normalFunc, std::function indexFunc); + void forEach(std::function vertexFunc, + std::function normalFunc, + std::function indexFunc); + protected: gpu::Stream::FormatPointer _vertexFormat; diff --git a/libraries/script-engine/src/ModelScriptingInterface.cpp b/libraries/script-engine/src/ModelScriptingInterface.cpp index 71b82a2d60..cfcb6da0dc 100644 --- a/libraries/script-engine/src/ModelScriptingInterface.cpp +++ b/libraries/script-engine/src/ModelScriptingInterface.cpp @@ -53,10 +53,8 @@ QString ModelScriptingInterface::meshToOBJ(MeshProxyList in) { return writeOBJToString(meshes); } - QScriptValue ModelScriptingInterface::appendMeshes(MeshProxyList in) { - int attributeTypeNormal = gpu::Stream::InputSlot::NORMAL; // libraries/gpu/src/gpu/Stream.h - + // figure out the size of the resulting mesh size_t totalVertexCount { 0 }; size_t totalAttributeCount { 0 }; size_t totalIndexCount { 0 }; @@ -67,6 +65,7 @@ QScriptValue ModelScriptingInterface::appendMeshes(MeshProxyList in) { totalIndexCount += mesh->getNumIndices(); } + // alloc the resulting mesh gpu::Resource::Size combinedVertexSize = totalVertexCount * sizeof(glm::vec3); unsigned char* combinedVertexData = new unsigned char[combinedVertexSize]; unsigned char* combinedVertexDataCursor = combinedVertexData; @@ -83,36 +82,22 @@ QScriptValue ModelScriptingInterface::appendMeshes(MeshProxyList in) { foreach (const MeshProxy* meshProxy, in) { MeshPointer mesh = meshProxy->getMeshPointer(); + mesh->forEach( + [&](glm::vec3 position){ + memcpy(combinedVertexDataCursor, &position, sizeof(position)); + combinedVertexDataCursor += sizeof(position); + }, + [&](glm::vec3 normal){ + memcpy(combinedNormalDataCursor, &normal, sizeof(normal)); + combinedNormalDataCursor += sizeof(normal); + }, + [&](uint32_t index){ + index += indexStartOffset; + memcpy(combinedIndexDataCursor, &index, sizeof(index)); + combinedIndexDataCursor += sizeof(index); + }); - // vertex data - const gpu::BufferView& vertexBufferView = mesh->getVertexBuffer(); gpu::BufferView::Index numVertices = (gpu::BufferView::Index)mesh->getNumVertices(); - for (gpu::BufferView::Index i = 0; i < numVertices; i ++) { - glm::vec3 pos = vertexBufferView.get(i); - memcpy(combinedVertexDataCursor, &pos, sizeof(pos)); - combinedVertexDataCursor += sizeof(pos); - } - - // normal data - const gpu::BufferView& normalsBufferView = mesh->getAttributeBuffer(attributeTypeNormal); - gpu::BufferView::Index numNormals = (gpu::BufferView::Index)mesh->getNumAttributes(); - for (gpu::BufferView::Index i = 0; i < numNormals; i ++) { - glm::vec3 normal = normalsBufferView.get(i); - memcpy(combinedNormalDataCursor, &normal, sizeof(normal)); - combinedNormalDataCursor += sizeof(normal); - } - // TODO -- other attributes - - // face data - const gpu::BufferView& indexBufferView = mesh->getIndexBuffer(); - gpu::BufferView::Index numIndexes = (gpu::BufferView::Index)mesh->getNumIndices(); - for (gpu::BufferView::Index i = 0; i < numIndexes; i ++) { - uint32_t index = indexBufferView.get(i); - index += indexStartOffset; - memcpy(combinedIndexDataCursor, &index, sizeof(index)); - combinedIndexDataCursor += sizeof(index); - } - indexStartOffset += numVertices; } @@ -124,6 +109,7 @@ QScriptValue ModelScriptingInterface::appendMeshes(MeshProxyList in) { gpu::BufferView combinedVertexBufferView(combinedVertexBufferPointer, vertexElement); result->setVertexBuffer(combinedVertexBufferView); + int attributeTypeNormal = gpu::Stream::InputSlot::NORMAL; // libraries/gpu/src/gpu/Stream.h gpu::Element normalElement = gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ); gpu::Buffer* combinedNormalsBuffer = new gpu::Buffer(combinedNormalSize, combinedNormalData); gpu::BufferPointer combinedNormalsBufferPointer(combinedNormalsBuffer); @@ -136,7 +122,6 @@ QScriptValue ModelScriptingInterface::appendMeshes(MeshProxyList in) { gpu::BufferView combinedIndexesBufferView(combinedIndexesBufferPointer, indexElement); result->setIndexBuffer(combinedIndexesBufferView); - std::vector parts; parts.emplace_back(model::Mesh::Part((model::Index)0, // startIndex (model::Index)result->getNumIndices(), // numIndices @@ -151,92 +136,6 @@ QScriptValue ModelScriptingInterface::appendMeshes(MeshProxyList in) { } -// QScriptValue ModelScriptingInterface::transformMesh(glm::mat4 transform, MeshProxy* meshProxy) { -// int attributeTypeNormal = gpu::Stream::InputSlot::NORMAL; // libraries/gpu/src/gpu/Stream.h - -// if (!meshProxy) { -// return QScriptValue(false); -// } - -// MeshPointer mesh = meshProxy->getMeshPointer(); - -// gpu::Resource::Size vertexSize = mesh->getNumVertices() * sizeof(glm::vec3); -// unsigned char* resultVertexData = new unsigned char[vertexSize]; -// unsigned char* vertexDataCursor = resultVertexData; - -// gpu::Resource::Size normalSize = mesh->getNumAttributes() * sizeof(glm::vec3); -// unsigned char* resultNormalData = new unsigned char[normalSize]; -// unsigned char* normalDataCursor = resultNormalData; - -// gpu::Resource::Size indexSize = mesh->getNumIndices() * sizeof(uint32_t); -// unsigned char* resultIndexData = new unsigned char[indexSize]; -// unsigned char* indexDataCursor = resultIndexData; - -// // vertex data -// const gpu::BufferView& vertexBufferView = mesh->getVertexBuffer(); -// gpu::BufferView::Index numVertices = (gpu::BufferView::Index)mesh->getNumVertices(); -// for (gpu::BufferView::Index i = 0; i < numVertices; i ++) { -// glm::vec3 pos = vertexBufferView.get(i); -// pos = glm::vec3(transform * glm::vec4(pos, 1.0f)); -// memcpy(vertexDataCursor, &pos, sizeof(pos)); -// vertexDataCursor += sizeof(pos); -// } - -// // normal data -// const gpu::BufferView& normalsBufferView = mesh->getAttributeBuffer(attributeTypeNormal); -// gpu::BufferView::Index numNormals = (gpu::BufferView::Index)mesh->getNumAttributes(); -// for (gpu::BufferView::Index i = 0; i < numNormals; i ++) { -// glm::vec3 normal = normalsBufferView.get(i); -// normal = glm::vec3(transform * glm::vec4(normal, 0.0f)); -// memcpy(normalDataCursor, &normal, sizeof(normal)); -// normalDataCursor += sizeof(normal); -// } -// // TODO -- other attributes - -// // face data -// const gpu::BufferView& indexBufferView = mesh->getIndexBuffer(); -// gpu::BufferView::Index numIndexes = (gpu::BufferView::Index)mesh->getNumIndices(); -// for (gpu::BufferView::Index i = 0; i < numIndexes; i ++) { -// uint32_t index = indexBufferView.get(i); -// memcpy(indexDataCursor, &index, sizeof(index)); -// indexDataCursor += sizeof(index); -// } - -// model::MeshPointer result(new model::Mesh()); - -// gpu::Element vertexElement = gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ); -// gpu::Buffer* resultVertexBuffer = new gpu::Buffer(vertexSize, resultVertexData); -// gpu::BufferPointer resultVertexBufferPointer(resultVertexBuffer); -// gpu::BufferView resultVertexBufferView(resultVertexBufferPointer, vertexElement); -// result->setVertexBuffer(resultVertexBufferView); - -// gpu::Element normalElement = gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ); -// gpu::Buffer* resultNormalsBuffer = new gpu::Buffer(normalSize, resultNormalData); -// gpu::BufferPointer resultNormalsBufferPointer(resultNormalsBuffer); -// gpu::BufferView resultNormalsBufferView(resultNormalsBufferPointer, normalElement); -// result->addAttribute(attributeTypeNormal, resultNormalsBufferView); - -// gpu::Element indexElement = gpu::Element(gpu::SCALAR, gpu::UINT32, gpu::RAW); -// gpu::Buffer* resultIndexesBuffer = new gpu::Buffer(indexSize, resultIndexData); -// gpu::BufferPointer resultIndexesBufferPointer(resultIndexesBuffer); -// gpu::BufferView resultIndexesBufferView(resultIndexesBufferPointer, indexElement); -// result->setIndexBuffer(resultIndexesBufferView); - - -// std::vector parts; -// parts.emplace_back(model::Mesh::Part((model::Index)0, // startIndex -// (model::Index)result->getNumIndices(), // numIndices -// (model::Index)0, // baseVertex -// model::Mesh::TRIANGLES)); // topology -// result->setPartBuffer(gpu::BufferView(new gpu::Buffer(parts.size() * sizeof(model::Mesh::Part), -// (gpu::Byte*) parts.data()), gpu::Element::PART_DRAWCALL)); - - - -// MeshProxy* resultProxy = new MeshProxy(result); -// return meshToScriptValue(_modelScriptEngine, resultProxy); -// } - QScriptValue ModelScriptingInterface::transformMesh(glm::mat4 transform, MeshProxy* meshProxy) { if (!meshProxy) { From b945d9c4cb92b80a78846108444556c35d57bb4f Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Sat, 18 Mar 2017 14:34:19 -0700 Subject: [PATCH 243/302] fix a bug that caused obj models with no material to randomly use the materials of other things in the view --- libraries/fbx/src/OBJReader.cpp | 5 +++-- libraries/fbx/src/OBJReader.h | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/libraries/fbx/src/OBJReader.cpp b/libraries/fbx/src/OBJReader.cpp index 73cf7a520e..0cb932b375 100644 --- a/libraries/fbx/src/OBJReader.cpp +++ b/libraries/fbx/src/OBJReader.cpp @@ -546,6 +546,7 @@ FBXGeometry* OBJReader::readOBJ(QByteArray& model, const QVariantHash& mapping, QString queryPart = _url.query(); bool suppressMaterialsHack = queryPart.contains("hifiusemat"); // If this appears in query string, don't fetch mtl even if used. OBJMaterial& preDefinedMaterial = materials[SMART_DEFAULT_MATERIAL_NAME]; + preDefinedMaterial.used = true; if (suppressMaterialsHack) { needsMaterialLibrary = preDefinedMaterial.userSpecifiesUV = false; // I said it was a hack... } @@ -594,8 +595,8 @@ FBXGeometry* OBJReader::readOBJ(QByteArray& model, const QVariantHash& mapping, } foreach (QString materialID, materials.keys()) { - OBJMaterial& objMaterial = materials[materialID]; - if (!objMaterial.used) { + OBJMaterial& objMaterial = materials[materialID]; + if (!objMaterial.used) { continue; } geometry.materials[materialID] = FBXMaterial(objMaterial.diffuseColor, diff --git a/libraries/fbx/src/OBJReader.h b/libraries/fbx/src/OBJReader.h index 200f11548d..b4a48c570e 100644 --- a/libraries/fbx/src/OBJReader.h +++ b/libraries/fbx/src/OBJReader.h @@ -58,7 +58,7 @@ public: QByteArray specularTextureFilename; bool used { false }; bool userSpecifiesUV { false }; - OBJMaterial() : shininess(96.0f), opacity(1.0f), diffuseColor(1.0f), specularColor(1.0f) {} + OBJMaterial() : shininess(0.0f), opacity(1.0f), diffuseColor(0.9f), specularColor(0.9f) {} }; class OBJReader: public QObject { // QObject so we can make network requests. From e6446b69136dda58a174f11a3776768eec020bde Mon Sep 17 00:00:00 2001 From: anshuman64 Date: Sat, 18 Mar 2017 19:59:23 -0700 Subject: [PATCH 244/302] New simplified, stand-alone build guide for Windows Still throws this error after trying to build in Visual Studio: Error 9 error MSB3073: The command "setlocal "C:\Program Files\CMake\bin\cmake.exe" "-DBUNDLE_EXECUTABLE=C:/Users/Chris i5 AMD/Documents/GitHub/hifi/build/tests/render-perf/Release/render-perf-test.exe" "-DBUNDLE_PLUGIN_DIR=C:/Users/Chris i5 AMD/Documents/GitHub/hifi/build/tests/render-perf/Release/plugins" -P "C:/Users/Chris i5 AMD/Documents/GitHub/hifi/build/tests/render-perf/FixupBundlePostBuild.cmake" if %errorlevel% neq 0 goto :cmEnd :cmEnd endlocal & call :cmErrorLevel %errorlevel% & goto :cmDone :cmErrorLevel exit /b %1 :cmDone if %errorlevel% neq 0 goto :VCEnd setlocal CMD /C "SET PATH=%PATH%;C:/Qt/Qt5.6.1/5.6/msvc2013_64/bin && C:/Qt/Qt5.6.1/5.6/msvc2013_64/bin/windeployqt.exe --release C:/Users/Chris i5 AMD/Documents/GitHub/hifi/build/tests/render-perf/Release/render-perf-test.exe" if %errorlevel% neq 0 goto :cmEnd :cmEnd endlocal & call :cmErrorLevel %errorlevel% & goto :cmDone :cmErrorLevel exit /b %1 :cmDone if %errorlevel% neq 0 goto :VCEnd setlocal if exist "C:/Users/Chris i5 AMD/Documents/GitHub/hifi/build/tests/render-perf/Release/audio/qtaudio_windows.dll" ( "C:/Program Files/CMake/bin/cmake.exe" -E remove "C:/Users/Chris i5 AMD/Documents/GitHub/hifi/build/tests/render-perf/Release/audio/qtaudio_windows.dll" && "C:/Program Files/CMake/bin/cmake.exe" -E copy "C:/Users/Chris i5 AMD/Documents/GitHub/hifi/build/ext/vc12/wasapi/project/src/wasapi/qtaudio_wasapi.dll" "C:/Users/Chris i5 AMD/Documents/GitHub/hifi/build/tests/render-perf/Release/audio" && "C:/Program Files/CMake/bin/cmake.exe" -E copy "C:/Users/Chris i5 AMD/Documents/GitHub/hifi/build/ext/vc12/wasapi/project/src/wasapi/qtaudio_wasapi.pdb" "C:/Users/Chris i5 AMD/Documents/GitHub/hifi/build/tests/render-perf/Release/audio" ) if %errorlevel% neq 0 goto :cmEnd if exist "C:/Users/Chris i5 AMD/Documents/GitHub/hifi/build/tests/render-perf/Release/audio/qtaudio_windowsd.dll" ( "C:/Program Files/CMake/bin/cmake.exe" -E remove "C:/Users/Chris i5 AMD/Documents/GitHub/hifi/build/tests/render-perf/Release/audio/qtaudio_windowsd.dll" && "C:/Program Files/CMake/bin/cmake.exe" -E copy "C:/Users/Chris i5 AMD/Documents/GitHub/hifi/build/ext/vc12/wasapi/project/src/wasapi/qtaudio_wasapid.dll" "C:/Users/Chris i5 AMD/Documents/GitHub/hifi/build/tests/render-perf/Release/audio" && "C:/Program Files/CMake/bin/cmake.exe" -E copy "C:/Users/Chris i5 AMD/Documents/GitHub/hifi/build/ext/vc12/wasapi/project/src/wasapi/qtaudio_wasapid.pdb" "C:/Users/Chris i5 AMD/Documents/GitHub/hifi/build/tests/render-perf/Release/audio" ) if %errorlevel% neq 0 goto :cmEnd :cmEnd endlocal & call :cmErrorLevel %errorlevel% & goto :cmDone :cmErrorLevel exit /b %1 :cmDone if %errorlevel% neq 0 goto :VCEnd :VCEnd" exited with code 1. C:\Program Files (x86)\MSBuild\Microsoft.Cpp\v4.0\V120\Microsoft.CppCommon.targets 132 5 render-perf-test --- BUILD_WIN.md | 144 +++++++++++++++++++++------------------------------ 1 file changed, 60 insertions(+), 84 deletions(-) diff --git a/BUILD_WIN.md b/BUILD_WIN.md index 45373d3093..5d7812342a 100644 --- a/BUILD_WIN.md +++ b/BUILD_WIN.md @@ -1,104 +1,80 @@ -Please read the [general build guide](BUILD.md) for information on dependencies required for all platforms. Only Windows specific instructions are found in this file. +This is a stand-alone guide for creating your first High Fidelity build for Windows 64-bit. -Interface can be built as 32 or 64 bit. +###Step 1. Installing Visual Studio 2013 -###Visual Studio 2013 +If you don't already have the Community or Professional edition of Visual Studio 2013, download and install [Visual Studio Community 2013](https://www.visualstudio.com/en-us/news/releasenotes/vs2013-community-vs). You do not need to install any of the optional components when going through the installer. -You can use the Community or Professional editions of Visual Studio 2013. +Note: Newer versions of Visual Studio are not yet compatible. -You can start a Visual Studio 2013 command prompt using the shortcut provided in the Visual Studio Tools folder installed as part of Visual Studio 2013. +###Step 2. Installing CMake -Or you can start a regular command prompt and then run: +Download and install the "win64-x64 Installer" from the [CMake Website](https://cmake.org/download/). Make sure "Add CMake to system PATH for all users" is checked when going through the installer. - "%VS120COMNTOOLS%\vsvars32.bat" +###Step 3. Installing Qt -####Windows SDK 8.1 +Download and install the [Qt 5.6.1 Installer](https://download.qt.io/official_releases/qt/5.6/5.6.1-1/qt-opensource-windows-x86-msvc2013_64-5.6.1-1.exe) -If using Visual Studio 2013 and building as a Visual Studio 2013 project you need the Windows 8 SDK which you should already have as part of installing Visual Studio 2013. You should be able to see it at `C:\Program Files (x86)\Windows Kits\8.1\Lib\winv6.3\um\x86`. +Make sure to select all components when going through the installer. -####nmake +###Step 4. Setting Qt Environment Variable -Some of the external projects may require nmake to compile and install. If it is not installed at the location listed below, please ensure that it is in your PATH so CMake can find it when required. +Go to "Control Panel > System > Advanced System Settings > Environment Variables > New..." +* Set "Variable name": QT_CMAKE_PREFIX_PATH +* Set "Variable value": `C:\Qt\Qt5.6.1\5.6\msvc2013_64\lib\cmake` -We expect nmake.exe to be located at the following path. +###Step 5. Installing OpenSSL - C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\bin +Download and install the "Win64 OpenSSL v1.0.xk" Installer from [this website](https://slproweb.com/products/Win32OpenSSL.html), where "x" is the number of the latest release (ex. Win64 OpenSSL v1.0.2k). -###Qt -You can use the online installer or the offline installer. If you use the offline installer, be sure to select the "OpenGL" version. - -* [Download the online installer](http://www.qt.io/download-open-source/#section-2) - * When it asks you to select components, ONLY select one of the following, 32- or 64-bit to match your build preference: - * Qt > Qt 5.6.1 > **msvc2013 32-bit** - * Qt > Qt 5.6.1 > **msvc2013 64-bit** - -* Download the offline installer, 32- or 64-bit to match your build preference: - * [32-bit](https://download.qt.io/official_releases/qt/5.6/5.6.1-1/qt-opensource-windows-x86-msvc2013-5.6.1-1.exe) - * [64-bit](https://download.qt.io/official_releases/qt/5.6/5.6.1-1/qt-opensource-windows-x86-msvc2013_64-5.6.1-1.exe) - -Once Qt is installed, you need to manually configure the following: -* Set the QT_CMAKE_PREFIX_PATH environment variable to your `Qt\5.6.1\msvc2013\lib\cmake` or `Qt\5.6.1\msvc2013_64\lib\cmake` directory. - * You can set an environment variable from Control Panel > System > Advanced System Settings > Environment Variables > New - -###External Libraries - -All libraries should be 32- or 64-bit to match your build preference. - -CMake will need to know where the headers and libraries for required external dependencies are. - -We use CMake's `fixup_bundle` to find the DLLs all of our executable targets require, and then copy them beside the executable in a post-build step. If `fixup_bundle` is having problems finding a DLL, you can fix it manually on your end by adding the folder containing that DLL to your path. Let us know which DLL CMake had trouble finding, as it is possible a tweak to our CMake files is required. - -The recommended route for CMake to find the external dependencies is to place all of the dependencies in one folder and set one ENV variable - HIFI_LIB_DIR. That ENV variable should point to a directory with the following structure: - - root_lib_dir - -> openssl - -> bin - -> include - -> lib - -For many of the external libraries where precompiled binaries are readily available you should be able to simply copy the extracted folder that you get from the download links provided at the top of the guide. Otherwise you may need to build from source and install the built product to this directory. The `root_lib_dir` in the above example can be wherever you choose on your system - as long as the environment variable HIFI_LIB_DIR is set to it. From here on, whenever you see %HIFI_LIB_DIR% you should substitute the directory that you chose. - -####OpenSSL - -Qt will use OpenSSL if it's available, but it doesn't install it, so you must install it separately. - -Your system may already have several versions of the OpenSSL DLL's (ssleay32.dll, libeay32.dll) lying around, but they may be the wrong version. If these DLL's are in the PATH then QT will try to use them, and if they're the wrong version then you will see the following errors in the console: - - QSslSocket: cannot resolve TLSv1_1_client_method - QSslSocket: cannot resolve TLSv1_2_client_method - QSslSocket: cannot resolve TLSv1_1_server_method - QSslSocket: cannot resolve TLSv1_2_server_method - QSslSocket: cannot resolve SSL_select_next_proto - QSslSocket: cannot resolve SSL_CTX_set_next_proto_select_cb - QSslSocket: cannot resolve SSL_get0_next_proto_negotiated - -To prevent these problems, install OpenSSL yourself. Download one of the following binary packages [from this website](https://slproweb.com/products/Win32OpenSSL.html): -* Win32 OpenSSL v1.0.1q -* Win64 OpenSSL v1.0.1q - -Install OpenSSL into the Windows system directory, to make sure that Qt uses the version that you've just installed, and not some other version. - -###Build High Fidelity using Visual Studio -Follow the same build steps from the CMake section of [BUILD.md](BUILD.md), but pass a different generator to CMake. - -For 32-bit builds: - - cmake .. -G "Visual Studio 12" - -For 64-bit builds: +###Step 6. Running CMake to Generate Build Files +Run Command Prompt from Start and run the following commands: + cd "%HIFI_DIR%" + mkdir build + cd build cmake .. -G "Visual Studio 12 Win64" + +Where %HIFI_DIR% is the directory for the highfidelity repository. -Open %HIFI_DIR%\build\hifi.sln and compile. +###Step 7. Making a Build -###Running Interface -If you need to debug Interface, you can run interface from within Visual Studio (see the section below). You can also run Interface by launching it from command line or File Explorer from %HIFI_DIR%\build\interface\Debug\interface.exe +Open '%HIFI_DIR%\build\hifi.sln' using Visual Studio. -###Debugging Interface -* In the Solution Explorer, right click interface and click Set as StartUp Project -* Set the "Working Directory" for the Interface debugging sessions to the Debug output directory so that your application can load resources. Do this: right click interface and click Properties, choose Debugging from Configuration Properties, set Working Directory to .\Debug -* Now you can run and debug interface through Visual Studio +Change the Solution Configuration (next to the green play button) from "Debug" to "Release" for best performance. -For better performance when running debug builds, set the environment variable ```_NO_DEBUG_HEAP``` to ```1``` +Run Build > Build Solution. -http://preshing.com/20110717/the-windows-heap-is-slow-when-launched-from-the-debugger/ +###Step 8. Testing the Interface + +Create another environment variable (see Step #4) +* Set "Variable name": _NO_DEBUG_HEAP +* Set "Variable value": 1 + +In Visual Studio, right+click "interface" under the Apps folder in Solution Explorer and select "Set as Startup Project". Run Debug > Start Debugging. + +Now, you should have a full build of High Fidelity and be able to run the Interface using Visual Studio. Please check our [Docs](https://wiki.highfidelity.com/wiki/Main_Page) for more information regarding the programming workflow. + +Note: You can also run Interface by launching it from command line or File Explorer from %HIFI_DIR%\build\interface\Debug\interface.exe + +###Troubleshooting + +For any problems after Step #6, first try this: +* Delete the highfidelity repository +* Restart your computer +* Redownload the [repository](https://github.com/highfidelity/hifi) +* Restart directions from Step #6 + +####CMake gives you the same error message repeatedly after the build fails + +Remove `CMakeCache.txt` found in the '%HIFI_DIR%\build' directory + +####nmake cannot be found + +Make sure nmake.exe is located at the following path: + C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\bin + +If not, add the directory where nmake is located to the PATH environment variable. + +####Qt is throwing an error + +Make sure you have the current version (5.6.1-1) installed and 'QT_CMAKE_PREFIX_PATH' environment variable is set correctly. From b876d22e46f21bd4c6e87eec2b8c6675255975cc Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Sat, 18 Mar 2017 22:36:24 -0700 Subject: [PATCH 245/302] fix handling of normals. don't crash when extracting a mesh from a polyvox with no 'on' voxels --- .../src/RenderablePolyVoxEntityItem.h | 2 ++ .../entities/src/EntityScriptingInterface.cpp | 10 ++++-- libraries/entities/src/PolyVoxEntityItem.h | 2 ++ libraries/model/src/model/Geometry.cpp | 31 +++++++++---------- .../src/ModelScriptingInterface.cpp | 7 ++++- 5 files changed, 32 insertions(+), 20 deletions(-) diff --git a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.h b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.h index fa2cdb952f..cf4672f068 100644 --- a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.h +++ b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.h @@ -61,6 +61,8 @@ public: virtual uint8_t getVoxel(int x, int y, int z) override; virtual bool setVoxel(int x, int y, int z, uint8_t toValue) override; + int getOnCount() const override { return _onCount; } + void render(RenderArgs* args) override; virtual bool supportsDetailedRayIntersection() const override { return true; } virtual bool findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction, diff --git a/libraries/entities/src/EntityScriptingInterface.cpp b/libraries/entities/src/EntityScriptingInterface.cpp index 37e9e940b1..7ab0c3f7d9 100644 --- a/libraries/entities/src/EntityScriptingInterface.cpp +++ b/libraries/entities/src/EntityScriptingInterface.cpp @@ -930,11 +930,15 @@ bool EntityScriptingInterface::setVoxelsInCuboid(QUuid entityID, const glm::vec3 void EntityScriptingInterface::voxelsToMesh(QUuid entityID, QScriptValue callback) { PROFILE_RANGE(script_entities, __FUNCTION__); - bool success; - QScriptValue mesh; + bool success { false }; + QScriptValue mesh { false }; polyVoxWorker(entityID, [&](PolyVoxEntityItem& polyVoxEntity) mutable { - success = polyVoxEntity.getMeshAsScriptValue(callback.engine(), mesh); + if (polyVoxEntity.getOnCount() == 0) { + success = true; + } else { + success = polyVoxEntity.getMeshAsScriptValue(callback.engine(), mesh); + } return true; }); diff --git a/libraries/entities/src/PolyVoxEntityItem.h b/libraries/entities/src/PolyVoxEntityItem.h index 943d273452..311a002a4a 100644 --- a/libraries/entities/src/PolyVoxEntityItem.h +++ b/libraries/entities/src/PolyVoxEntityItem.h @@ -57,6 +57,8 @@ class PolyVoxEntityItem : public EntityItem { virtual void setVoxelData(QByteArray voxelData); virtual const QByteArray getVoxelData() const; + virtual int getOnCount() const { return 0; } + enum PolyVoxSurfaceStyle { SURFACE_MARCHING_CUBES, SURFACE_CUBIC, diff --git a/libraries/model/src/model/Geometry.cpp b/libraries/model/src/model/Geometry.cpp index d511ae6cb9..04b0db92d3 100755 --- a/libraries/model/src/model/Geometry.cpp +++ b/libraries/model/src/model/Geometry.cpp @@ -138,15 +138,13 @@ Box Mesh::evalPartsBound(int partStart, int partEnd) const { model::MeshPointer Mesh::map(std::function vertexFunc, std::function normalFunc, std::function indexFunc) { - int attributeTypeNormal = gpu::Stream::InputSlot::NORMAL; // libraries/gpu/src/gpu/Stream.h - // vertex data - gpu::Resource::Size vertexSize = getNumVertices() * sizeof(glm::vec3); - unsigned char* resultVertexData = new unsigned char[vertexSize]; - unsigned char* vertexDataCursor = resultVertexData; - const gpu::BufferView& vertexBufferView = getVertexBuffer(); gpu::BufferView::Index numVertices = (gpu::BufferView::Index)getNumVertices(); + gpu::Resource::Size vertexSize = numVertices * sizeof(glm::vec3); + unsigned char* resultVertexData = new unsigned char[vertexSize]; + unsigned char* vertexDataCursor = resultVertexData; + for (gpu::BufferView::Index i = 0; i < numVertices; i ++) { glm::vec3 pos = vertexFunc(vertexBufferView.get(i)); memcpy(vertexDataCursor, &pos, sizeof(pos)); @@ -154,12 +152,13 @@ model::MeshPointer Mesh::map(std::function vertexFunc, } // normal data - gpu::Resource::Size normalSize = getNumAttributes() * sizeof(glm::vec3); - unsigned char* resultNormalData = new unsigned char[normalSize]; + int attributeTypeNormal = gpu::Stream::InputSlot::NORMAL; // libraries/gpu/src/gpu/Stream.h + const gpu::BufferView& normalsBufferView = getAttributeBuffer(attributeTypeNormal); + gpu::BufferView::Index numNormals = (gpu::BufferView::Index)normalsBufferView.getNumElements(); + gpu::Resource::Size normalSize = numNormals * sizeof(glm::vec3); + unsigned char* resultNormalData = new unsigned char[normalSize]; unsigned char* normalDataCursor = resultNormalData; - const gpu::BufferView& normalsBufferView = getAttributeBuffer(attributeTypeNormal); - gpu::BufferView::Index numNormals = (gpu::BufferView::Index)getNumAttributes(); for (gpu::BufferView::Index i = 0; i < numNormals; i ++) { glm::vec3 normal = normalFunc(normalsBufferView.get(i)); memcpy(normalDataCursor, &normal, sizeof(normal)); @@ -168,12 +167,12 @@ model::MeshPointer Mesh::map(std::function vertexFunc, // TODO -- other attributes // face data - gpu::Resource::Size indexSize = getNumIndices() * sizeof(uint32_t); - unsigned char* resultIndexData = new unsigned char[indexSize]; + const gpu::BufferView& indexBufferView = getIndexBuffer(); + gpu::BufferView::Index numIndexes = (gpu::BufferView::Index)getNumIndices(); + gpu::Resource::Size indexSize = numIndexes * sizeof(uint32_t); + unsigned char* resultIndexData = new unsigned char[indexSize]; unsigned char* indexDataCursor = resultIndexData; - const gpu::BufferView& indexBufferView = getIndexBuffer(); - gpu::BufferView::Index numIndexes = (gpu::BufferView::Index)getNumIndices(); for (gpu::BufferView::Index i = 0; i < numIndexes; i ++) { uint32_t index = indexFunc(indexBufferView.get(i)); memcpy(indexDataCursor, &index, sizeof(index)); @@ -229,7 +228,7 @@ void Mesh::forEach(std::function vertexFunc, // normal data const gpu::BufferView& normalsBufferView = getAttributeBuffer(attributeTypeNormal); - gpu::BufferView::Index numNormals = (gpu::BufferView::Index)getNumAttributes(); + gpu::BufferView::Index numNormals = (gpu::BufferView::Index) normalsBufferView.getNumElements(); for (gpu::BufferView::Index i = 0; i < numNormals; i ++) { normalFunc(normalsBufferView.get(i)); } @@ -237,7 +236,7 @@ void Mesh::forEach(std::function vertexFunc, // face data const gpu::BufferView& indexBufferView = getIndexBuffer(); - gpu::BufferView::Index numIndexes = (gpu::BufferView::Index)getNumIndices(); + gpu::BufferView::Index numIndexes = (gpu::BufferView::Index)getNumIndices(); for (gpu::BufferView::Index i = 0; i < numIndexes; i ++) { indexFunc(indexBufferView.get(i)); } diff --git a/libraries/script-engine/src/ModelScriptingInterface.cpp b/libraries/script-engine/src/ModelScriptingInterface.cpp index cfcb6da0dc..833ac5b64d 100644 --- a/libraries/script-engine/src/ModelScriptingInterface.cpp +++ b/libraries/script-engine/src/ModelScriptingInterface.cpp @@ -61,7 +61,12 @@ QScriptValue ModelScriptingInterface::appendMeshes(MeshProxyList in) { foreach (const MeshProxy* meshProxy, in) { MeshPointer mesh = meshProxy->getMeshPointer(); totalVertexCount += mesh->getNumVertices(); - totalAttributeCount += mesh->getNumAttributes(); + + int attributeTypeNormal = gpu::Stream::InputSlot::NORMAL; // libraries/gpu/src/gpu/Stream.h + const gpu::BufferView& normalsBufferView = mesh->getAttributeBuffer(attributeTypeNormal); + gpu::BufferView::Index numNormals = (gpu::BufferView::Index)normalsBufferView.getNumElements(); + totalAttributeCount += numNormals; + totalIndexCount += mesh->getNumIndices(); } From b60534a85567cb61b32dfce5f9b52595586acd38 Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Sun, 19 Mar 2017 14:00:56 +0100 Subject: [PATCH 246/302] - fix for multiple boppo's appearing after ESS restart (disconnect from message event instead of connect) - default volume for sounds coming from ESS --- unpublishedScripts/marketplace/boppo/boppoServer.js | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/unpublishedScripts/marketplace/boppo/boppoServer.js b/unpublishedScripts/marketplace/boppo/boppoServer.js index 1abc5a135c..28690c0a00 100644 --- a/unpublishedScripts/marketplace/boppo/boppoServer.js +++ b/unpublishedScripts/marketplace/boppo/boppoServer.js @@ -26,6 +26,9 @@ var MESSAGE_HIT = 'hit'; var MESSAGE_ENTER_ZONE = 'enter-zone'; var MESSAGE_UNLOAD_FIX = 'unload-fix'; + + var DEFAULT_SOUND_VOLUME = 0.6; + // don't set the search radius too high, it might remove boppo's from other nearby instances var BOPPO_SEARCH_RADIUS = 4.0; @@ -116,6 +119,9 @@ var playSoundAtBoxingRing = function(sound, properties) { var _properties = properties ? properties : {}; + if (_properties['volume'] === undefined) { + _properties['volume'] = DEFAULT_SOUND_VOLUME; + } _properties['position'] = Entities.getEntityProperties(_entityID, ['position']).position; // play beep return Audio.playSound(sound, _properties); @@ -285,7 +291,7 @@ if (_updateInterval) { Script.clearInterval(_updateInterval); } - Messages.messageReceived.connect(onMessage); + Messages.messageReceived.disconnect(onMessage); Messages.unsubscribe(_channel); Entities.deleteEntity(_boppoClownID); print('endOfUnload'); From c430e0c9d6d6fd9b6596ab6dc5eee10760f73ff8 Mon Sep 17 00:00:00 2001 From: anshuman64 Date: Sun, 19 Mar 2017 12:07:45 -0700 Subject: [PATCH 247/302] Small changes based on Zach's recommendations --- BUILD_WIN.md | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/BUILD_WIN.md b/BUILD_WIN.md index 5d7812342a..e37bf27503 100644 --- a/BUILD_WIN.md +++ b/BUILD_WIN.md @@ -8,23 +8,23 @@ Note: Newer versions of Visual Studio are not yet compatible. ###Step 2. Installing CMake -Download and install the "win64-x64 Installer" from the [CMake Website](https://cmake.org/download/). Make sure "Add CMake to system PATH for all users" is checked when going through the installer. +Download and install the CMake 3.8.0-rc2 "win64-x64 Installer" from the [CMake Website](https://cmake.org/download/). Make sure "Add CMake to system PATH for all users" is checked when going through the installer. ###Step 3. Installing Qt -Download and install the [Qt 5.6.1 Installer](https://download.qt.io/official_releases/qt/5.6/5.6.1-1/qt-opensource-windows-x86-msvc2013_64-5.6.1-1.exe) +Download and install the [Qt 5.6.1 Installer](https://download.qt.io/official_releases/qt/5.6/5.6.1-1/qt-opensource-windows-x86-msvc2013_64-5.6.1-1.exe). Please note that the download file is large (850MB) and may take some time. Make sure to select all components when going through the installer. ###Step 4. Setting Qt Environment Variable -Go to "Control Panel > System > Advanced System Settings > Environment Variables > New..." +Go to "Control Panel > System > Advanced System Settings > Environment Variables > New..." (or search “Environment Variables” in Start Search). * Set "Variable name": QT_CMAKE_PREFIX_PATH * Set "Variable value": `C:\Qt\Qt5.6.1\5.6\msvc2013_64\lib\cmake` ###Step 5. Installing OpenSSL -Download and install the "Win64 OpenSSL v1.0.xk" Installer from [this website](https://slproweb.com/products/Win32OpenSSL.html), where "x" is the number of the latest release (ex. Win64 OpenSSL v1.0.2k). +Download and install the "Win64 OpenSSL v1.0.2k" Installer from [this website](https://slproweb.com/products/Win32OpenSSL.html). ###Step 6. Running CMake to Generate Build Files @@ -44,7 +44,7 @@ Change the Solution Configuration (next to the green play button) from "Debug" t Run Build > Build Solution. -###Step 8. Testing the Interface +###Step 8. Testing Interface Create another environment variable (see Step #4) * Set "Variable name": _NO_DEBUG_HEAP @@ -54,12 +54,12 @@ In Visual Studio, right+click "interface" under the Apps folder in Solution Expl Now, you should have a full build of High Fidelity and be able to run the Interface using Visual Studio. Please check our [Docs](https://wiki.highfidelity.com/wiki/Main_Page) for more information regarding the programming workflow. -Note: You can also run Interface by launching it from command line or File Explorer from %HIFI_DIR%\build\interface\Debug\interface.exe +Note: You can also run Interface by launching it from command line or File Explorer from %HIFI_DIR%\build\interface\Release\interface.exe ###Troubleshooting For any problems after Step #6, first try this: -* Delete the highfidelity repository +* Delete your locally cloned copy of the highfidelity repository * Restart your computer * Redownload the [repository](https://github.com/highfidelity/hifi) * Restart directions from Step #6 @@ -77,4 +77,5 @@ If not, add the directory where nmake is located to the PATH environment variabl ####Qt is throwing an error -Make sure you have the current version (5.6.1-1) installed and 'QT_CMAKE_PREFIX_PATH' environment variable is set correctly. +Make sure you have the correct version (5.6.1-1) installed and 'QT_CMAKE_PREFIX_PATH' environment variable is set correctly. + From 8584d80bb8540ba25e923127e17b06fac9adb278 Mon Sep 17 00:00:00 2001 From: Sam Cake Date: Sun, 19 Mar 2017 17:00:42 -0700 Subject: [PATCH 248/302] KEep exploring the ambient lighting --- libraries/render-utils/src/LightAmbient.slh | 13 ++++--------- libraries/render-utils/src/RenderPipelines.cpp | 2 +- 2 files changed, 5 insertions(+), 10 deletions(-) diff --git a/libraries/render-utils/src/LightAmbient.slh b/libraries/render-utils/src/LightAmbient.slh index a4e3764830..37df768924 100644 --- a/libraries/render-utils/src/LightAmbient.slh +++ b/libraries/render-utils/src/LightAmbient.slh @@ -77,18 +77,13 @@ void evalLightingAmbient(out vec3 diffuse, out vec3 specular, LightAmbient ambie // Diffuse from ambient diffuse = (1.0 - metallic) * sphericalHarmonics_evalSphericalLight(getLightAmbientSphere(ambient), normal).xyz; - if (gl_FragCoord.x > 1000) { - vec3 ambientFresnel = vec3 (1.0) - fresnelSchlickAmbient(fresnel, eyeDir, normal, 1.0 - roughness); - diffuse *= ambientFresnel * (1.0 / 3.14); - - - if (gl_FragCoord.x > 2000) { - diffuse *= obscurance; - } + if (gl_FragCoord.x > 300) { + vec3 ambientFresnelDiffuse = vec3 (1.0) - fresnelSchlickAmbient(fresnel, eyeDir, normal, 1.0 - roughness); + diffuse *= ambientFresnelDiffuse; } // Specular highlight from ambient - specular = evalAmbientSpecularIrradiance(ambient, eyeDir, normal, roughness, fresnel) * obscurance * getLightAmbientIntensity(ambient); + specular = evalAmbientSpecularIrradiance(ambient, eyeDir, normal, roughness, fresnel); <@if supportScattering@> diff --git a/libraries/render-utils/src/RenderPipelines.cpp b/libraries/render-utils/src/RenderPipelines.cpp index 4fbac4170e..414bcf0d63 100644 --- a/libraries/render-utils/src/RenderPipelines.cpp +++ b/libraries/render-utils/src/RenderPipelines.cpp @@ -307,7 +307,7 @@ void initForwardPipelines(render::ShapePlumber& plumber) { void addPlumberPipeline(ShapePlumber& plumber, const ShapeKey& key, const gpu::ShaderPointer& vertex, const gpu::ShaderPointer& pixel) { // These key-values' pipelines are added by this functor in addition to the key passed - assert(!key.isWireFrame()); + assert(!key.isWireframe()); assert(!key.isDepthBiased()); assert(key.isCullFace()); From 3a08611c26d96e01f5c0a32e2869dea595d3d364 Mon Sep 17 00:00:00 2001 From: Sam Cake Date: Sun, 19 Mar 2017 17:33:28 -0700 Subject: [PATCH 249/302] Fix the typo in the assert for isWireframe --- libraries/render-utils/src/RenderPipelines.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/render-utils/src/RenderPipelines.cpp b/libraries/render-utils/src/RenderPipelines.cpp index 4fbac4170e..414bcf0d63 100644 --- a/libraries/render-utils/src/RenderPipelines.cpp +++ b/libraries/render-utils/src/RenderPipelines.cpp @@ -307,7 +307,7 @@ void initForwardPipelines(render::ShapePlumber& plumber) { void addPlumberPipeline(ShapePlumber& plumber, const ShapeKey& key, const gpu::ShaderPointer& vertex, const gpu::ShaderPointer& pixel) { // These key-values' pipelines are added by this functor in addition to the key passed - assert(!key.isWireFrame()); + assert(!key.isWireframe()); assert(!key.isDepthBiased()); assert(key.isCullFace()); From 3612b9fccc91e2acfc4c4d339c0632437fb26feb Mon Sep 17 00:00:00 2001 From: Mohammed Nafees Date: Sun, 19 Mar 2017 07:40:22 -0400 Subject: [PATCH 250/302] Remove Edit > Scripts Editor and all related source files --- interface/src/Application.cpp | 2 - interface/src/Menu.cpp | 5 - interface/src/Menu.h | 1 - interface/src/ui/DialogsManager.cpp | 7 - interface/src/ui/DialogsManager.h | 2 - interface/src/ui/ScriptEditBox.cpp | 111 -------- interface/src/ui/ScriptEditBox.h | 38 --- interface/src/ui/ScriptEditorWidget.cpp | 256 ----------------- interface/src/ui/ScriptEditorWidget.h | 64 ----- interface/src/ui/ScriptEditorWindow.cpp | 259 ----------------- interface/src/ui/ScriptEditorWindow.h | 64 ----- interface/src/ui/ScriptLineNumberArea.cpp | 28 -- interface/src/ui/ScriptLineNumberArea.h | 32 --- interface/src/ui/ScriptsTableWidget.cpp | 49 ---- interface/src/ui/ScriptsTableWidget.h | 28 -- interface/ui/scriptEditorWidget.ui | 142 ---------- interface/ui/scriptEditorWindow.ui | 324 ---------------------- 17 files changed, 1412 deletions(-) delete mode 100644 interface/src/ui/ScriptEditBox.cpp delete mode 100644 interface/src/ui/ScriptEditBox.h delete mode 100644 interface/src/ui/ScriptEditorWidget.cpp delete mode 100644 interface/src/ui/ScriptEditorWidget.h delete mode 100644 interface/src/ui/ScriptEditorWindow.cpp delete mode 100644 interface/src/ui/ScriptEditorWindow.h delete mode 100644 interface/src/ui/ScriptLineNumberArea.cpp delete mode 100644 interface/src/ui/ScriptLineNumberArea.h delete mode 100644 interface/src/ui/ScriptsTableWidget.cpp delete mode 100644 interface/src/ui/ScriptsTableWidget.h delete mode 100644 interface/ui/scriptEditorWidget.ui delete mode 100644 interface/ui/scriptEditorWindow.ui diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 03f992bf7a..880f9ca8f1 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -2768,8 +2768,6 @@ void Application::keyPressEvent(QKeyEvent* event) { case Qt::Key_S: if (isShifted && isMeta && !isOption) { Menu::getInstance()->triggerOption(MenuOption::SuppressShortTimings); - } else if (isOption && !isShifted && !isMeta) { - Menu::getInstance()->triggerOption(MenuOption::ScriptEditor); } else if (!isOption && !isShifted && isMeta) { takeSnapshot(true); } diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 8e124d27c7..3d0b7735ad 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -120,11 +120,6 @@ Menu::Menu() { scriptEngines.data(), SLOT(reloadAllScripts()), QAction::NoRole, UNSPECIFIED_POSITION, "Advanced"); - // Edit > Scripts Editor... [advanced] - addActionToQMenuAndActionHash(editMenu, MenuOption::ScriptEditor, Qt::ALT | Qt::Key_S, - dialogsManager.data(), SLOT(showScriptEditor()), - QAction::NoRole, UNSPECIFIED_POSITION, "Advanced"); - // Edit > Console... [advanced] addActionToQMenuAndActionHash(editMenu, MenuOption::Console, Qt::CTRL | Qt::ALT | Qt::Key_J, DependencyManager::get().data(), diff --git a/interface/src/Menu.h b/interface/src/Menu.h index e0ac340edc..ec5cbba948 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -168,7 +168,6 @@ namespace MenuOption { const QString RunningScripts = "Running Scripts..."; const QString RunClientScriptTests = "Run Client Script Tests"; const QString RunTimingTests = "Run Timing Tests"; - const QString ScriptEditor = "Script Editor..."; const QString ScriptedMotorControl = "Enable Scripted Motor Control"; const QString SendWrongDSConnectVersion = "Send wrong DS connect version"; const QString SendWrongProtocolVersion = "Send wrong protocol version"; diff --git a/interface/src/ui/DialogsManager.cpp b/interface/src/ui/DialogsManager.cpp index 20b9f6b173..52ce84df6e 100644 --- a/interface/src/ui/DialogsManager.cpp +++ b/interface/src/ui/DialogsManager.cpp @@ -27,7 +27,6 @@ #include "LoginDialog.h" #include "OctreeStatsDialog.h" #include "PreferencesDialog.h" -#include "ScriptEditorWindow.h" #include "UpdateDialog.h" template @@ -126,12 +125,6 @@ void DialogsManager::hmdToolsClosed() { } } -void DialogsManager::showScriptEditor() { - maybeCreateDialog(_scriptEditor); - _scriptEditor->show(); - _scriptEditor->raise(); -} - void DialogsManager::showTestingResults() { if (!_testingDialog) { _testingDialog = new TestingDialog(qApp->getWindow()); diff --git a/interface/src/ui/DialogsManager.h b/interface/src/ui/DialogsManager.h index 4e3b785679..662cbd1640 100644 --- a/interface/src/ui/DialogsManager.h +++ b/interface/src/ui/DialogsManager.h @@ -51,7 +51,6 @@ public slots: void octreeStatsDetails(); void lodTools(); void hmdTools(bool showTools); - void showScriptEditor(); void showDomainConnectionDialog(); void showTestingResults(); @@ -79,7 +78,6 @@ private: QPointer _hmdToolsDialog; QPointer _lodToolsDialog; QPointer _octreeStatsDialog; - QPointer _scriptEditor; QPointer _testingDialog; QPointer _domainConnectionDialog; }; diff --git a/interface/src/ui/ScriptEditBox.cpp b/interface/src/ui/ScriptEditBox.cpp deleted file mode 100644 index 2aea225b17..0000000000 --- a/interface/src/ui/ScriptEditBox.cpp +++ /dev/null @@ -1,111 +0,0 @@ -// -// ScriptEditBox.cpp -// interface/src/ui -// -// Created by Thijs Wenker on 4/30/14. -// Copyright 2014 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#include "ScriptEditBox.h" - -#include -#include - -#include "ScriptLineNumberArea.h" - -ScriptEditBox::ScriptEditBox(QWidget* parent) : - QPlainTextEdit(parent) -{ - _scriptLineNumberArea = new ScriptLineNumberArea(this); - - connect(this, &ScriptEditBox::blockCountChanged, this, &ScriptEditBox::updateLineNumberAreaWidth); - connect(this, &ScriptEditBox::updateRequest, this, &ScriptEditBox::updateLineNumberArea); - connect(this, &ScriptEditBox::cursorPositionChanged, this, &ScriptEditBox::highlightCurrentLine); - - updateLineNumberAreaWidth(0); - highlightCurrentLine(); -} - -int ScriptEditBox::lineNumberAreaWidth() { - int digits = 1; - const int SPACER_PIXELS = 3; - const int BASE_TEN = 10; - int max = qMax(1, blockCount()); - while (max >= BASE_TEN) { - max /= BASE_TEN; - digits++; - } - return SPACER_PIXELS + fontMetrics().width(QLatin1Char('H')) * digits; -} - -void ScriptEditBox::updateLineNumberAreaWidth(int blockCount) { - setViewportMargins(lineNumberAreaWidth(), 0, 0, 0); -} - -void ScriptEditBox::updateLineNumberArea(const QRect& rect, int deltaY) { - if (deltaY) { - _scriptLineNumberArea->scroll(0, deltaY); - } else { - _scriptLineNumberArea->update(0, rect.y(), _scriptLineNumberArea->width(), rect.height()); - } - - if (rect.contains(viewport()->rect())) { - updateLineNumberAreaWidth(0); - } -} - -void ScriptEditBox::resizeEvent(QResizeEvent* event) { - QPlainTextEdit::resizeEvent(event); - - QRect localContentsRect = contentsRect(); - _scriptLineNumberArea->setGeometry(QRect(localContentsRect.left(), localContentsRect.top(), lineNumberAreaWidth(), - localContentsRect.height())); -} - -void ScriptEditBox::highlightCurrentLine() { - QList extraSelections; - - if (!isReadOnly()) { - QTextEdit::ExtraSelection selection; - - QColor lineColor = QColor(Qt::gray).lighter(); - - selection.format.setBackground(lineColor); - selection.format.setProperty(QTextFormat::FullWidthSelection, true); - selection.cursor = textCursor(); - selection.cursor.clearSelection(); - extraSelections.append(selection); - } - - setExtraSelections(extraSelections); -} - -void ScriptEditBox::lineNumberAreaPaintEvent(QPaintEvent* event) -{ - QPainter painter(_scriptLineNumberArea); - painter.fillRect(event->rect(), Qt::lightGray); - QTextBlock block = firstVisibleBlock(); - int blockNumber = block.blockNumber(); - int top = (int) blockBoundingGeometry(block).translated(contentOffset()).top(); - int bottom = top + (int) blockBoundingRect(block).height(); - - while (block.isValid() && top <= event->rect().bottom()) { - if (block.isVisible() && bottom >= event->rect().top()) { - QFont font = painter.font(); - font.setBold(this->textCursor().blockNumber() == block.blockNumber()); - painter.setFont(font); - QString number = QString::number(blockNumber + 1); - painter.setPen(Qt::black); - painter.drawText(0, top, _scriptLineNumberArea->width(), fontMetrics().height(), - Qt::AlignRight, number); - } - - block = block.next(); - top = bottom; - bottom = top + (int) blockBoundingRect(block).height(); - blockNumber++; - } -} diff --git a/interface/src/ui/ScriptEditBox.h b/interface/src/ui/ScriptEditBox.h deleted file mode 100644 index 0b037db16a..0000000000 --- a/interface/src/ui/ScriptEditBox.h +++ /dev/null @@ -1,38 +0,0 @@ -// -// ScriptEditBox.h -// interface/src/ui -// -// Created by Thijs Wenker on 4/30/14. -// Copyright 2014 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#ifndef hifi_ScriptEditBox_h -#define hifi_ScriptEditBox_h - -#include - -class ScriptEditBox : public QPlainTextEdit { - Q_OBJECT - -public: - ScriptEditBox(QWidget* parent = NULL); - - void lineNumberAreaPaintEvent(QPaintEvent* event); - int lineNumberAreaWidth(); - -protected: - void resizeEvent(QResizeEvent* event) override; - -private slots: - void updateLineNumberAreaWidth(int blockCount); - void highlightCurrentLine(); - void updateLineNumberArea(const QRect& rect, int deltaY); - -private: - QWidget* _scriptLineNumberArea; -}; - -#endif // hifi_ScriptEditBox_h diff --git a/interface/src/ui/ScriptEditorWidget.cpp b/interface/src/ui/ScriptEditorWidget.cpp deleted file mode 100644 index ada6b11355..0000000000 --- a/interface/src/ui/ScriptEditorWidget.cpp +++ /dev/null @@ -1,256 +0,0 @@ -// -// ScriptEditorWidget.cpp -// interface/src/ui -// -// Created by Thijs Wenker on 4/14/14. -// Copyright 2014 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#include "ui_scriptEditorWidget.h" -#include "ScriptEditorWidget.h" -#include "ScriptEditorWindow.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include "Application.h" -#include "ScriptHighlighting.h" - -ScriptEditorWidget::ScriptEditorWidget() : - _scriptEditorWidgetUI(new Ui::ScriptEditorWidget), - _scriptEngine(NULL), - _isRestarting(false), - _isReloading(false) -{ - setAttribute(Qt::WA_DeleteOnClose); - - _scriptEditorWidgetUI->setupUi(this); - - connect(_scriptEditorWidgetUI->scriptEdit->document(), &QTextDocument::modificationChanged, this, - &ScriptEditorWidget::scriptModified); - connect(_scriptEditorWidgetUI->scriptEdit->document(), &QTextDocument::contentsChanged, this, - &ScriptEditorWidget::onScriptModified); - - // remove the title bar (see the Qt docs on setTitleBarWidget) - setTitleBarWidget(new QWidget()); - QFontMetrics fm(_scriptEditorWidgetUI->scriptEdit->font()); - _scriptEditorWidgetUI->scriptEdit->setTabStopWidth(fm.width('0') * 4); - // We create a new ScriptHighligting QObject and provide it with a parent so this is NOT a memory leak. - new ScriptHighlighting(_scriptEditorWidgetUI->scriptEdit->document()); - QTimer::singleShot(0, _scriptEditorWidgetUI->scriptEdit, SLOT(setFocus())); - - _console = new JSConsole(this); - _console->setFixedHeight(CONSOLE_HEIGHT); - _scriptEditorWidgetUI->verticalLayout->addWidget(_console); - connect(_scriptEditorWidgetUI->clearButton, &QPushButton::clicked, _console, &JSConsole::clear); -} - -ScriptEditorWidget::~ScriptEditorWidget() { - delete _scriptEditorWidgetUI; - delete _console; -} - -void ScriptEditorWidget::onScriptModified() { - if(_scriptEditorWidgetUI->onTheFlyCheckBox->isChecked() && isModified() && isRunning() && !_isReloading) { - _isRestarting = true; - setRunning(false); - // Script is restarted once current script instance finishes. - } -} - -void ScriptEditorWidget::onScriptFinished(const QString& scriptPath) { - _scriptEngine = NULL; - _console->setScriptEngine(NULL); - if (_isRestarting) { - _isRestarting = false; - setRunning(true); - } -} - -bool ScriptEditorWidget::isModified() { - return _scriptEditorWidgetUI->scriptEdit->document()->isModified(); -} - -bool ScriptEditorWidget::isRunning() { - return (_scriptEngine != NULL) ? _scriptEngine->isRunning() : false; -} - -bool ScriptEditorWidget::setRunning(bool run) { - if (run && isModified() && !save()) { - return false; - } - - if (_scriptEngine != NULL) { - disconnect(_scriptEngine, &ScriptEngine::runningStateChanged, this, &ScriptEditorWidget::runningStateChanged); - disconnect(_scriptEngine, &ScriptEngine::update, this, &ScriptEditorWidget::onScriptModified); - disconnect(_scriptEngine, &ScriptEngine::finished, this, &ScriptEditorWidget::onScriptFinished); - } - - auto scriptEngines = DependencyManager::get(); - if (run) { - const QString& scriptURLString = QUrl(_currentScript).toString(); - // Reload script so that an out of date copy is not retrieved from the cache - _scriptEngine = scriptEngines->loadScript(scriptURLString, true, true, false, true); - connect(_scriptEngine, &ScriptEngine::runningStateChanged, this, &ScriptEditorWidget::runningStateChanged); - connect(_scriptEngine, &ScriptEngine::update, this, &ScriptEditorWidget::onScriptModified); - connect(_scriptEngine, &ScriptEngine::finished, this, &ScriptEditorWidget::onScriptFinished); - } else { - connect(_scriptEngine, &ScriptEngine::finished, this, &ScriptEditorWidget::onScriptFinished); - const QString& scriptURLString = QUrl(_currentScript).toString(); - scriptEngines->stopScript(scriptURLString); - _scriptEngine = NULL; - } - _console->setScriptEngine(_scriptEngine); - return true; -} - -bool ScriptEditorWidget::saveFile(const QString &scriptPath) { - QFile file(scriptPath); - if (!file.open(QFile::WriteOnly | QFile::Text)) { - OffscreenUi::warning(this, tr("Interface"), tr("Cannot write script %1:\n%2.").arg(scriptPath) - .arg(file.errorString())); - return false; - } - - QTextStream out(&file); - out << _scriptEditorWidgetUI->scriptEdit->toPlainText(); - file.close(); - - setScriptFile(scriptPath); - return true; -} - -void ScriptEditorWidget::loadFile(const QString& scriptPath) { - QUrl url(scriptPath); - - // if the scheme length is one or lower, maybe they typed in a file, let's try - const int WINDOWS_DRIVE_LETTER_SIZE = 1; - if (url.scheme().size() <= WINDOWS_DRIVE_LETTER_SIZE) { - QFile file(scriptPath); - if (!file.open(QFile::ReadOnly | QFile::Text)) { - OffscreenUi::warning(this, tr("Interface"), tr("Cannot read script %1:\n%2.").arg(scriptPath) - .arg(file.errorString())); - return; - } - QTextStream in(&file); - _scriptEditorWidgetUI->scriptEdit->setPlainText(in.readAll()); - file.close(); - setScriptFile(scriptPath); - - if (_scriptEngine != NULL) { - disconnect(_scriptEngine, &ScriptEngine::runningStateChanged, this, &ScriptEditorWidget::runningStateChanged); - disconnect(_scriptEngine, &ScriptEngine::update, this, &ScriptEditorWidget::onScriptModified); - disconnect(_scriptEngine, &ScriptEngine::finished, this, &ScriptEditorWidget::onScriptFinished); - } - } else { - QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance(); - QNetworkRequest networkRequest = QNetworkRequest(url); - networkRequest.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true); - networkRequest.setHeader(QNetworkRequest::UserAgentHeader, HIGH_FIDELITY_USER_AGENT); - QNetworkReply* reply = networkAccessManager.get(networkRequest); - qDebug() << "Downloading included script at" << scriptPath; - QEventLoop loop; - QObject::connect(reply, &QNetworkReply::finished, &loop, &QEventLoop::quit); - loop.exec(); - _scriptEditorWidgetUI->scriptEdit->setPlainText(reply->readAll()); - delete reply; - - if (!saveAs()) { - static_cast(this->parent()->parent()->parent())->terminateCurrentTab(); - } - } - const QString& scriptURLString = QUrl(_currentScript).toString(); - _scriptEngine = DependencyManager::get()->getScriptEngine(scriptURLString); - if (_scriptEngine != NULL) { - connect(_scriptEngine, &ScriptEngine::runningStateChanged, this, &ScriptEditorWidget::runningStateChanged); - connect(_scriptEngine, &ScriptEngine::update, this, &ScriptEditorWidget::onScriptModified); - connect(_scriptEngine, &ScriptEngine::finished, this, &ScriptEditorWidget::onScriptFinished); - } - _console->setScriptEngine(_scriptEngine); -} - -bool ScriptEditorWidget::save() { - return _currentScript.isEmpty() ? saveAs() : saveFile(_currentScript); -} - -bool ScriptEditorWidget::saveAs() { - auto scriptEngines = DependencyManager::get(); - QString fileName = QFileDialog::getSaveFileName(this, tr("Save script"), - qApp->getPreviousScriptLocation(), - tr("JavaScript Files (*.js)")); - if (!fileName.isEmpty()) { - qApp->setPreviousScriptLocation(fileName); - return saveFile(fileName); - } else { - return false; - } -} - -void ScriptEditorWidget::setScriptFile(const QString& scriptPath) { - _currentScript = scriptPath; - _currentScriptModified = QFileInfo(_currentScript).lastModified(); - _scriptEditorWidgetUI->scriptEdit->document()->setModified(false); - setWindowModified(false); - - emit scriptnameChanged(); -} - -bool ScriptEditorWidget::questionSave() { - if (_scriptEditorWidgetUI->scriptEdit->document()->isModified()) { - QMessageBox::StandardButton button = OffscreenUi::warning(this, tr("Interface"), - tr("The script has been modified.\nDo you want to save your changes?"), QMessageBox::Save | QMessageBox::Discard | - QMessageBox::Cancel, QMessageBox::Save); - return button == QMessageBox::Save ? save() : (button == QMessageBox::Discard); - } - return true; -} - -void ScriptEditorWidget::onWindowActivated() { - if (!_isReloading) { - _isReloading = true; - - QDateTime fileStamp = QFileInfo(_currentScript).lastModified(); - if (fileStamp > _currentScriptModified) { - bool doReload = false; - auto window = static_cast(this->parent()->parent()->parent()); - window->inModalDialog = true; - if (window->autoReloadScripts() - || OffscreenUi::question(this, tr("Reload Script"), - tr("The following file has been modified outside of the Interface editor:") + "\n" + _currentScript + "\n" - + (isModified() - ? tr("Do you want to reload it and lose the changes you've made in the Interface editor?") - : tr("Do you want to reload it?")), - QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) { - doReload = true; - } - window->inModalDialog = false; - if (doReload) { - loadFile(_currentScript); - if (_scriptEditorWidgetUI->onTheFlyCheckBox->isChecked() && isRunning()) { - _isRestarting = true; - setRunning(false); - // Script is restarted once current script instance finishes. - } - } else { - _currentScriptModified = fileStamp; // Asked and answered. Don't ask again until the external file is changed again. - } - } - _isReloading = false; - } -} diff --git a/interface/src/ui/ScriptEditorWidget.h b/interface/src/ui/ScriptEditorWidget.h deleted file mode 100644 index f53fd7b718..0000000000 --- a/interface/src/ui/ScriptEditorWidget.h +++ /dev/null @@ -1,64 +0,0 @@ -// -// ScriptEditorWidget.h -// interface/src/ui -// -// Created by Thijs Wenker on 4/14/14. -// Copyright 2014 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#ifndef hifi_ScriptEditorWidget_h -#define hifi_ScriptEditorWidget_h - -#include - -#include "JSConsole.h" -#include "ScriptEngine.h" - -namespace Ui { - class ScriptEditorWidget; -} - -class ScriptEditorWidget : public QDockWidget { - Q_OBJECT - -public: - ScriptEditorWidget(); - ~ScriptEditorWidget(); - - bool isModified(); - bool isRunning(); - bool setRunning(bool run); - bool saveFile(const QString& scriptPath); - void loadFile(const QString& scriptPath); - void setScriptFile(const QString& scriptPath); - bool save(); - bool saveAs(); - bool questionSave(); - const QString getScriptName() const { return _currentScript; }; - -signals: - void runningStateChanged(); - void scriptnameChanged(); - void scriptModified(); - -public slots: - void onWindowActivated(); - -private slots: - void onScriptModified(); - void onScriptFinished(const QString& scriptName); - -private: - JSConsole* _console; - Ui::ScriptEditorWidget* _scriptEditorWidgetUI; - ScriptEngine* _scriptEngine; - QString _currentScript; - QDateTime _currentScriptModified; - bool _isRestarting; - bool _isReloading; -}; - -#endif // hifi_ScriptEditorWidget_h diff --git a/interface/src/ui/ScriptEditorWindow.cpp b/interface/src/ui/ScriptEditorWindow.cpp deleted file mode 100644 index 58abd23979..0000000000 --- a/interface/src/ui/ScriptEditorWindow.cpp +++ /dev/null @@ -1,259 +0,0 @@ -// -// ScriptEditorWindow.cpp -// interface/src/ui -// -// Created by Thijs Wenker on 4/14/14. -// Copyright 2014 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#include - -#include "ui_scriptEditorWindow.h" -#include "ScriptEditorWindow.h" -#include "ScriptEditorWidget.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include "Application.h" -#include "PathUtils.h" - -ScriptEditorWindow::ScriptEditorWindow(QWidget* parent) : - QWidget(parent), - _ScriptEditorWindowUI(new Ui::ScriptEditorWindow), - _loadMenu(new QMenu), - _saveMenu(new QMenu) -{ - setAttribute(Qt::WA_DeleteOnClose); - - _ScriptEditorWindowUI->setupUi(this); - - this->setWindowFlags(Qt::Tool); - addScriptEditorWidget("New script"); - connect(_loadMenu, &QMenu::aboutToShow, this, &ScriptEditorWindow::loadMenuAboutToShow); - _ScriptEditorWindowUI->loadButton->setMenu(_loadMenu); - - _saveMenu->addAction("Save as..", this, SLOT(saveScriptAsClicked()), Qt::CTRL | Qt::SHIFT | Qt::Key_S); - - _ScriptEditorWindowUI->saveButton->setMenu(_saveMenu); - - connect(new QShortcut(QKeySequence("Ctrl+N"), this), &QShortcut::activated, this, &ScriptEditorWindow::newScriptClicked); - connect(new QShortcut(QKeySequence("Ctrl+S"), this), &QShortcut::activated, this,&ScriptEditorWindow::saveScriptClicked); - connect(new QShortcut(QKeySequence("Ctrl+O"), this), &QShortcut::activated, this, &ScriptEditorWindow::loadScriptClicked); - connect(new QShortcut(QKeySequence("F5"), this), &QShortcut::activated, this, &ScriptEditorWindow::toggleRunScriptClicked); - - _ScriptEditorWindowUI->loadButton->setIcon(QIcon(QPixmap(PathUtils::resourcesPath() + "icons/load-script.svg"))); - _ScriptEditorWindowUI->newButton->setIcon(QIcon(QPixmap(PathUtils::resourcesPath() + "icons/new-script.svg"))); - _ScriptEditorWindowUI->saveButton->setIcon(QIcon(QPixmap(PathUtils::resourcesPath() + "icons/save-script.svg"))); - _ScriptEditorWindowUI->toggleRunButton->setIcon(QIcon(QPixmap(PathUtils::resourcesPath() + "icons/start-script.svg"))); -} - -ScriptEditorWindow::~ScriptEditorWindow() { - delete _ScriptEditorWindowUI; -} - -void ScriptEditorWindow::setRunningState(bool run) { - if (_ScriptEditorWindowUI->tabWidget->currentIndex() != -1) { - static_cast(_ScriptEditorWindowUI->tabWidget->currentWidget())->setRunning(run); - } - this->updateButtons(); -} - -void ScriptEditorWindow::updateButtons() { - bool isRunning = _ScriptEditorWindowUI->tabWidget->currentIndex() != -1 && - static_cast(_ScriptEditorWindowUI->tabWidget->currentWidget())->isRunning(); - _ScriptEditorWindowUI->toggleRunButton->setEnabled(_ScriptEditorWindowUI->tabWidget->currentIndex() != -1); - _ScriptEditorWindowUI->toggleRunButton->setIcon(QIcon(QPixmap(PathUtils::resourcesPath() + ((isRunning ? - "icons/stop-script.svg" : "icons/start-script.svg"))))); -} - -void ScriptEditorWindow::loadScriptMenu(const QString& scriptName) { - addScriptEditorWidget("loading...")->loadFile(scriptName); - updateButtons(); -} - -void ScriptEditorWindow::loadScriptClicked() { - QString scriptName = QFileDialog::getOpenFileName(this, tr("Interface"), - qApp->getPreviousScriptLocation(), - tr("JavaScript Files (*.js)")); - if (!scriptName.isEmpty()) { - qApp->setPreviousScriptLocation(scriptName); - addScriptEditorWidget("loading...")->loadFile(scriptName); - updateButtons(); - } -} - -void ScriptEditorWindow::loadMenuAboutToShow() { - _loadMenu->clear(); - QStringList runningScripts = DependencyManager::get()->getRunningScripts(); - if (runningScripts.count() > 0) { - QSignalMapper* signalMapper = new QSignalMapper(this); - foreach (const QString& runningScript, runningScripts) { - QAction* runningScriptAction = new QAction(runningScript, _loadMenu); - connect(runningScriptAction, SIGNAL(triggered()), signalMapper, SLOT(map())); - signalMapper->setMapping(runningScriptAction, runningScript); - _loadMenu->addAction(runningScriptAction); - } - connect(signalMapper, SIGNAL(mapped(const QString &)), this, SLOT(loadScriptMenu(const QString&))); - } else { - QAction* naAction = new QAction("(no running scripts)", _loadMenu); - naAction->setDisabled(true); - _loadMenu->addAction(naAction); - } -} - -void ScriptEditorWindow::newScriptClicked() { - addScriptEditorWidget(QString("New script")); -} - -void ScriptEditorWindow::toggleRunScriptClicked() { - this->setRunningState(!(_ScriptEditorWindowUI->tabWidget->currentIndex() !=-1 - && static_cast(_ScriptEditorWindowUI->tabWidget->currentWidget())->isRunning())); -} - -void ScriptEditorWindow::saveScriptClicked() { - if (_ScriptEditorWindowUI->tabWidget->currentIndex() != -1) { - ScriptEditorWidget* currentScriptWidget = static_cast(_ScriptEditorWindowUI->tabWidget - ->currentWidget()); - currentScriptWidget->save(); - } -} - -void ScriptEditorWindow::saveScriptAsClicked() { - if (_ScriptEditorWindowUI->tabWidget->currentIndex() != -1) { - ScriptEditorWidget* currentScriptWidget = static_cast(_ScriptEditorWindowUI->tabWidget - ->currentWidget()); - currentScriptWidget->saveAs(); - } -} - -ScriptEditorWidget* ScriptEditorWindow::addScriptEditorWidget(QString title) { - ScriptEditorWidget* newScriptEditorWidget = new ScriptEditorWidget(); - connect(newScriptEditorWidget, &ScriptEditorWidget::scriptnameChanged, this, &ScriptEditorWindow::updateScriptNameOrStatus); - connect(newScriptEditorWidget, &ScriptEditorWidget::scriptModified, this, &ScriptEditorWindow::updateScriptNameOrStatus); - connect(newScriptEditorWidget, &ScriptEditorWidget::runningStateChanged, this, &ScriptEditorWindow::updateButtons); - connect(this, &ScriptEditorWindow::windowActivated, newScriptEditorWidget, &ScriptEditorWidget::onWindowActivated); - _ScriptEditorWindowUI->tabWidget->addTab(newScriptEditorWidget, title); - _ScriptEditorWindowUI->tabWidget->setCurrentWidget(newScriptEditorWidget); - newScriptEditorWidget->setUpdatesEnabled(true); - newScriptEditorWidget->adjustSize(); - return newScriptEditorWidget; -} - -void ScriptEditorWindow::tabSwitched(int tabIndex) { - this->updateButtons(); - if (_ScriptEditorWindowUI->tabWidget->currentIndex() != -1) { - ScriptEditorWidget* currentScriptWidget = static_cast(_ScriptEditorWindowUI->tabWidget - ->currentWidget()); - QString modifiedStar = (currentScriptWidget->isModified() ? "*" : ""); - if (currentScriptWidget->getScriptName().length() > 0) { - this->setWindowTitle("Script Editor [" + currentScriptWidget->getScriptName() + modifiedStar + "]"); - } else { - this->setWindowTitle("Script Editor [New script" + modifiedStar + "]"); - } - } else { - this->setWindowTitle("Script Editor"); - } -} - -void ScriptEditorWindow::tabCloseRequested(int tabIndex) { - if (ignoreCloseForModal(nullptr)) { - return; - } - ScriptEditorWidget* closingScriptWidget = static_cast(_ScriptEditorWindowUI->tabWidget - ->widget(tabIndex)); - if(closingScriptWidget->questionSave()) { - _ScriptEditorWindowUI->tabWidget->removeTab(tabIndex); - } -} - -// If this operating system window causes a qml overlay modal dialog (which might not even be seen by the user), closing this window -// will crash the code that was waiting on the dialog result. So that code whousl set inModalDialog to true while the question is up. -// This code will not be necessary when switch out all operating system windows for qml overlays. -bool ScriptEditorWindow::ignoreCloseForModal(QCloseEvent* event) { - if (!inModalDialog) { - return false; - } - // Deliberately not using OffscreenUi, so that the dialog is seen. - QMessageBox::information(this, tr("Interface"), tr("There is a modal dialog that must be answered before closing."), - QMessageBox::Discard, QMessageBox::Discard); - if (event) { - event->ignore(); // don't close - } - return true; -} - -void ScriptEditorWindow::closeEvent(QCloseEvent *event) { - if (ignoreCloseForModal(event)) { - return; - } - bool unsaved_docs_warning = false; - for (int i = 0; i < _ScriptEditorWindowUI->tabWidget->count(); i++){ - if(static_cast(_ScriptEditorWindowUI->tabWidget->widget(i))->isModified()){ - unsaved_docs_warning = true; - break; - } - } - - if (!unsaved_docs_warning || QMessageBox::warning(this, tr("Interface"), - tr("There are some unsaved scripts, are you sure you want to close the editor? Changes will be lost!"), - QMessageBox::Discard | QMessageBox::Cancel, QMessageBox::Cancel) == QMessageBox::Discard) { - event->accept(); - } else { - event->ignore(); - } -} - -void ScriptEditorWindow::updateScriptNameOrStatus() { - ScriptEditorWidget* source = static_cast(QObject::sender()); - QString modifiedStar = (source->isModified()? "*" : ""); - if (source->getScriptName().length() > 0) { - for (int i = 0; i < _ScriptEditorWindowUI->tabWidget->count(); i++){ - if (_ScriptEditorWindowUI->tabWidget->widget(i) == source) { - _ScriptEditorWindowUI->tabWidget->setTabText(i, modifiedStar + QFileInfo(source->getScriptName()).fileName()); - _ScriptEditorWindowUI->tabWidget->setTabToolTip(i, source->getScriptName()); - } - } - } - - if (_ScriptEditorWindowUI->tabWidget->currentWidget() == source) { - if (source->getScriptName().length() > 0) { - this->setWindowTitle("Script Editor [" + source->getScriptName() + modifiedStar + "]"); - } else { - this->setWindowTitle("Script Editor [New script" + modifiedStar + "]"); - } - } -} - -void ScriptEditorWindow::terminateCurrentTab() { - if (_ScriptEditorWindowUI->tabWidget->currentIndex() != -1) { - _ScriptEditorWindowUI->tabWidget->removeTab(_ScriptEditorWindowUI->tabWidget->currentIndex()); - this->raise(); - } -} - -bool ScriptEditorWindow::autoReloadScripts() { - return _ScriptEditorWindowUI->autoReloadCheckBox->isChecked(); -} - -bool ScriptEditorWindow::event(QEvent* event) { - if (event->type() == QEvent::WindowActivate) { - emit windowActivated(); - } - return QWidget::event(event); -} - diff --git a/interface/src/ui/ScriptEditorWindow.h b/interface/src/ui/ScriptEditorWindow.h deleted file mode 100644 index af9863d136..0000000000 --- a/interface/src/ui/ScriptEditorWindow.h +++ /dev/null @@ -1,64 +0,0 @@ -// -// ScriptEditorWindow.h -// interface/src/ui -// -// Created by Thijs Wenker on 4/14/14. -// Copyright 2014 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#ifndef hifi_ScriptEditorWindow_h -#define hifi_ScriptEditorWindow_h - -#include "ScriptEditorWidget.h" - -namespace Ui { - class ScriptEditorWindow; -} - -class ScriptEditorWindow : public QWidget { - Q_OBJECT - -public: - ScriptEditorWindow(QWidget* parent = nullptr); - ~ScriptEditorWindow(); - - void terminateCurrentTab(); - bool autoReloadScripts(); - - bool inModalDialog { false }; - bool ignoreCloseForModal(QCloseEvent* event); - -signals: - void windowActivated(); - -protected: - void closeEvent(QCloseEvent* event) override; - virtual bool event(QEvent* event) override; - -private: - Ui::ScriptEditorWindow* _ScriptEditorWindowUI; - QMenu* _loadMenu; - QMenu* _saveMenu; - - ScriptEditorWidget* addScriptEditorWidget(QString title); - void setRunningState(bool run); - void setScriptName(const QString& scriptName); - -private slots: - void loadScriptMenu(const QString& scriptName); - void loadScriptClicked(); - void newScriptClicked(); - void toggleRunScriptClicked(); - void saveScriptClicked(); - void saveScriptAsClicked(); - void loadMenuAboutToShow(); - void tabSwitched(int tabIndex); - void tabCloseRequested(int tabIndex); - void updateScriptNameOrStatus(); - void updateButtons(); -}; - -#endif // hifi_ScriptEditorWindow_h diff --git a/interface/src/ui/ScriptLineNumberArea.cpp b/interface/src/ui/ScriptLineNumberArea.cpp deleted file mode 100644 index 6d7e9185ea..0000000000 --- a/interface/src/ui/ScriptLineNumberArea.cpp +++ /dev/null @@ -1,28 +0,0 @@ -// -// ScriptLineNumberArea.cpp -// interface/src/ui -// -// Created by Thijs Wenker on 4/30/14. -// Copyright 2014 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#include "ScriptLineNumberArea.h" - -#include "ScriptEditBox.h" - -ScriptLineNumberArea::ScriptLineNumberArea(ScriptEditBox* scriptEditBox) : - QWidget(scriptEditBox) -{ - _scriptEditBox = scriptEditBox; -} - -QSize ScriptLineNumberArea::sizeHint() const { - return QSize(_scriptEditBox->lineNumberAreaWidth(), 0); -} - -void ScriptLineNumberArea::paintEvent(QPaintEvent* event) { - _scriptEditBox->lineNumberAreaPaintEvent(event); -} diff --git a/interface/src/ui/ScriptLineNumberArea.h b/interface/src/ui/ScriptLineNumberArea.h deleted file mode 100644 index 77de8244ce..0000000000 --- a/interface/src/ui/ScriptLineNumberArea.h +++ /dev/null @@ -1,32 +0,0 @@ -// -// ScriptLineNumberArea.h -// interface/src/ui -// -// Created by Thijs Wenker on 4/30/14. -// Copyright 2014 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#ifndef hifi_ScriptLineNumberArea_h -#define hifi_ScriptLineNumberArea_h - -#include - -class ScriptEditBox; - -class ScriptLineNumberArea : public QWidget { - -public: - ScriptLineNumberArea(ScriptEditBox* scriptEditBox); - QSize sizeHint() const override; - -protected: - void paintEvent(QPaintEvent* event) override; - -private: - ScriptEditBox* _scriptEditBox; -}; - -#endif // hifi_ScriptLineNumberArea_h diff --git a/interface/src/ui/ScriptsTableWidget.cpp b/interface/src/ui/ScriptsTableWidget.cpp deleted file mode 100644 index 7b4f9e6b1f..0000000000 --- a/interface/src/ui/ScriptsTableWidget.cpp +++ /dev/null @@ -1,49 +0,0 @@ -// -// ScriptsTableWidget.cpp -// interface -// -// Created by Mohammed Nafees on 04/03/2014. -// Copyright (c) 2014 High Fidelity, Inc. All rights reserved. -// -// 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 "ScriptsTableWidget.h" - -ScriptsTableWidget::ScriptsTableWidget(QWidget* parent) : - QTableWidget(parent) { - verticalHeader()->setVisible(false); - horizontalHeader()->setVisible(false); - setShowGrid(false); - setSelectionMode(QAbstractItemView::NoSelection); - setEditTriggers(QAbstractItemView::NoEditTriggers); - setStyleSheet("QTableWidget { border: none; background: transparent; color: #333333; } QToolTip { color: #000000; background: #f9f6e4; padding: 2px; }"); - setToolTipDuration(200); - setWordWrap(true); - setGeometry(0, 0, parent->width(), parent->height()); -} - -void ScriptsTableWidget::paintEvent(QPaintEvent* event) { - QPainter painter(viewport()); - painter.setPen(QColor::fromRgb(225, 225, 225)); // #e1e1e1 - - int y = 0; - for (int i = 0; i < rowCount(); i++) { - painter.drawLine(5, rowHeight(i) + y, width(), rowHeight(i) + y); - y += rowHeight(i); - } - painter.end(); - - QTableWidget::paintEvent(event); -} - -void ScriptsTableWidget::keyPressEvent(QKeyEvent* event) { - // Ignore keys so they will propagate correctly - event->ignore(); -} diff --git a/interface/src/ui/ScriptsTableWidget.h b/interface/src/ui/ScriptsTableWidget.h deleted file mode 100644 index f5e3407e97..0000000000 --- a/interface/src/ui/ScriptsTableWidget.h +++ /dev/null @@ -1,28 +0,0 @@ -// -// ScriptsTableWidget.h -// interface -// -// Created by Mohammed Nafees on 04/03/2014. -// Copyright (c) 2014 High Fidelity, Inc. All rights reserved. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#ifndef hifi__ScriptsTableWidget_h -#define hifi__ScriptsTableWidget_h - -#include -#include - -class ScriptsTableWidget : public QTableWidget { - Q_OBJECT -public: - explicit ScriptsTableWidget(QWidget* parent); - -protected: - virtual void paintEvent(QPaintEvent* event) override; - virtual void keyPressEvent(QKeyEvent* event) override; -}; - -#endif // hifi__ScriptsTableWidget_h diff --git a/interface/ui/scriptEditorWidget.ui b/interface/ui/scriptEditorWidget.ui deleted file mode 100644 index e2e538a595..0000000000 --- a/interface/ui/scriptEditorWidget.ui +++ /dev/null @@ -1,142 +0,0 @@ - - - ScriptEditorWidget - - - - 0 - 0 - 691 - 549 - - - - - 0 - 0 - - - - - 690 - 328 - - - - font-family: Helvetica, Arial, sans-serif; - - - QDockWidget::DockWidgetFloatable|QDockWidget::DockWidgetMovable - - - Qt::NoDockWidgetArea - - - Edit Script - - - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - Courier - -1 - 50 - false - false - - - - font: 16px "Courier"; - - - - - - - 0 - - - 0 - - - 0 - - - - - - 0 - 0 - - - - font: 13px "Helvetica","Arial","sans-serif"; - - - Debug Log: - - - - - - - - Helvetica,Arial,sans-serif - -1 - 50 - false - false - - - - font: 13px "Helvetica","Arial","sans-serif"; - - - Run on the fly (Careful: Any valid change made to the code will run immediately) - - - - - - - Clear - - - - 16 - 16 - - - - - - - - - - - - ScriptEditBox - QTextEdit -
ui/ScriptEditBox.h
-
-
- -
diff --git a/interface/ui/scriptEditorWindow.ui b/interface/ui/scriptEditorWindow.ui deleted file mode 100644 index 1e50aaef0b..0000000000 --- a/interface/ui/scriptEditorWindow.ui +++ /dev/null @@ -1,324 +0,0 @@ - - - ScriptEditorWindow - - - Qt::NonModal - - - - 0 - 0 - 780 - 717 - - - - - 400 - 250 - - - - Script Editor - - - font-family: Helvetica, Arial, sans-serif; - - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - 3 - - - QLayout::SetNoConstraint - - - 0 - - - 0 - - - - - New Script (Ctrl+N) - - - New - - - - 32 - 32 - - - - - - - - - 30 - 0 - - - - - 25 - 0 - - - - Load Script (Ctrl+O) - - - Load - - - - 32 - 32 - - - - false - - - QToolButton::MenuButtonPopup - - - Qt::ToolButtonIconOnly - - - - - - - - 30 - 0 - - - - - 32 - 0 - - - - Qt::NoFocus - - - Qt::NoContextMenu - - - Save Script (Ctrl+S) - - - Save - - - - 32 - 32 - - - - 316 - - - QToolButton::MenuButtonPopup - - - - - - - Toggle Run Script (F5) - - - Run/Stop - - - - 32 - 32 - - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - font: 13px "Helvetica","Arial","sans-serif"; - - - Automatically reload externally changed files - - - - - - - - - true - - - - 250 - 80 - - - - QTabWidget::West - - - QTabWidget::Triangular - - - -1 - - - Qt::ElideNone - - - true - - - true - - - - - - - - - saveButton - clicked() - ScriptEditorWindow - saveScriptClicked() - - - 236 - 10 - - - 199 - 264 - - - - - toggleRunButton - clicked() - ScriptEditorWindow - toggleRunScriptClicked() - - - 330 - 10 - - - 199 - 264 - - - - - newButton - clicked() - ScriptEditorWindow - newScriptClicked() - - - 58 - 10 - - - 199 - 264 - - - - - loadButton - clicked() - ScriptEditorWindow - loadScriptClicked() - - - 85 - 10 - - - 199 - 264 - - - - - tabWidget - currentChanged(int) - ScriptEditorWindow - tabSwitched(int) - - - 352 - 360 - - - 352 - 340 - - - - - tabWidget - tabCloseRequested(int) - ScriptEditorWindow - tabCloseRequested(int) - - - 352 - 360 - - - 352 - 340 - - - - - From c46052398071165c2292f50e8963d52b1ed55990 Mon Sep 17 00:00:00 2001 From: Mohammed Nafees Date: Mon, 20 Mar 2017 00:25:32 -0400 Subject: [PATCH 251/302] Remove related icon files for Scripts Editor --- interface/resources/icons/load-script.svg | 125 ---- interface/resources/icons/new-script.svg | 129 ---- interface/resources/icons/save-script.svg | 674 --------------------- interface/resources/icons/start-script.svg | 550 ----------------- interface/resources/icons/stop-script.svg | 163 ----- 5 files changed, 1641 deletions(-) delete mode 100644 interface/resources/icons/load-script.svg delete mode 100644 interface/resources/icons/new-script.svg delete mode 100644 interface/resources/icons/save-script.svg delete mode 100644 interface/resources/icons/start-script.svg delete mode 100644 interface/resources/icons/stop-script.svg diff --git a/interface/resources/icons/load-script.svg b/interface/resources/icons/load-script.svg deleted file mode 100644 index 21be61c321..0000000000 --- a/interface/resources/icons/load-script.svg +++ /dev/null @@ -1,125 +0,0 @@ - - - - - - - - - - image/svg+xml - - - - - T.Hofmeister - - - - - - - - - - - - - - - - - - - - - - diff --git a/interface/resources/icons/new-script.svg b/interface/resources/icons/new-script.svg deleted file mode 100644 index f68fcfa967..0000000000 --- a/interface/resources/icons/new-script.svg +++ /dev/null @@ -1,129 +0,0 @@ - - - - - - - - - - image/svg+xml - - - - - T.Hofmeister - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/interface/resources/icons/save-script.svg b/interface/resources/icons/save-script.svg deleted file mode 100644 index 04d41b8302..0000000000 --- a/interface/resources/icons/save-script.svg +++ /dev/null @@ -1,674 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - image/svg+xml - - - - - T.Hofmeister - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/interface/resources/icons/start-script.svg b/interface/resources/icons/start-script.svg deleted file mode 100644 index 994eb61efe..0000000000 --- a/interface/resources/icons/start-script.svg +++ /dev/null @@ -1,550 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - image/svg+xml - - - - - Maximillian Merlin - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/interface/resources/icons/stop-script.svg b/interface/resources/icons/stop-script.svg deleted file mode 100644 index 31cdcee749..0000000000 --- a/interface/resources/icons/stop-script.svg +++ /dev/null @@ -1,163 +0,0 @@ - - - - - - - - - - image/svg+xml - - - - - Maximillian Merlin - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - From a804bc723f1a899fbc0c5dfe8cd36b310318936f Mon Sep 17 00:00:00 2001 From: Sam Cake Date: Mon, 20 Mar 2017 00:32:06 -0700 Subject: [PATCH 252/302] Keep pushing and debugging ambient lighting --- libraries/render-utils/src/LightAmbient.slh | 27 ++++++++++++++------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/libraries/render-utils/src/LightAmbient.slh b/libraries/render-utils/src/LightAmbient.slh index 37df768924..bd4636c1f0 100644 --- a/libraries/render-utils/src/LightAmbient.slh +++ b/libraries/render-utils/src/LightAmbient.slh @@ -30,9 +30,8 @@ vec3 fresnelSchlickAmbient(vec3 fresnelColor, vec3 lightDir, vec3 halfDir, float <$declareSkyboxMap()$> <@endif@> -vec3 evalAmbientSpecularIrradiance(LightAmbient ambient, vec3 fragEyeDir, vec3 fragNormal, float roughness, vec3 fresnel) { +vec3 evalAmbientSpecularIrradiance(LightAmbient ambient, vec3 fragEyeDir, vec3 fragNormal, float roughness) { vec3 direction = -reflect(fragEyeDir, fragNormal); - vec3 ambientFresnel = fresnelSchlickAmbient(fresnel, fragEyeDir, fragNormal, 1.0 - roughness); vec3 specularLight; <@if supportIfAmbientMapElseAmbientSphere@> if (getLightHasAmbientMap(ambient)) @@ -53,7 +52,7 @@ vec3 evalAmbientSpecularIrradiance(LightAmbient ambient, vec3 fragEyeDir, vec3 f } <@endif@> - return specularLight * ambientFresnel; + return specularLight; } <@endfunc@> @@ -74,16 +73,26 @@ void evalLightingAmbient(out vec3 diffuse, out vec3 specular, LightAmbient ambie <@endif@> ) { - // Diffuse from ambient - diffuse = (1.0 - metallic) * sphericalHarmonics_evalSphericalLight(getLightAmbientSphere(ambient), normal).xyz; + // Fresnel + vec3 ambientFresnel = fresnelSchlickAmbient(fresnel, eyeDir, normal, 1.0 - roughness); - if (gl_FragCoord.x > 300) { - vec3 ambientFresnelDiffuse = vec3 (1.0) - fresnelSchlickAmbient(fresnel, eyeDir, normal, 1.0 - roughness); - diffuse *= ambientFresnelDiffuse; + // Diffuse from ambient + diffuse = (1.0 - metallic) * (vec3 (1.0) - ambientFresnel); + + if (gl_FragCoord.x > 600) { + float levels = getLightAmbientMapNumMips(ambient); + float lod = min(floor((roughness)* levels), levels); + diffuse *= evalSkyboxLight(normal, lod).xyz; + } else { + diffuse *= sphericalHarmonics_evalSphericalLight(getLightAmbientSphere(ambient), normal).xyz; + } + + if (gl_FragCoord.y > 600) { + diffuse *= 1.0 / 3.14; } // Specular highlight from ambient - specular = evalAmbientSpecularIrradiance(ambient, eyeDir, normal, roughness, fresnel); + specular = evalAmbientSpecularIrradiance(ambient, eyeDir, normal, roughness) * ambientFresnel; <@if supportScattering@> From 14c8c3f4db766ad3f7f09a3955bfea565cd8406c Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Mon, 20 Mar 2017 09:39:29 -0700 Subject: [PATCH 253/302] Return existing textures when matching hash for a previously found live texture --- .../src/model-networking/KTXCache.cpp | 5 +- .../src/model-networking/TextureCache.cpp | 140 ++++++++++-------- .../src/model-networking/TextureCache.h | 7 + libraries/shared/src/shared/Storage.cpp | 21 ++- libraries/shared/src/shared/Storage.h | 5 + 5 files changed, 112 insertions(+), 66 deletions(-) diff --git a/libraries/model-networking/src/model-networking/KTXCache.cpp b/libraries/model-networking/src/model-networking/KTXCache.cpp index 908d3245ce..63d35fe4a4 100644 --- a/libraries/model-networking/src/model-networking/KTXCache.cpp +++ b/libraries/model-networking/src/model-networking/KTXCache.cpp @@ -40,5 +40,8 @@ KTXFile::KTXFile(Metadata&& metadata, const std::string& filepath) : std::unique_ptr KTXFile::getKTX() const { ktx::StoragePointer storage = std::make_shared(getFilepath().c_str()); - return ktx::KTX::create(storage); + if (*storage) { + return ktx::KTX::create(storage); + } + return {}; } diff --git a/libraries/model-networking/src/model-networking/TextureCache.cpp b/libraries/model-networking/src/model-networking/TextureCache.cpp index e51b016904..5dfaddd471 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.cpp +++ b/libraries/model-networking/src/model-networking/TextureCache.cpp @@ -188,6 +188,35 @@ NetworkTexturePointer TextureCache::getTexture(const QUrl& url, Type type, const return ResourceCache::getResource(url, QUrl(), &extra).staticCast(); } +gpu::TexturePointer TextureCache::getTextureByHash(const std::string& hash) { + std::weak_ptr weakPointer; + { + std::unique_lock lock(_texturesByHashesMutex); + weakPointer = _texturesByHashes[hash]; + } + auto result = weakPointer.lock(); + if (result) { + qCWarning(modelnetworking) << "QQQ Returning live texture for hash " << hash.c_str(); + } + return result; +} + +gpu::TexturePointer TextureCache::cacheTextureByHash(const std::string& hash, const gpu::TexturePointer& texture) { + gpu::TexturePointer result; + { + std::unique_lock lock(_texturesByHashesMutex); + result = _texturesByHashes[hash].lock(); + if (!result) { + _texturesByHashes[hash] = texture; + result = texture; + } else { + qCWarning(modelnetworking) << "QQQ Swapping out texture with previous live texture in hash " << hash.c_str(); + } + } + return result; +} + + gpu::TexturePointer getFallbackTextureForType(NetworkTexture::Type type) { gpu::TexturePointer result; auto textureCache = DependencyManager::get(); @@ -386,15 +415,6 @@ private: int _maxNumPixels; }; -class KTXReader : public Reader { -public: - KTXReader(const QWeakPointer& resource, const QUrl& url, const KTXFilePointer& ktxFile); - void read() override final; - -private: - KTXFilePointer _file; -}; - void NetworkTexture::downloadFinished(const QByteArray& data) { loadContent(data); } @@ -408,15 +428,39 @@ void NetworkTexture::loadContent(const QByteArray& content) { hash = hasher.result().toHex().toStdString(); } - std::unique_ptr reader; - KTXFilePointer ktxFile; - if (!_cache.isNull() && (ktxFile = static_cast(_cache.data())->_ktxCache.getFile(hash))) { - reader.reset(new KTXReader(_self, _url, ktxFile)); - } else { - reader.reset(new ImageReader(_self, _url, content, hash, _maxNumPixels)); + auto textureCache = static_cast(_cache.data()); + + if (textureCache != nullptr) { + // If we already have a live texture with the same hash, use it + auto texture = textureCache->getTextureByHash(hash); + + // If there is no live texture, check if there's an existing KTX file + if (!texture) { + KTXFilePointer ktxFile = textureCache->_ktxCache.getFile(hash); + if (ktxFile) { + // Ensure that the KTX deserialization worked + auto ktx = ktxFile->getKTX(); + if (ktx) { + texture.reset(gpu::Texture::unserialize(ktx)); + // Ensure that the texture population worked + if (texture) { + texture->setKtxBacking(ktx); + texture = textureCache->cacheTextureByHash(hash, texture); + } + } + } + } + + // If we found the texture either because it's in use or via KTX deserialization, + // set the image and return immediately. + if (texture) { + setImage(texture, texture->getWidth(), texture->getHeight()); + return; + } } - QThreadPool::globalInstance()->start(reader.release()); + // We failed to find an existing live or KTX texture, so trigger an image reader + QThreadPool::globalInstance()->start(new ImageReader(_self, _url, content, hash, _maxNumPixels)); } Reader::Reader(const QWeakPointer& resource, const QUrl& url) : @@ -526,22 +570,34 @@ void ImageReader::read() { texture->setFallbackTexture(networkTexture->getFallbackTexture()); } + auto textureCache = DependencyManager::get(); // Save the image into a KTXFile - auto ktx = gpu::Texture::serialize(*texture); - if (ktx) { - const char* data = reinterpret_cast(ktx->_storage->data()); - size_t length = ktx->_storage->size(); + auto memKtx = gpu::Texture::serialize(*texture); + if (!memKtx) { + qCWarning(modelnetworking) << "Unable to serialize texture to KTX " << _url; + } + + if (memKtx && textureCache) { + const char* data = reinterpret_cast(memKtx->_storage->data()); + size_t length = memKtx->_storage->size(); KTXFilePointer file; - auto& ktxCache = DependencyManager::get()->_ktxCache; - if (!ktx || !(file = ktxCache.writeFile(data, KTXCache::Metadata(_hash, length)))) { + auto& ktxCache = textureCache->_ktxCache; + if (!memKtx || !(file = ktxCache.writeFile(data, KTXCache::Metadata(_hash, length)))) { qCWarning(modelnetworking) << _url << "file cache failed"; } else { resource.staticCast()->_file = file; - auto ktx = file->getKTX(); - texture->setKtxBacking(ktx); + auto fileKtx = file->getKTX(); + if (fileKtx) { + texture->setKtxBacking(fileKtx); + } } - } else { - qCWarning(modelnetworking) << "Unable to serialize texture to KTX " << _url; + } + + // We replace the texture with the one stored in the cache. This deals with the possible race condition of two different + // images with the same hash being loaded concurrently. Only one of them will make it into the cache by hash first and will + // be the winner + if (textureCache) { + texture = textureCache->cacheTextureByHash(_hash, texture); } } @@ -554,35 +610,3 @@ void ImageReader::read() { qCDebug(modelnetworking) << _url << "loading stopped; resource out of scope"; } } - -KTXReader::KTXReader(const QWeakPointer& resource, const QUrl& url, - const KTXFilePointer& ktxFile) : - Reader(resource, url), _file(ktxFile) {} - -void KTXReader::read() { - PROFILE_RANGE_EX(resource_parse_image_ktx, __FUNCTION__, 0xffff0000, 0, { { "url", _url.toString() } }); - - gpu::TexturePointer texture; - { - auto resource = _resource.lock(); // to ensure the resource is still needed - if (!resource) { - qCDebug(modelnetworking) << _url << "loading stopped; resource out of scope"; - return; - } - - resource.staticCast()->_file = _file; - auto ktx = _file->getKTX(); - texture.reset(gpu::Texture::unserialize(ktx)); - texture->setKtxBacking(ktx); - } - - auto resource = _resource.lock(); // to ensure the resource is still needed - if (resource) { - QMetaObject::invokeMethod(resource.data(), "setImage", - Q_ARG(gpu::TexturePointer, texture), - Q_ARG(int, texture->getWidth()), Q_ARG(int, texture->getHeight())); - } else { - qCDebug(modelnetworking) << _url << "loading stopped; resource out of scope"; - } - -} diff --git a/libraries/model-networking/src/model-networking/TextureCache.h b/libraries/model-networking/src/model-networking/TextureCache.h index 8ee3f59416..6005cc1226 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.h +++ b/libraries/model-networking/src/model-networking/TextureCache.h @@ -137,6 +137,10 @@ public: NetworkTexturePointer getTexture(const QUrl& url, Type type = Type::DEFAULT_TEXTURE, const QByteArray& content = QByteArray(), int maxNumPixels = ABSOLUTE_MAX_TEXTURE_NUM_PIXELS); + + gpu::TexturePointer getTextureByHash(const std::string& hash); + gpu::TexturePointer cacheTextureByHash(const std::string& hash, const gpu::TexturePointer& texture); + protected: // Overload ResourceCache::prefetch to allow specifying texture type for loads Q_INVOKABLE ScriptableResource* prefetch(const QUrl& url, int type, int maxNumPixels = ABSOLUTE_MAX_TEXTURE_NUM_PIXELS); @@ -155,6 +159,9 @@ private: static const std::string KTX_DIRNAME; static const std::string KTX_EXT; KTXCache _ktxCache; + // Map from image hashes to texture weak pointers + std::unordered_map> _texturesByHashes; + std::mutex _texturesByHashesMutex; gpu::TexturePointer _permutationNormalTexture; gpu::TexturePointer _whiteTexture; diff --git a/libraries/shared/src/shared/Storage.cpp b/libraries/shared/src/shared/Storage.cpp index 6754854c40..3c46347a49 100644 --- a/libraries/shared/src/shared/Storage.cpp +++ b/libraries/shared/src/shared/Storage.cpp @@ -8,7 +8,11 @@ #include "Storage.h" -#include +#include +#include +#include + +Q_LOGGING_CATEGORY(storagelogging, "hifi.core.storage") using namespace storage; @@ -64,12 +68,15 @@ StoragePointer FileStorage::create(const QString& filename, size_t size, const u } FileStorage::FileStorage(const QString& filename) : _file(filename) { - if (!_file.open(QFile::ReadOnly)) { - throw std::runtime_error("Unable to open file"); - } - _mapped = _file.map(0, _file.size()); - if (!_mapped) { - throw std::runtime_error("Unable to map file"); + if (_file.open(QFile::ReadOnly)) { + _mapped = _file.map(0, _file.size()); + if (_mapped) { + _valid = true; + } else { + qCWarning(storagelogging) << "Failed to map file " << filename; + } + } else { + qCWarning(storagelogging) << "Failed to open file " << filename; } } diff --git a/libraries/shared/src/shared/Storage.h b/libraries/shared/src/shared/Storage.h index 4b97e14178..306984040f 100644 --- a/libraries/shared/src/shared/Storage.h +++ b/libraries/shared/src/shared/Storage.h @@ -25,6 +25,7 @@ namespace storage { virtual ~Storage() {} virtual const uint8_t* data() const = 0; virtual size_t size() const = 0; + virtual operator bool() const { return true; } StoragePointer createView(size_t size = 0, size_t offset = 0) const; StoragePointer toFileStorage(const QString& filename) const; @@ -41,6 +42,7 @@ namespace storage { const uint8_t* data() const override { return _data.data(); } uint8_t* data() { return _data.data(); } size_t size() const override { return _data.size(); } + operator bool() const override { return true; } private: std::vector _data; }; @@ -56,7 +58,9 @@ namespace storage { const uint8_t* data() const override { return _mapped; } size_t size() const override { return _file.size(); } + operator bool() const override { return _valid; } private: + bool _valid { false }; QFile _file; uint8_t* _mapped { nullptr }; }; @@ -66,6 +70,7 @@ namespace storage { ViewStorage(const storage::StoragePointer& owner, size_t size, const uint8_t* data); const uint8_t* data() const override { return _data; } size_t size() const override { return _size; } + operator bool() const override { return *_owner; } private: const storage::StoragePointer _owner; const size_t _size; From df644edde4f2048276201bc648f1438e67b85547 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Mon, 20 Mar 2017 09:45:50 -0700 Subject: [PATCH 254/302] Fix hand controller not able to select light/particle icons --- scripts/system/edit.js | 2 +- scripts/system/libraries/entitySelectionTool.js | 8 +++++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/scripts/system/edit.js b/scripts/system/edit.js index 42ebb1b8db..fb5a3c2f73 100644 --- a/scripts/system/edit.js +++ b/scripts/system/edit.js @@ -42,7 +42,7 @@ var selectionManager = SelectionManager; const PARTICLE_SYSTEM_URL = Script.resolvePath("assets/images/icon-particles.svg"); const POINT_LIGHT_URL = Script.resolvePath("assets/images/icon-point-light.svg"); const SPOT_LIGHT_URL = Script.resolvePath("assets/images/icon-spot-light.svg"); -var entityIconOverlayManager = new EntityIconOverlayManager(['Light', 'ParticleEffect'], function(entityID) { +entityIconOverlayManager = new EntityIconOverlayManager(['Light', 'ParticleEffect'], function(entityID) { var properties = Entities.getEntityProperties(entityID, ['type', 'isSpotlight']); if (properties.type === 'Light') { return { diff --git a/scripts/system/libraries/entitySelectionTool.js b/scripts/system/libraries/entitySelectionTool.js index d68a525458..a8c4300fbe 100644 --- a/scripts/system/libraries/entitySelectionTool.js +++ b/scripts/system/libraries/entitySelectionTool.js @@ -1032,10 +1032,12 @@ SelectionDisplay = (function() { var pickRay = controllerComputePickRay(); if (pickRay) { var entityIntersection = Entities.findRayIntersection(pickRay, true); - - + var iconIntersection = entityIconOverlayManager.findRayIntersection(pickRay); var overlayIntersection = Overlays.findRayIntersection(pickRay); - if (entityIntersection.intersects && + + if (iconIntersection.intersects) { + selectionManager.setSelections([iconIntersection.entityID]); + } else if (entityIntersection.intersects && (!overlayIntersection.intersects || (entityIntersection.distance < overlayIntersection.distance))) { if (HMD.tabletID === entityIntersection.entityID) { From 20022ebdc881d0884873b85e536373e31e255773 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Wed, 15 Mar 2017 17:44:35 -0700 Subject: [PATCH 255/302] use deleteLater for ESS script engine on nodeKilled --- assignment-client/src/scripts/EntityScriptServer.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/assignment-client/src/scripts/EntityScriptServer.cpp b/assignment-client/src/scripts/EntityScriptServer.cpp index f674ab006c..a47919bdd7 100644 --- a/assignment-client/src/scripts/EntityScriptServer.cpp +++ b/assignment-client/src/scripts/EntityScriptServer.cpp @@ -395,7 +395,8 @@ void EntityScriptServer::selectAudioFormat(const QString& selectedCodecName) { void EntityScriptServer::resetEntitiesScriptEngine() { auto engineName = QString("about:Entities %1").arg(++_entitiesScriptEngineCount); - auto newEngine = QSharedPointer(new ScriptEngine(ScriptEngine::ENTITY_SERVER_SCRIPT, NO_SCRIPT, engineName)); + auto newEngine = QSharedPointer(new ScriptEngine(ScriptEngine::ENTITY_SERVER_SCRIPT, NO_SCRIPT, engineName), + &ScriptEngine::deleteLater); auto webSocketServerConstructorValue = newEngine->newFunction(WebSocketServerClass::constructor); newEngine->globalObject().setProperty("WebSocketServer", webSocketServerConstructorValue); From a4d72c4e6766208ea323a2a8fe323758fe99234c Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 16 Mar 2017 10:29:22 -0700 Subject: [PATCH 256/302] make AudioScriptingInterface a Dependency --- .../src/scripts/EntityScriptServer.cpp | 2 ++ interface/src/Application.cpp | 21 ++++++++++--------- .../src/AudioScriptingInterface.cpp | 5 ----- .../src/AudioScriptingInterface.h | 9 +++++--- libraries/script-engine/src/ScriptEngine.cpp | 6 +++++- 5 files changed, 24 insertions(+), 19 deletions(-) diff --git a/assignment-client/src/scripts/EntityScriptServer.cpp b/assignment-client/src/scripts/EntityScriptServer.cpp index a47919bdd7..cea2d24534 100644 --- a/assignment-client/src/scripts/EntityScriptServer.cpp +++ b/assignment-client/src/scripts/EntityScriptServer.cpp @@ -58,6 +58,8 @@ EntityScriptServer::EntityScriptServer(ReceivedMessage& message) : ThreadedAssig DependencyManager::registerInheritance(); + DependencyManager::set(); + DependencyManager::set(); DependencyManager::set(); DependencyManager::set(); diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 02fe2a2dd3..9157bb7144 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -740,23 +740,24 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo } }); - auto& audioScriptingInterface = AudioScriptingInterface::getInstance(); + auto audioScriptingInterface = DependencyManager::set(); connect(audioThread, &QThread::started, audioIO.data(), &AudioClient::start); connect(audioIO.data(), &AudioClient::destroyed, audioThread, &QThread::quit); connect(audioThread, &QThread::finished, audioThread, &QThread::deleteLater); connect(audioIO.data(), &AudioClient::muteToggled, this, &Application::audioMuteToggled); - connect(audioIO.data(), &AudioClient::mutedByMixer, &audioScriptingInterface, &AudioScriptingInterface::mutedByMixer); - connect(audioIO.data(), &AudioClient::receivedFirstPacket, &audioScriptingInterface, &AudioScriptingInterface::receivedFirstPacket); - connect(audioIO.data(), &AudioClient::disconnected, &audioScriptingInterface, &AudioScriptingInterface::disconnected); + connect(audioIO.data(), &AudioClient::mutedByMixer, audioScriptingInterface.data(), &AudioScriptingInterface::mutedByMixer); + connect(audioIO.data(), &AudioClient::receivedFirstPacket, audioScriptingInterface.data(), &AudioScriptingInterface::receivedFirstPacket); + connect(audioIO.data(), &AudioClient::disconnected, audioScriptingInterface.data(), &AudioScriptingInterface::disconnected); connect(audioIO.data(), &AudioClient::muteEnvironmentRequested, [](glm::vec3 position, float radius) { auto audioClient = DependencyManager::get(); + auto audioScriptingInterface = DependencyManager::get(); auto myAvatarPosition = DependencyManager::get()->getMyAvatar()->getPosition(); float distance = glm::distance(myAvatarPosition, position); bool shouldMute = !audioClient->isMuted() && (distance < radius); if (shouldMute) { audioClient->toggleMute(); - AudioScriptingInterface::getInstance().environmentMuted(); + audioScriptingInterface->environmentMuted(); } }); @@ -1181,10 +1182,10 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo // set the local loopback interface for local sounds AudioInjector::setLocalAudioInterface(audioIO.data()); - AudioScriptingInterface::getInstance().setLocalAudioInterface(audioIO.data()); - connect(audioIO.data(), &AudioClient::noiseGateOpened, &AudioScriptingInterface::getInstance(), &AudioScriptingInterface::noiseGateOpened); - connect(audioIO.data(), &AudioClient::noiseGateClosed, &AudioScriptingInterface::getInstance(), &AudioScriptingInterface::noiseGateClosed); - connect(audioIO.data(), &AudioClient::inputReceived, &AudioScriptingInterface::getInstance(), &AudioScriptingInterface::inputReceived); + audioScriptingInterface->setLocalAudioInterface(audioIO.data()); + connect(audioIO.data(), &AudioClient::noiseGateOpened, audioScriptingInterface.data(), &AudioScriptingInterface::noiseGateOpened); + connect(audioIO.data(), &AudioClient::noiseGateClosed, audioScriptingInterface.data(), &AudioScriptingInterface::noiseGateClosed); + connect(audioIO.data(), &AudioClient::inputReceived, audioScriptingInterface.data(), &AudioScriptingInterface::inputReceived); this->installEventFilter(this); @@ -1949,7 +1950,7 @@ void Application::initializeUi() { // For some reason there is already an "Application" object in the QML context, // though I can't find it. Hence, "ApplicationInterface" rootContext->setContextProperty("ApplicationInterface", this); - rootContext->setContextProperty("Audio", &AudioScriptingInterface::getInstance()); + rootContext->setContextProperty("Audio", DependencyManager::get().data()); rootContext->setContextProperty("AudioStats", DependencyManager::get()->getStats().data()); rootContext->setContextProperty("AudioScope", DependencyManager::get().data()); diff --git a/libraries/script-engine/src/AudioScriptingInterface.cpp b/libraries/script-engine/src/AudioScriptingInterface.cpp index fcc1f201f9..8452494d95 100644 --- a/libraries/script-engine/src/AudioScriptingInterface.cpp +++ b/libraries/script-engine/src/AudioScriptingInterface.cpp @@ -19,11 +19,6 @@ void registerAudioMetaTypes(QScriptEngine* engine) { qScriptRegisterMetaType(engine, soundSharedPointerToScriptValue, soundSharedPointerFromScriptValue); } -AudioScriptingInterface& AudioScriptingInterface::getInstance() { - static AudioScriptingInterface staticInstance; - return staticInstance; -} - AudioScriptingInterface::AudioScriptingInterface() : _localAudioInterface(NULL) { diff --git a/libraries/script-engine/src/AudioScriptingInterface.h b/libraries/script-engine/src/AudioScriptingInterface.h index 6cce78d48f..e97bc329c6 100644 --- a/libraries/script-engine/src/AudioScriptingInterface.h +++ b/libraries/script-engine/src/AudioScriptingInterface.h @@ -14,18 +14,20 @@ #include #include +#include #include class ScriptAudioInjector; -class AudioScriptingInterface : public QObject { +class AudioScriptingInterface : public QObject, public Dependency { Q_OBJECT -public: - static AudioScriptingInterface& getInstance(); + SINGLETON_DEPENDENCY +public: void setLocalAudioInterface(AbstractAudioInterface* audioInterface) { _localAudioInterface = audioInterface; } protected: + // this method is protected to stop C++ callers from calling, but invokable from script Q_INVOKABLE ScriptAudioInjector* playSound(SharedSoundPointer sound, const AudioInjectorOptions& injectorOptions = AudioInjectorOptions()); @@ -42,6 +44,7 @@ signals: private: AudioScriptingInterface(); + AbstractAudioInterface* _localAudioInterface; }; diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index 3e4e070717..1b3d5057f5 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -627,6 +627,9 @@ void ScriptEngine::init() { qScriptRegisterMetaType(this, qWSCloseCodeToScriptValue, qWSCloseCodeFromScriptValue); qScriptRegisterMetaType(this, wscReadyStateToScriptValue, wscReadyStateFromScriptValue); + // NOTE: You do not want to end up creating new instances of singletons here. They will be on the ScriptEngine thread + // and are likely to be unusable if we "reset" the ScriptEngine by creating a new one (on a whole new thread). + registerGlobalObject("Script", this); { @@ -638,7 +641,8 @@ void ScriptEngine::init() { resetModuleCache(); } - registerGlobalObject("Audio", &AudioScriptingInterface::getInstance()); + registerGlobalObject("Audio", DependencyManager::get().data()); + registerGlobalObject("Entities", entityScriptingInterface.data()); registerGlobalObject("Quat", &_quatLibrary); registerGlobalObject("Vec3", &_vec3Library); From c63a2c9cda223f8eeba2ed13c3dbf778ba57cd2b Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 16 Mar 2017 15:07:54 -0700 Subject: [PATCH 257/302] remove requirement of ScriptEngine in ScriptEngines slots --- libraries/script-engine/src/ScriptEngine.cpp | 8 ++++---- libraries/script-engine/src/ScriptEngine.h | 8 ++++---- libraries/script-engine/src/ScriptEngines.cpp | 20 +++++-------------- libraries/script-engine/src/ScriptEngines.h | 10 +++++----- 4 files changed, 18 insertions(+), 28 deletions(-) diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index 1b3d5057f5..a5c94c1bb4 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -464,17 +464,17 @@ void ScriptEngine::loadURL(const QUrl& scriptURL, bool reload) { void ScriptEngine::scriptErrorMessage(const QString& message) { qCCritical(scriptengine) << qPrintable(message); - emit errorMessage(message); + emit errorMessage(message, getFilename()); } void ScriptEngine::scriptWarningMessage(const QString& message) { qCWarning(scriptengine) << message; - emit warningMessage(message); + emit warningMessage(message, getFilename()); } void ScriptEngine::scriptInfoMessage(const QString& message) { qCInfo(scriptengine) << message; - emit infoMessage(message); + emit infoMessage(message, getFilename()); } // Even though we never pass AnimVariantMap directly to and from javascript, the queued invokeMethod of @@ -1351,7 +1351,7 @@ QUrl ScriptEngine::resourcesPath() const { } void ScriptEngine::print(const QString& message) { - emit printedMessage(message); + emit printedMessage(message, getFilename()); } // Script.require.resolve -- like resolvePath, but performs more validation and throws exceptions on invalid module identifiers (for consistency with Node.js) diff --git a/libraries/script-engine/src/ScriptEngine.h b/libraries/script-engine/src/ScriptEngine.h index 95f377d92b..5ea8d052e9 100644 --- a/libraries/script-engine/src/ScriptEngine.h +++ b/libraries/script-engine/src/ScriptEngine.h @@ -236,10 +236,10 @@ signals: void scriptEnding(); void finished(const QString& fileNameString, ScriptEngine* engine); void cleanupMenuItem(const QString& menuItemString); - void printedMessage(const QString& message); - void errorMessage(const QString& message); - void warningMessage(const QString& message); - void infoMessage(const QString& message); + void printedMessage(const QString& message, const QString& scriptName); + void errorMessage(const QString& message, const QString& scriptName); + void warningMessage(const QString& message, const QString& scriptName); + void infoMessage(const QString& message, const QString& scriptName); void runningStateChanged(); void loadScript(const QString& scriptName, bool isUserLoaded); void reloadScript(const QString& scriptName, bool isUserLoaded); diff --git a/libraries/script-engine/src/ScriptEngines.cpp b/libraries/script-engine/src/ScriptEngines.cpp index 57887d2d96..88b0e0b7b5 100644 --- a/libraries/script-engine/src/ScriptEngines.cpp +++ b/libraries/script-engine/src/ScriptEngines.cpp @@ -34,34 +34,24 @@ ScriptsModel& getScriptsModel() { return scriptsModel; } -void ScriptEngines::onPrintedMessage(const QString& message) { - auto scriptEngine = qobject_cast(sender()); - auto scriptName = scriptEngine ? scriptEngine->getFilename() : ""; +void ScriptEngines::onPrintedMessage(const QString& message, const QString& scriptName) { emit printedMessage(message, scriptName); } -void ScriptEngines::onErrorMessage(const QString& message) { - auto scriptEngine = qobject_cast(sender()); - auto scriptName = scriptEngine ? scriptEngine->getFilename() : ""; +void ScriptEngines::onErrorMessage(const QString& message, const QString& scriptName) { emit errorMessage(message, scriptName); } -void ScriptEngines::onWarningMessage(const QString& message) { - auto scriptEngine = qobject_cast(sender()); - auto scriptName = scriptEngine ? scriptEngine->getFilename() : ""; +void ScriptEngines::onWarningMessage(const QString& message, const QString& scriptName) { emit warningMessage(message, scriptName); } -void ScriptEngines::onInfoMessage(const QString& message) { - auto scriptEngine = qobject_cast(sender()); - auto scriptName = scriptEngine ? scriptEngine->getFilename() : ""; +void ScriptEngines::onInfoMessage(const QString& message, const QString& scriptName) { emit infoMessage(message, scriptName); } void ScriptEngines::onErrorLoadingScript(const QString& url) { - auto scriptEngine = qobject_cast(sender()); - auto scriptName = scriptEngine ? scriptEngine->getFilename() : ""; - emit errorLoadingScript(url, scriptName); + emit errorLoadingScript(url); } ScriptEngines::ScriptEngines(ScriptEngine::Context context) diff --git a/libraries/script-engine/src/ScriptEngines.h b/libraries/script-engine/src/ScriptEngines.h index 2fadfc81f8..63b7e8f11c 100644 --- a/libraries/script-engine/src/ScriptEngines.h +++ b/libraries/script-engine/src/ScriptEngines.h @@ -79,13 +79,13 @@ signals: void errorMessage(const QString& message, const QString& engineName); void warningMessage(const QString& message, const QString& engineName); void infoMessage(const QString& message, const QString& engineName); - void errorLoadingScript(const QString& url, const QString& engineName); + void errorLoadingScript(const QString& url); public slots: - void onPrintedMessage(const QString& message); - void onErrorMessage(const QString& message); - void onWarningMessage(const QString& message); - void onInfoMessage(const QString& message); + void onPrintedMessage(const QString& message, const QString& scriptName); + void onErrorMessage(const QString& message, const QString& scriptName); + void onWarningMessage(const QString& message, const QString& scriptName); + void onInfoMessage(const QString& message, const QString& scriptName); void onErrorLoadingScript(const QString& url); protected slots: From 0a118bd268d0033fd35ce6c99891b7bb9092379d Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 16 Mar 2017 18:41:26 -0700 Subject: [PATCH 258/302] add a guard for trading entity servers by checking node count --- .../src/scripts/EntityScriptServer.cpp | 21 ++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/assignment-client/src/scripts/EntityScriptServer.cpp b/assignment-client/src/scripts/EntityScriptServer.cpp index cea2d24534..c46d658c52 100644 --- a/assignment-client/src/scripts/EntityScriptServer.cpp +++ b/assignment-client/src/scripts/EntityScriptServer.cpp @@ -326,7 +326,26 @@ void EntityScriptServer::nodeActivated(SharedNodePointer activatedNode) { void EntityScriptServer::nodeKilled(SharedNodePointer killedNode) { switch (killedNode->getType()) { case NodeType::EntityServer: { - clear(); + // Before we clear, make sure this was our only entity server. + // Otherwise we're assuming that we have "trading" entity servers + // (an old one going away and a new one coming onboard) + // and that we shouldn't clear here because we're still doing work. + bool hasAnotherEntityServer = false; + auto nodeList = DependencyManager::get(); + + nodeList->eachNodeBreakable([&hasAnotherEntityServer, &killedNode](const SharedNodePointer& node){ + if (node->getType() == NodeType::EntityServer && node->getUUID() != killedNode->getUUID()) { + // we're talking to > 1 entity servers, we know we won't clear + hasAnotherEntityServer = true; + return false; + } + + return true; + }); + + if (hasAnotherEntityServer) { + clear(); + } break; } From 96cf6b8c0418ca4e8548792d921405d70c81db30 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Fri, 17 Mar 2017 13:19:59 -0700 Subject: [PATCH 259/302] fix clear check for missing other entity servers --- assignment-client/src/scripts/EntityScriptServer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assignment-client/src/scripts/EntityScriptServer.cpp b/assignment-client/src/scripts/EntityScriptServer.cpp index c46d658c52..954c25a342 100644 --- a/assignment-client/src/scripts/EntityScriptServer.cpp +++ b/assignment-client/src/scripts/EntityScriptServer.cpp @@ -343,7 +343,7 @@ void EntityScriptServer::nodeKilled(SharedNodePointer killedNode) { return true; }); - if (hasAnotherEntityServer) { + if (!hasAnotherEntityServer) { clear(); } From 3b8636b75c3df242c0aeecbca6e01f386e238f45 Mon Sep 17 00:00:00 2001 From: trent Date: Mon, 20 Mar 2017 17:55:52 -0400 Subject: [PATCH 260/302] Doubled-ish the size of the physics status icons, fixed texture coordinates to be right-side up. --- libraries/render/src/render/drawItemStatus.slv | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/render/src/render/drawItemStatus.slv b/libraries/render/src/render/drawItemStatus.slv index cb4ae7ebd2..792f2733c5 100644 --- a/libraries/render/src/render/drawItemStatus.slv +++ b/libraries/render/src/render/drawItemStatus.slv @@ -75,7 +75,7 @@ void main(void) { vec4(1.0, 1.0, 0.0, 1.0) ); - const vec2 ICON_PIXEL_SIZE = vec2(20, 20); + const vec2 ICON_PIXEL_SIZE = vec2(36, 36); const vec2 MARGIN_PIXEL_SIZE = vec2(2, 2); const vec2 ICON_GRID_SLOTS[MAX_NUM_ICONS] = vec2[MAX_NUM_ICONS](vec2(-1.5, 0.5), vec2(-0.5, 0.5), @@ -114,7 +114,7 @@ void main(void) { varColor = vec4(paintRainbow(abs(iconStatus.y)), 1.0); // Pass the texcoord and the z texcoord is representing the texture icon - varTexcoord = vec3((quadPos.xy + 1.0) * 0.5, iconStatus.z); + varTexcoord = vec3( (quadPos.x + 1.0) * 0.5, (quadPos.y + 1.0) * -0.5, iconStatus.z); // Also changes the size of the notification vec2 iconScale = ICON_PIXEL_SIZE; From e05abf2a2f36cb08b4fa9f371a5b1a4b359cbac9 Mon Sep 17 00:00:00 2001 From: samcake Date: Mon, 20 Mar 2017 16:41:21 -0700 Subject: [PATCH 261/302] Fixing the spherical harmonics gneration --- libraries/gpu/src/gpu/Texture.cpp | 19 +++++++++++++------ libraries/render-utils/src/LightAmbient.slh | 15 +-------------- 2 files changed, 14 insertions(+), 20 deletions(-) diff --git a/libraries/gpu/src/gpu/Texture.cpp b/libraries/gpu/src/gpu/Texture.cpp index 5b0c4c876a..f6283b95e3 100755 --- a/libraries/gpu/src/gpu/Texture.cpp +++ b/libraries/gpu/src/gpu/Texture.cpp @@ -794,7 +794,16 @@ bool sphericalHarmonicsFromTexture(const gpu::Texture& cubeTexture, std::vector< for(int face=0; face < gpu::Texture::NUM_CUBE_FACES; face++) { PROFILE_RANGE(render_gpu, "ProcessFace"); - auto numComponents = cubeTexture.accessStoredMipFace(0,face)->getFormat().getScalarCount(); + auto mipFormat = cubeTexture.accessStoredMipFace(0, face)->getFormat(); + auto numComponents = mipFormat.getScalarCount(); + int roffset { 0 }; + int goffset { 1 }; + int boffset { 2 }; + if ((mipFormat.getSemantic() == gpu::BGRA) || (mipFormat.getSemantic() == gpu::SBGRA)) { + roffset = 2; + boffset = 0; + } + auto data = cubeTexture.accessStoredMipFace(0,face)->readData(); if (data == nullptr) { continue; @@ -882,9 +891,9 @@ bool sphericalHarmonicsFromTexture(const gpu::Texture& cubeTexture, std::vector< for (int i = 0; i < stride; ++i) { for (int j = 0; j < stride; ++j) { int k = (int)(x + i - halfStride + (y + j - halfStride) * width) * numComponents; - red += ColorUtils::sRGB8ToLinearFloat(data[k]); - green += ColorUtils::sRGB8ToLinearFloat(data[k + 1]); - blue += ColorUtils::sRGB8ToLinearFloat(data[k + 2]); + red += ColorUtils::sRGB8ToLinearFloat(data[k + roffset]); + green += ColorUtils::sRGB8ToLinearFloat(data[k + goffset]); + blue += ColorUtils::sRGB8ToLinearFloat(data[k + boffset]); } } glm::vec3 clr(red, green, blue); @@ -911,8 +920,6 @@ bool sphericalHarmonicsFromTexture(const gpu::Texture& cubeTexture, std::vector< // save result for(uint i=0; i < sqOrder; i++) { - // gamma Correct - // output[i] = linearTosRGB(glm::vec3(resultR[i], resultG[i], resultB[i])); output[i] = glm::vec3(resultR[i], resultG[i], resultB[i]); } diff --git a/libraries/render-utils/src/LightAmbient.slh b/libraries/render-utils/src/LightAmbient.slh index bd4636c1f0..e343d8c239 100644 --- a/libraries/render-utils/src/LightAmbient.slh +++ b/libraries/render-utils/src/LightAmbient.slh @@ -77,24 +77,11 @@ void evalLightingAmbient(out vec3 diffuse, out vec3 specular, LightAmbient ambie vec3 ambientFresnel = fresnelSchlickAmbient(fresnel, eyeDir, normal, 1.0 - roughness); // Diffuse from ambient - diffuse = (1.0 - metallic) * (vec3 (1.0) - ambientFresnel); - - if (gl_FragCoord.x > 600) { - float levels = getLightAmbientMapNumMips(ambient); - float lod = min(floor((roughness)* levels), levels); - diffuse *= evalSkyboxLight(normal, lod).xyz; - } else { - diffuse *= sphericalHarmonics_evalSphericalLight(getLightAmbientSphere(ambient), normal).xyz; - } - - if (gl_FragCoord.y > 600) { - diffuse *= 1.0 / 3.14; - } + diffuse = (1.0 - metallic) * (vec3(1.0) - ambientFresnel) * sphericalHarmonics_evalSphericalLight(getLightAmbientSphere(ambient), normal).xyz; // Specular highlight from ambient specular = evalAmbientSpecularIrradiance(ambient, eyeDir, normal, roughness) * ambientFresnel; - <@if supportScattering@> float ambientOcclusion = curvatureAO(lowNormalCurvature.w * 20.0f) * 0.5f; float ambientOcclusionHF = curvatureAO(midNormalCurvature.w * 8.0f) * 0.5f; From 73dd2abdafdfc8cc71fa843b3b7ef31f5c44717a Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Mon, 20 Mar 2017 17:29:44 -0700 Subject: [PATCH 262/302] Delete old sitting points code --- .../entities/src/EntityItemProperties.cpp | 21 -------------- libraries/entities/src/EntityItemProperties.h | 4 --- .../entities/src/EntityScriptingInterface.cpp | 6 ++-- libraries/entities/src/PropertyGroup.h | 29 ++----------------- libraries/fbx/src/FBXReader.cpp | 13 --------- libraries/fbx/src/FBXReader.h | 20 ------------- 6 files changed, 5 insertions(+), 88 deletions(-) diff --git a/libraries/entities/src/EntityItemProperties.cpp b/libraries/entities/src/EntityItemProperties.cpp index ea81df3801..1ed020e592 100644 --- a/libraries/entities/src/EntityItemProperties.cpp +++ b/libraries/entities/src/EntityItemProperties.cpp @@ -49,13 +49,6 @@ EntityItemProperties::EntityItemProperties(EntityPropertyFlags desiredProperties } -void EntityItemProperties::setSittingPoints(const QVector& sittingPoints) { - _sittingPoints.clear(); - foreach (SittingPoint sitPoint, sittingPoints) { - _sittingPoints.append(sitPoint); - } -} - void EntityItemProperties::calculateNaturalPosition(const glm::vec3& min, const glm::vec3& max) { glm::vec3 halfDimension = (max - min) / 2.0f; _naturalPosition = max - halfDimension; @@ -546,20 +539,6 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine, bool COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_TEXTURES, textures); } - // Sitting properties support - if (!skipDefaults && !strictSemantics) { - QScriptValue sittingPoints = engine->newObject(); - for (int i = 0; i < _sittingPoints.size(); ++i) { - QScriptValue sittingPoint = engine->newObject(); - sittingPoint.setProperty("name", _sittingPoints.at(i).name); - sittingPoint.setProperty("position", vec3toScriptValue(engine, _sittingPoints.at(i).position)); - sittingPoint.setProperty("rotation", quatToScriptValue(engine, _sittingPoints.at(i).rotation)); - sittingPoints.setProperty(i, sittingPoint); - } - sittingPoints.setProperty("length", _sittingPoints.size()); - COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER_ALWAYS(sittingPoints, sittingPoints); // gettable, but not settable - } - if (!skipDefaults && !strictSemantics) { AABox aaBox = getAABox(); QScriptValue boundingBox = engine->newObject(); diff --git a/libraries/entities/src/EntityItemProperties.h b/libraries/entities/src/EntityItemProperties.h index 419740e4ea..590298e102 100644 --- a/libraries/entities/src/EntityItemProperties.h +++ b/libraries/entities/src/EntityItemProperties.h @@ -22,7 +22,6 @@ #include #include -#include // for SittingPoint #include #include #include @@ -255,8 +254,6 @@ public: void clearID() { _id = UNKNOWN_ENTITY_ID; _idSet = false; } void markAllChanged(); - void setSittingPoints(const QVector& sittingPoints); - const glm::vec3& getNaturalDimensions() const { return _naturalDimensions; } void setNaturalDimensions(const glm::vec3& value) { _naturalDimensions = value; } @@ -325,7 +322,6 @@ private: // NOTE: The following are pseudo client only properties. They are only used in clients which can access // properties of model geometry. But these properties are not serialized like other properties. - QVector _sittingPoints; QVariantMap _textureNames; glm::vec3 _naturalDimensions; glm::vec3 _naturalPosition; diff --git a/libraries/entities/src/EntityScriptingInterface.cpp b/libraries/entities/src/EntityScriptingInterface.cpp index 91d39e9490..355bef5170 100644 --- a/libraries/entities/src/EntityScriptingInterface.cpp +++ b/libraries/entities/src/EntityScriptingInterface.cpp @@ -292,13 +292,11 @@ EntityItemProperties EntityScriptingInterface::getEntityProperties(QUuid identit results = entity->getProperties(desiredProperties); - // TODO: improve sitting points and naturalDimensions in the future, - // for now we've included the old sitting points model behavior for entity types that are models - // we've also added this hack for setting natural dimensions of models + // TODO: improve naturalDimensions in the future, + // for now we've added this hack for setting natural dimensions of models if (entity->getType() == EntityTypes::Model) { const FBXGeometry* geometry = _entityTree->getGeometryForEntity(entity); if (geometry) { - results.setSittingPoints(geometry->sittingPoints); Extents meshExtents = geometry->getUnscaledMeshExtents(); results.setNaturalDimensions(meshExtents.maximum - meshExtents.minimum); results.calculateNaturalPosition(meshExtents.minimum, meshExtents.maximum); diff --git a/libraries/entities/src/PropertyGroup.h b/libraries/entities/src/PropertyGroup.h index 38b1e5f599..f45d19f5eb 100644 --- a/libraries/entities/src/PropertyGroup.h +++ b/libraries/entities/src/PropertyGroup.h @@ -14,9 +14,11 @@ #include -//#include "EntityItemProperties.h" +#include + #include "EntityPropertyFlags.h" + class EntityItemProperties; class EncodeBitstreamParams; class OctreePacketData; @@ -24,31 +26,6 @@ class EntityTreeElementExtraEncodeData; class ReadBitstreamToTreeParams; using EntityTreeElementExtraEncodeDataPointer = std::shared_ptr; -#include - -/* -#include - -#include -#include - -#include -#include -#include - -#include -#include // for SittingPoint -#include -#include -#include - -#include "EntityItemID.h" -#include "PropertyGroupMacros.h" -#include "EntityTypes.h" -*/ - -//typedef PropertyFlags EntityPropertyFlags; - class PropertyGroup { public: diff --git a/libraries/fbx/src/FBXReader.cpp b/libraries/fbx/src/FBXReader.cpp index fcaef90527..b9e1edc168 100644 --- a/libraries/fbx/src/FBXReader.cpp +++ b/libraries/fbx/src/FBXReader.cpp @@ -1795,19 +1795,6 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS } geometry.palmDirection = parseVec3(mapping.value("palmDirection", "0, -1, 0").toString()); - // Add sitting points - QVariantHash sittingPoints = mapping.value("sit").toHash(); - for (QVariantHash::const_iterator it = sittingPoints.constBegin(); it != sittingPoints.constEnd(); it++) { - SittingPoint sittingPoint; - sittingPoint.name = it.key(); - - QVariantList properties = it->toList(); - sittingPoint.position = parseVec3(properties.at(0).toString()); - sittingPoint.rotation = glm::quat(glm::radians(parseVec3(properties.at(1).toString()))); - - geometry.sittingPoints.append(sittingPoint); - } - // attempt to map any meshes to a named model for (QHash::const_iterator m = meshIDsToMeshIndices.constBegin(); m != meshIDsToMeshIndices.constEnd(); m++) { diff --git a/libraries/fbx/src/FBXReader.h b/libraries/fbx/src/FBXReader.h index 6e51c413dc..fa047e512f 100644 --- a/libraries/fbx/src/FBXReader.h +++ b/libraries/fbx/src/FBXReader.h @@ -265,24 +265,6 @@ public: Q_DECLARE_METATYPE(FBXAnimationFrame) Q_DECLARE_METATYPE(QVector) -/// A point where an avatar can sit -class SittingPoint { -public: - QString name; - glm::vec3 position; // relative postion - glm::quat rotation; // relative orientation -}; - -inline bool operator==(const SittingPoint& lhs, const SittingPoint& rhs) -{ - return (lhs.name == rhs.name) && (lhs.position == rhs.position) && (lhs.rotation == rhs.rotation); -} - -inline bool operator!=(const SittingPoint& lhs, const SittingPoint& rhs) -{ - return (lhs.name != rhs.name) || (lhs.position != rhs.position) || (lhs.rotation != rhs.rotation); -} - /// A set of meshes extracted from an FBX document. class FBXGeometry { public: @@ -320,8 +302,6 @@ public: glm::vec3 palmDirection; - QVector sittingPoints; - glm::vec3 neckPivot; Extents bindExtents; From c9d1916eea0cf4c63689df333a2affa51046a84d Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Mon, 20 Mar 2017 18:24:19 -0700 Subject: [PATCH 263/302] fix for build with win path with spaces --- .../PackageLibrariesForDeployment.cmake | 28 +++++++++---------- .../SymlinkOrCopyDirectoryBesideTarget.cmake | 6 ++-- interface/CMakeLists.txt | 8 +++--- 3 files changed, 21 insertions(+), 21 deletions(-) diff --git a/cmake/macros/PackageLibrariesForDeployment.cmake b/cmake/macros/PackageLibrariesForDeployment.cmake index 795e3642a5..d324776572 100644 --- a/cmake/macros/PackageLibrariesForDeployment.cmake +++ b/cmake/macros/PackageLibrariesForDeployment.cmake @@ -24,9 +24,9 @@ macro(PACKAGE_LIBRARIES_FOR_DEPLOYMENT) TARGET ${TARGET_NAME} POST_BUILD COMMAND ${CMAKE_COMMAND} - -DBUNDLE_EXECUTABLE=$ - -DBUNDLE_PLUGIN_DIR=$/${PLUGIN_PATH} - -P ${CMAKE_CURRENT_BINARY_DIR}/FixupBundlePostBuild.cmake + -DBUNDLE_EXECUTABLE="$" + -DBUNDLE_PLUGIN_DIR="$/${PLUGIN_PATH}" + -P "${CMAKE_CURRENT_BINARY_DIR}/FixupBundlePostBuild.cmake" ) find_program(WINDEPLOYQT_COMMAND windeployqt PATHS ${QT_DIR}/bin NO_DEFAULT_PATH) @@ -39,27 +39,27 @@ macro(PACKAGE_LIBRARIES_FOR_DEPLOYMENT) add_custom_command( TARGET ${TARGET_NAME} POST_BUILD - COMMAND CMD /C "SET PATH=%PATH%;${QT_DIR}/bin && ${WINDEPLOYQT_COMMAND} ${EXTRA_DEPLOY_OPTIONS} $<$,$,$>:--release> $" + COMMAND CMD /C "SET PATH=%PATH%;${QT_DIR}/bin && ${WINDEPLOYQT_COMMAND} ${EXTRA_DEPLOY_OPTIONS} $<$,$,$>:--release> \"$\"" ) - set(QTAUDIO_PATH $/audio) - set(QTAUDIO_WIN7_PATH $/audioWin7/audio) - set(QTAUDIO_WIN8_PATH $/audioWin8/audio) + set(QTAUDIO_PATH "$/audio") + set(QTAUDIO_WIN7_PATH "$/audioWin7/audio") + set(QTAUDIO_WIN8_PATH "$/audioWin8/audio") # copy qtaudio_wasapi.dll and qtaudio_windows.dll in the correct directories for runtime selection add_custom_command( TARGET ${TARGET_NAME} POST_BUILD - COMMAND ${CMAKE_COMMAND} -E make_directory ${QTAUDIO_WIN7_PATH} - COMMAND ${CMAKE_COMMAND} -E make_directory ${QTAUDIO_WIN8_PATH} + COMMAND ${CMAKE_COMMAND} -E make_directory "${QTAUDIO_WIN7_PATH}" + COMMAND ${CMAKE_COMMAND} -E make_directory "${QTAUDIO_WIN8_PATH}" # copy release DLLs - COMMAND if exist ${QTAUDIO_PATH}/qtaudio_windows.dll ( ${CMAKE_COMMAND} -E copy ${QTAUDIO_PATH}/qtaudio_windows.dll ${QTAUDIO_WIN7_PATH} ) - COMMAND if exist ${QTAUDIO_PATH}/qtaudio_windows.dll ( ${CMAKE_COMMAND} -E copy ${WASAPI_DLL_PATH}/qtaudio_wasapi.dll ${QTAUDIO_WIN8_PATH} ) + COMMAND if exist "${QTAUDIO_PATH}/qtaudio_windows.dll" ( ${CMAKE_COMMAND} -E copy "${QTAUDIO_PATH}/qtaudio_windows.dll" "${QTAUDIO_WIN7_PATH}" ) + COMMAND if exist "${QTAUDIO_PATH}/qtaudio_windows.dll" ( ${CMAKE_COMMAND} -E copy "${WASAPI_DLL_PATH}/qtaudio_wasapi.dll" "${QTAUDIO_WIN8_PATH}" ) # copy debug DLLs - COMMAND if exist ${QTAUDIO_PATH}/qtaudio_windowsd.dll ( ${CMAKE_COMMAND} -E copy ${QTAUDIO_PATH}/qtaudio_windowsd.dll ${QTAUDIO_WIN7_PATH} ) - COMMAND if exist ${QTAUDIO_PATH}/qtaudio_windowsd.dll ( ${CMAKE_COMMAND} -E copy ${WASAPI_DLL_PATH}/qtaudio_wasapid.dll ${QTAUDIO_WIN8_PATH} ) + COMMAND if exist "${QTAUDIO_PATH}/qtaudio_windowsd.dll" ( ${CMAKE_COMMAND} -E copy "${QTAUDIO_PATH}/qtaudio_windowsd.dll" "${QTAUDIO_WIN7_PATH}" ) + COMMAND if exist "${QTAUDIO_PATH}/qtaudio_windowsd.dll" ( ${CMAKE_COMMAND} -E copy "${WASAPI_DLL_PATH}/qtaudio_wasapid.dll" "${QTAUDIO_WIN8_PATH}" ) # remove directory - COMMAND ${CMAKE_COMMAND} -E remove_directory ${QTAUDIO_PATH} + COMMAND ${CMAKE_COMMAND} -E remove_directory "${QTAUDIO_PATH}" ) endif () diff --git a/cmake/macros/SymlinkOrCopyDirectoryBesideTarget.cmake b/cmake/macros/SymlinkOrCopyDirectoryBesideTarget.cmake index 37a7a9caa0..9ae47aad82 100644 --- a/cmake/macros/SymlinkOrCopyDirectoryBesideTarget.cmake +++ b/cmake/macros/SymlinkOrCopyDirectoryBesideTarget.cmake @@ -14,7 +14,7 @@ macro(SYMLINK_OR_COPY_DIRECTORY_BESIDE_TARGET _SHOULD_SYMLINK _DIRECTORY _DESTIN # remove the current directory add_custom_command( TARGET ${TARGET_NAME} POST_BUILD - COMMAND "${CMAKE_COMMAND}" -E remove_directory $/${_DESTINATION} + COMMAND "${CMAKE_COMMAND}" -E remove_directory "$/${_DESTINATION}" ) if (${_SHOULD_SYMLINK}) @@ -48,8 +48,8 @@ macro(SYMLINK_OR_COPY_DIRECTORY_BESIDE_TARGET _SHOULD_SYMLINK _DIRECTORY _DESTIN # copy the directory add_custom_command( TARGET ${TARGET_NAME} POST_BUILD - COMMAND ${CMAKE_COMMAND} -E copy_directory ${_DIRECTORY} - $/${_DESTINATION} + COMMAND ${CMAKE_COMMAND} -E copy_directory "${_DIRECTORY}" + "$/${_DESTINATION}" ) endif () # glob everything in this directory - add a custom command to copy any files diff --git a/interface/CMakeLists.txt b/interface/CMakeLists.txt index dbc484d0b9..f64b84cdf0 100644 --- a/interface/CMakeLists.txt +++ b/interface/CMakeLists.txt @@ -288,7 +288,7 @@ if (APPLE) add_custom_command(TARGET ${TARGET_NAME} POST_BUILD COMMAND "${CMAKE_COMMAND}" -E copy_directory "${CMAKE_SOURCE_DIR}/scripts" - $/../Resources/scripts + "$/../Resources/scripts" ) # call the fixup_interface macro to add required bundling commands for installation @@ -299,10 +299,10 @@ else (APPLE) add_custom_command(TARGET ${TARGET_NAME} POST_BUILD COMMAND "${CMAKE_COMMAND}" -E copy_directory "${PROJECT_SOURCE_DIR}/resources" - $/resources + "$/resources" COMMAND "${CMAKE_COMMAND}" -E copy_directory "${CMAKE_SOURCE_DIR}/scripts" - $/scripts + "$/scripts" ) # link target to external libraries @@ -337,7 +337,7 @@ endif() add_bugsplat() if (WIN32) - set(EXTRA_DEPLOY_OPTIONS "--qmldir ${PROJECT_SOURCE_DIR}/resources/qml") + set(EXTRA_DEPLOY_OPTIONS "--qmldir \"${PROJECT_SOURCE_DIR}/resources/qml\"") set(TARGET_INSTALL_DIR ${INTERFACE_INSTALL_DIR}) set(TARGET_INSTALL_COMPONENT ${CLIENT_COMPONENT}) From 6d664fd668b519751c03b635fac2763dfdf4858e Mon Sep 17 00:00:00 2001 From: Triplelexx Date: Tue, 21 Mar 2017 19:54:32 +0000 Subject: [PATCH 264/302] add getForward, getFront returns getForward getForward better describes the returned vector as a direction --- libraries/script-engine/src/Quat.cpp | 5 +++++ libraries/script-engine/src/Quat.h | 3 ++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/libraries/script-engine/src/Quat.cpp b/libraries/script-engine/src/Quat.cpp index 6c2e7a349e..64d1c294c6 100644 --- a/libraries/script-engine/src/Quat.cpp +++ b/libraries/script-engine/src/Quat.cpp @@ -68,7 +68,12 @@ glm::quat Quat::inverse(const glm::quat& q) { return glm::inverse(q); } +// redundant, calls getForward which better describes the returned vector as a direction glm::vec3 Quat::getFront(const glm::quat& orientation) { + return getForward(orientation); +} + +glm::vec3 Quat::getForward(const glm::quat& orientation) { return orientation * Vectors::FRONT; } diff --git a/libraries/script-engine/src/Quat.h b/libraries/script-engine/src/Quat.h index b51f1cb47e..6a15bbac83 100644 --- a/libraries/script-engine/src/Quat.h +++ b/libraries/script-engine/src/Quat.h @@ -45,7 +45,8 @@ public slots: glm::quat fromPitchYawRollDegrees(float pitch, float yaw, float roll); // degrees glm::quat fromPitchYawRollRadians(float pitch, float yaw, float roll); // radians glm::quat inverse(const glm::quat& q); - glm::vec3 getFront(const glm::quat& orientation); + glm::vec3 getFront(const glm::quat& orientation); // redundant, calls getForward which better describes the returned vector as a direction + glm::vec3 getForward(const glm::quat& orientation); glm::vec3 getRight(const glm::quat& orientation); glm::vec3 getUp(const glm::quat& orientation); glm::vec3 safeEulerAngles(const glm::quat& orientation); // degrees From a3828ce45c508fc115adc14cd9e1aa02a049760d Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Tue, 21 Mar 2017 13:42:24 -0700 Subject: [PATCH 265/302] Fixing comment --- libraries/fbx/src/FBXReader.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/fbx/src/FBXReader.cpp b/libraries/fbx/src/FBXReader.cpp index 52a51daac3..d5236558a4 100644 --- a/libraries/fbx/src/FBXReader.cpp +++ b/libraries/fbx/src/FBXReader.cpp @@ -1468,7 +1468,7 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS // Create the Material Library consolidateFBXMaterials(mapping); - // We can't scale allow the scaling of a given image to different sizes, because the hash used for the KTX cache is based on the original image + // We can't allow the scaling of a given image to different sizes, because the hash used for the KTX cache is based on the original image // Allowing scaling of the same image to different sizes would cause different KTX files to target the same cache key #if 0 // HACK: until we get proper LOD management we're going to cap model textures From 248c932d50dcd479badd97c70d339eb8afee31c5 Mon Sep 17 00:00:00 2001 From: Triplelexx Date: Tue, 21 Mar 2017 22:11:18 +0000 Subject: [PATCH 266/302] inline getFront --- libraries/script-engine/src/Quat.cpp | 5 ----- libraries/script-engine/src/Quat.h | 3 ++- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/libraries/script-engine/src/Quat.cpp b/libraries/script-engine/src/Quat.cpp index 64d1c294c6..6d49ed27c1 100644 --- a/libraries/script-engine/src/Quat.cpp +++ b/libraries/script-engine/src/Quat.cpp @@ -68,11 +68,6 @@ glm::quat Quat::inverse(const glm::quat& q) { return glm::inverse(q); } -// redundant, calls getForward which better describes the returned vector as a direction -glm::vec3 Quat::getFront(const glm::quat& orientation) { - return getForward(orientation); -} - glm::vec3 Quat::getForward(const glm::quat& orientation) { return orientation * Vectors::FRONT; } diff --git a/libraries/script-engine/src/Quat.h b/libraries/script-engine/src/Quat.h index 6a15bbac83..8a88767a41 100644 --- a/libraries/script-engine/src/Quat.h +++ b/libraries/script-engine/src/Quat.h @@ -45,7 +45,8 @@ public slots: glm::quat fromPitchYawRollDegrees(float pitch, float yaw, float roll); // degrees glm::quat fromPitchYawRollRadians(float pitch, float yaw, float roll); // radians glm::quat inverse(const glm::quat& q); - glm::vec3 getFront(const glm::quat& orientation); // redundant, calls getForward which better describes the returned vector as a direction + // redundant, calls getForward which better describes the returned vector as a direction + glm::vec3 getFront(const glm::quat& orientation) { return getForward(orientation); } glm::vec3 getForward(const glm::quat& orientation); glm::vec3 getRight(const glm::quat& orientation); glm::vec3 getUp(const glm::quat& orientation); From b7980569cce5571e7c428ef340eee4af2a48ede2 Mon Sep 17 00:00:00 2001 From: Triplelexx Date: Tue, 21 Mar 2017 22:13:33 +0000 Subject: [PATCH 267/302] change all references of IDENTITY_FRONT to IDENTITY_FORWARD --- interface/src/Application.cpp | 4 ++-- interface/src/avatar/Avatar.h | 2 +- interface/src/avatar/Head.cpp | 4 ++-- interface/src/avatar/Head.h | 2 +- interface/src/avatar/MyAvatar.cpp | 6 +++--- libraries/animation/src/Rig.cpp | 4 ++-- libraries/avatars/src/HeadData.cpp | 2 +- libraries/octree/src/OctreeQuery.cpp | 2 +- libraries/render-utils/src/LightStage.cpp | 4 ++-- libraries/shared/src/GLMHelpers.h | 2 +- libraries/shared/src/ViewFrustum.cpp | 2 +- libraries/shared/src/ViewFrustum.h | 2 +- 12 files changed, 18 insertions(+), 18 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 4b274906e7..dbe269d1a1 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -2131,7 +2131,7 @@ void Application::paintGL() { PerformanceTimer perfTimer("CameraUpdates"); auto myAvatar = getMyAvatar(); - boomOffset = myAvatar->getScale() * myAvatar->getBoomLength() * -IDENTITY_FRONT; + boomOffset = myAvatar->getScale() * myAvatar->getBoomLength() * -IDENTITY_FORWARD; if (_myCamera.getMode() == CAMERA_MODE_FIRST_PERSON || _myCamera.getMode() == CAMERA_MODE_THIRD_PERSON) { Menu::getInstance()->setIsOptionChecked(MenuOption::FirstPerson, myAvatar->getBoomLength() <= MyAvatar::ZOOM_MIN); @@ -3954,7 +3954,7 @@ void Application::updateMyAvatarLookAtPosition() { auto lookingAtHead = static_pointer_cast(lookingAt)->getHead(); const float MAXIMUM_FACE_ANGLE = 65.0f * RADIANS_PER_DEGREE; - glm::vec3 lookingAtFaceOrientation = lookingAtHead->getFinalOrientationInWorldFrame() * IDENTITY_FRONT; + glm::vec3 lookingAtFaceOrientation = lookingAtHead->getFinalOrientationInWorldFrame() * IDENTITY_FORWARD; glm::vec3 fromLookingAtToMe = glm::normalize(myAvatar->getHead()->getEyePosition() - lookingAtHead->getEyePosition()); float faceAngle = glm::angle(lookingAtFaceOrientation, fromLookingAtToMe); diff --git a/interface/src/avatar/Avatar.h b/interface/src/avatar/Avatar.h index ca4dbd2af8..d8c78d972b 100644 --- a/interface/src/avatar/Avatar.h +++ b/interface/src/avatar/Avatar.h @@ -236,7 +236,7 @@ protected: glm::vec3 getBodyRightDirection() const { return getOrientation() * IDENTITY_RIGHT; } glm::vec3 getBodyUpDirection() const { return getOrientation() * IDENTITY_UP; } - glm::vec3 getBodyFrontDirection() const { return getOrientation() * IDENTITY_FRONT; } + glm::vec3 getBodyFrontDirection() const { return getOrientation() * IDENTITY_FORWARD; } glm::quat computeRotationFromBodyToWorldUp(float proportion = 1.0f) const; void measureMotionDerivatives(float deltaTime); diff --git a/interface/src/avatar/Head.cpp b/interface/src/avatar/Head.cpp index d7bf2b79bf..f4fb844d9b 100644 --- a/interface/src/avatar/Head.cpp +++ b/interface/src/avatar/Head.cpp @@ -268,7 +268,7 @@ void Head::applyEyelidOffset(glm::quat headOrientation) { return; } - glm::quat eyeRotation = rotationBetween(headOrientation * IDENTITY_FRONT, getLookAtPosition() - _eyePosition); + glm::quat eyeRotation = rotationBetween(headOrientation * IDENTITY_FORWARD, getLookAtPosition() - _eyePosition); eyeRotation = eyeRotation * glm::angleAxis(safeEulerAngles(headOrientation).y, IDENTITY_UP); // Rotation w.r.t. head float eyePitch = safeEulerAngles(eyeRotation).x; @@ -375,7 +375,7 @@ glm::quat Head::getCameraOrientation() const { glm::quat Head::getEyeRotation(const glm::vec3& eyePosition) const { glm::quat orientation = getOrientation(); glm::vec3 lookAtDelta = _lookAtPosition - eyePosition; - return rotationBetween(orientation * IDENTITY_FRONT, lookAtDelta + glm::length(lookAtDelta) * _saccade) * orientation; + return rotationBetween(orientation * IDENTITY_FORWARD, lookAtDelta + glm::length(lookAtDelta) * _saccade) * orientation; } void Head::setFinalPitch(float finalPitch) { diff --git a/interface/src/avatar/Head.h b/interface/src/avatar/Head.h index 3d25c79087..d556d1efc4 100644 --- a/interface/src/avatar/Head.h +++ b/interface/src/avatar/Head.h @@ -58,7 +58,7 @@ public: const glm::vec3& getSaccade() const { return _saccade; } glm::vec3 getRightDirection() const { return getOrientation() * IDENTITY_RIGHT; } glm::vec3 getUpDirection() const { return getOrientation() * IDENTITY_UP; } - glm::vec3 getFrontDirection() const { return getOrientation() * IDENTITY_FRONT; } + glm::vec3 getFrontDirection() const { return getOrientation() * IDENTITY_FORWARD; } glm::quat getEyeRotation(const glm::vec3& eyePosition) const; diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index b40ef601ea..bf43a457b7 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -1068,7 +1068,7 @@ void MyAvatar::updateLookAtTargetAvatar() { _lookAtTargetAvatar.reset(); _targetAvatarPosition = glm::vec3(0.0f); - glm::vec3 lookForward = getHead()->getFinalOrientationInWorldFrame() * IDENTITY_FRONT; + glm::vec3 lookForward = getHead()->getFinalOrientationInWorldFrame() * IDENTITY_FORWARD; glm::vec3 cameraPosition = qApp->getCamera()->getPosition(); float smallestAngleTo = glm::radians(DEFAULT_FIELD_OF_VIEW_DEGREES) / 2.0f; @@ -1770,7 +1770,7 @@ void MyAvatar::updateActionMotor(float deltaTime) { } // compute action input - glm::vec3 front = (getDriveKey(TRANSLATE_Z)) * IDENTITY_FRONT; + glm::vec3 front = (getDriveKey(TRANSLATE_Z)) * IDENTITY_FORWARD; glm::vec3 right = (getDriveKey(TRANSLATE_X)) * IDENTITY_RIGHT; glm::vec3 direction = front + right; @@ -2053,7 +2053,7 @@ void MyAvatar::goToLocation(const glm::vec3& newPosition, // move the user a couple units away const float DISTANCE_TO_USER = 2.0f; - _goToPosition = newPosition - quatOrientation * IDENTITY_FRONT * DISTANCE_TO_USER; + _goToPosition = newPosition - quatOrientation * IDENTITY_FORWARD * DISTANCE_TO_USER; } _goToOrientation = quatOrientation; diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index 9ecd0f6352..88cfacbd66 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -558,13 +558,13 @@ static const std::vector LATERAL_SPEEDS = { 0.2f, 0.65f }; // m/s void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPosition, const glm::vec3& worldVelocity, const glm::quat& worldRotation, CharacterControllerState ccState) { - glm::vec3 front = worldRotation * IDENTITY_FRONT; + glm::vec3 front = worldRotation * IDENTITY_FORWARD; glm::vec3 workingVelocity = worldVelocity; { glm::vec3 localVel = glm::inverse(worldRotation) * workingVelocity; - float forwardSpeed = glm::dot(localVel, IDENTITY_FRONT); + float forwardSpeed = glm::dot(localVel, IDENTITY_FORWARD); float lateralSpeed = glm::dot(localVel, IDENTITY_RIGHT); float turningSpeed = glm::orientedAngle(front, _lastFront, IDENTITY_UP) / deltaTime; diff --git a/libraries/avatars/src/HeadData.cpp b/libraries/avatars/src/HeadData.cpp index 72516d9740..d2b9e8cee4 100644 --- a/libraries/avatars/src/HeadData.cpp +++ b/libraries/avatars/src/HeadData.cpp @@ -65,7 +65,7 @@ glm::quat HeadData::getOrientation() const { void HeadData::setOrientation(const glm::quat& orientation) { // rotate body about vertical axis glm::quat bodyOrientation = _owningAvatar->getOrientation(); - glm::vec3 newFront = glm::inverse(bodyOrientation) * (orientation * IDENTITY_FRONT); + glm::vec3 newFront = glm::inverse(bodyOrientation) * (orientation * IDENTITY_FORWARD); bodyOrientation = bodyOrientation * glm::angleAxis(atan2f(-newFront.x, -newFront.z), glm::vec3(0.0f, 1.0f, 0.0f)); _owningAvatar->setOrientation(bodyOrientation); diff --git a/libraries/octree/src/OctreeQuery.cpp b/libraries/octree/src/OctreeQuery.cpp index a639eccaba..7d9fc7d08c 100644 --- a/libraries/octree/src/OctreeQuery.cpp +++ b/libraries/octree/src/OctreeQuery.cpp @@ -142,6 +142,6 @@ int OctreeQuery::parseData(ReceivedMessage& message) { } glm::vec3 OctreeQuery::calculateCameraDirection() const { - glm::vec3 direction = glm::vec3(_cameraOrientation * glm::vec4(IDENTITY_FRONT, 0.0f)); + glm::vec3 direction = glm::vec3(_cameraOrientation * glm::vec4(IDENTITY_FORWARD, 0.0f)); return direction; } diff --git a/libraries/render-utils/src/LightStage.cpp b/libraries/render-utils/src/LightStage.cpp index 66a9797d3c..dd6a046dea 100644 --- a/libraries/render-utils/src/LightStage.cpp +++ b/libraries/render-utils/src/LightStage.cpp @@ -27,9 +27,9 @@ void LightStage::Shadow::setKeylightFrustum(const ViewFrustum& viewFrustum, floa const auto& direction = glm::normalize(_light->getDirection()); glm::quat orientation; if (direction == IDENTITY_UP) { - orientation = glm::quat(glm::mat3(-IDENTITY_RIGHT, IDENTITY_FRONT, -IDENTITY_UP)); + orientation = glm::quat(glm::mat3(-IDENTITY_RIGHT, IDENTITY_FORWARD, -IDENTITY_UP)); } else if (direction == -IDENTITY_UP) { - orientation = glm::quat(glm::mat3(IDENTITY_RIGHT, IDENTITY_FRONT, IDENTITY_UP)); + orientation = glm::quat(glm::mat3(IDENTITY_RIGHT, IDENTITY_FORWARD, IDENTITY_UP)); } else { auto side = glm::normalize(glm::cross(direction, IDENTITY_UP)); auto up = glm::normalize(glm::cross(side, direction)); diff --git a/libraries/shared/src/GLMHelpers.h b/libraries/shared/src/GLMHelpers.h index 609c3ab08b..deb87930fc 100644 --- a/libraries/shared/src/GLMHelpers.h +++ b/libraries/shared/src/GLMHelpers.h @@ -50,7 +50,7 @@ using glm::quat; // this is where the coordinate system is represented const glm::vec3 IDENTITY_RIGHT = glm::vec3( 1.0f, 0.0f, 0.0f); const glm::vec3 IDENTITY_UP = glm::vec3( 0.0f, 1.0f, 0.0f); -const glm::vec3 IDENTITY_FRONT = glm::vec3( 0.0f, 0.0f,-1.0f); +const glm::vec3 IDENTITY_FORWARD = glm::vec3( 0.0f, 0.0f,-1.0f); glm::quat safeMix(const glm::quat& q1, const glm::quat& q2, float alpha); diff --git a/libraries/shared/src/ViewFrustum.cpp b/libraries/shared/src/ViewFrustum.cpp index a0b7d17e46..7e4f64686b 100644 --- a/libraries/shared/src/ViewFrustum.cpp +++ b/libraries/shared/src/ViewFrustum.cpp @@ -31,7 +31,7 @@ void ViewFrustum::setOrientation(const glm::quat& orientationAsQuaternion) { _orientation = orientationAsQuaternion; _right = glm::vec3(orientationAsQuaternion * glm::vec4(IDENTITY_RIGHT, 0.0f)); _up = glm::vec3(orientationAsQuaternion * glm::vec4(IDENTITY_UP, 0.0f)); - _direction = glm::vec3(orientationAsQuaternion * glm::vec4(IDENTITY_FRONT, 0.0f)); + _direction = glm::vec3(orientationAsQuaternion * glm::vec4(IDENTITY_FORWARD, 0.0f)); _view = glm::translate(mat4(), _position) * glm::mat4_cast(_orientation); } diff --git a/libraries/shared/src/ViewFrustum.h b/libraries/shared/src/ViewFrustum.h index 9a6cb9ab68..221b0b5a07 100644 --- a/libraries/shared/src/ViewFrustum.h +++ b/libraries/shared/src/ViewFrustum.h @@ -153,7 +153,7 @@ private: glm::quat _orientation; // orientation in world-frame // calculated from orientation - glm::vec3 _direction = IDENTITY_FRONT; + glm::vec3 _direction = IDENTITY_FORWARD; glm::vec3 _up = IDENTITY_UP; glm::vec3 _right = IDENTITY_RIGHT; From a964edee917893353fc76d6d1b8c797766fd2fc7 Mon Sep 17 00:00:00 2001 From: Triplelexx Date: Tue, 21 Mar 2017 22:25:30 +0000 Subject: [PATCH 268/302] change all references of Quat.getFront to Quat.getForward in scripts directory --- scripts/developer/tests/ambientSoundTest.js | 2 +- .../developer/tests/basicEntityTest/entitySpawner.js | 2 +- .../batonSoundTestEntitySpawner.js | 2 +- scripts/developer/tests/entityServerStampedeTest.js | 2 +- scripts/developer/tests/entityStampedeTest.js | 2 +- scripts/developer/tests/lodTest.js | 2 +- scripts/developer/tests/mat4test.js | 4 ++-- scripts/developer/tests/performance/tribbles.js | 2 +- .../rapidProceduralChange/rapidProceduralChangeTest.js | 4 ++-- scripts/developer/tests/sphereLODTest.js | 2 +- scripts/developer/tests/testInterval.js | 2 +- scripts/developer/tests/unit_tests/entityUnitTests.js | 2 +- scripts/developer/tests/viveTouchpadTest.js | 4 ++-- .../utilities/render/photobooth/photobooth.js | 2 +- scripts/system/away.js | 2 +- scripts/system/controllers/grab.js | 4 ++-- scripts/system/controllers/handControllerGrab.js | 2 +- scripts/system/controllers/handControllerPointer.js | 2 +- scripts/system/edit.js | 10 +++++----- scripts/system/libraries/WebTablet.js | 2 +- scripts/system/libraries/entityCameraTool.js | 4 ++-- scripts/system/libraries/entitySelectionTool.js | 2 +- scripts/system/libraries/soundArray.js | 2 +- scripts/system/nameTag.js | 4 ++-- scripts/system/pal.js | 2 +- scripts/system/voxels.js | 2 +- scripts/tutorials/NBody/makePlanets.js | 2 +- scripts/tutorials/butterflies.js | 6 +++--- scripts/tutorials/createCow.js | 2 +- scripts/tutorials/createDice.js | 4 ++-- scripts/tutorials/createFlashlight.js | 2 +- scripts/tutorials/createGolfClub.js | 2 +- scripts/tutorials/createPictureFrame.js | 2 +- scripts/tutorials/createPingPongGun.js | 2 +- scripts/tutorials/createPistol.js | 2 +- scripts/tutorials/createSoundMaker.js | 2 +- scripts/tutorials/entity_scripts/golfClub.js | 4 ++-- scripts/tutorials/entity_scripts/pingPongGun.js | 4 ++-- scripts/tutorials/entity_scripts/pistol.js | 2 +- scripts/tutorials/makeBlocks.js | 2 +- 40 files changed, 55 insertions(+), 55 deletions(-) diff --git a/scripts/developer/tests/ambientSoundTest.js b/scripts/developer/tests/ambientSoundTest.js index 5b373715c0..d048d5f73d 100644 --- a/scripts/developer/tests/ambientSoundTest.js +++ b/scripts/developer/tests/ambientSoundTest.js @@ -4,7 +4,7 @@ var uuid = Entities.addEntity({ shape: "Icosahedron", dimensions: Vec3.HALF, script: Script.resolvePath('../../tutorials/entity_scripts/ambientSound.js'), - position: Vec3.sum(Vec3.multiply(5, Quat.getFront(MyAvatar.orientation)), MyAvatar.position), + position: Vec3.sum(Vec3.multiply(5, Quat.getForward(MyAvatar.orientation)), MyAvatar.position), userData: JSON.stringify({ soundURL: WAVE, maxVolume: 0.1, diff --git a/scripts/developer/tests/basicEntityTest/entitySpawner.js b/scripts/developer/tests/basicEntityTest/entitySpawner.js index a2f38f59eb..538e9145f5 100644 --- a/scripts/developer/tests/basicEntityTest/entitySpawner.js +++ b/scripts/developer/tests/basicEntityTest/entitySpawner.js @@ -2,7 +2,7 @@ orientation = Quat.safeEulerAngles(orientation); orientation.x = 0; orientation = Quat.fromVec3Degrees(orientation); - var center = Vec3.sum(MyAvatar.position, Vec3.multiply(3, Quat.getFront(orientation))); + var center = Vec3.sum(MyAvatar.position, Vec3.multiply(3, Quat.getForward(orientation))); // Math.random ensures no caching of script var SCRIPT_URL = Script.resolvePath("myEntityScript.js") diff --git a/scripts/developer/tests/batonSoundEntityTest/batonSoundTestEntitySpawner.js b/scripts/developer/tests/batonSoundEntityTest/batonSoundTestEntitySpawner.js index fdcef8d32c..f5fc35a1de 100644 --- a/scripts/developer/tests/batonSoundEntityTest/batonSoundTestEntitySpawner.js +++ b/scripts/developer/tests/batonSoundEntityTest/batonSoundTestEntitySpawner.js @@ -2,7 +2,7 @@ orientation = Quat.safeEulerAngles(orientation); orientation.x = 0; orientation = Quat.fromVec3Degrees(orientation); - var center = Vec3.sum(MyAvatar.position, Vec3.multiply(3, Quat.getFront(orientation))); + var center = Vec3.sum(MyAvatar.position, Vec3.multiply(3, Quat.getForward(orientation))); // Math.random ensures no caching of script var SCRIPT_URL = Script.resolvePath("batonSoundTestEntityScript.js") diff --git a/scripts/developer/tests/entityServerStampedeTest.js b/scripts/developer/tests/entityServerStampedeTest.js index 3fcf01bb34..33aa53f9b1 100644 --- a/scripts/developer/tests/entityServerStampedeTest.js +++ b/scripts/developer/tests/entityServerStampedeTest.js @@ -4,7 +4,7 @@ var DIV = NUM_ENTITIES / Math.PI / 2; var PASS_SCRIPT_URL = Script.resolvePath('entityServerStampedeTest-entity.js'); var FAIL_SCRIPT_URL = Script.resolvePath('entityStampedeTest-entity-fail.js'); -var origin = Vec3.sum(MyAvatar.position, Vec3.multiply(5, Quat.getFront(MyAvatar.orientation))); +var origin = Vec3.sum(MyAvatar.position, Vec3.multiply(5, Quat.getForward(MyAvatar.orientation))); origin.y += HMD.eyeHeight; var uuids = []; diff --git a/scripts/developer/tests/entityStampedeTest.js b/scripts/developer/tests/entityStampedeTest.js index c5040a9796..644bf0a216 100644 --- a/scripts/developer/tests/entityStampedeTest.js +++ b/scripts/developer/tests/entityStampedeTest.js @@ -4,7 +4,7 @@ var DIV = NUM_ENTITIES / Math.PI / 2; var PASS_SCRIPT_URL = Script.resolvePath('').replace('.js', '-entity.js'); var FAIL_SCRIPT_URL = Script.resolvePath('').replace('.js', '-entity-fail.js'); -var origin = Vec3.sum(MyAvatar.position, Vec3.multiply(5, Quat.getFront(MyAvatar.orientation))); +var origin = Vec3.sum(MyAvatar.position, Vec3.multiply(5, Quat.getForward(MyAvatar.orientation))); origin.y += HMD.eyeHeight; var uuids = []; diff --git a/scripts/developer/tests/lodTest.js b/scripts/developer/tests/lodTest.js index 4b6706cd70..ce91b54d0f 100644 --- a/scripts/developer/tests/lodTest.js +++ b/scripts/developer/tests/lodTest.js @@ -19,7 +19,7 @@ var WIDTH = MAX_DIM * NUM_SPHERES; var entities = []; var right = Quat.getRight(Camera.orientation); // Starting position will be 30 meters in front of the camera -var position = Vec3.sum(Camera.position, Vec3.multiply(30, Quat.getFront(Camera.orientation))); +var position = Vec3.sum(Camera.position, Vec3.multiply(30, Quat.getForward(Camera.orientation))); position = Vec3.sum(position, Vec3.multiply(-WIDTH/2, right)); for (var i = 0; i < NUM_SPHERES; ++i) { diff --git a/scripts/developer/tests/mat4test.js b/scripts/developer/tests/mat4test.js index ebce420dcb..2d7c994631 100644 --- a/scripts/developer/tests/mat4test.js +++ b/scripts/developer/tests/mat4test.js @@ -143,10 +143,10 @@ function testInverse() { function testFront() { var test0 = IDENTITY; - assert(mat4FuzzyEqual({x: 0, y: 0, z: -1}, Mat4.getFront(test0))); + assert(mat4FuzzyEqual({x: 0, y: 0, z: -1}, Mat4.getForward(test0))); var test1 = Mat4.createFromScaleRotAndTrans(ONE_HALF, ROT_Y_180, ONE_TWO_THREE); - assert(mat4FuzzyEqual({x: 0, y: 0, z: 1}, Mat4.getFront(test1))); + assert(mat4FuzzyEqual({x: 0, y: 0, z: 1}, Mat4.getForward(test1))); } function testMat4() { diff --git a/scripts/developer/tests/performance/tribbles.js b/scripts/developer/tests/performance/tribbles.js index 4c04f8b5b7..c5735b7359 100644 --- a/scripts/developer/tests/performance/tribbles.js +++ b/scripts/developer/tests/performance/tribbles.js @@ -43,7 +43,7 @@ var HOW_FAR_UP = RANGE / 1.5; // higher (for uneven ground) above range/2 (for var totalCreated = 0; var offset = Vec3.sum(Vec3.multiply(HOW_FAR_UP, Vec3.UNIT_Y), - Vec3.multiply(HOW_FAR_IN_FRONT_OF_ME, Quat.getFront(Camera.orientation))); + Vec3.multiply(HOW_FAR_IN_FRONT_OF_ME, Quat.getForward(Camera.orientation))); var center = Vec3.sum(MyAvatar.position, offset); function randomVector(range) { diff --git a/scripts/developer/tests/rapidProceduralChange/rapidProceduralChangeTest.js b/scripts/developer/tests/rapidProceduralChange/rapidProceduralChangeTest.js index 6897a1b70f..e28a7b01e2 100644 --- a/scripts/developer/tests/rapidProceduralChange/rapidProceduralChangeTest.js +++ b/scripts/developer/tests/rapidProceduralChange/rapidProceduralChangeTest.js @@ -20,9 +20,9 @@ orientation = Quat.safeEulerAngles(orientation); orientation.x = 0; orientation = Quat.fromVec3Degrees(orientation); -var centerUp = Vec3.sum(MyAvatar.position, Vec3.multiply(3, Quat.getFront(orientation))); +var centerUp = Vec3.sum(MyAvatar.position, Vec3.multiply(3, Quat.getForward(orientation))); centerUp.y += 0.5; -var centerDown = Vec3.sum(MyAvatar.position, Vec3.multiply(3, Quat.getFront(orientation))); +var centerDown = Vec3.sum(MyAvatar.position, Vec3.multiply(3, Quat.getForward(orientation))); centerDown.y -= 0.5; var ENTITY_SHADER_URL = "https://s3-us-west-1.amazonaws.com/hifi-content/eric/shaders/uniformTest.fs"; diff --git a/scripts/developer/tests/sphereLODTest.js b/scripts/developer/tests/sphereLODTest.js index dc19094664..d0cb35eaa1 100644 --- a/scripts/developer/tests/sphereLODTest.js +++ b/scripts/developer/tests/sphereLODTest.js @@ -15,7 +15,7 @@ MyAvatar.orientation = Quat.fromPitchYawRollDegrees(0, 0, 0); orientation = Quat.safeEulerAngles(MyAvatar.orientation); orientation.x = 0; orientation = Quat.fromVec3Degrees(orientation); -var tablePosition = Vec3.sum(MyAvatar.position, Quat.getFront(orientation)); +var tablePosition = Vec3.sum(MyAvatar.position, Quat.getForward(orientation)); tablePosition.y += 0.5; diff --git a/scripts/developer/tests/testInterval.js b/scripts/developer/tests/testInterval.js index 94a5fe1fa5..7898610c6d 100644 --- a/scripts/developer/tests/testInterval.js +++ b/scripts/developer/tests/testInterval.js @@ -12,7 +12,7 @@ var UPDATE_HZ = 60; // standard script update rate var UPDATE_INTERVAL = 1000/UPDATE_HZ; // standard script update interval var UPDATE_WORK_EFFORT = 0; // 1000 is light work, 1000000 ~= 30ms -var basePosition = Vec3.sum(Camera.getPosition(), Quat.getFront(Camera.getOrientation())); +var basePosition = Vec3.sum(Camera.getPosition(), Quat.getForward(Camera.getOrientation())); var timerBox = Entities.addEntity( { type: "Box", diff --git a/scripts/developer/tests/unit_tests/entityUnitTests.js b/scripts/developer/tests/unit_tests/entityUnitTests.js index 033a484663..1372676901 100644 --- a/scripts/developer/tests/unit_tests/entityUnitTests.js +++ b/scripts/developer/tests/unit_tests/entityUnitTests.js @@ -1,7 +1,7 @@ describe('Entity', function() { var center = Vec3.sum( MyAvatar.position, - Vec3.multiply(3, Quat.getFront(Camera.getOrientation())) + Vec3.multiply(3, Quat.getForward(Camera.getOrientation())) ); var boxEntity; var boxProps = { diff --git a/scripts/developer/tests/viveTouchpadTest.js b/scripts/developer/tests/viveTouchpadTest.js index 913da5888d..802186708c 100644 --- a/scripts/developer/tests/viveTouchpadTest.js +++ b/scripts/developer/tests/viveTouchpadTest.js @@ -24,8 +24,8 @@ var boxZAxis, boxYAxis; var prevThumbDown = false; function init() { - boxPosition = Vec3.sum(MyAvatar.position, Vec3.multiply(3, Quat.getFront(Camera.getOrientation()))); - var front = Quat.getFront(Camera.getOrientation()); + boxPosition = Vec3.sum(MyAvatar.position, Vec3.multiply(3, Quat.getForward(Camera.getOrientation()))); + var front = Quat.getForward(Camera.getOrientation()); boxZAxis = Vec3.normalize(Vec3.cross(front, Y_AXIS)); boxYAxis = Vec3.normalize(Vec3.cross(boxZAxis, front)); diff --git a/scripts/developer/utilities/render/photobooth/photobooth.js b/scripts/developer/utilities/render/photobooth/photobooth.js index 3e86d83a98..2a17fd8da0 100644 --- a/scripts/developer/utilities/render/photobooth/photobooth.js +++ b/scripts/developer/utilities/render/photobooth/photobooth.js @@ -9,7 +9,7 @@ PhotoBooth.init = function () { var success = Clipboard.importEntities(PHOTOBOOTH_SETUP_JSON_URL); var frontFactor = 10; - var frontUnitVec = Vec3.normalize(Quat.getFront(MyAvatar.orientation)); + var frontUnitVec = Vec3.normalize(Quat.getForward(MyAvatar.orientation)); var frontOffset = Vec3.multiply(frontUnitVec,frontFactor); var rightFactor = 3; var rightUnitVec = Vec3.normalize(Quat.getRight(MyAvatar.orientation)); diff --git a/scripts/system/away.js b/scripts/system/away.js index 541fe6f679..1b11612ef0 100644 --- a/scripts/system/away.js +++ b/scripts/system/away.js @@ -87,7 +87,7 @@ function moveCloserToCamera(positionAtHUD) { // we don't actually want to render at the slerped look at... instead, we want to render // slightly closer to the camera than that. var MOVE_CLOSER_TO_CAMERA_BY = -0.25; - var cameraFront = Quat.getFront(Camera.orientation); + var cameraFront = Quat.getForward(Camera.orientation); var closerToCamera = Vec3.multiply(cameraFront, MOVE_CLOSER_TO_CAMERA_BY); // slightly closer to camera var slightlyCloserPosition = Vec3.sum(positionAtHUD, closerToCamera); diff --git a/scripts/system/controllers/grab.js b/scripts/system/controllers/grab.js index f0b6663bec..05b2eefeb5 100644 --- a/scripts/system/controllers/grab.js +++ b/scripts/system/controllers/grab.js @@ -463,7 +463,7 @@ Grabber.prototype.moveEvent = function(event) { var orientation = Camera.getOrientation(); var dragOffset = Vec3.multiply(drag.x, Quat.getRight(orientation)); dragOffset = Vec3.sum(dragOffset, Vec3.multiply(-drag.y, Quat.getUp(orientation))); - var axis = Vec3.cross(dragOffset, Quat.getFront(orientation)); + var axis = Vec3.cross(dragOffset, Quat.getForward(orientation)); axis = Vec3.normalize(axis); var ROTATE_STRENGTH = 0.4; // magic number tuned by hand var angle = ROTATE_STRENGTH * Math.sqrt((drag.x * drag.x) + (drag.y * drag.y)); @@ -487,7 +487,7 @@ Grabber.prototype.moveEvent = function(event) { if (this.mode === "verticalCylinder") { // for this mode we recompute the plane based on current Camera - var planeNormal = Quat.getFront(Camera.getOrientation()); + var planeNormal = Quat.getForward(Camera.getOrientation()); planeNormal.y = 0; planeNormal = Vec3.normalize(planeNormal); var pointOnCylinder = Vec3.multiply(planeNormal, this.xzDistanceToGrab); diff --git a/scripts/system/controllers/handControllerGrab.js b/scripts/system/controllers/handControllerGrab.js index 51b01e60a2..e83e31aaa5 100644 --- a/scripts/system/controllers/handControllerGrab.js +++ b/scripts/system/controllers/handControllerGrab.js @@ -1481,7 +1481,7 @@ function MyController(hand) { var pickRay = { origin: PICK_WITH_HAND_RAY ? worldHandPosition : Camera.position, direction: PICK_WITH_HAND_RAY ? Quat.getUp(worldHandRotation) : Vec3.mix(Quat.getUp(worldHandRotation), - Quat.getFront(Camera.orientation), + Quat.getForward(Camera.orientation), HAND_HEAD_MIX_RATIO), length: PICK_MAX_DISTANCE }; diff --git a/scripts/system/controllers/handControllerPointer.js b/scripts/system/controllers/handControllerPointer.js index f8a336a017..eb94428100 100644 --- a/scripts/system/controllers/handControllerPointer.js +++ b/scripts/system/controllers/handControllerPointer.js @@ -174,7 +174,7 @@ function calculateRayUICollisionPoint(position, direction) { // interect HUD plane, 1m in front of camera, using formula: // scale = hudNormal dot (hudPoint - position) / hudNormal dot direction // intersection = postion + scale*direction - var hudNormal = Quat.getFront(Camera.getOrientation()); + var hudNormal = Quat.getForward(Camera.getOrientation()); var hudPoint = Vec3.sum(Camera.getPosition(), hudNormal); // must also scale if PLANAR_PERPENDICULAR_HUD_DISTANCE!=1 var denominator = Vec3.dot(hudNormal, direction); if (denominator === 0) { diff --git a/scripts/system/edit.js b/scripts/system/edit.js index fb5a3c2f73..8b02eb1550 100644 --- a/scripts/system/edit.js +++ b/scripts/system/edit.js @@ -816,7 +816,7 @@ function mouseClickEvent(event) { if (0 < x && sizeOK) { selectedEntityID = foundEntity; orientation = MyAvatar.orientation; - intersection = rayPlaneIntersection(pickRay, P, Quat.getFront(orientation)); + intersection = rayPlaneIntersection(pickRay, P, Quat.getForward(orientation)); if (!event.isShifted) { @@ -1338,12 +1338,12 @@ function handeMenuEvent(menuItem) { } function getPositionToCreateEntity() { var HALF_TREE_SCALE = 16384; - var direction = Quat.getFront(MyAvatar.orientation); + var direction = Quat.getForward(MyAvatar.orientation); var distance = 1; var position = Vec3.sum(MyAvatar.position, Vec3.multiply(direction, distance)); if (Camera.mode === "entity" || Camera.mode === "independent") { - position = Vec3.sum(Camera.position, Vec3.multiply(Quat.getFront(Camera.orientation), distance)) + position = Vec3.sum(Camera.position, Vec3.multiply(Quat.getForward(Camera.orientation), distance)) } position.y += 0.5; if (position.x > HALF_TREE_SCALE || position.y > HALF_TREE_SCALE || position.z > HALF_TREE_SCALE) { @@ -1355,13 +1355,13 @@ function getPositionToCreateEntity() { function getPositionToImportEntity() { var dimensions = Clipboard.getContentsDimensions(); var HALF_TREE_SCALE = 16384; - var direction = Quat.getFront(MyAvatar.orientation); + var direction = Quat.getForward(MyAvatar.orientation); var longest = 1; longest = Math.sqrt(Math.pow(dimensions.x, 2) + Math.pow(dimensions.z, 2)); var position = Vec3.sum(MyAvatar.position, Vec3.multiply(direction, longest)); if (Camera.mode === "entity" || Camera.mode === "independent") { - position = Vec3.sum(Camera.position, Vec3.multiply(Quat.getFront(Camera.orientation), longest)) + position = Vec3.sum(Camera.position, Vec3.multiply(Quat.getForward(Camera.orientation), longest)) } if (position.x > HALF_TREE_SCALE || position.y > HALF_TREE_SCALE || position.z > HALF_TREE_SCALE) { diff --git a/scripts/system/libraries/WebTablet.js b/scripts/system/libraries/WebTablet.js index dd2aaf346b..8e2aaec2d5 100644 --- a/scripts/system/libraries/WebTablet.js +++ b/scripts/system/libraries/WebTablet.js @@ -78,7 +78,7 @@ function calcSpawnInfo(hand, height) { rotation: lookAtRot }; } else { - var front = Quat.getFront(headRot); + var front = Quat.getForward(headRot); finalPosition = Vec3.sum(headPos, Vec3.multiply(0.6, front)); var orientation = Quat.lookAt({x: 0, y: 0, z: 0}, front, {x: 0, y: 1, z: 0}); return { diff --git a/scripts/system/libraries/entityCameraTool.js b/scripts/system/libraries/entityCameraTool.js index 301b60f550..6becc81d9b 100644 --- a/scripts/system/libraries/entityCameraTool.js +++ b/scripts/system/libraries/entityCameraTool.js @@ -158,7 +158,7 @@ CameraManager = function() { that.zoomDistance = INITIAL_ZOOM_DISTANCE; that.targetZoomDistance = that.zoomDistance + 3.0; var focalPoint = Vec3.sum(Camera.getPosition(), - Vec3.multiply(that.zoomDistance, Quat.getFront(Camera.getOrientation()))); + Vec3.multiply(that.zoomDistance, Quat.getForward(Camera.getOrientation()))); // Determine the correct yaw and pitch to keep the camera in the same location var dPos = Vec3.subtract(focalPoint, Camera.getPosition()); @@ -435,7 +435,7 @@ CameraManager = function() { }); var q = Quat.multiply(yRot, xRot); - var pos = Vec3.multiply(Quat.getFront(q), that.zoomDistance); + var pos = Vec3.multiply(Quat.getForward(q), that.zoomDistance); Camera.setPosition(Vec3.sum(that.focalPoint, pos)); yRot = Quat.angleAxis(that.yaw - 180, { diff --git a/scripts/system/libraries/entitySelectionTool.js b/scripts/system/libraries/entitySelectionTool.js index a8c4300fbe..9d4bf7d9a8 100644 --- a/scripts/system/libraries/entitySelectionTool.js +++ b/scripts/system/libraries/entitySelectionTool.js @@ -2517,7 +2517,7 @@ SelectionDisplay = (function() { onBegin: function(event) { pickRay = generalComputePickRay(event.x, event.y); - upDownPickNormal = Quat.getFront(lastCameraOrientation); + upDownPickNormal = Quat.getForward(lastCameraOrientation); // Remove y component so the y-axis lies along the plane we picking on - this will // give movements that follow the mouse. upDownPickNormal.y = 0; diff --git a/scripts/system/libraries/soundArray.js b/scripts/system/libraries/soundArray.js index f59c88a723..7e5da11948 100644 --- a/scripts/system/libraries/soundArray.js +++ b/scripts/system/libraries/soundArray.js @@ -36,7 +36,7 @@ SoundArray = function(audioOptions, autoUpdateAudioPosition) { }; this.updateAudioPosition = function() { var position = MyAvatar.position; - var forwardVector = Quat.getFront(MyAvatar.orientation); + var forwardVector = Quat.getForward(MyAvatar.orientation); this.audioOptions.position = Vec3.sum(position, forwardVector); }; }; diff --git a/scripts/system/nameTag.js b/scripts/system/nameTag.js index e25db69064..17944bcf85 100644 --- a/scripts/system/nameTag.js +++ b/scripts/system/nameTag.js @@ -33,7 +33,7 @@ Script.setTimeout(function() { }, STARTUP_DELAY); function addNameTag() { - var nameTagPosition = Vec3.sum(MyAvatar.getHeadPosition(), Vec3.multiply(HEAD_OFFSET, Quat.getFront(MyAvatar.orientation))); + var nameTagPosition = Vec3.sum(MyAvatar.getHeadPosition(), Vec3.multiply(HEAD_OFFSET, Quat.getForward(MyAvatar.orientation))); nameTagPosition.y += HEIGHT_ABOVE_HEAD; var nameTagProperties = { name: MyAvatar.displayName + ' Name Tag', @@ -49,7 +49,7 @@ function addNameTag() { function updateNameTag() { var nameTagProps = Entities.getEntityProperties(nameTagEntityID); - var nameTagPosition = Vec3.sum(MyAvatar.getHeadPosition(), Vec3.multiply(HEAD_OFFSET, Quat.getFront(MyAvatar.orientation))); + var nameTagPosition = Vec3.sum(MyAvatar.getHeadPosition(), Vec3.multiply(HEAD_OFFSET, Quat.getForward(MyAvatar.orientation))); nameTagPosition.y += HEIGHT_ABOVE_HEAD; Entities.editEntity(nameTagEntityID, { diff --git a/scripts/system/pal.js b/scripts/system/pal.js index fdb6cbcaf5..b47ad754f7 100644 --- a/scripts/system/pal.js +++ b/scripts/system/pal.js @@ -298,7 +298,7 @@ function populateUserList(selectData, oldAudioData) { verticalHalfAngle = filter && (frustum.fieldOfView / 2), horizontalHalfAngle = filter && (verticalHalfAngle * frustum.aspectRatio), orientation = filter && Camera.orientation, - front = filter && Quat.getFront(orientation), + front = filter && Quat.getForward(orientation), verticalAngleNormal = filter && Quat.getRight(orientation), horizontalAngleNormal = filter && Quat.getUp(orientation); avatars.forEach(function (id) { // sorting the identifiers is just an aid for debugging diff --git a/scripts/system/voxels.js b/scripts/system/voxels.js index 3c219ebc7a..2f1d0eced9 100644 --- a/scripts/system/voxels.js +++ b/scripts/system/voxels.js @@ -253,7 +253,7 @@ function addTerrainBlock() { if (alreadyThere) { // there is already a terrain block under MyAvatar. // try in front of the avatar. - facingPosition = Vec3.sum(MyAvatar.position, Vec3.multiply(8.0, Quat.getFront(Camera.getOrientation()))); + facingPosition = Vec3.sum(MyAvatar.position, Vec3.multiply(8.0, Quat.getForward(Camera.getOrientation()))); facingPosition = Vec3.sum(facingPosition, { x: 8, y: 8, diff --git a/scripts/tutorials/NBody/makePlanets.js b/scripts/tutorials/NBody/makePlanets.js index 58a3c7cc2d..21415ccdc2 100644 --- a/scripts/tutorials/NBody/makePlanets.js +++ b/scripts/tutorials/NBody/makePlanets.js @@ -53,7 +53,7 @@ var deleteButton = toolBar.addOverlay("image", { }); function inFrontOfMe(distance) { - return Vec3.sum(Camera.getPosition(), Vec3.multiply(distance, Quat.getFront(Camera.getOrientation()))); + return Vec3.sum(Camera.getPosition(), Vec3.multiply(distance, Quat.getForward(Camera.getOrientation()))); } function onButtonClick() { diff --git a/scripts/tutorials/butterflies.js b/scripts/tutorials/butterflies.js index 55bafc0a27..9d8d1de52c 100644 --- a/scripts/tutorials/butterflies.js +++ b/scripts/tutorials/butterflies.js @@ -44,8 +44,8 @@ var FIXED_LOCATION = false; if (!FIXED_LOCATION) { var flockPosition = Vec3.sum(MyAvatar.position,Vec3.sum( - Vec3.multiply(Quat.getFront(MyAvatar.orientation), DISTANCE_ABOVE_ME), - Vec3.multiply(Quat.getFront(MyAvatar.orientation), DISTANCE_IN_FRONT_OF_ME))); + Vec3.multiply(Quat.getForward(MyAvatar.orientation), DISTANCE_ABOVE_ME), + Vec3.multiply(Quat.getForward(MyAvatar.orientation), DISTANCE_IN_FRONT_OF_ME))); } else { var flockPosition = { x: 4999.6, y: 4986.5, z: 5003.5 }; } @@ -119,7 +119,7 @@ function updateButterflies(deltaTime) { var HORIZ_SCALE = 0.50; var VERT_SCALE = 0.50; var newHeading = Math.random() * 360.0; - var newVelocity = Vec3.multiply(HORIZ_SCALE, Quat.getFront(Quat.fromPitchYawRollDegrees(0.0, newHeading, 0.0))); + var newVelocity = Vec3.multiply(HORIZ_SCALE, Quat.getForward(Quat.fromPitchYawRollDegrees(0.0, newHeading, 0.0))); newVelocity.y = (Math.random() + 0.5) * VERT_SCALE; Entities.editEntity(butterflies[i], { rotation: Quat.fromPitchYawRollDegrees(-80 + Math.random() * 20, newHeading, (Math.random() - 0.5) * 10), velocity: newVelocity } ); diff --git a/scripts/tutorials/createCow.js b/scripts/tutorials/createCow.js index 7446aa0fd0..16498e0e8c 100644 --- a/scripts/tutorials/createCow.js +++ b/scripts/tutorials/createCow.js @@ -18,7 +18,7 @@ var orientation = MyAvatar.orientation; orientation = Quat.safeEulerAngles(orientation); orientation.x = 0; orientation = Quat.fromVec3Degrees(orientation); -var center = Vec3.sum(MyAvatar.getHeadPosition(), Vec3.multiply(2, Quat.getFront(orientation))); +var center = Vec3.sum(MyAvatar.getHeadPosition(), Vec3.multiply(2, Quat.getForward(orientation))); // An entity is described and created by specifying a map of properties var cow = Entities.addEntity({ diff --git a/scripts/tutorials/createDice.js b/scripts/tutorials/createDice.js index 0d39d11d48..46ad0172aa 100644 --- a/scripts/tutorials/createDice.js +++ b/scripts/tutorials/createDice.js @@ -127,8 +127,8 @@ function mousePressEvent(event) { deleteDice(); } else if (clickedOverlay == diceButton) { var HOW_HARD = 2.0; - var position = Vec3.sum(Camera.getPosition(), Quat.getFront(Camera.getOrientation())); - var velocity = Vec3.multiply(HOW_HARD, Quat.getFront(Camera.getOrientation())); + var position = Vec3.sum(Camera.getPosition(), Quat.getForward(Camera.getOrientation())); + var velocity = Vec3.multiply(HOW_HARD, Quat.getForward(Camera.getOrientation())); shootDice(position, velocity); madeSound = false; } diff --git a/scripts/tutorials/createFlashlight.js b/scripts/tutorials/createFlashlight.js index 0e3581a435..f3e1e72182 100644 --- a/scripts/tutorials/createFlashlight.js +++ b/scripts/tutorials/createFlashlight.js @@ -16,7 +16,7 @@ var center = Vec3.sum(Vec3.sum(MyAvatar.position, { x: 0, y: 0.5, z: 0 -}), Vec3.multiply(0.5, Quat.getFront(Camera.getOrientation()))); +}), Vec3.multiply(0.5, Quat.getForward(Camera.getOrientation()))); var flashlight = Entities.addEntity({ type: "Model", diff --git a/scripts/tutorials/createGolfClub.js b/scripts/tutorials/createGolfClub.js index aa9834276a..21e60f26ef 100644 --- a/scripts/tutorials/createGolfClub.js +++ b/scripts/tutorials/createGolfClub.js @@ -15,7 +15,7 @@ var orientation = MyAvatar.orientation; orientation = Quat.safeEulerAngles(orientation); orientation.x = 0; orientation = Quat.fromVec3Degrees(orientation); -var center = Vec3.sum(MyAvatar.getHeadPosition(), Vec3.multiply(2, Quat.getFront(orientation))); +var center = Vec3.sum(MyAvatar.getHeadPosition(), Vec3.multiply(2, Quat.getForward(orientation))); var CLUB_MODEL = "http://hifi-production.s3.amazonaws.com/tutorials/golfClub/putter_VR.fbx"; var CLUB_COLLISION_HULL = "http://hifi-production.s3.amazonaws.com/tutorials/golfClub/club_collision_hull.obj"; diff --git a/scripts/tutorials/createPictureFrame.js b/scripts/tutorials/createPictureFrame.js index 4a1e5b16a7..873b604bfa 100644 --- a/scripts/tutorials/createPictureFrame.js +++ b/scripts/tutorials/createPictureFrame.js @@ -14,7 +14,7 @@ var center = Vec3.sum(Vec3.sum(MyAvatar.position, { x: 0, y: 0.5, z: 0 -}), Vec3.multiply(1, Quat.getFront(Camera.getOrientation()))); +}), Vec3.multiply(1, Quat.getForward(Camera.getOrientation()))); // this is just a model exported from blender with a texture named 'Picture' on one face. also made it emissive so it doesn't require lighting. var MODEL_URL = "http://hifi-production.s3.amazonaws.com/tutorials/pictureFrame/finalFrame.fbx"; diff --git a/scripts/tutorials/createPingPongGun.js b/scripts/tutorials/createPingPongGun.js index a077e5308d..c86a78e96d 100644 --- a/scripts/tutorials/createPingPongGun.js +++ b/scripts/tutorials/createPingPongGun.js @@ -14,7 +14,7 @@ var center = Vec3.sum(Vec3.sum(MyAvatar.position, { x: 0, y: 0.5, z: 0 -}), Vec3.multiply(0.5, Quat.getFront(Camera.getOrientation()))); +}), Vec3.multiply(0.5, Quat.getForward(Camera.getOrientation()))); var pingPongGunProperties = { diff --git a/scripts/tutorials/createPistol.js b/scripts/tutorials/createPistol.js index ae2f398840..8851f53d09 100644 --- a/scripts/tutorials/createPistol.js +++ b/scripts/tutorials/createPistol.js @@ -6,7 +6,7 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -var center = Vec3.sum(MyAvatar.position, Vec3.multiply(1.5, Quat.getFront(Camera.getOrientation()))); +var center = Vec3.sum(MyAvatar.position, Vec3.multiply(1.5, Quat.getForward(Camera.getOrientation()))); var SCRIPT_URL = "http://hifi-production.s3.amazonaws.com/tutorials/entity_scripts/pistol.js"; var MODEL_URL = "http://hifi-production.s3.amazonaws.com/tutorials/pistol/gun.fbx"; var COLLISION_SOUND_URL = 'http://hifi-production.s3.amazonaws.com/tutorials/pistol/drop.wav' diff --git a/scripts/tutorials/createSoundMaker.js b/scripts/tutorials/createSoundMaker.js index b79c650e27..2d86864982 100644 --- a/scripts/tutorials/createSoundMaker.js +++ b/scripts/tutorials/createSoundMaker.js @@ -13,7 +13,7 @@ var center = Vec3.sum(Vec3.sum(MyAvatar.position, { x: 0, y: 0.5, z: 0 -}), Vec3.multiply(1, Quat.getFront(Camera.getOrientation()))); +}), Vec3.multiply(1, Quat.getForward(Camera.getOrientation()))); function makeBell() { var soundMakerProperties = { diff --git a/scripts/tutorials/entity_scripts/golfClub.js b/scripts/tutorials/entity_scripts/golfClub.js index 2df3be8b60..6342838aa4 100644 --- a/scripts/tutorials/entity_scripts/golfClub.js +++ b/scripts/tutorials/entity_scripts/golfClub.js @@ -57,7 +57,7 @@ // Position yourself facing in the direction you were originally facing, but with a // point on the ground *away* meters from *position* and in front of you. - var offset = Quat.getFront(MyAvatar.orientation); + var offset = Quat.getForward(MyAvatar.orientation); offset.y = 0.0; offset = Vec3.multiply(-away, Vec3.normalize(offset)); var newAvatarPosition = Vec3.sum(position, offset); @@ -72,7 +72,7 @@ } function inFrontOfMe() { - return Vec3.sum(MyAvatar.position, Vec3.multiply(BALL_DROP_DISTANCE, Quat.getFront(MyAvatar.orientation))); + return Vec3.sum(MyAvatar.position, Vec3.multiply(BALL_DROP_DISTANCE, Quat.getForward(MyAvatar.orientation))); } function avatarHalfHeight() { diff --git a/scripts/tutorials/entity_scripts/pingPongGun.js b/scripts/tutorials/entity_scripts/pingPongGun.js index 4ec0254747..aaaf1bd1e1 100644 --- a/scripts/tutorials/entity_scripts/pingPongGun.js +++ b/scripts/tutorials/entity_scripts/pingPongGun.js @@ -94,7 +94,7 @@ }, shootBall: function(gunProperties) { - var forwardVec = Quat.getFront(Quat.multiply(gunProperties.rotation, Quat.fromPitchYawRollDegrees(0, 180, 0))); + var forwardVec = Quat.getForward(Quat.multiply(gunProperties.rotation, Quat.fromPitchYawRollDegrees(0, 180, 0))); forwardVec = Vec3.normalize(forwardVec); forwardVec = Vec3.multiply(forwardVec, GUN_FORCE); @@ -131,7 +131,7 @@ getGunTipPosition: function(properties) { //the tip of the gun is going to be in a different place than the center, so we move in space relative to the model to find that position - var frontVector = Quat.getFront(properties.rotation); + var frontVector = Quat.getForward(properties.rotation); var frontOffset = Vec3.multiply(frontVector, GUN_TIP_FWD_OFFSET); var upVector = Quat.getUp(properties.rotation); var upOffset = Vec3.multiply(upVector, GUN_TIP_UP_OFFSET); diff --git a/scripts/tutorials/entity_scripts/pistol.js b/scripts/tutorials/entity_scripts/pistol.js index 73a6daab93..38eb929177 100644 --- a/scripts/tutorials/entity_scripts/pistol.js +++ b/scripts/tutorials/entity_scripts/pistol.js @@ -69,7 +69,7 @@ var gunProps = Entities.getEntityProperties(this.entityID, ['position', 'rotation']); this.position = gunProps.position; this.rotation = gunProps.rotation; - this.firingDirection = Quat.getFront(this.rotation); + this.firingDirection = Quat.getForward(this.rotation); var upVec = Quat.getUp(this.rotation); this.barrelPoint = Vec3.sum(this.position, Vec3.multiply(upVec, this.laserOffsets.y)); this.laserTip = Vec3.sum(this.barrelPoint, Vec3.multiply(this.firingDirection, this.laserLength)); diff --git a/scripts/tutorials/makeBlocks.js b/scripts/tutorials/makeBlocks.js index 54bdead792..9a3e649e7a 100644 --- a/scripts/tutorials/makeBlocks.js +++ b/scripts/tutorials/makeBlocks.js @@ -34,7 +34,7 @@ var SCRIPT_URL = Script.resolvePath("./entity_scripts/magneticBlock.js"); - var frontVector = Quat.getFront(MyAvatar.orientation); + var frontVector = Quat.getForward(MyAvatar.orientation); frontVector.y += VERTICAL_OFFSET; for (var x = 0; x < COLUMNS; x++) { for (var y = 0; y < ROWS; y++) { From 8df0395754e436d06beba2ac4ebe5168496bd85e Mon Sep 17 00:00:00 2001 From: Triplelexx Date: Tue, 21 Mar 2017 22:28:12 +0000 Subject: [PATCH 269/302] change getFrontDirection to getForwardDirection --- interface/src/avatar/Head.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/interface/src/avatar/Head.h b/interface/src/avatar/Head.h index d556d1efc4..aa801e5eb5 100644 --- a/interface/src/avatar/Head.h +++ b/interface/src/avatar/Head.h @@ -58,14 +58,14 @@ public: const glm::vec3& getSaccade() const { return _saccade; } glm::vec3 getRightDirection() const { return getOrientation() * IDENTITY_RIGHT; } glm::vec3 getUpDirection() const { return getOrientation() * IDENTITY_UP; } - glm::vec3 getFrontDirection() const { return getOrientation() * IDENTITY_FORWARD; } + glm::vec3 getForwardDirection() const { return getOrientation() * IDENTITY_FORWARD; } glm::quat getEyeRotation(const glm::vec3& eyePosition) const; const glm::vec3& getRightEyePosition() const { return _rightEyePosition; } const glm::vec3& getLeftEyePosition() const { return _leftEyePosition; } - glm::vec3 getRightEarPosition() const { return _rightEyePosition + (getRightDirection() * EYE_EAR_GAP) + (getFrontDirection() * -EYE_EAR_GAP); } - glm::vec3 getLeftEarPosition() const { return _leftEyePosition + (getRightDirection() * -EYE_EAR_GAP) + (getFrontDirection() * -EYE_EAR_GAP); } + glm::vec3 getRightEarPosition() const { return _rightEyePosition + (getRightDirection() * EYE_EAR_GAP) + (getForwardDirection() * -EYE_EAR_GAP); } + glm::vec3 getLeftEarPosition() const { return _leftEyePosition + (getRightDirection() * -EYE_EAR_GAP) + (getForwardDirection() * -EYE_EAR_GAP); } glm::vec3 getMouthPosition() const { return _eyePosition - getUpDirection() * glm::length(_rightEyePosition - _leftEyePosition); } bool getReturnToCenter() const { return _returnHeadToCenter; } // Do you want head to try to return to center (depends on interface detected) From 66d697873ffb521a6efb808345d655f0b7fc50c6 Mon Sep 17 00:00:00 2001 From: Triplelexx Date: Tue, 21 Mar 2017 22:31:06 +0000 Subject: [PATCH 270/302] change mat4test back to getFront premature, including in seperate commit --- scripts/developer/tests/mat4test.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/developer/tests/mat4test.js b/scripts/developer/tests/mat4test.js index 2d7c994631..ebce420dcb 100644 --- a/scripts/developer/tests/mat4test.js +++ b/scripts/developer/tests/mat4test.js @@ -143,10 +143,10 @@ function testInverse() { function testFront() { var test0 = IDENTITY; - assert(mat4FuzzyEqual({x: 0, y: 0, z: -1}, Mat4.getForward(test0))); + assert(mat4FuzzyEqual({x: 0, y: 0, z: -1}, Mat4.getFront(test0))); var test1 = Mat4.createFromScaleRotAndTrans(ONE_HALF, ROT_Y_180, ONE_TWO_THREE); - assert(mat4FuzzyEqual({x: 0, y: 0, z: 1}, Mat4.getForward(test1))); + assert(mat4FuzzyEqual({x: 0, y: 0, z: 1}, Mat4.getFront(test1))); } function testMat4() { From 6e740a5fbb161c0e9798dbf8ed7be8aeeec1c148 Mon Sep 17 00:00:00 2001 From: Triplelexx Date: Tue, 21 Mar 2017 22:33:37 +0000 Subject: [PATCH 271/302] change Mat4 getFront to getForward --- libraries/script-engine/src/Mat4.cpp | 2 +- libraries/script-engine/src/Mat4.h | 4 +++- scripts/developer/tests/mat4test.js | 4 ++-- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/libraries/script-engine/src/Mat4.cpp b/libraries/script-engine/src/Mat4.cpp index 52b9690321..6676d0cde1 100644 --- a/libraries/script-engine/src/Mat4.cpp +++ b/libraries/script-engine/src/Mat4.cpp @@ -54,7 +54,7 @@ glm::mat4 Mat4::inverse(const glm::mat4& m) const { return glm::inverse(m); } -glm::vec3 Mat4::getFront(const glm::mat4& m) const { +glm::vec3 Mat4::getForward(const glm::mat4& m) const { return glm::vec3(-m[0][2], -m[1][2], -m[2][2]); } diff --git a/libraries/script-engine/src/Mat4.h b/libraries/script-engine/src/Mat4.h index 8b2a8aa8c1..19bbbe178a 100644 --- a/libraries/script-engine/src/Mat4.h +++ b/libraries/script-engine/src/Mat4.h @@ -37,7 +37,9 @@ public slots: glm::mat4 inverse(const glm::mat4& m) const; - glm::vec3 getFront(const glm::mat4& m) const; + // redundant, calls getForward which better describes the returned vector as a direction + glm::vec3 getFront(const glm::mat4& m) const { return getForward(m); } + glm::vec3 getForward(const glm::mat4& m) const; glm::vec3 getRight(const glm::mat4& m) const; glm::vec3 getUp(const glm::mat4& m) const; diff --git a/scripts/developer/tests/mat4test.js b/scripts/developer/tests/mat4test.js index ebce420dcb..2d7c994631 100644 --- a/scripts/developer/tests/mat4test.js +++ b/scripts/developer/tests/mat4test.js @@ -143,10 +143,10 @@ function testInverse() { function testFront() { var test0 = IDENTITY; - assert(mat4FuzzyEqual({x: 0, y: 0, z: -1}, Mat4.getFront(test0))); + assert(mat4FuzzyEqual({x: 0, y: 0, z: -1}, Mat4.getForward(test0))); var test1 = Mat4.createFromScaleRotAndTrans(ONE_HALF, ROT_Y_180, ONE_TWO_THREE); - assert(mat4FuzzyEqual({x: 0, y: 0, z: 1}, Mat4.getFront(test1))); + assert(mat4FuzzyEqual({x: 0, y: 0, z: 1}, Mat4.getForward(test1))); } function testMat4() { From 2515563a0bcf821fab60419e59880b59c241b87a Mon Sep 17 00:00:00 2001 From: Triplelexx Date: Tue, 21 Mar 2017 22:35:07 +0000 Subject: [PATCH 272/302] change getFront to getForward --- interface/resources/qml/AssetServer.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/resources/qml/AssetServer.qml b/interface/resources/qml/AssetServer.qml index cf61a2ae4a..6f3076b408 100644 --- a/interface/resources/qml/AssetServer.qml +++ b/interface/resources/qml/AssetServer.qml @@ -206,7 +206,7 @@ ScrollingWindow { print("Error: model cannot be both static mesh and dynamic. This should never happen."); } else if (url) { var name = assetProxyModel.data(treeView.selection.currentIndex); - var addPosition = Vec3.sum(MyAvatar.position, Vec3.multiply(2, Quat.getFront(MyAvatar.orientation))); + var addPosition = Vec3.sum(MyAvatar.position, Vec3.multiply(2, Quat.getForward(MyAvatar.orientation))); var gravity; if (dynamic) { // Create a vector <0, -10, 0>. { x: 0, y: -10, z: 0 } won't work because Qt is dumb and this is a From e60108cc8c5dd21ec08ce2b68d0f1c23b8f5860f Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Tue, 21 Mar 2017 16:57:53 -0700 Subject: [PATCH 273/302] PR comments --- libraries/gpu/src/gpu/Texture.h | 1 + libraries/gpu/src/gpu/Texture_ktx.cpp | 6 +++--- libraries/ktx/src/ktx/KTX.cpp | 2 +- libraries/ktx/src/ktx/Reader.cpp | 8 ++++---- libraries/ktx/src/ktx/Writer.cpp | 4 ++-- 5 files changed, 11 insertions(+), 10 deletions(-) diff --git a/libraries/gpu/src/gpu/Texture.h b/libraries/gpu/src/gpu/Texture.h index c7fb496fc0..0212e1845f 100755 --- a/libraries/gpu/src/gpu/Texture.h +++ b/libraries/gpu/src/gpu/Texture.h @@ -163,6 +163,7 @@ class Texture : public Resource { static void updateTextureCPUMemoryUsage(Size prevObjectSize, Size newObjectSize); public: + static const uint32_t CUBE_FACE_COUNT { 6 }; static uint32_t getTextureCPUCount(); static Size getTextureCPUMemoryUsage(); static uint32_t getTextureGPUCount(); diff --git a/libraries/gpu/src/gpu/Texture_ktx.cpp b/libraries/gpu/src/gpu/Texture_ktx.cpp index 5fed4d82bc..5f0ededee7 100644 --- a/libraries/gpu/src/gpu/Texture_ktx.cpp +++ b/libraries/gpu/src/gpu/Texture_ktx.cpp @@ -113,7 +113,7 @@ ktx::KTXUniquePointer Texture::serialize(const Texture& texture) { } else { header.setCube(texture.getWidth(), texture.getHeight()); } - numFaces = 6; + numFaces = Texture::CUBE_FACE_COUNT; break; } default: @@ -130,9 +130,9 @@ ktx::KTXUniquePointer Texture::serialize(const Texture& texture) { if (numFaces == 1) { images.emplace_back(ktx::Image((uint32_t)mip->getSize(), 0, mip->readData())); } else { - ktx::Image::FaceBytes cubeFaces(6); + ktx::Image::FaceBytes cubeFaces(Texture::CUBE_FACE_COUNT); cubeFaces[0] = mip->readData(); - for (int face = 1; face < 6; face++) { + for (uint32_t face = 1; face < Texture::CUBE_FACE_COUNT; face++) { cubeFaces[face] = texture.accessStoredMipFace(level, face)->readData(); } images.emplace_back(ktx::Image((uint32_t)mip->getSize(), 0, cubeFaces)); diff --git a/libraries/ktx/src/ktx/KTX.cpp b/libraries/ktx/src/ktx/KTX.cpp index f9bfb377f5..bbd4e1bc86 100644 --- a/libraries/ktx/src/ktx/KTX.cpp +++ b/libraries/ktx/src/ktx/KTX.cpp @@ -63,7 +63,7 @@ size_t Header::evalFaceSize(uint32_t level) const { } size_t Header::evalImageSize(uint32_t level) const { auto faceSize = evalFaceSize(level); - if (numberOfFaces == 6 && numberOfArrayElements == 0) { + if (numberOfFaces == NUM_CUBEMAPFACES && numberOfArrayElements == 0) { return faceSize; } else { return (getNumberOfSlices() * numberOfFaces * faceSize); diff --git a/libraries/ktx/src/ktx/Reader.cpp b/libraries/ktx/src/ktx/Reader.cpp index 5aa6919844..277ce42e69 100644 --- a/libraries/ktx/src/ktx/Reader.cpp +++ b/libraries/ktx/src/ktx/Reader.cpp @@ -151,10 +151,10 @@ namespace ktx { if ((currentPtr - srcBytes) + imageSize <= (srcSize)) { auto padding = Header::evalPadding(imageSize); - if (numFaces == 6) { - size_t faceSize = imageSize / 6; - Image::FaceBytes faces(6); - for (uint32_t face = 0; face < 6; face++) { + if (numFaces == NUM_CUBEMAPFACES) { + size_t faceSize = imageSize / NUM_CUBEMAPFACES; + Image::FaceBytes faces(NUM_CUBEMAPFACES); + for (uint32_t face = 0; face < NUM_CUBEMAPFACES; face++) { faces[face] = currentPtr; currentPtr += faceSize; } diff --git a/libraries/ktx/src/ktx/Writer.cpp b/libraries/ktx/src/ktx/Writer.cpp index 06fba326d5..25b363d31b 100644 --- a/libraries/ktx/src/ktx/Writer.cpp +++ b/libraries/ktx/src/ktx/Writer.cpp @@ -149,9 +149,9 @@ namespace ktx { destImages.emplace_back(Image((uint32_t) imageSize, padding, currentPtr)); currentPtr += imageSize; } else { - Image::FaceBytes faceBytes(6); + Image::FaceBytes faceBytes(NUM_CUBEMAPFACES); auto faceSize = srcImages[l]._faceSize; - for (int face = 0; face < 6; face++) { + for (int face = 0; face < NUM_CUBEMAPFACES; face++) { memcpy(currentPtr, srcImages[l]._faceBytes[face], faceSize); faceBytes[face] = currentPtr; currentPtr += faceSize; From dff67886d099d66f29cfc146651c66810466b092 Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Wed, 22 Mar 2017 17:28:10 +0100 Subject: [PATCH 274/302] CR Changes - remove double constructor, use creation function instead --- unpublishedScripts/marketplace/boppo/boppoClownEntity.js | 8 ++++---- unpublishedScripts/marketplace/boppo/boppoServer.js | 8 +++----- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/unpublishedScripts/marketplace/boppo/boppoClownEntity.js b/unpublishedScripts/marketplace/boppo/boppoClownEntity.js index 2fa4441e52..36f2bf5ab0 100644 --- a/unpublishedScripts/marketplace/boppo/boppoClownEntity.js +++ b/unpublishedScripts/marketplace/boppo/boppoClownEntity.js @@ -8,7 +8,7 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -/* globals BoppoClownEntity:true LookAtTarget */ +/* globals LookAtTarget */ (function() { var SFX_PREFIX = 'https://hifi-content.s3-us-west-1.amazonaws.com/caitlyn/production/elBoppo/sfx/'; @@ -21,7 +21,7 @@ Script.include('lookAtEntity.js'); - BoppoClownEntity = function() { + var createBoppoClownEntity = function() { var _this, _entityID, _boppoUserData, @@ -38,7 +38,7 @@ return {}; }; - BoppoClownEntity = function () { + var BoppoClownEntity = function () { _this = this; PUNCH_SOUNDS.forEach(function(punch) { _punchSounds.push(SoundCache.getSound(SFX_PREFIX + punch)); @@ -76,5 +76,5 @@ return new BoppoClownEntity(); }; - return new BoppoClownEntity(); + return createBoppoClownEntity(); }); diff --git a/unpublishedScripts/marketplace/boppo/boppoServer.js b/unpublishedScripts/marketplace/boppo/boppoServer.js index 28690c0a00..f03154573c 100644 --- a/unpublishedScripts/marketplace/boppo/boppoServer.js +++ b/unpublishedScripts/marketplace/boppo/boppoServer.js @@ -8,8 +8,6 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -/* globals BoppoServer:true */ - (function() { var SFX_PREFIX = 'https://hifi-content.s3-us-west-1.amazonaws.com/caitlyn/production/elBoppo/sfx/'; var CLOWN_LAUGHS = [ @@ -41,7 +39,7 @@ var TICK_TOCK_FROM = 3; // seconds var COOLDOWN_TIME_MS = MILLISECONDS_PER_SECOND * 3; - BoppoServer = function() { + var createBoppoServer = function() { var _this, _isInitialized = false, _clownLaughs = [], @@ -164,7 +162,7 @@ } }; - BoppoServer = function () { + var BoppoServer = function () { _this = this; _hits = 0; _boppoClownID = null; @@ -301,5 +299,5 @@ return new BoppoServer(); }; - return new BoppoServer(); + return createBoppoServer(); }); From 2d1ca99e2e07e41295210ba199a1ae0acc15de57 Mon Sep 17 00:00:00 2001 From: Triplelexx Date: Wed, 22 Mar 2017 17:00:12 +0000 Subject: [PATCH 275/302] change variable names that use Quat.getForward --- scripts/developer/tests/viveTouchpadTest.js | 6 +++--- .../utilities/render/photobooth/photobooth.js | 9 ++++----- scripts/system/away.js | 4 ++-- scripts/system/libraries/WebTablet.js | 6 +++--- scripts/system/pal.js | 6 +++--- scripts/tutorials/entity_scripts/pingPongGun.js | 14 +++++++------- scripts/tutorials/makeBlocks.js | 6 +++--- 7 files changed, 25 insertions(+), 26 deletions(-) diff --git a/scripts/developer/tests/viveTouchpadTest.js b/scripts/developer/tests/viveTouchpadTest.js index 802186708c..b5d9575adf 100644 --- a/scripts/developer/tests/viveTouchpadTest.js +++ b/scripts/developer/tests/viveTouchpadTest.js @@ -25,9 +25,9 @@ var prevThumbDown = false; function init() { boxPosition = Vec3.sum(MyAvatar.position, Vec3.multiply(3, Quat.getForward(Camera.getOrientation()))); - var front = Quat.getForward(Camera.getOrientation()); - boxZAxis = Vec3.normalize(Vec3.cross(front, Y_AXIS)); - boxYAxis = Vec3.normalize(Vec3.cross(boxZAxis, front)); + var forward = Quat.getForward(Camera.getOrientation()); + boxZAxis = Vec3.normalize(Vec3.cross(forward, Y_AXIS)); + boxYAxis = Vec3.normalize(Vec3.cross(boxZAxis, forward)); boxEntity = Entities.addEntity({ type: "Box", diff --git a/scripts/developer/utilities/render/photobooth/photobooth.js b/scripts/developer/utilities/render/photobooth/photobooth.js index 2a17fd8da0..e822955563 100644 --- a/scripts/developer/utilities/render/photobooth/photobooth.js +++ b/scripts/developer/utilities/render/photobooth/photobooth.js @@ -8,12 +8,11 @@ var PhotoBooth = {}; PhotoBooth.init = function () { var success = Clipboard.importEntities(PHOTOBOOTH_SETUP_JSON_URL); - var frontFactor = 10; - var frontUnitVec = Vec3.normalize(Quat.getForward(MyAvatar.orientation)); - var frontOffset = Vec3.multiply(frontUnitVec,frontFactor); + var forwardFactor = 10; + var forwardUnitVector = Vec3.normalize(Quat.getForward(MyAvatar.orientation)); + var forwardOffset = Vec3.multiply(forwardUnitVector,forwardFactor); var rightFactor = 3; - var rightUnitVec = Vec3.normalize(Quat.getRight(MyAvatar.orientation)); - var spawnLocation = Vec3.sum(Vec3.sum(MyAvatar.position,frontOffset),rightFactor); + var spawnLocation = Vec3.sum(Vec3.sum(MyAvatar.position,forwardOffset),rightFactor); if (success) { this.pastedEntityIDs = Clipboard.pasteEntities(spawnLocation); this.processPastedEntities(); diff --git a/scripts/system/away.js b/scripts/system/away.js index 1b11612ef0..4ca938d492 100644 --- a/scripts/system/away.js +++ b/scripts/system/away.js @@ -87,8 +87,8 @@ function moveCloserToCamera(positionAtHUD) { // we don't actually want to render at the slerped look at... instead, we want to render // slightly closer to the camera than that. var MOVE_CLOSER_TO_CAMERA_BY = -0.25; - var cameraFront = Quat.getForward(Camera.orientation); - var closerToCamera = Vec3.multiply(cameraFront, MOVE_CLOSER_TO_CAMERA_BY); // slightly closer to camera + var cameraForward = Quat.getForward(Camera.orientation); + var closerToCamera = Vec3.multiply(cameraForward, MOVE_CLOSER_TO_CAMERA_BY); // slightly closer to camera var slightlyCloserPosition = Vec3.sum(positionAtHUD, closerToCamera); return slightlyCloserPosition; diff --git a/scripts/system/libraries/WebTablet.js b/scripts/system/libraries/WebTablet.js index 8e2aaec2d5..32f45188dc 100644 --- a/scripts/system/libraries/WebTablet.js +++ b/scripts/system/libraries/WebTablet.js @@ -78,9 +78,9 @@ function calcSpawnInfo(hand, height) { rotation: lookAtRot }; } else { - var front = Quat.getForward(headRot); - finalPosition = Vec3.sum(headPos, Vec3.multiply(0.6, front)); - var orientation = Quat.lookAt({x: 0, y: 0, z: 0}, front, {x: 0, y: 1, z: 0}); + var forward = Quat.getForward(headRot); + finalPosition = Vec3.sum(headPos, Vec3.multiply(0.6, forward)); + var orientation = Quat.lookAt({x: 0, y: 0, z: 0}, forward, {x: 0, y: 1, z: 0}); return { position: finalPosition, rotation: Quat.multiply(orientation, {x: 0, y: 1, z: 0, w: 0}) diff --git a/scripts/system/pal.js b/scripts/system/pal.js index b47ad754f7..d85fbce19a 100644 --- a/scripts/system/pal.js +++ b/scripts/system/pal.js @@ -298,7 +298,7 @@ function populateUserList(selectData, oldAudioData) { verticalHalfAngle = filter && (frustum.fieldOfView / 2), horizontalHalfAngle = filter && (verticalHalfAngle * frustum.aspectRatio), orientation = filter && Camera.orientation, - front = filter && Quat.getForward(orientation), + forward = filter && Quat.getForward(orientation), verticalAngleNormal = filter && Quat.getRight(orientation), horizontalAngleNormal = filter && Quat.getUp(orientation); avatars.forEach(function (id) { // sorting the identifiers is just an aid for debugging @@ -316,8 +316,8 @@ function populateUserList(selectData, oldAudioData) { return; } var normal = id && filter && Vec3.normalize(Vec3.subtract(avatar.position, myPosition)); - var horizontal = normal && angleBetweenVectorsInPlane(normal, front, horizontalAngleNormal); - var vertical = normal && angleBetweenVectorsInPlane(normal, front, verticalAngleNormal); + var horizontal = normal && angleBetweenVectorsInPlane(normal, forward, horizontalAngleNormal); + var vertical = normal && angleBetweenVectorsInPlane(normal, forward, verticalAngleNormal); if (id && filter && ((Math.abs(horizontal) > horizontalHalfAngle) || (Math.abs(vertical) > verticalHalfAngle))) { return; } diff --git a/scripts/tutorials/entity_scripts/pingPongGun.js b/scripts/tutorials/entity_scripts/pingPongGun.js index aaaf1bd1e1..5ba4b15ea7 100644 --- a/scripts/tutorials/entity_scripts/pingPongGun.js +++ b/scripts/tutorials/entity_scripts/pingPongGun.js @@ -94,9 +94,9 @@ }, shootBall: function(gunProperties) { - var forwardVec = Quat.getForward(Quat.multiply(gunProperties.rotation, Quat.fromPitchYawRollDegrees(0, 180, 0))); - forwardVec = Vec3.normalize(forwardVec); - forwardVec = Vec3.multiply(forwardVec, GUN_FORCE); + var forwardVector = Quat.getForward(Quat.multiply(gunProperties.rotation, Quat.fromPitchYawRollDegrees(0, 180, 0))); + forwardVector = Vec3.normalize(forwardVector); + forwardVector = Vec3.multiply(forwardVector, GUN_FORCE); var properties = { name: 'Tutorial Ping Pong Ball', @@ -111,7 +111,7 @@ rotation: gunProperties.rotation, position: this.getGunTipPosition(gunProperties), gravity: PING_PONG_GUN_GRAVITY, - velocity: forwardVec, + velocity: forwardVector, lifetime: 10 }; @@ -131,12 +131,12 @@ getGunTipPosition: function(properties) { //the tip of the gun is going to be in a different place than the center, so we move in space relative to the model to find that position - var frontVector = Quat.getForward(properties.rotation); - var frontOffset = Vec3.multiply(frontVector, GUN_TIP_FWD_OFFSET); + var forwardVector = Quat.getForward(properties.rotation); + var forwardOffset = Vec3.multiply(forwardVector, GUN_TIP_FWD_OFFSET); var upVector = Quat.getUp(properties.rotation); var upOffset = Vec3.multiply(upVector, GUN_TIP_UP_OFFSET); - var gunTipPosition = Vec3.sum(properties.position, frontOffset); + var gunTipPosition = Vec3.sum(properties.position, forwardOffset); gunTipPosition = Vec3.sum(gunTipPosition, upOffset); return gunTipPosition; diff --git a/scripts/tutorials/makeBlocks.js b/scripts/tutorials/makeBlocks.js index 9a3e649e7a..11311f6a59 100644 --- a/scripts/tutorials/makeBlocks.js +++ b/scripts/tutorials/makeBlocks.js @@ -34,8 +34,8 @@ var SCRIPT_URL = Script.resolvePath("./entity_scripts/magneticBlock.js"); - var frontVector = Quat.getForward(MyAvatar.orientation); - frontVector.y += VERTICAL_OFFSET; + var forwardVector = Quat.getForward(MyAvatar.orientation); + forwardVector.y += VERTICAL_OFFSET; for (var x = 0; x < COLUMNS; x++) { for (var y = 0; y < ROWS; y++) { @@ -61,7 +61,7 @@ cloneLimit: 9999 } }), - position: Vec3.sum(MyAvatar.position, Vec3.sum(frontOffset, frontVector)), + position: Vec3.sum(MyAvatar.position, Vec3.sum(forwardOffset, forwardVector)), color: newColor(), script: SCRIPT_URL }); From 2fc8dd48de68699301507f098de7d87056e177a6 Mon Sep 17 00:00:00 2001 From: Triplelexx Date: Wed, 22 Mar 2017 17:13:45 +0000 Subject: [PATCH 276/302] change testFront to testForward --- scripts/developer/tests/mat4test.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/developer/tests/mat4test.js b/scripts/developer/tests/mat4test.js index 2d7c994631..4e835ec82f 100644 --- a/scripts/developer/tests/mat4test.js +++ b/scripts/developer/tests/mat4test.js @@ -141,7 +141,7 @@ function testInverse() { assert(mat4FuzzyEqual(IDENTITY, Mat4.multiply(test2, Mat4.inverse(test2)))); } -function testFront() { +function testForward() { var test0 = IDENTITY; assert(mat4FuzzyEqual({x: 0, y: 0, z: -1}, Mat4.getForward(test0))); @@ -157,7 +157,7 @@ function testMat4() { testTransformPoint(); testTransformVector(); testInverse(); - testFront(); + testForward(); print("MAT4 TEST complete! (" + (testCount - failureCount) + "/" + testCount + ") tests passed!"); } From c2cff55427fefb7f61de7e44f18018d8fffced00 Mon Sep 17 00:00:00 2001 From: Triplelexx Date: Wed, 22 Mar 2017 18:41:06 +0000 Subject: [PATCH 277/302] rename fronts to forwards --- libraries/animation/src/Rig.cpp | 6 +++--- libraries/animation/src/Rig.h | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index 88cfacbd66..0520e5c5a1 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -558,7 +558,7 @@ static const std::vector LATERAL_SPEEDS = { 0.2f, 0.65f }; // m/s void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPosition, const glm::vec3& worldVelocity, const glm::quat& worldRotation, CharacterControllerState ccState) { - glm::vec3 front = worldRotation * IDENTITY_FORWARD; + glm::vec3 forward = worldRotation * IDENTITY_FORWARD; glm::vec3 workingVelocity = worldVelocity; { @@ -566,7 +566,7 @@ void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPos float forwardSpeed = glm::dot(localVel, IDENTITY_FORWARD); float lateralSpeed = glm::dot(localVel, IDENTITY_RIGHT); - float turningSpeed = glm::orientedAngle(front, _lastFront, IDENTITY_UP) / deltaTime; + float turningSpeed = glm::orientedAngle(forward, _lastForward, IDENTITY_UP) / deltaTime; // filter speeds using a simple moving average. _averageForwardSpeed.updateAverage(forwardSpeed); @@ -852,7 +852,7 @@ void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPos _lastEnableInverseKinematics = _enableInverseKinematics; } - _lastFront = front; + _lastForward = forward; _lastPosition = worldPosition; _lastVelocity = workingVelocity; } diff --git a/libraries/animation/src/Rig.h b/libraries/animation/src/Rig.h index b2cc877460..41cc5cabc6 100644 --- a/libraries/animation/src/Rig.h +++ b/libraries/animation/src/Rig.h @@ -267,7 +267,7 @@ protected: int _rightElbowJointIndex { -1 }; int _rightShoulderJointIndex { -1 }; - glm::vec3 _lastFront; + glm::vec3 _lastForward; glm::vec3 _lastPosition; glm::vec3 _lastVelocity; From 31ec9b832fbe7f460961b9c36abb55c040fbd973 Mon Sep 17 00:00:00 2001 From: Triplelexx Date: Wed, 22 Mar 2017 18:43:30 +0000 Subject: [PATCH 278/302] rename fronts to forwards --- libraries/avatars/src/HeadData.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/avatars/src/HeadData.cpp b/libraries/avatars/src/HeadData.cpp index d2b9e8cee4..bf8593f1d9 100644 --- a/libraries/avatars/src/HeadData.cpp +++ b/libraries/avatars/src/HeadData.cpp @@ -65,8 +65,8 @@ glm::quat HeadData::getOrientation() const { void HeadData::setOrientation(const glm::quat& orientation) { // rotate body about vertical axis glm::quat bodyOrientation = _owningAvatar->getOrientation(); - glm::vec3 newFront = glm::inverse(bodyOrientation) * (orientation * IDENTITY_FORWARD); - bodyOrientation = bodyOrientation * glm::angleAxis(atan2f(-newFront.x, -newFront.z), glm::vec3(0.0f, 1.0f, 0.0f)); + glm::vec3 newForward = glm::inverse(bodyOrientation) * (orientation * IDENTITY_FORWARD); + bodyOrientation = bodyOrientation * glm::angleAxis(atan2f(-newForward.x, -newForward.z), glm::vec3(0.0f, 1.0f, 0.0f)); _owningAvatar->setOrientation(bodyOrientation); // the rest goes to the head From fd312f6bf83322b74229464a53684277875d8e78 Mon Sep 17 00:00:00 2001 From: Triplelexx Date: Wed, 22 Mar 2017 18:49:34 +0000 Subject: [PATCH 279/302] rename fronts to forwards --- interface/src/avatar/Avatar.h | 4 +++- interface/src/avatar/MyAvatar.cpp | 4 ++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/interface/src/avatar/Avatar.h b/interface/src/avatar/Avatar.h index d8c78d972b..ef45eeb1b5 100644 --- a/interface/src/avatar/Avatar.h +++ b/interface/src/avatar/Avatar.h @@ -236,7 +236,9 @@ protected: glm::vec3 getBodyRightDirection() const { return getOrientation() * IDENTITY_RIGHT; } glm::vec3 getBodyUpDirection() const { return getOrientation() * IDENTITY_UP; } - glm::vec3 getBodyFrontDirection() const { return getOrientation() * IDENTITY_FORWARD; } + // redundant, calls getBodyForwardDirection which better describes the returned vector as a direction + glm::vec3 getBodyFrontDirection() const { return getBodyForwardDirection(); } + glm::vec3 getBodyForwardDirection() const { return getOrientation() * IDENTITY_FORWARD; } glm::quat computeRotationFromBodyToWorldUp(float proportion = 1.0f) const; void measureMotionDerivatives(float deltaTime); diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index bf43a457b7..e0f4b55393 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -1770,10 +1770,10 @@ void MyAvatar::updateActionMotor(float deltaTime) { } // compute action input - glm::vec3 front = (getDriveKey(TRANSLATE_Z)) * IDENTITY_FORWARD; + glm::vec3 forward = (getDriveKey(TRANSLATE_Z)) * IDENTITY_FORWARD; glm::vec3 right = (getDriveKey(TRANSLATE_X)) * IDENTITY_RIGHT; - glm::vec3 direction = front + right; + glm::vec3 direction = forward + right; CharacterController::State state = _characterController.getState(); if (state == CharacterController::State::Hover) { // we're flying --> support vertical motion From d1db04f93a68f9724ee150e78965de14fe735184 Mon Sep 17 00:00:00 2001 From: Triplelexx Date: Wed, 22 Mar 2017 18:57:00 +0000 Subject: [PATCH 280/302] fix lost forwardOffset declaration --- scripts/tutorials/makeBlocks.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/tutorials/makeBlocks.js b/scripts/tutorials/makeBlocks.js index 11311f6a59..432f7444c4 100644 --- a/scripts/tutorials/makeBlocks.js +++ b/scripts/tutorials/makeBlocks.js @@ -39,7 +39,7 @@ for (var x = 0; x < COLUMNS; x++) { for (var y = 0; y < ROWS; y++) { - var frontOffset = { + var forwardOffset = { x: 0, y: SIZE * y + SIZE, z: SIZE * x + SIZE From 216f20ed2c4db85cb982af993e3358a61e40b67e Mon Sep 17 00:00:00 2001 From: Triplelexx Date: Wed, 22 Mar 2017 19:03:39 +0000 Subject: [PATCH 281/302] rename fronts to forwards --- tests/render-perf/src/Camera.hpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/render-perf/src/Camera.hpp b/tests/render-perf/src/Camera.hpp index a3b33ceb14..ada1277c47 100644 --- a/tests/render-perf/src/Camera.hpp +++ b/tests/render-perf/src/Camera.hpp @@ -123,16 +123,16 @@ public: void update(float deltaTime) { if (moving()) { - glm::vec3 camFront = getOrientation() * Vectors::FRONT; + glm::vec3 camForward = getOrientation() * Vectors::FRONT; glm::vec3 camRight = getOrientation() * Vectors::RIGHT; glm::vec3 camUp = getOrientation() * Vectors::UP; float moveSpeed = deltaTime * movementSpeed; if (keys[FORWARD]) { - position += camFront * moveSpeed; + position += camForward * moveSpeed; } if (keys[BACK]) { - position -= camFront * moveSpeed; + position -= camForward * moveSpeed; } if (keys[LEFT]) { position -= camRight * moveSpeed; From 36619cd2e4bc2ceb1d0ec153e3f17cfa95815f01 Mon Sep 17 00:00:00 2001 From: trent Date: Wed, 22 Mar 2017 15:07:29 -0400 Subject: [PATCH 282/302] Fixed issue with duplicate physics status icons per sub-object. --- interface/src/avatar/CauterizedModel.cpp | 6 +- libraries/render-utils/src/Model.cpp | 82 ++++++++++++------------ libraries/render-utils/src/Model.h | 10 +-- 3 files changed, 49 insertions(+), 49 deletions(-) diff --git a/interface/src/avatar/CauterizedModel.cpp b/interface/src/avatar/CauterizedModel.cpp index d8db83fbb7..f479ed9a35 100644 --- a/interface/src/avatar/CauterizedModel.cpp +++ b/interface/src/avatar/CauterizedModel.cpp @@ -56,9 +56,9 @@ void CauterizedModel::createVisibleRenderItemSet() { } // We should not have any existing renderItems if we enter this section of code - Q_ASSERT(_modelMeshRenderItemsSet.isEmpty()); + Q_ASSERT(_modelMeshRenderItems.isEmpty()); - _modelMeshRenderItemsSet.clear(); + _modelMeshRenderItems.clear(); Transform transform; transform.setTranslation(_translation); @@ -81,7 +81,7 @@ void CauterizedModel::createVisibleRenderItemSet() { int numParts = (int)mesh->getNumParts(); for (int partIndex = 0; partIndex < numParts; partIndex++) { auto ptr = std::make_shared(this, i, partIndex, shapeID, transform, offset); - _modelMeshRenderItemsSet << std::static_pointer_cast(ptr); + _modelMeshRenderItems << std::static_pointer_cast(ptr); shapeID++; } } diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index c584b0bc21..3448c9e8da 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -176,11 +176,11 @@ void Model::setOffset(const glm::vec3& offset) { } void Model::calculateTextureInfo() { - if (!_hasCalculatedTextureInfo && isLoaded() && getGeometry()->areTexturesLoaded() && !_modelMeshRenderItems.isEmpty()) { + if (!_hasCalculatedTextureInfo && isLoaded() && getGeometry()->areTexturesLoaded() && !_modelMeshRenderItemsMap.isEmpty()) { size_t textureSize = 0; int textureCount = 0; bool allTexturesLoaded = true; - foreach(auto renderItem, _modelMeshRenderItemsSet) { + foreach(auto renderItem, _modelMeshRenderItems) { auto meshPart = renderItem.get(); textureSize += meshPart->getMaterialTextureSize(); textureCount += meshPart->getMaterialTextureCount(); @@ -236,7 +236,7 @@ void Model::updateRenderItems() { uint32_t deleteGeometryCounter = self->_deleteGeometryCounter; render::PendingChanges pendingChanges; - foreach (auto itemID, self->_modelMeshRenderItems.keys()) { + foreach (auto itemID, self->_modelMeshRenderItemsMap.keys()) { pendingChanges.updateItem(itemID, [deleteGeometryCounter](ModelMeshPartPayload& data) { if (data._model && data._model->isLoaded()) { // Ensure the model geometry was not reset between frames @@ -259,7 +259,7 @@ void Model::updateRenderItems() { Transform collisionMeshOffset; collisionMeshOffset.setIdentity(); Transform modelTransform = self->getTransform(); - foreach (auto itemID, self->_collisionRenderItems.keys()) { + foreach(auto itemID, self->_collisionRenderItemsMap.keys()) { pendingChanges.updateItem(itemID, [modelTransform, collisionMeshOffset](MeshPartPayload& data) { // update the model transform for this render item. data.updateTransform(modelTransform, collisionMeshOffset); @@ -539,11 +539,11 @@ void Model::setVisibleInScene(bool newValue, std::shared_ptr scen _isVisible = newValue; render::PendingChanges pendingChanges; - foreach (auto item, _modelMeshRenderItems.keys()) { - pendingChanges.resetItem(item, _modelMeshRenderItems[item]); + foreach (auto item, _modelMeshRenderItemsMap.keys()) { + pendingChanges.resetItem(item, _modelMeshRenderItemsMap[item]); } - foreach (auto item, _collisionRenderItems.keys()) { - pendingChanges.resetItem(item, _collisionRenderItems[item]); + foreach(auto item, _collisionRenderItemsMap.keys()) { + pendingChanges.resetItem(item, _collisionRenderItemsMap[item]); } scene->enqueuePendingChanges(pendingChanges); } @@ -555,11 +555,11 @@ void Model::setLayeredInFront(bool layered, std::shared_ptr scene _isLayeredInFront = layered; render::PendingChanges pendingChanges; - foreach(auto item, _modelMeshRenderItems.keys()) { - pendingChanges.resetItem(item, _modelMeshRenderItems[item]); + foreach(auto item, _modelMeshRenderItemsMap.keys()) { + pendingChanges.resetItem(item, _modelMeshRenderItemsMap[item]); } - foreach(auto item, _collisionRenderItems.keys()) { - pendingChanges.resetItem(item, _collisionRenderItems[item]); + foreach(auto item, _collisionRenderItemsMap.keys()) { + pendingChanges.resetItem(item, _collisionRenderItemsMap[item]); } scene->enqueuePendingChanges(pendingChanges); } @@ -576,39 +576,39 @@ bool Model::addToScene(std::shared_ptr scene, bool somethingAdded = false; if (_collisionGeometry) { if (_collisionRenderItems.empty()) { - foreach (auto renderItem, _collisionRenderItemsSet) { + foreach (auto renderItem, _collisionRenderItems) { auto item = scene->allocateID(); auto renderPayload = std::make_shared(renderItem); - if (statusGetters.size()) { + if (_collisionRenderItems.empty() && statusGetters.size()) { renderPayload->addStatusGetters(statusGetters); } pendingChanges.resetItem(item, renderPayload); - _collisionRenderItems.insert(item, renderPayload); + _collisionRenderItemsMap.insert(item, renderPayload); } somethingAdded = !_collisionRenderItems.empty(); } } else { - if (_modelMeshRenderItems.empty()) { + if (_modelMeshRenderItemsMap.empty()) { bool hasTransparent = false; size_t verticesCount = 0; - foreach(auto renderItem, _modelMeshRenderItemsSet) { + foreach(auto renderItem, _modelMeshRenderItems) { auto item = scene->allocateID(); auto renderPayload = std::make_shared(renderItem); - if (statusGetters.size()) { + if (_modelMeshRenderItemsMap.empty() && statusGetters.size()) { renderPayload->addStatusGetters(statusGetters); } pendingChanges.resetItem(item, renderPayload); hasTransparent = hasTransparent || renderItem.get()->getShapeKey().isTranslucent(); verticesCount += renderItem.get()->getVerticesCount(); - _modelMeshRenderItems.insert(item, renderPayload); + _modelMeshRenderItemsMap.insert(item, renderPayload); _modelMeshRenderItemIDs.emplace_back(item); } - somethingAdded = !_modelMeshRenderItems.empty(); + somethingAdded = !_modelMeshRenderItemsMap.empty(); _renderInfoVertexCount = verticesCount; - _renderInfoDrawCalls = _modelMeshRenderItems.count(); + _renderInfoDrawCalls = _modelMeshRenderItemsMap.count(); _renderInfoHasTransparent = hasTransparent; } } @@ -623,18 +623,18 @@ bool Model::addToScene(std::shared_ptr scene, } void Model::removeFromScene(std::shared_ptr scene, render::PendingChanges& pendingChanges) { - foreach (auto item, _modelMeshRenderItems.keys()) { + foreach (auto item, _modelMeshRenderItemsMap.keys()) { pendingChanges.removeItem(item); } _modelMeshRenderItemIDs.clear(); + _modelMeshRenderItemsMap.clear(); _modelMeshRenderItems.clear(); - _modelMeshRenderItemsSet.clear(); - foreach (auto item, _collisionRenderItems.keys()) { + foreach(auto item, _collisionRenderItemsMap.keys()) { pendingChanges.removeItem(item); } _collisionRenderItems.clear(); - _collisionRenderItemsSet.clear(); + _collisionRenderItems.clear(); _addedToScene = false; _renderInfoVertexCount = 0; @@ -1052,8 +1052,8 @@ void Model::updateRig(float deltaTime, glm::mat4 parentTransform) { } void Model::computeMeshPartLocalBounds() { - for (auto& part : _modelMeshRenderItemsSet) { - assert(part->_meshIndex < _modelMeshRenderItemsSet.size()); + for (auto& part : _modelMeshRenderItems) { + assert(part->_meshIndex < _modelMeshRenderItems.size()); const Model::MeshState& state = _meshStates.at(part->_meshIndex); part->computeAdjustedLocalBound(state.clusterMatrices); } @@ -1167,7 +1167,7 @@ AABox Model::getRenderableMeshBound() const { } else { // Build a bound using the last known bound from all the renderItems. AABox totalBound; - for (auto& renderItem : _modelMeshRenderItemsSet) { + for (auto& renderItem : _modelMeshRenderItems) { totalBound += renderItem->getBound(); } return totalBound; @@ -1180,11 +1180,11 @@ const render::ItemIDs& Model::fetchRenderItemIDs() const { void Model::createRenderItemSet() { if (_collisionGeometry) { - if (_collisionRenderItemsSet.empty()) { + if (_collisionRenderItems.empty()) { createCollisionRenderItemSet(); } } else { - if (_modelMeshRenderItemsSet.empty()) { + if (_modelMeshRenderItems.empty()) { createVisibleRenderItemSet(); } } @@ -1201,9 +1201,9 @@ void Model::createVisibleRenderItemSet() { } // We should not have any existing renderItems if we enter this section of code - Q_ASSERT(_modelMeshRenderItemsSet.isEmpty()); + Q_ASSERT(_modelMeshRenderItems.isEmpty()); - _modelMeshRenderItemsSet.clear(); + _modelMeshRenderItems.clear(); Transform transform; transform.setTranslation(_translation); @@ -1225,7 +1225,7 @@ void Model::createVisibleRenderItemSet() { // Create the render payloads int numParts = (int)mesh->getNumParts(); for (int partIndex = 0; partIndex < numParts; partIndex++) { - _modelMeshRenderItemsSet << std::make_shared(this, i, partIndex, shapeID, transform, offset); + _modelMeshRenderItems << std::make_shared(this, i, partIndex, shapeID, transform, offset); shapeID++; } } @@ -1241,7 +1241,7 @@ void Model::createCollisionRenderItemSet() { const auto& meshes = _collisionGeometry->getMeshes(); // We should not have any existing renderItems if we enter this section of code - Q_ASSERT(_collisionRenderItemsSet.isEmpty()); + Q_ASSERT(_collisionRenderItems.isEmpty()); Transform identity; identity.setIdentity(); @@ -1262,7 +1262,7 @@ void Model::createCollisionRenderItemSet() { model::MaterialPointer& material = _collisionMaterials[partIndex % NUM_COLLISION_HULL_COLORS]; auto payload = std::make_shared(mesh, partIndex, material); payload->updateTransform(identity, offset); - _collisionRenderItemsSet << payload; + _collisionRenderItems << payload; } } } @@ -1283,28 +1283,28 @@ bool Model::initWhenReady(render::ScenePointer scene) { bool addedPendingChanges = false; if (_collisionGeometry) { - foreach (auto renderItem, _collisionRenderItemsSet) { + foreach (auto renderItem, _collisionRenderItems) { auto item = scene->allocateID(); auto renderPayload = std::make_shared(renderItem); - _collisionRenderItems.insert(item, renderPayload); + _collisionRenderItemsMap.insert(item, renderPayload); pendingChanges.resetItem(item, renderPayload); } addedPendingChanges = !_collisionRenderItems.empty(); } else { bool hasTransparent = false; size_t verticesCount = 0; - foreach (auto renderItem, _modelMeshRenderItemsSet) { + foreach (auto renderItem, _modelMeshRenderItems) { auto item = scene->allocateID(); auto renderPayload = std::make_shared(renderItem); hasTransparent = hasTransparent || renderItem.get()->getShapeKey().isTranslucent(); verticesCount += renderItem.get()->getVerticesCount(); - _modelMeshRenderItems.insert(item, renderPayload); + _modelMeshRenderItemsMap.insert(item, renderPayload); pendingChanges.resetItem(item, renderPayload); } - addedPendingChanges = !_modelMeshRenderItems.empty(); + addedPendingChanges = !_modelMeshRenderItemsMap.empty(); _renderInfoVertexCount = verticesCount; - _renderInfoDrawCalls = _modelMeshRenderItems.count(); + _renderInfoDrawCalls = _modelMeshRenderItemsMap.count(); _renderInfoHasTransparent = hasTransparent; } _addedToScene = addedPendingChanges; diff --git a/libraries/render-utils/src/Model.h b/libraries/render-utils/src/Model.h index 41821736f7..bb283cce1f 100644 --- a/libraries/render-utils/src/Model.h +++ b/libraries/render-utils/src/Model.h @@ -248,7 +248,7 @@ public: const MeshState& getMeshState(int index) { return _meshStates.at(index); } uint32_t getGeometryCounter() const { return _deleteGeometryCounter; } - const QMap& getRenderItems() const { return _modelMeshRenderItems; } + const QMap& getRenderItems() const { return _modelMeshRenderItemsMap; } void renderDebugMeshBoxes(gpu::Batch& batch); @@ -373,11 +373,11 @@ protected: static AbstractViewStateInterface* _viewState; - QSet> _collisionRenderItemsSet; - QMap _collisionRenderItems; + QVector> _collisionRenderItems; + QMap _collisionRenderItemsMap; - QSet> _modelMeshRenderItemsSet; - QMap _modelMeshRenderItems; + QVector> _modelMeshRenderItems; + QMap _modelMeshRenderItemsMap; render::ItemIDs _modelMeshRenderItemIDs; From c7b949262019704eda4763b70adff390d8712f2e Mon Sep 17 00:00:00 2001 From: Triplelexx Date: Wed, 22 Mar 2017 19:14:20 +0000 Subject: [PATCH 283/302] revert removing rightUnitVec ..... --- scripts/developer/utilities/render/photobooth/photobooth.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/scripts/developer/utilities/render/photobooth/photobooth.js b/scripts/developer/utilities/render/photobooth/photobooth.js index e822955563..b78986be1a 100644 --- a/scripts/developer/utilities/render/photobooth/photobooth.js +++ b/scripts/developer/utilities/render/photobooth/photobooth.js @@ -12,6 +12,8 @@ var forwardUnitVector = Vec3.normalize(Quat.getForward(MyAvatar.orientation)); var forwardOffset = Vec3.multiply(forwardUnitVector,forwardFactor); var rightFactor = 3; + // TODO: rightUnitVec is unused and spawnLocation declaration is incorrect + var rightUnitVec = Vec3.normalize(Quat.getRight(MyAvatar.orientation)); var spawnLocation = Vec3.sum(Vec3.sum(MyAvatar.position,forwardOffset),rightFactor); if (success) { this.pastedEntityIDs = Clipboard.pasteEntities(spawnLocation); From c71e57753be6ff9daf31e8404b84f53b7978206c Mon Sep 17 00:00:00 2001 From: trent Date: Wed, 22 Mar 2017 15:23:40 -0400 Subject: [PATCH 284/302] Removing changes (albeit benign ones) from another PR. --- libraries/fbx/src/OBJReader.cpp | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/libraries/fbx/src/OBJReader.cpp b/libraries/fbx/src/OBJReader.cpp index c1bb72dff8..0cb932b375 100644 --- a/libraries/fbx/src/OBJReader.cpp +++ b/libraries/fbx/src/OBJReader.cpp @@ -267,7 +267,7 @@ void OBJReader::parseMaterialLibrary(QIODevice* device) { } if (token == "map_Kd") { currentMaterial.diffuseTextureFilename = filename; - } else if( token == "map_Ks" ) { + } else { currentMaterial.specularTextureFilename = filename; } } @@ -612,9 +612,6 @@ FBXGeometry* OBJReader::readOBJ(QByteArray& model, const QVariantHash& mapping, if (!objMaterial.diffuseTextureFilename.isEmpty()) { fbxMaterial.albedoTexture.filename = objMaterial.diffuseTextureFilename; } - if (!objMaterial.specularTextureFilename.isEmpty()) { - fbxMaterial.specularTexture.filename = objMaterial.specularTextureFilename; - } modelMaterial->setEmissive(fbxMaterial.emissiveColor); modelMaterial->setAlbedo(fbxMaterial.diffuseColor); From 09cbace975b5eb4e605758b69fca4518af11d3ab Mon Sep 17 00:00:00 2001 From: Triplelexx Date: Wed, 22 Mar 2017 21:54:21 +0000 Subject: [PATCH 285/302] remove unused getBodyForwardDirection --- interface/src/avatar/Avatar.h | 3 --- 1 file changed, 3 deletions(-) diff --git a/interface/src/avatar/Avatar.h b/interface/src/avatar/Avatar.h index ef45eeb1b5..d4bd03367e 100644 --- a/interface/src/avatar/Avatar.h +++ b/interface/src/avatar/Avatar.h @@ -236,9 +236,6 @@ protected: glm::vec3 getBodyRightDirection() const { return getOrientation() * IDENTITY_RIGHT; } glm::vec3 getBodyUpDirection() const { return getOrientation() * IDENTITY_UP; } - // redundant, calls getBodyForwardDirection which better describes the returned vector as a direction - glm::vec3 getBodyFrontDirection() const { return getBodyForwardDirection(); } - glm::vec3 getBodyForwardDirection() const { return getOrientation() * IDENTITY_FORWARD; } glm::quat computeRotationFromBodyToWorldUp(float proportion = 1.0f) const; void measureMotionDerivatives(float deltaTime); From 9a3686fc1a3a53d2c0255e1dd2b57df2f5fa2db5 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Wed, 22 Mar 2017 14:55:11 -0700 Subject: [PATCH 286/302] Fixing merge conflict --- libraries/gpu/src/gpu/Texture.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/gpu/src/gpu/Texture.cpp b/libraries/gpu/src/gpu/Texture.cpp index 872a78fc9c..1f66b2900e 100755 --- a/libraries/gpu/src/gpu/Texture.cpp +++ b/libraries/gpu/src/gpu/Texture.cpp @@ -762,7 +762,7 @@ bool sphericalHarmonicsFromTexture(const gpu::Texture& cubeTexture, std::vector< for(int face=0; face < gpu::Texture::NUM_CUBE_FACES; face++) { PROFILE_RANGE(render_gpu, "ProcessFace"); - auto mipFormat = cubeTexture.accessStoredMipFace(0, face)->getFormat(); + auto mipFormat = cubeTexture.getStoredMipFormat(); auto numComponents = mipFormat.getScalarCount(); int roffset { 0 }; int goffset { 1 }; From 6a17e328e8634c64db8e1d103478bb32881c1afe Mon Sep 17 00:00:00 2001 From: Triplelexx Date: Wed, 22 Mar 2017 22:13:39 +0000 Subject: [PATCH 287/302] fix myVisibility typo --- scripts/system/tablet-users.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/system/tablet-users.js b/scripts/system/tablet-users.js index 88ffa33a88..d5c381917e 100644 --- a/scripts/system/tablet-users.js +++ b/scripts/system/tablet-users.js @@ -30,7 +30,7 @@ } else { // default to friends if it can't be determined myVisibility = "friends"; - GlobalServices.findableBy = myVisibilty; + GlobalServices.findableBy = myVisibility; } var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system"); From 893fcba283093f8a5b1af7db5dd18e3af2dfed3b Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Wed, 22 Mar 2017 17:14:40 -0700 Subject: [PATCH 288/302] Return the correct format and internal format combo for depth textures --- libraries/gpu-gl/src/gpu/gl/GLTexelFormat.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/libraries/gpu-gl/src/gpu/gl/GLTexelFormat.cpp b/libraries/gpu-gl/src/gpu/gl/GLTexelFormat.cpp index fa639aab11..7e26e65e02 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLTexelFormat.cpp +++ b/libraries/gpu-gl/src/gpu/gl/GLTexelFormat.cpp @@ -274,8 +274,10 @@ GLTexelFormat GLTexelFormat::evalGLTexelFormat(const Element& dstFormat, const E break; case gpu::DEPTH: + texel.format = GL_DEPTH_COMPONENT; texel.internalFormat = GL_DEPTH_COMPONENT32; break; + case gpu::DEPTH_STENCIL: texel.type = GL_UNSIGNED_INT_24_8; texel.format = GL_DEPTH_STENCIL; From 4868a1618a751c66da58ed2c376a276a8ff3ecc6 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Wed, 22 Mar 2017 18:10:54 -0700 Subject: [PATCH 289/302] Add initialization to sampler stamp --- libraries/gpu/src/gpu/Texture.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/libraries/gpu/src/gpu/Texture.h b/libraries/gpu/src/gpu/Texture.h index 0212e1845f..f7297b3280 100755 --- a/libraries/gpu/src/gpu/Texture.h +++ b/libraries/gpu/src/gpu/Texture.h @@ -536,12 +536,12 @@ protected: std::string _source; std::unique_ptr< Storage > _storage; - Stamp _stamp = 0; + Stamp _stamp { 0 }; Sampler _sampler; - Stamp _samplerStamp; + Stamp _samplerStamp { 0 }; - uint32 _size = 0; + uint32 _size { 0 }; Element _texelFormat; uint16 _width { 1 }; @@ -554,7 +554,7 @@ protected: uint16 _maxMip { 0 }; uint16 _minMip { 0 }; - Type _type = TEX_1D; + Type _type { TEX_1D }; Usage _usage; From 3a40fd886f8062a39258e199dd565a7538747f15 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Wed, 22 Mar 2017 18:11:20 -0700 Subject: [PATCH 290/302] Fix comparison between GL stamps and GPU stamps --- libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp b/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp index 84806d82c3..8dbef09f06 100644 --- a/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp @@ -53,12 +53,12 @@ GLTexture* GL41Backend::syncGPUObject(const TexturePointer& texturePointer) { // FIXME internalize to GL41Texture 'sync' function if (object->isOutdated()) { object->withPreservedTexture([&] { - if (object->_contentStamp < texture.getDataStamp()) { + if (object->_contentStamp <= texture.getDataStamp()) { // FIXME implement synchronous texture transfer here object->syncContent(); } - if (object->_samplerStamp < texture.getSamplerStamp()) { + if (object->_samplerStamp <= texture.getSamplerStamp()) { object->syncSampler(); } }); From 944255a37b4bf8e3c8f16500231f876279e64b16 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Wed, 22 Mar 2017 22:44:05 -0700 Subject: [PATCH 291/302] Fix crash in render-perf-test --- libraries/entities-renderer/src/EntityTreeRenderer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.cpp b/libraries/entities-renderer/src/EntityTreeRenderer.cpp index 8c4498edc6..fb6054a514 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.cpp +++ b/libraries/entities-renderer/src/EntityTreeRenderer.cpp @@ -996,7 +996,7 @@ void EntityTreeRenderer::checkAndCallPreload(const EntityItemID& entityID, const } bool shouldLoad = entity->shouldPreloadScript() && _entitiesScriptEngine; QString scriptUrl = entity->getScript(); - if ((unloadFirst && shouldLoad) || scriptUrl.isEmpty()) { + if (shouldLoad && (unloadFirst || scriptUrl.isEmpty())) { _entitiesScriptEngine->unloadEntityScript(entityID); entity->scriptHasUnloaded(); } From b53cb5934bf6ded9999cfec9b2eef028fa7343bc Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Wed, 22 Mar 2017 23:35:29 -0700 Subject: [PATCH 292/302] make sure the AC agent has an AudioScriptingInterface --- assignment-client/src/Agent.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/assignment-client/src/Agent.cpp b/assignment-client/src/Agent.cpp index a0c80453e0..a5063b09b6 100644 --- a/assignment-client/src/Agent.cpp +++ b/assignment-client/src/Agent.cpp @@ -62,6 +62,7 @@ Agent::Agent(ReceivedMessage& message) : DependencyManager::set(); DependencyManager::set(); + DependencyManager::set(); DependencyManager::set(); DependencyManager::set(); DependencyManager::set(); From d4353e1d1985294d4c52f7c4444455b301987298 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Thu, 23 Mar 2017 11:39:42 -0700 Subject: [PATCH 293/302] Fix warnings --- libraries/avatars/src/AvatarData.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index 3c33451f02..dc806a4115 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -47,9 +47,6 @@ quint64 DEFAULT_FILTERED_LOG_EXPIRY = 2 * USECS_PER_SECOND; using namespace std; -const glm::vec3 DEFAULT_LOCAL_AABOX_CORNER(-0.5f); -const glm::vec3 DEFAULT_LOCAL_AABOX_SCALE(1.0f); - const QString AvatarData::FRAME_NAME = "com.highfidelity.recording.AvatarData"; static const int TRANSLATION_COMPRESSION_RADIX = 12; From 8116de7c145bd191088ca162fb85fcedf5b1898d Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Thu, 23 Mar 2017 13:45:13 -0700 Subject: [PATCH 294/302] Force thread linkage --- CMakeLists.txt | 2 ++ cmake/macros/SetupHifiPlugin.cmake | 1 + cmake/macros/SetupHifiProject.cmake | 1 + cmake/macros/SetupHifiTestCase.cmake | 1 + 4 files changed, 5 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 0703866ac6..aa2c353453 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -30,6 +30,8 @@ project(hifi) add_definitions(-DGLM_FORCE_RADIANS) set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -DDEBUG") +find_package( Threads ) + if (WIN32) add_definitions(-DNOMINMAX -D_CRT_SECURE_NO_WARNINGS) diff --git a/cmake/macros/SetupHifiPlugin.cmake b/cmake/macros/SetupHifiPlugin.cmake index 0db91cb9e6..7e56ea3db2 100644 --- a/cmake/macros/SetupHifiPlugin.cmake +++ b/cmake/macros/SetupHifiPlugin.cmake @@ -9,6 +9,7 @@ macro(SETUP_HIFI_PLUGIN) set(${TARGET_NAME}_SHARED 1) setup_hifi_library(${ARGV}) add_dependencies(interface ${TARGET_NAME}) + target_link_libraries(${TARGET_NAME} ${CMAKE_THREAD_LIBS_INIT}) set_target_properties(${TARGET_NAME} PROPERTIES FOLDER "Plugins") if (APPLE) diff --git a/cmake/macros/SetupHifiProject.cmake b/cmake/macros/SetupHifiProject.cmake index 8695063556..8759c949f3 100644 --- a/cmake/macros/SetupHifiProject.cmake +++ b/cmake/macros/SetupHifiProject.cmake @@ -43,6 +43,7 @@ macro(SETUP_HIFI_PROJECT) foreach(QT_MODULE ${${TARGET_NAME}_DEPENDENCY_QT_MODULES}) target_link_libraries(${TARGET_NAME} Qt5::${QT_MODULE}) endforeach() + target_link_libraries(${TARGET_NAME} ${CMAKE_THREAD_LIBS_INIT}) target_glm() diff --git a/cmake/macros/SetupHifiTestCase.cmake b/cmake/macros/SetupHifiTestCase.cmake index 38239d6e97..6c7d38e19c 100644 --- a/cmake/macros/SetupHifiTestCase.cmake +++ b/cmake/macros/SetupHifiTestCase.cmake @@ -108,6 +108,7 @@ macro(SETUP_HIFI_TESTCASE) foreach(QT_MODULE ${${TARGET_NAME}_DEPENDENCY_QT_MODULES}) target_link_libraries(${TARGET_NAME} Qt5::${QT_MODULE}) endforeach() + target_link_libraries(${TARGET_NAME} ${CMAKE_THREAD_LIBS_INIT}) set_target_properties(${TARGET_NAME} PROPERTIES FOLDER "hidden/test-executables") From 5a5eb6e168397169facc6e71909b9ff350f007c1 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Thu, 23 Mar 2017 14:09:34 -0700 Subject: [PATCH 295/302] Still trying to fix build error --- tests/render-utils/CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/render-utils/CMakeLists.txt b/tests/render-utils/CMakeLists.txt index e7e80f7726..5ec6a28b5c 100644 --- a/tests/render-utils/CMakeLists.txt +++ b/tests/render-utils/CMakeLists.txt @@ -7,5 +7,6 @@ set_target_properties(${TARGET_NAME} PROPERTIES FOLDER "Tests/manual-tests/") # link in the shared libraries link_hifi_libraries(render-utils gl gpu gpu-gl shared) +target_link_libraries(${TARGET_NAME} ${CMAKE_THREAD_LIBS_INIT}) package_libraries_for_deployment() From 56c5f06365d1ad043293af1d06cd93d3a85e2805 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Thu, 23 Mar 2017 16:29:08 -0700 Subject: [PATCH 296/302] Fix Oculus Touch + Remote not being detected after initial launch --- .../oculus/src/OculusControllerManager.cpp | 23 ++++++++++++++----- plugins/oculus/src/OculusControllerManager.h | 2 ++ 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/plugins/oculus/src/OculusControllerManager.cpp b/plugins/oculus/src/OculusControllerManager.cpp index ce59eafd50..87c52de3e7 100644 --- a/plugins/oculus/src/OculusControllerManager.cpp +++ b/plugins/oculus/src/OculusControllerManager.cpp @@ -20,6 +20,8 @@ #include #include +#include + #include "OculusHelpers.h" Q_DECLARE_LOGGING_CATEGORY(oculus) @@ -42,26 +44,33 @@ bool OculusControllerManager::activate() { } Q_ASSERT(_session); - // register with UserInputMapper - auto userInputMapper = DependencyManager::get(); + checkForConnectedDevices(); + + return true; +} + +void OculusControllerManager::checkForConnectedDevices() { + if (_touch && _remote) { + return; + } unsigned int controllerConnected = ovr_GetConnectedControllerTypes(_session); - if ((controllerConnected & ovrControllerType_Remote) == ovrControllerType_Remote) { + if (!_remote && (controllerConnected & ovrControllerType_Remote) == ovrControllerType_Remote) { if (OVR_SUCCESS(ovr_GetInputState(_session, ovrControllerType_Remote, &_inputState))) { + auto userInputMapper = DependencyManager::get(); _remote = std::make_shared(*this); userInputMapper->registerDevice(_remote); } } - if ((controllerConnected & ovrControllerType_Touch) != 0) { + if (!_touch && (controllerConnected & ovrControllerType_Touch) != 0) { if (OVR_SUCCESS(ovr_GetInputState(_session, ovrControllerType_Touch, &_inputState))) { + auto userInputMapper = DependencyManager::get(); _touch = std::make_shared(*this); userInputMapper->registerDevice(_touch); } } - - return true; } void OculusControllerManager::deactivate() { @@ -85,6 +94,8 @@ void OculusControllerManager::deactivate() { void OculusControllerManager::pluginUpdate(float deltaTime, const controller::InputCalibrationData& inputCalibrationData) { PerformanceTimer perfTimer("OculusControllerManager::TouchDevice::update"); + checkForConnectedDevices(); + if (_touch) { if (OVR_SUCCESS(ovr_GetInputState(_session, ovrControllerType_Touch, &_inputState))) { _touch->update(deltaTime, inputCalibrationData); diff --git a/plugins/oculus/src/OculusControllerManager.h b/plugins/oculus/src/OculusControllerManager.h index 98e0e3d650..23ef52ab64 100644 --- a/plugins/oculus/src/OculusControllerManager.h +++ b/plugins/oculus/src/OculusControllerManager.h @@ -91,6 +91,8 @@ private: friend class OculusControllerManager; }; + void checkForConnectedDevices(); + ovrSession _session { nullptr }; ovrInputState _inputState {}; RemoteDevice::Pointer _remote; From 4f8d9b190b4a8f2d04409c4b953e46cf5929dda0 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Thu, 23 Mar 2017 17:49:55 -0700 Subject: [PATCH 297/302] Show green box when model entity has bad url --- .../src/RenderableModelEntityItem.cpp | 211 +++++++++--------- .../src/RenderableModelEntityItem.h | 2 +- 2 files changed, 109 insertions(+), 104 deletions(-) diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp index 935dd4e796..613bd5d543 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp @@ -371,109 +371,7 @@ void RenderableModelEntityItem::render(RenderArgs* args) { _model->updateRenderItems(); } - if (hasModel()) { - // Prepare the current frame - { - if (!_model || _needsModelReload) { - // TODO: this getModel() appears to be about 3% of model render time. We should optimize - PerformanceTimer perfTimer("getModel"); - auto renderer = qSharedPointerCast(args->_renderer); - getModel(renderer); - - // Remap textures immediately after loading to avoid flicker - remapTextures(); - } - - if (_model) { - if (hasRenderAnimation()) { - if (!jointsMapped()) { - QStringList modelJointNames = _model->getJointNames(); - mapJoints(modelJointNames); - } - } - - _jointDataLock.withWriteLock([&] { - getAnimationFrame(); - - // relay any inbound joint changes from scripts/animation/network to the model/rig - for (int index = 0; index < _localJointRotations.size(); index++) { - if (_localJointRotationsDirty[index]) { - glm::quat rotation = _localJointRotations[index]; - _model->setJointRotation(index, true, rotation, 1.0f); - _localJointRotationsDirty[index] = false; - } - } - for (int index = 0; index < _localJointTranslations.size(); index++) { - if (_localJointTranslationsDirty[index]) { - glm::vec3 translation = _localJointTranslations[index]; - _model->setJointTranslation(index, true, translation, 1.0f); - _localJointTranslationsDirty[index] = false; - } - } - }); - updateModelBounds(); - } - } - - // Enqueue updates for the next frame - if (_model) { - -#ifdef WANT_EXTRA_RENDER_DEBUGGING - // debugging... - gpu::Batch& batch = *args->_batch; - _model->renderDebugMeshBoxes(batch); -#endif - - render::ScenePointer scene = AbstractViewStateInterface::instance()->getMain3DScene(); - - // FIXME: this seems like it could be optimized if we tracked our last known visible state in - // the renderable item. As it stands now the model checks it's visible/invisible state - // so most of the time we don't do anything in this function. - _model->setVisibleInScene(getVisible(), scene); - - // Remap textures for the next frame to avoid flicker - remapTextures(); - - // update whether the model should be showing collision mesh (this may flag for fixupInScene) - bool showingCollisionGeometry = (bool)(args->_debugFlags & (int)RenderArgs::RENDER_DEBUG_HULLS); - if (showingCollisionGeometry != _showCollisionGeometry) { - ShapeType type = getShapeType(); - _showCollisionGeometry = showingCollisionGeometry; - if (_showCollisionGeometry && type != SHAPE_TYPE_STATIC_MESH && type != SHAPE_TYPE_NONE) { - // NOTE: it is OK if _collisionMeshKey is nullptr - model::MeshPointer mesh = collisionMeshCache.getMesh(_collisionMeshKey); - // NOTE: the model will render the collisionGeometry if it has one - _model->setCollisionMesh(mesh); - } else { - // release mesh - if (_collisionMeshKey) { - collisionMeshCache.releaseMesh(_collisionMeshKey); - } - // clear model's collision geometry - model::MeshPointer mesh = nullptr; - _model->setCollisionMesh(mesh); - } - } - - if (_model->needsFixupInScene()) { - render::PendingChanges pendingChanges; - - _model->removeFromScene(scene, pendingChanges); - - render::Item::Status::Getters statusGetters; - makeEntityItemStatusGetters(getThisPointer(), statusGetters); - _model->addToScene(scene, pendingChanges, statusGetters); - - scene->enqueuePendingChanges(pendingChanges); - } - - auto& currentURL = getParsedModelURL(); - if (currentURL != _model->getURL()) { - // Defer setting the url to the render thread - getModel(_myRenderer); - } - } - } else { + if (!hasModel() || (_model && _model->didVisualGeometryRequestFail())) { static glm::vec4 greenColor(0.0f, 1.0f, 0.0f, 1.0f); gpu::Batch& batch = *args->_batch; bool success; @@ -482,6 +380,109 @@ void RenderableModelEntityItem::render(RenderArgs* args) { batch.setModelTransform(shapeTransform); // we want to include the scale as well DependencyManager::get()->renderWireCubeInstance(batch, greenColor); } + return; + } + + // Prepare the current frame + { + if (!_model || _needsModelReload) { + // TODO: this getModel() appears to be about 3% of model render time. We should optimize + PerformanceTimer perfTimer("getModel"); + auto renderer = qSharedPointerCast(args->_renderer); + getModel(renderer); + + // Remap textures immediately after loading to avoid flicker + remapTextures(); + } + + if (_model) { + if (hasRenderAnimation()) { + if (!jointsMapped()) { + QStringList modelJointNames = _model->getJointNames(); + mapJoints(modelJointNames); + } + } + + _jointDataLock.withWriteLock([&] { + getAnimationFrame(); + + // relay any inbound joint changes from scripts/animation/network to the model/rig + for (int index = 0; index < _localJointRotations.size(); index++) { + if (_localJointRotationsDirty[index]) { + glm::quat rotation = _localJointRotations[index]; + _model->setJointRotation(index, true, rotation, 1.0f); + _localJointRotationsDirty[index] = false; + } + } + for (int index = 0; index < _localJointTranslations.size(); index++) { + if (_localJointTranslationsDirty[index]) { + glm::vec3 translation = _localJointTranslations[index]; + _model->setJointTranslation(index, true, translation, 1.0f); + _localJointTranslationsDirty[index] = false; + } + } + }); + updateModelBounds(); + } + } + + // Enqueue updates for the next frame + if (_model) { + +#ifdef WANT_EXTRA_RENDER_DEBUGGING + // debugging... + gpu::Batch& batch = *args->_batch; + _model->renderDebugMeshBoxes(batch); +#endif + + render::ScenePointer scene = AbstractViewStateInterface::instance()->getMain3DScene(); + + // FIXME: this seems like it could be optimized if we tracked our last known visible state in + // the renderable item. As it stands now the model checks it's visible/invisible state + // so most of the time we don't do anything in this function. + _model->setVisibleInScene(getVisible(), scene); + + // Remap textures for the next frame to avoid flicker + remapTextures(); + + // update whether the model should be showing collision mesh (this may flag for fixupInScene) + bool showingCollisionGeometry = (bool)(args->_debugFlags & (int)RenderArgs::RENDER_DEBUG_HULLS); + if (showingCollisionGeometry != _showCollisionGeometry) { + ShapeType type = getShapeType(); + _showCollisionGeometry = showingCollisionGeometry; + if (_showCollisionGeometry && type != SHAPE_TYPE_STATIC_MESH && type != SHAPE_TYPE_NONE) { + // NOTE: it is OK if _collisionMeshKey is nullptr + model::MeshPointer mesh = collisionMeshCache.getMesh(_collisionMeshKey); + // NOTE: the model will render the collisionGeometry if it has one + _model->setCollisionMesh(mesh); + } else { + // release mesh + if (_collisionMeshKey) { + collisionMeshCache.releaseMesh(_collisionMeshKey); + } + // clear model's collision geometry + model::MeshPointer mesh = nullptr; + _model->setCollisionMesh(mesh); + } + } + + if (_model->needsFixupInScene()) { + render::PendingChanges pendingChanges; + + _model->removeFromScene(scene, pendingChanges); + + render::Item::Status::Getters statusGetters; + makeEntityItemStatusGetters(getThisPointer(), statusGetters); + _model->addToScene(scene, pendingChanges, statusGetters); + + scene->enqueuePendingChanges(pendingChanges); + } + + auto& currentURL = getParsedModelURL(); + if (currentURL != _model->getURL()) { + // Defer setting the url to the render thread + getModel(_myRenderer); + } } } @@ -587,6 +588,10 @@ EntityItemProperties RenderableModelEntityItem::getProperties(EntityPropertyFlag return properties; } +bool RenderableModelEntityItem::supportsDetailedRayIntersection() const { + return _model && _model->isLoaded(); +} + bool RenderableModelEntityItem::findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction, bool& keepSearching, OctreeElementPointer& element, float& distance, BoxFace& face, glm::vec3& surfaceNormal, void** intersectedObject, bool precisionPicking) const { diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.h b/libraries/entities-renderer/src/RenderableModelEntityItem.h index bac2118326..057ca36e13 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.h +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.h @@ -46,7 +46,7 @@ public: void updateModelBounds(); virtual void render(RenderArgs* args) override; - virtual bool supportsDetailedRayIntersection() const override { return true; } + virtual bool supportsDetailedRayIntersection() const override; virtual bool findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction, bool& keepSearching, OctreeElementPointer& element, float& distance, BoxFace& face, glm::vec3& surfaceNormal, From e1e51614d9549f000b9db43b78445118c4d8d527 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Fri, 24 Mar 2017 11:16:54 -0700 Subject: [PATCH 298/302] don't hang loop if entity isDead --- libraries/physics/src/PhysicalEntitySimulation.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/libraries/physics/src/PhysicalEntitySimulation.cpp b/libraries/physics/src/PhysicalEntitySimulation.cpp index bd76b2d70f..6f5b474810 100644 --- a/libraries/physics/src/PhysicalEntitySimulation.cpp +++ b/libraries/physics/src/PhysicalEntitySimulation.cpp @@ -208,6 +208,7 @@ void PhysicalEntitySimulation::getObjectsToAddToPhysics(VectorOfMotionStates& re assert(!entity->getPhysicsInfo()); if (entity->isDead()) { prepareEntityForDelete(entity); + entityItr = _entitiesToAddToPhysics.erase(entityItr); } else if (!entity->shouldBePhysical()) { // this entity should no longer be on the internal _entitiesToAddToPhysics entityItr = _entitiesToAddToPhysics.erase(entityItr); From aaa78d108b380726fe374a782d30f7ffdb38e284 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Fri, 24 Mar 2017 11:17:24 -0700 Subject: [PATCH 299/302] if a model has too many meshes, give up. --- .../entities-renderer/src/RenderableModelEntityItem.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp index 935dd4e796..f865e39b21 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp @@ -807,6 +807,13 @@ void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& shapeInfo) { auto& meshes = _model->getGeometry()->getMeshes(); int32_t numMeshes = (int32_t)(meshes.size()); + const int MAX_ALLOWED_MESH_COUNT = 500; + if (numMeshes > MAX_ALLOWED_MESH_COUNT) { + // too many will cause the deadlock timer to throw... + shapeInfo.setParams(SHAPE_TYPE_BOX, 0.5f * dimensions); + return; + } + ShapeInfo::PointCollection& pointCollection = shapeInfo.getPointCollection(); pointCollection.clear(); if (type == SHAPE_TYPE_SIMPLE_COMPOUND) { From fa606446a794bdfec7b7ce430df8a62f56ad681b Mon Sep 17 00:00:00 2001 From: Lexx Date: Fri, 24 Mar 2017 18:27:38 +0000 Subject: [PATCH 300/302] remove cache buster --- scripts/system/audioMuteOverlay.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/system/audioMuteOverlay.js b/scripts/system/audioMuteOverlay.js index 473e06b556..cf07402d64 100644 --- a/scripts/system/audioMuteOverlay.js +++ b/scripts/system/audioMuteOverlay.js @@ -15,7 +15,7 @@ // (function() { // BEGIN LOCAL_SCOPE - var utilsPath = Script.resolvePath('../developer/libraries/utils.js?v='+ Date.now()); + var utilsPath = Script.resolvePath('../developer/libraries/utils.js'); Script.include(utilsPath); var TWEEN_SPEED = 0.025; From 82d502ae9f494f07c573d4798ac6a2fb50b82638 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Fri, 24 Mar 2017 13:35:37 -0700 Subject: [PATCH 301/302] Correctly initialize a file cache with the proper key --- libraries/networking/src/FileCache.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/networking/src/FileCache.cpp b/libraries/networking/src/FileCache.cpp index f8a86903cb..0a859d511b 100644 --- a/libraries/networking/src/FileCache.cpp +++ b/libraries/networking/src/FileCache.cpp @@ -67,9 +67,9 @@ void FileCache::initialize() { // load persisted files foreach(QString filename, files) { - const Key key = filename.section('.', 0, 1).toStdString(); + const Key key = filename.section('.', 0, 0).toStdString(); const std::string filepath = dir.filePath(filename).toStdString(); - const size_t length = std::ifstream(filepath, std::ios::binary | std::ios::ate).tellg(); + const size_t length = QFileInfo(filepath.c_str()).size(); addFile(Metadata(key, length), filepath); } From f7de74109b022d0c5131281a23d1ee7d40441161 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Fri, 24 Mar 2017 15:10:26 -0700 Subject: [PATCH 302/302] Added jsdoc documentation for some of MyAvatar properties and methods --- interface/src/avatar/MyAvatar.h | 162 ++++++++++++++++++++++++++- interface/src/ui/overlays/Overlays.h | 2 +- tools/jsdoc/plugins/hifi.js | 9 +- 3 files changed, 164 insertions(+), 9 deletions(-) diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index 5f812f1f99..9f93fa1bd2 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -46,6 +46,60 @@ Q_DECLARE_METATYPE(AudioListenerMode); class MyAvatar : public Avatar { Q_OBJECT + + /**jsdoc + * Your avatar is your in-world representation of you. The MyAvatar API is used to manipulate the avatar. + * For example, using the MyAvatar API you can customize the avatar's appearance, run custom avatar animations, + * change the avatar's position within the domain, or manage the avatar's collisions with other objects. + * NOTE: MyAvatar extends Avatar and AvatarData, see those namespace for more properties/methods. + * + * @namespace MyAvatar + * @augments Avatar + * @property shouldRenderLocally {bool} Set it to true if you would like to see MyAvatar in your local interface, + * and false if you would not like to see MyAvatar in your local interface. + * @property motorVelocity {Vec3} Can be used to move the avatar with this velocity. + * @property motorTimescale {float} Specifies how quickly the avatar should accelerate to meet the motorVelocity, + * smaller values will result in higher acceleration. + * @property motorReferenceFrame {string} Reference frame of the motorVelocity, must be one of the following: "avatar", "camera", "world" + * @property collisionSoundURL {string} Specifies the sound to play when the avatar experiences a collision. + * You can provide a mono or stereo 16-bit WAV file running at either 24 Khz or 48 Khz. + * The latter is downsampled by the audio mixer, so all audio effectively plays back at a 24 Khz sample rate. + * 48 Khz RAW files are also supported. + * @property audioListenerMode {number} When hearing spatialized audio this determines where the listener placed. + * Should be one of the following values: + * MyAvatar.audioListenerModeHead - the listener located at the avatar's head. + * MyAvatar.audioListenerModeCamera - the listener is relative to the camera. + * MyAvatar.audioListenerModeCustom - the listener is at a custom location specified by the MyAvatar.customListenPosition + * and MyAvatar.customListenOrientation properties. + * @property customListenPosition {Vec3} If MyAvatar.audioListenerMode == MyAvatar.audioListenerModeHead, then this determines the position + * of audio spatialization listener. + * @property customListenOreintation {Quat} If MyAvatar.audioListenerMode == MyAvatar.audioListenerModeHead, then this determines the orientation + * of the audio spatialization listener. + * @property audioListenerModeHead {number} READ-ONLY. When passed to MyAvatar.audioListenerMode, it will set the audio listener + * around the avatar's head. + * @property audioListenerModeCamera {number} READ-ONLY. When passed to MyAvatar.audioListenerMode, it will set the audio listener + * around the camera. + * @property audioListenerModeCustom {number} READ-ONLY. When passed to MyAvatar.audioListenerMode, it will set the audio listener + * around the value specified by MyAvatar.customListenPosition and MyAvatar.customListenOrientation. + * @property leftHandPosition {Vec3} READ-ONLY. The desired position of the left wrist in avatar space, determined by the hand controllers. + * Note: only valid if hand controllers are in use. + * @property rightHandPosition {Vec3} READ-ONLY. The desired position of the right wrist in avatar space, determined by the hand controllers. + * Note: only valid if hand controllers are in use. + * @property leftHandTipPosition {Vec3} READ-ONLY. A position 30 cm offset from MyAvatar.leftHandPosition + * @property rightHandTipPosition {Vec3} READ-ONLY. A position 30 cm offset from MyAvatar.rightHandPosition + * @property leftHandPose {Pose} READ-ONLY. Returns full pose (translation, orientation, velocity & angularVelocity) of the desired + * wrist position, determined by the hand controllers. + * @property rightHandPose {Pose} READ-ONLY. Returns full pose (translation, orientation, velocity & angularVelocity) of the desired + * wrist position, determined by the hand controllers. + * @property leftHandTipPose {Pose} READ-ONLY. Returns a pose offset 30 cm from MyAvatar.leftHandPose + * @property rightHandTipPose {Pose} READ-ONLY. Returns a pose offset 30 cm from MyAvatar.rightHandPose + * @property hmdLeanRecenterEnabled {bool} This can be used disable the hmd lean recenter behavior. This behavior is what causes your avatar + * to follow your HMD as you walk around the room, in room scale VR. Disabling this is useful if you desire to pin the avatar to a fixed location. + * @property characterControllerEnabled {bool} This can be used to disable collisions between the avatar and the world. + * @property useAdvancedMovementControls {bool} Stores the user preference only, does not change user mappings, this is done in the defaultScript + * "scripts/system/controllers/toggleAdvancedMovementForHandControllers.js". + */ + Q_PROPERTY(bool shouldRenderLocally READ getShouldRenderLocally WRITE setShouldRenderLocally) Q_PROPERTY(glm::vec3 motorVelocity READ getScriptedMotorVelocity WRITE setScriptedMotorVelocity) Q_PROPERTY(float motorTimescale READ getScriptedMotorTimescale WRITE setScriptedMotorTimescale) @@ -106,7 +160,19 @@ public: void reset(bool andRecenter = false, bool andReload = true, bool andHead = true); Q_INVOKABLE void resetSensorsAndBody(); + + /**jsdoc + * Moves and orients the avatar, such that it is directly underneath the HMD, with toes pointed forward. + * @function MyAvatar.centerBody + */ Q_INVOKABLE void centerBody(); // thread-safe + + + /**jsdoc + * The internal inverse-kinematics system maintains a record of which joints are "locked". Sometimes it is useful to forget this history, to prevent + * contorted joints. + * @function MyAvatar.clearIKJointLimitHistory + */ Q_INVOKABLE void clearIKJointLimitHistory(); // thread-safe void update(float deltaTime); @@ -137,23 +203,109 @@ public: void setRealWorldFieldOfView(float realWorldFov) { _realWorldFieldOfView.set(realWorldFov); } + /**jsdoc + * The default position in world coordinates of the point directly between the avatar's eyes + * @function MyAvatar.getDefaultEyePosition + * @example This example gets the default eye position and prints it to the debug log. + * var defaultEyePosition = MyAvatar.getDefaultEyePosition(); + * print (JSON.stringify(defaultEyePosition)); + * @returns {Vec3} Position between the avatar's eyes. + */ Q_INVOKABLE glm::vec3 getDefaultEyePosition() const; float getRealWorldFieldOfView() { return _realWorldFieldOfView.get(); } - // Interrupt the current animation with a custom animation. + /**jsdoc + * The avatar animation system includes a set of default animations along with rules for how those animations are blended + * together with procedural data (such as look at vectors, hand sensors etc.). overrideAnimation() is used to completely + * override all motion from the default animation system (including inverse kinematics for hand and head controllers) and + * play a specified animation. To end this animation and restore the default animations, use MyAvatar.restoreAnimation. + * @function MyAvatar.overrideAnimation + * @example Play a clapping animation on your avatar for three seconds. + * // Clap your hands for 3 seconds then restore animation back to the avatar. + * var ANIM_URL = "https://s3.amazonaws.com/hifi-public/animations/ClapAnimations/ClapHands_Standing.fbx"; + * MyAvatar.overrideAnimation(ANIM_URL, 30, true, 0, 53); + * Script.setTimeout(function () { + * MyAvatar.restoreAnimation(); + * }, 3000); + * @param url {string} The URL to the animation file. Animation files need to be .FBX format, but only need to contain the avatar skeleton and animation data. + * @param fps {number} The frames per second (FPS) rate for the animation playback. 30 FPS is normal speed. + * @param loop {bool} Set to true if the animation should loop. + * @param firstFrame {number} The frame the animation should start at. + * @param lastFrame {number} The frame the animation should end at. + */ Q_INVOKABLE void overrideAnimation(const QString& url, float fps, bool loop, float firstFrame, float lastFrame); - // Stop the animation that was started with overrideAnimation and go back to the standard animation. + /**jsdoc + * The avatar animation system includes a set of default animations along with rules for how those animations are blended together with + * procedural data (such as look at vectors, hand sensors etc.). Playing your own custom animations will override the default animations. + * restoreAnimation() is used to restore all motion from the default animation system including inverse kinematics for hand and head + * controllers. If you aren't currently playing an override animation, this function will have no effect. + * @function MyAvatar.restoreAnimation + * @example Play a clapping animation on your avatar for three seconds. + * // Clap your hands for 3 seconds then restore animation back to the avatar. + * var ANIM_URL = "https://s3.amazonaws.com/hifi-public/animations/ClapAnimations/ClapHands_Standing.fbx"; + * MyAvatar.overrideAnimation(ANIM_URL, 30, true, 0, 53); + * Script.setTimeout(function () { + * MyAvatar.restoreAnimation(); + * }, 3000); + */ Q_INVOKABLE void restoreAnimation(); - // Returns a list of all clips that are available + /**jsdoc + * Each avatar has an avatar-animation.json file that defines which animations are used and how they are blended together with procedural data + * (such as look at vectors, hand sensors etc.). Each animation specified in the avatar-animation.json file is known as an animation role. + * Animation roles map to easily understandable actions that the avatar can perform, such as "idleStand", "idleTalk", or "walkFwd." + * getAnimationRoles() is used get the list of animation roles defined in the avatar-animation.json. + * @function MyAvatar.getAnimatationRoles + * @example This example prints the list of animation roles defined in the avatar's avatar-animation.json file to the debug log. + * var roles = MyAvatar.getAnimationRoles(); + * print("Animation Roles:"); + * for (var i = 0; i < roles.length; i++) { + * print(roles[i]); + * } + * @returns {string[]} Array of role strings + */ Q_INVOKABLE QStringList getAnimationRoles(); - // Replace an existing standard role animation with a custom one. + /**jsdoc + * Each avatar has an avatar-animation.json file that defines a set of animation roles. Animation roles map to easily understandable actions + * that the avatar can perform, such as "idleStand", "idleTalk", or "walkFwd". To get the full list of roles, use getAnimationRoles(). + * For each role, the avatar-animation.json defines when the animation is used, the animation clip (.FBX) used, and how animations are blended + * together with procedural data (such as look at vectors, hand sensors etc.). + * overrideRoleAnimation() is used to change the animation clip (.FBX) associated with a specified animation role. + * Note: Hand roles only affect the hand. Other 'main' roles, like 'idleStand', 'idleTalk', 'takeoffStand' are full body. + * @function MyAvatar.overrideRoleAnimation + * @example The default avatar-animation.json defines an "idleStand" animation role. This role specifies that when the avatar is not moving, + * an animation clip of the avatar idling with hands hanging at its side will be used. It also specifies that when the avatar moves, the animation + * will smoothly blend to the walking animation used by the "walkFwd" animation role. + * In this example, the "idleStand" role animation clip has been replaced with a clapping animation clip. Now instead of standing with its arms + * hanging at its sides when it is not moving, the avatar will stand and clap its hands. Note that just as it did before, as soon as the avatar + * starts to move, the animation will smoothly blend into the walk animation used by the "walkFwd" animation role. + * // An animation of the avatar clapping its hands while standing + * var ANIM_URL = "https://s3.amazonaws.com/hifi-public/animations/ClapAnimations/ClapHands_Standing.fbx"; + * MyAvatar.overrideRoleAnimation("idleStand", ANIM_URL, 30, true, 0, 53); + * // To restore the default animation, use MyAvatar.restoreRoleAnimation(). + * @param role {string} The animation role to override + * @param url {string} The URL to the animation file. Animation files need to be .FBX format, but only need to contain the avatar skeleton and animation data. + * @param fps {number} The frames per second (FPS) rate for the animation playback. 30 FPS is normal speed. + * @param loop {bool} Set to true if the animation should loop + * @param firstFrame {number} The frame the animation should start at + * @param lastFrame {number} The frame the animation should end at + */ Q_INVOKABLE void overrideRoleAnimation(const QString& role, const QString& url, float fps, bool loop, float firstFrame, float lastFrame); - // remove an animation role override and return to the standard animation. + /**jsdoc + * Each avatar has an avatar-animation.json file that defines a set of animation roles. Animation roles map to easily understandable actions that + * the avatar can perform, such as "idleStand", "idleTalk", or "walkFwd". To get the full list of roles, use getAnimationRoles(). For each role, + * the avatar-animation.json defines when the animation is used, the animation clip (.FBX) used, and how animations are blended together with + * procedural data (such as look at vectors, hand sensors etc.). You can change the animation clip (.FBX) associated with a specified animation + * role using overrideRoleAnimation(). + * restoreRoleAnimation() is used to restore a specified animation role's default animation clip. If you have not specified an override animation + * for the specified role, this function will have no effect. + * @function MyAvatar.restoreRoleAnimation + * @param rule {string} The animation role clip to restore + */ Q_INVOKABLE void restoreRoleAnimation(const QString& role); // Adds handler(animStateDictionaryIn) => animStateDictionaryOut, which will be invoked just before each animGraph state update. diff --git a/interface/src/ui/overlays/Overlays.h b/interface/src/ui/overlays/Overlays.h index c35c7c1ced..a1d4be8376 100644 --- a/interface/src/ui/overlays/Overlays.h +++ b/interface/src/ui/overlays/Overlays.h @@ -213,7 +213,7 @@ public slots: * @function Overlays.findOverlays * @param {Vec3} center the point to search from. * @param {float} radius search radius - * @return {List of Overlays.OverlayID} list of overlays withing the radius + * @return {Overlays.OverlayID[]} list of overlays withing the radius */ QVector findOverlays(const glm::vec3& center, float radius) const; diff --git a/tools/jsdoc/plugins/hifi.js b/tools/jsdoc/plugins/hifi.js index 8be15c4103..c15f01efe9 100644 --- a/tools/jsdoc/plugins/hifi.js +++ b/tools/jsdoc/plugins/hifi.js @@ -15,13 +15,16 @@ exports.handlers = { // directories to scan for jsdoc comments var dirList = [ '../../interface/src', + '../../interface/src/avatar', '../../interface/src/scripting', '../../interface/src/ui/overlays', - '../../libraries/script-engine/src', - '../../libraries/networking/src', '../../libraries/animation/src', + '../../libraries/avatars/src', + '../../libraries/controllers/src/controllers/', '../../libraries/entities/src', - '../../libraries/shared/src' + '../../libraries/networking/src', + '../../libraries/shared/src', + '../../libraries/script-engine/src', ]; var exts = ['.h', '.cpp'];