From 2fe9af54d4c579d64c850a7d991e746dfb8a279f Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Wed, 18 Nov 2015 13:59:32 -0800 Subject: [PATCH 01/10] Add textures property to polyline --- .../src/RenderablePolyLineEntityItem.cpp | 17 ++++++++++++----- .../src/RenderablePolyLineEntityItem.h | 8 ++++++-- libraries/entities/src/PolyLineEntityItem.cpp | 8 +++++++- libraries/entities/src/PolyLineEntityItem.h | 11 ++++++++++- 4 files changed, 35 insertions(+), 9 deletions(-) diff --git a/libraries/entities-renderer/src/RenderablePolyLineEntityItem.cpp b/libraries/entities-renderer/src/RenderablePolyLineEntityItem.cpp index 32418199b8..76cf4fac3d 100644 --- a/libraries/entities-renderer/src/RenderablePolyLineEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderablePolyLineEntityItem.cpp @@ -36,7 +36,6 @@ PolyLineEntityItem(entityItemID, properties) { gpu::PipelinePointer RenderablePolyLineEntityItem::_pipeline; gpu::Stream::FormatPointer RenderablePolyLineEntityItem::_format; -gpu::TexturePointer RenderablePolyLineEntityItem::_texture; int32_t RenderablePolyLineEntityItem::PAINTSTROKE_GPU_SLOT; void RenderablePolyLineEntityItem::createPipeline() { @@ -44,9 +43,6 @@ void RenderablePolyLineEntityItem::createPipeline() { static const int COLOR_OFFSET = 24; static const int TEXTURE_OFFSET = 28; - auto textureCache = DependencyManager::get(); - QString path = PathUtils::resourcesPath() + "images/paintStroke.png"; - _texture = textureCache->getImageTexture(path); _format.reset(new gpu::Stream::Format()); _format->setAttribute(gpu::Stream::POSITION, 0, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ), 0); _format->setAttribute(gpu::Stream::NORMAL, 0, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ), NORMAL_OFFSET); @@ -132,6 +128,13 @@ void RenderablePolyLineEntityItem::render(RenderArgs* args) { createPipeline(); } + if (!_texture || _texturesChangedFlag) { + auto textureCache = DependencyManager::get(); + QString path = _textures.isEmpty() ? PathUtils::resourcesPath() + "images/paintStroke.png" : _textures; + _texture = textureCache->getTexture(QUrl(path)); + _texturesChangedFlag = false; + } + PerformanceTimer perfTimer("RenderablePolyLineEntityItem::render"); Q_ASSERT(getType() == EntityTypes::PolyLine); @@ -147,7 +150,11 @@ void RenderablePolyLineEntityItem::render(RenderArgs* args) { batch.setModelTransform(transform); batch.setPipeline(_pipeline); - batch.setResourceTexture(PAINTSTROKE_GPU_SLOT, _texture); + if (_texture->isLoaded()) { + batch.setResourceTexture(PAINTSTROKE_GPU_SLOT, _texture->getGPUTexture()); + } else { + batch.setResourceTexture(PAINTSTROKE_GPU_SLOT, args->_whiteTexture); + } batch.setInputFormat(_format); batch.setInputBuffer(0, _verticesBuffer, 0, _format->getChannels().at(0)._stride); diff --git a/libraries/entities-renderer/src/RenderablePolyLineEntityItem.h b/libraries/entities-renderer/src/RenderablePolyLineEntityItem.h index c8a47cce0c..618f8c66a6 100644 --- a/libraries/entities-renderer/src/RenderablePolyLineEntityItem.h +++ b/libraries/entities-renderer/src/RenderablePolyLineEntityItem.h @@ -12,10 +12,13 @@ #ifndef hifi_RenderablePolyLineEntityItem_h #define hifi_RenderablePolyLineEntityItem_h + #include +#include #include #include "RenderableEntityItem.h" -#include +#include + #include @@ -29,9 +32,10 @@ public: SIMPLE_RENDERABLE(); + NetworkTexturePointer _texture; + static gpu::PipelinePointer _pipeline; static gpu::Stream::FormatPointer _format; - static gpu::TexturePointer _texture; static int32_t PAINTSTROKE_GPU_SLOT; protected: diff --git a/libraries/entities/src/PolyLineEntityItem.cpp b/libraries/entities/src/PolyLineEntityItem.cpp index f1be431ce8..45c967f78d 100644 --- a/libraries/entities/src/PolyLineEntityItem.cpp +++ b/libraries/entities/src/PolyLineEntityItem.cpp @@ -37,7 +37,8 @@ _pointsChanged(true), _points(QVector(0.0f)), _vertices(QVector(0.0f)), _normals(QVector(0.0f)), -_strokeWidths(QVector(0.0f)) +_strokeWidths(QVector(0.0f)), +_textures("") { _type = EntityTypes::PolyLine; _created = properties.getCreated(); @@ -56,6 +57,7 @@ EntityItemProperties PolyLineEntityItem::getProperties(EntityPropertyFlags desir COPY_ENTITY_PROPERTY_TO_PROPERTIES(linePoints, getLinePoints); COPY_ENTITY_PROPERTY_TO_PROPERTIES(normals, getNormals); COPY_ENTITY_PROPERTY_TO_PROPERTIES(strokeWidths, getStrokeWidths); + COPY_ENTITY_PROPERTY_TO_PROPERTIES(textures, getTextures); properties._glowLevel = getGlowLevel(); properties._glowLevelChanged = false; @@ -72,6 +74,7 @@ bool PolyLineEntityItem::setProperties(const EntityItemProperties& properties) { SET_ENTITY_PROPERTY_FROM_PROPERTIES(linePoints, setLinePoints); SET_ENTITY_PROPERTY_FROM_PROPERTIES(normals, setNormals); SET_ENTITY_PROPERTY_FROM_PROPERTIES(strokeWidths, setStrokeWidths); + SET_ENTITY_PROPERTY_FROM_PROPERTIES(textures, setTextures); if (somethingChanged) { bool wantDebug = false; @@ -196,6 +199,7 @@ int PolyLineEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* da READ_ENTITY_PROPERTY(PROP_LINE_POINTS, QVector, setLinePoints); READ_ENTITY_PROPERTY(PROP_NORMALS, QVector, setNormals); READ_ENTITY_PROPERTY(PROP_STROKE_WIDTHS, QVector, setStrokeWidths); + READ_ENTITY_PROPERTY(PROP_TEXTURES, QString, setTextures); return bytesRead; } @@ -209,6 +213,7 @@ EntityPropertyFlags PolyLineEntityItem::getEntityProperties(EncodeBitstreamParam requestedProperties += PROP_LINE_POINTS; requestedProperties += PROP_NORMALS; requestedProperties += PROP_STROKE_WIDTHS; + requestedProperties += PROP_TEXTURES; return requestedProperties; } @@ -228,6 +233,7 @@ void PolyLineEntityItem::appendSubclassData(OctreePacketData* packetData, Encode APPEND_ENTITY_PROPERTY(PROP_LINE_POINTS, getLinePoints()); APPEND_ENTITY_PROPERTY(PROP_NORMALS, getNormals()); APPEND_ENTITY_PROPERTY(PROP_STROKE_WIDTHS, getStrokeWidths()); + APPEND_ENTITY_PROPERTY(PROP_TEXTURES, getTextures()); } void PolyLineEntityItem::debugDump() const { diff --git a/libraries/entities/src/PolyLineEntityItem.h b/libraries/entities/src/PolyLineEntityItem.h index 86a1dfb6e0..9e9d3f9013 100644 --- a/libraries/entities/src/PolyLineEntityItem.h +++ b/libraries/entities/src/PolyLineEntityItem.h @@ -67,7 +67,14 @@ class PolyLineEntityItem : public EntityItem { bool setStrokeWidths(const QVector& strokeWidths); const QVector& getStrokeWidths() const{ return _strokeWidths; } - + + const QString& getTextures() const { return _textures; } + void setTextures(const QString& textures) { + if (_textures != textures) { + _textures = textures; + _texturesChangedFlag = true; + } + } virtual ShapeType getShapeType() const { return SHAPE_TYPE_LINE; } @@ -90,6 +97,8 @@ class PolyLineEntityItem : public EntityItem { QVector _vertices; QVector _normals; QVector _strokeWidths; + QString _textures; + bool _texturesChangedFlag { false }; mutable QReadWriteLock _quadReadWriteLock; }; From 3d0afc3c5ff0910d2b92b5ee9843d844d4315696 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Wed, 18 Nov 2015 14:00:00 -0800 Subject: [PATCH 02/10] Update entities version number for polyline change --- libraries/networking/src/udt/PacketHeaders.cpp | 2 +- libraries/networking/src/udt/PacketHeaders.h | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/libraries/networking/src/udt/PacketHeaders.cpp b/libraries/networking/src/udt/PacketHeaders.cpp index f5c66617a8..2411ee23ac 100644 --- a/libraries/networking/src/udt/PacketHeaders.cpp +++ b/libraries/networking/src/udt/PacketHeaders.cpp @@ -41,7 +41,7 @@ PacketVersion versionForPacketType(PacketType packetType) { case PacketType::EntityAdd: case PacketType::EntityEdit: case PacketType::EntityData: - return VERSION_ENTITIES_PARTICLES_ADDITIVE_BLENDING; + return VERSION_ENTITIES_POLYLINE_TEXTURE; case PacketType::AvatarData: case PacketType::BulkAvatarData: default: diff --git a/libraries/networking/src/udt/PacketHeaders.h b/libraries/networking/src/udt/PacketHeaders.h index e0a847dcc6..23df89b3d7 100644 --- a/libraries/networking/src/udt/PacketHeaders.h +++ b/libraries/networking/src/udt/PacketHeaders.h @@ -159,5 +159,6 @@ const PacketVersion VERSION_ENTITIES_ANIMATION_PROPERTIES_GROUP = 46; const PacketVersion VERSION_ENTITIES_KEYLIGHT_PROPERTIES_GROUP = 47; const PacketVersion VERSION_ENTITIES_KEYLIGHT_PROPERTIES_GROUP_BIS = 48; const PacketVersion VERSION_ENTITIES_PARTICLES_ADDITIVE_BLENDING = 49; +const PacketVersion VERSION_ENTITIES_POLYLINE_TEXTURE = 50; #endif // hifi_PacketHeaders_h From 271387f96e0a1947cf62f19603cfae4312067747 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Wed, 18 Nov 2015 18:03:31 -0800 Subject: [PATCH 03/10] use a unique_ptr for linkedData in Node --- assignment-client/src/AssignmentClientMonitor.cpp | 6 ++++-- assignment-client/src/audio/AudioMixer.cpp | 3 ++- assignment-client/src/avatars/AvatarMixer.cpp | 3 ++- assignment-client/src/entities/EntityServer.cpp | 4 ++-- assignment-client/src/entities/EntityServer.h | 4 +++- assignment-client/src/octree/OctreeServer.cpp | 6 +++--- assignment-client/src/octree/OctreeServer.h | 4 +++- domain-server/src/DomainServer.cpp | 4 +++- libraries/networking/src/Node.cpp | 5 ----- libraries/networking/src/Node.h | 8 ++++---- 10 files changed, 26 insertions(+), 21 deletions(-) diff --git a/assignment-client/src/AssignmentClientMonitor.cpp b/assignment-client/src/AssignmentClientMonitor.cpp index 7b3d5695e1..763945fa3e 100644 --- a/assignment-client/src/AssignmentClientMonitor.cpp +++ b/assignment-client/src/AssignmentClientMonitor.cpp @@ -9,6 +9,7 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +#include #include #include @@ -227,8 +228,9 @@ void AssignmentClientMonitor::handleChildStatusPacket(QSharedPointer p matchingNode = DependencyManager::get()->addOrUpdateNode (senderID, NodeType::Unassigned, senderSockAddr, senderSockAddr, false, false); - childData = new AssignmentClientChildData(Assignment::Type::AllTypes); - matchingNode->setLinkedData(childData); + auto childData = std::unique_ptr + { new AssignmentClientChildData(Assignment::Type::AllTypes) }; + matchingNode->setLinkedData(std::move(childData)); } else { // tell unknown assignment-client child to exit. qDebug() << "Asking unknown child at" << senderSockAddr << "to exit."; diff --git a/assignment-client/src/audio/AudioMixer.cpp b/assignment-client/src/audio/AudioMixer.cpp index 1d8908845f..5ca9f46f10 100644 --- a/assignment-client/src/audio/AudioMixer.cpp +++ b/assignment-client/src/audio/AudioMixer.cpp @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -652,7 +653,7 @@ void AudioMixer::run() { nodeList->addNodeTypeToInterestSet(NodeType::Agent); nodeList->linkedDataCreateCallback = [](Node* node) { - node->setLinkedData(new AudioMixerClientData()); + node->setLinkedData(std::unique_ptr { new AudioMixerClientData }); }; // wait until we have the domain-server settings, otherwise we bail diff --git a/assignment-client/src/avatars/AvatarMixer.cpp b/assignment-client/src/avatars/AvatarMixer.cpp index 1e17467c3b..1198767944 100644 --- a/assignment-client/src/avatars/AvatarMixer.cpp +++ b/assignment-client/src/avatars/AvatarMixer.cpp @@ -11,6 +11,7 @@ #include #include +#include #include #include @@ -519,7 +520,7 @@ void AvatarMixer::run() { nodeList->addNodeTypeToInterestSet(NodeType::Agent); nodeList->linkedDataCreateCallback = [] (Node* node) { - node->setLinkedData(new AvatarMixerClientData()); + node->setLinkedData(std::unique_ptr { new AvatarMixerClientData }); }; // setup the timer that will be fired on the broadcast thread diff --git a/assignment-client/src/entities/EntityServer.cpp b/assignment-client/src/entities/EntityServer.cpp index 2fafaa6731..8b1524972f 100644 --- a/assignment-client/src/entities/EntityServer.cpp +++ b/assignment-client/src/entities/EntityServer.cpp @@ -46,8 +46,8 @@ void EntityServer::handleEntityPacket(QSharedPointer packet, SharedNod } } -OctreeQueryNode* EntityServer::createOctreeQueryNode() { - return new EntityNodeData(); +std::unique_ptr EntityServer::createOctreeQueryNode() { + return std::unique_ptr { new EntityNodeData() }; } OctreePointer EntityServer::createTree() { diff --git a/assignment-client/src/entities/EntityServer.h b/assignment-client/src/entities/EntityServer.h index 065834cbc2..eeb87f9ef1 100644 --- a/assignment-client/src/entities/EntityServer.h +++ b/assignment-client/src/entities/EntityServer.h @@ -14,6 +14,8 @@ #include "../octree/OctreeServer.h" +#include + #include "EntityItem.h" #include "EntityServerConsts.h" #include "EntityTree.h" @@ -26,7 +28,7 @@ public: ~EntityServer(); // Subclasses must implement these methods - virtual OctreeQueryNode* createOctreeQueryNode() override ; + virtual std::unique_ptr createOctreeQueryNode() override ; virtual char getMyNodeType() const override { return NodeType::EntityServer; } virtual PacketType getMyQueryMessageType() const override { return PacketType::EntityQuery; } virtual const char* getMyServerName() const override { return MODEL_SERVER_NAME; } diff --git a/assignment-client/src/octree/OctreeServer.cpp b/assignment-client/src/octree/OctreeServer.cpp index 84749bd975..d1b604f97f 100644 --- a/assignment-client/src/octree/OctreeServer.cpp +++ b/assignment-client/src/octree/OctreeServer.cpp @@ -1108,9 +1108,9 @@ void OctreeServer::run() { #endif nodeList->linkedDataCreateCallback = [] (Node* node) { - OctreeQueryNode* newQueryNodeData = _instance->createOctreeQueryNode(); - newQueryNodeData->init(); - node->setLinkedData(newQueryNodeData); + auto queryNodeData = _instance->createOctreeQueryNode(); + queryNodeData->init(); + node->setLinkedData(std::move(queryNodeData)); }; srand((unsigned)time(0)); diff --git a/assignment-client/src/octree/OctreeServer.h b/assignment-client/src/octree/OctreeServer.h index b8e4a5c261..e3083dea07 100644 --- a/assignment-client/src/octree/OctreeServer.h +++ b/assignment-client/src/octree/OctreeServer.h @@ -12,6 +12,8 @@ #ifndef hifi_OctreeServer_h #define hifi_OctreeServer_h +#include + #include #include #include @@ -61,7 +63,7 @@ public: quint64 getLoadElapsedTime() const { return (_persistThread) ? _persistThread->getLoadElapsedTime() : 0; } // Subclasses must implement these methods - virtual OctreeQueryNode* createOctreeQueryNode() = 0; + virtual std::unique_ptr createOctreeQueryNode() = 0; virtual char getMyNodeType() const = 0; virtual PacketType getMyQueryMessageType() const = 0; virtual const char* getMyServerName() const = 0; diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index d7bcec2431..4f866c965f 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -11,6 +11,8 @@ #include "DomainServer.h" +#include + #include #include #include @@ -1640,7 +1642,7 @@ void DomainServer::refreshStaticAssignmentAndAddToQueue(SharedAssignmentPointer& void DomainServer::nodeAdded(SharedNodePointer node) { // we don't use updateNodeWithData, so add the DomainServerNodeData to the node here - node->setLinkedData(new DomainServerNodeData()); + node->setLinkedData(std::unique_ptr { new DomainServerNodeData() }); } void DomainServer::nodeKilled(SharedNodePointer node) { diff --git a/libraries/networking/src/Node.cpp b/libraries/networking/src/Node.cpp index 243dca78e2..918bd5b972 100644 --- a/libraries/networking/src/Node.cpp +++ b/libraries/networking/src/Node.cpp @@ -48,7 +48,6 @@ Node::Node(const QUuid& uuid, NodeType_t type, const HifiSockAddr& publicSocket, NetworkPeer(uuid, publicSocket, localSocket, parent), _type(type), _connectionSecret(connectionSecret), - _linkedData(NULL), _isAlive(true), _pingMs(-1), // "Uninitialized" _clockSkewUsec(0), @@ -61,10 +60,6 @@ Node::Node(const QUuid& uuid, NodeType_t type, const HifiSockAddr& publicSocket, setType(_type); } -Node::~Node() { - delete _linkedData; -} - void Node::setType(char type) { _type = type; diff --git a/libraries/networking/src/Node.h b/libraries/networking/src/Node.h index 38d6678ba5..2b35516787 100644 --- a/libraries/networking/src/Node.h +++ b/libraries/networking/src/Node.h @@ -12,6 +12,7 @@ #ifndef hifi_Node_h #define hifi_Node_h +#include #include #include @@ -34,7 +35,6 @@ public: const HifiSockAddr& publicSocket, const HifiSockAddr& localSocket, bool canAdjustLocks, bool canRez, const QUuid& connectionSecret = QUuid(), QObject* parent = 0); - ~Node(); bool operator==(const Node& otherNode) const { return _uuid == otherNode._uuid; } bool operator!=(const Node& otherNode) const { return !(*this == otherNode); } @@ -45,8 +45,8 @@ public: const QUuid& getConnectionSecret() const { return _connectionSecret; } void setConnectionSecret(const QUuid& connectionSecret) { _connectionSecret = connectionSecret; } - NodeData* getLinkedData() const { return _linkedData; } - void setLinkedData(NodeData* linkedData) { _linkedData = linkedData; } + NodeData* getLinkedData() const { return _linkedData.get(); } + void setLinkedData(std::unique_ptr linkedData) { _linkedData = std::move(linkedData); } bool isAlive() const { return _isAlive; } void setAlive(bool isAlive) { _isAlive = isAlive; } @@ -75,7 +75,7 @@ private: NodeType_t _type; QUuid _connectionSecret; - NodeData* _linkedData; + std::unique_ptr _linkedData; bool _isAlive; int _pingMs; int _clockSkewUsec; From b122dad7eab3b28936409c6ef2315eefbfdb6609 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Thu, 19 Nov 2015 15:16:23 -0800 Subject: [PATCH 04/10] Add begin and middle textures to InfiniteLine --- examples/libraries/line.js | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/examples/libraries/line.js b/examples/libraries/line.js index c21bf2f3ad..d5e5c83028 100644 --- a/examples/libraries/line.js +++ b/examples/libraries/line.js @@ -19,10 +19,11 @@ var MAX_LINE_LENGTH = 40; // This must be 2 or greater; var DEFAULT_STROKE_WIDTH = 0.1; var DEFAULT_LIFETIME = 20; var DEFAULT_COLOR = { red: 255, green: 255, blue: 255 }; -var PolyLine = function(position, color, lifetime) { +var PolyLine = function(position, color, lifetime, texture) { this.position = position; this.color = color; this.lifetime = lifetime === undefined ? DEFAULT_LIFETIME : lifetime; + this.texture = texture ? texture : ""; this.points = [ ]; this.strokeWidths = [ @@ -37,7 +38,8 @@ var PolyLine = function(position, color, lifetime) { strokeWidths: this.strokeWidths, dimensions: LINE_DIMENSIONS, color: color, - lifetime: lifetime + lifetime: lifetime, + textures: this.texture }); }; @@ -98,26 +100,29 @@ PolyLine.prototype.destroy = function() { // InfiniteLine -InfiniteLine = function(position, color, lifetime) { +InfiniteLine = function(position, color, lifetime, textureBegin, textureMiddle) { this.position = position; this.color = color; this.lifetime = lifetime === undefined ? DEFAULT_LIFETIME : lifetime; this.lines = []; this.size = 0; + + this.textureBegin = textureBegin ? textureBegin : ""; + this.textureMiddle = textureMiddle ? textureMiddle : ""; }; InfiniteLine.prototype.enqueuePoint = function(position, strokeWidth) { var currentLine; if (this.lines.length == 0) { - currentLine = new PolyLine(position, this.color, this.lifetime); + currentLine = new PolyLine(position, this.color, this.lifetime, this.textureBegin); this.lines.push(currentLine); } else { currentLine = this.lines[this.lines.length - 1]; } if (currentLine.isFull()) { - var newLine = new PolyLine(currentLine.getLastPoint(), this.color, this.lifetime); + var newLine = new PolyLine(currentLine.getLastPoint(), this.color, this.lifetime, this.textureMiddle); newLine.enqueuePoint(currentLine.getLastPoint(), strokeWidth); this.lines.push(newLine); currentLine = newLine; From 9e31614fce4379adf4e5675d54a7e5c40af174fc Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Thu, 19 Nov 2015 15:16:46 -0800 Subject: [PATCH 05/10] Fix textures property not being sent for polyline --- libraries/entities/src/EntityItemProperties.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/libraries/entities/src/EntityItemProperties.cpp b/libraries/entities/src/EntityItemProperties.cpp index 4f5d256969..78a4f3e8b6 100644 --- a/libraries/entities/src/EntityItemProperties.cpp +++ b/libraries/entities/src/EntityItemProperties.cpp @@ -432,6 +432,7 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine, bool COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_LINE_POINTS, linePoints); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_NORMALS, normals); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_STROKE_WIDTHS, strokeWidths); + COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_TEXTURES, textures); } // Sitting properties support @@ -1011,6 +1012,7 @@ bool EntityItemProperties::encodeEntityEditPacket(PacketType command, EntityItem APPEND_ENTITY_PROPERTY(PROP_LINE_POINTS, properties.getLinePoints()); APPEND_ENTITY_PROPERTY(PROP_NORMALS, properties.getNormals()); APPEND_ENTITY_PROPERTY(PROP_STROKE_WIDTHS, properties.getStrokeWidths()); + APPEND_ENTITY_PROPERTY(PROP_TEXTURES, properties.getTextures()); } APPEND_ENTITY_PROPERTY(PROP_MARKETPLACE_ID, properties.getMarketplaceID()); @@ -1287,6 +1289,7 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_LINE_POINTS, QVector, setLinePoints); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_NORMALS, QVector, setNormals); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_STROKE_WIDTHS, QVector, setStrokeWidths); + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_TEXTURES, QString, setTextures); } READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_MARKETPLACE_ID, QString, setMarketplaceID); From 3b4011999234682f39c8ca4ef8324b799c3f91a6 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Fri, 20 Nov 2015 11:35:24 -0800 Subject: [PATCH 06/10] use Agent session UUID to request script --- assignment-client/src/Agent.cpp | 118 +++++++++++++++++------------ assignment-client/src/Agent.h | 5 ++ domain-server/src/DomainServer.cpp | 52 +++++++------ 3 files changed, 103 insertions(+), 72 deletions(-) diff --git a/assignment-client/src/Agent.cpp b/assignment-client/src/Agent.cpp index f19f4ff86b..5ec4b91fa6 100644 --- a/assignment-client/src/Agent.cpp +++ b/assignment-client/src/Agent.cpp @@ -22,6 +22,7 @@ #include #include #include +#include #include #include @@ -116,6 +117,11 @@ void Agent::handleAudioPacket(QSharedPointer packet) { const QString AGENT_LOGGING_NAME = "agent"; void Agent::run() { + + // make sure we request our script once the agent connects to the domain + auto nodeList = DependencyManager::get(); + connect(&nodeList->getDomainHandler(), &DomainHandler::connectedToDomain, this, &Agent::requestScript); + ThreadedAssignment::commonInit(AGENT_LOGGING_NAME, NodeType::Agent); // Setup MessagesClient @@ -125,72 +131,86 @@ void Agent::run() { messagesClient->moveToThread(messagesThread); connect(messagesThread, &QThread::started, messagesClient.data(), &MessagesClient::init); messagesThread->start(); + + nodeList->addSetOfNodeTypesToNodeInterestSet({ + NodeType::AudioMixer, NodeType::AvatarMixer, NodeType::EntityServer, NodeType::MessagesMixer + }); +} - +void Agent::requestScript() { auto nodeList = DependencyManager::get(); - nodeList->addSetOfNodeTypesToNodeInterestSet(NodeSet() - << NodeType::AudioMixer - << NodeType::AvatarMixer - << NodeType::EntityServer - << NodeType::MessagesMixer - ); - + disconnect(&nodeList->getDomainHandler(), &DomainHandler::connectedToDomain, this, &Agent::requestScript); + // figure out the URL for the script for this agent assignment QUrl scriptURL; if (_payload.isEmpty()) { - scriptURL = QUrl(QString("http://%1:%2/assignment/%3") - .arg(DependencyManager::get()->getDomainHandler().getIP().toString()) - .arg(DOMAIN_SERVER_HTTP_PORT) - .arg(uuidStringWithoutCurlyBraces(_uuid))); + scriptURL = QUrl(QString("http://%1:%2/assignment/%3/") + .arg(nodeList->getDomainHandler().getIP().toString()) + .arg(DOMAIN_SERVER_HTTP_PORT) + .arg(uuidStringWithoutCurlyBraces(nodeList->getSessionUUID()))); } else { scriptURL = QUrl(_payload); } - + + // setup a network access manager and QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance(); - QNetworkRequest networkRequest = QNetworkRequest(scriptURL); - networkRequest.setHeader(QNetworkRequest::UserAgentHeader, HIGH_FIDELITY_USER_AGENT); - QNetworkReply* reply = networkAccessManager.get(networkRequest); - + QNetworkDiskCache* cache = new QNetworkDiskCache(); QString cachePath = QStandardPaths::writableLocation(QStandardPaths::DataLocation); cache->setCacheDirectory(!cachePath.isEmpty() ? cachePath : "agentCache"); networkAccessManager.setCache(cache); - + + QNetworkRequest networkRequest = QNetworkRequest(scriptURL); + networkRequest.setHeader(QNetworkRequest::UserAgentHeader, HIGH_FIDELITY_USER_AGENT); + qDebug() << "Downloading script at" << scriptURL.toString(); + QNetworkReply* reply = networkAccessManager.get(networkRequest); + connect(reply, &QNetworkReply::finished, this, &Agent::scriptRequestFinished); +} - QEventLoop loop; - QObject::connect(reply, SIGNAL(finished()), &loop, SLOT(quit())); +void Agent::scriptRequestFinished() { + auto reply = qobject_cast(sender()); + + if (reply->error() != QNetworkReply::NoError) { + _scriptContents = reply->readAll(); + qDebug() << "Downloaded script:" << _scriptContents; + + // we could just call executeScript directly - we use a QueuedConnect to allow scriptRequestFinished + // to return before calling executeScript + QMetaObject::invokeMethod(this, "executeScript", Qt::QueuedConnection); + } else { + qDebug() << "Failed to download script at" << reply->url().toString() << " - bailing on assignment."; + qDebug() << "QNetworkReply error was" << reply->errorString(); + setFinished(true); + } + + reply->deleteLater(); +} - loop.exec(); - - QString scriptContents(reply->readAll()); - delete reply; - - qDebug() << "Downloaded script:" << scriptContents; - - _scriptEngine = std::unique_ptr(new ScriptEngine(scriptContents, _payload)); +void Agent::executeScript() { + _scriptEngine = std::unique_ptr(new ScriptEngine(_scriptContents, _payload)); _scriptEngine->setParent(this); // be the parent of the script engine so it gets moved when we do - + // setup an Avatar for the script to use auto scriptedAvatar = DependencyManager::get(); connect(_scriptEngine.get(), SIGNAL(update(float)), scriptedAvatar.data(), SLOT(update(float)), Qt::ConnectionType::QueuedConnection); scriptedAvatar->setForceFaceTrackerConnected(true); - + // call model URL setters with empty URLs so our avatar, if user, will have the default models scriptedAvatar->setFaceModelURL(QUrl()); scriptedAvatar->setSkeletonModelURL(QUrl()); // give this AvatarData object to the script engine _scriptEngine->registerGlobalObject("Avatar", scriptedAvatar.data()); - - + + using namespace recording; static const FrameType AVATAR_FRAME_TYPE = Frame::registerFrameType(AvatarData::FRAME_NAME); - // FIXME how to deal with driving multiple avatars locally? + // FIXME how to deal with driving multiple avatars locally? Frame::registerFrameHandler(AVATAR_FRAME_TYPE, [this, scriptedAvatar](Frame::ConstPointer frame) { AvatarData::fromFrame(frame->data, *scriptedAvatar); }); - - + + using namespace recording; static const FrameType AUDIO_FRAME_TYPE = Frame::registerFrameType(AudioConstants::AUDIO_FRAME_NAME); Frame::registerFrameHandler(AUDIO_FRAME_TYPE, [this, &scriptedAvatar](Frame::ConstPointer frame) { @@ -201,32 +221,30 @@ void Agent::run() { audioTransform.setRotation(scriptedAvatar->getOrientation()); AbstractAudioInterface::emitAudioPacket(audio.data(), audio.size(), audioSequenceNumber, audioTransform, PacketType::MicrophoneAudioNoEcho); }); - - - + auto avatarHashMap = DependencyManager::set(); _scriptEngine->registerGlobalObject("AvatarList", avatarHashMap.data()); - + auto& packetReceiver = DependencyManager::get()->getPacketReceiver(); packetReceiver.registerListener(PacketType::BulkAvatarData, avatarHashMap.data(), "processAvatarDataPacket"); packetReceiver.registerListener(PacketType::KillAvatar, avatarHashMap.data(), "processKillAvatar"); packetReceiver.registerListener(PacketType::AvatarIdentity, avatarHashMap.data(), "processAvatarIdentityPacket"); packetReceiver.registerListener(PacketType::AvatarBillboard, avatarHashMap.data(), "processAvatarBillboardPacket"); - + // register ourselves to the script engine _scriptEngine->registerGlobalObject("Agent", this); - - // FIXME -we shouldn't be calling this directly, it's normally called by run(), not sure why + + // FIXME -we shouldn't be calling this directly, it's normally called by run(), not sure why // viewers would need this called. //_scriptEngine->init(); // must be done before we set up the viewers - + _scriptEngine->registerGlobalObject("SoundCache", DependencyManager::get().data()); - + QScriptValue webSocketServerConstructorValue = _scriptEngine->newFunction(WebSocketServerClass::constructor); _scriptEngine->globalObject().setProperty("WebSocketServer", webSocketServerConstructorValue); - + auto entityScriptingInterface = DependencyManager::get(); - + _scriptEngine->registerGlobalObject("EntityViewer", &_entityViewer); // we need to make sure that init has been called for our EntityScriptingInterface @@ -237,15 +255,15 @@ void Agent::run() { _entityViewer.init(); entityScriptingInterface->setEntityTree(_entityViewer.getTree()); - + // wire up our additional agent related processing to the update signal QObject::connect(_scriptEngine.get(), &ScriptEngine::update, this, &Agent::processAgentAvatarAndAudio); - + _scriptEngine->run(); - + Frame::clearFrameHandler(AUDIO_FRAME_TYPE); Frame::clearFrameHandler(AVATAR_FRAME_TYPE); - + setFinished(true); } diff --git a/assignment-client/src/Agent.h b/assignment-client/src/Agent.h index d643b65267..205d4d4a80 100644 --- a/assignment-client/src/Agent.h +++ b/assignment-client/src/Agent.h @@ -55,6 +55,10 @@ public slots: void playAvatarSound(Sound* avatarSound) { setAvatarSound(avatarSound); } private slots: + void requestScript(); + void scriptRequestFinished(); + void executeScript(); + void handleAudioPacket(QSharedPointer packet); void handleOctreePacket(QSharedPointer packet, SharedNodePointer senderNode); void handleJurisdictionPacket(QSharedPointer packet, SharedNodePointer senderNode); @@ -73,6 +77,7 @@ private: void sendAvatarIdentityPacket(); void sendAvatarBillboardPacket(); + QString _scriptContents; bool _isListeningToAudioStream = false; Sound* _avatarSound = nullptr; int _numAvatarSoundSentBytes = 0; diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index b4243ef8a0..ddc0003eaa 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -1097,29 +1097,37 @@ bool DomainServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url if (connection->requestOperation() == QNetworkAccessManager::GetOperation && assignmentRegex.indexIn(url.path()) != -1) { - QUuid matchingUUID = QUuid(assignmentRegex.cap(1)); - - SharedAssignmentPointer matchingAssignment = _allAssignments.value(matchingUUID); - if (!matchingAssignment) { - // check if we have a pending assignment that matches this temp UUID, and it is a scripted assignment - QUuid assignmentUUID = _gatekeeper.assignmentUUIDForPendingAssignment(matchingUUID); - if (!assignmentUUID.isNull()) { - matchingAssignment = _allAssignments.value(assignmentUUID); - - if (matchingAssignment && matchingAssignment->getType() == Assignment::AgentType) { - // we have a matching assignment and it is for the right type, have the HTTP manager handle it - // via correct URL for the script so the client can download - - QUrl scriptURL = url; - scriptURL.setPath(URI_ASSIGNMENT + "/scripts/" - + uuidStringWithoutCurlyBraces(assignmentUUID)); - - // have the HTTPManager serve the appropriate script file - return _httpManager.handleHTTPRequest(connection, scriptURL, true); - } - } + QUuid nodeUUID = QUuid(assignmentRegex.cap(1)); + + auto matchingNode = nodeList->nodeWithUUID(nodeUUID); + + // don't handle if we don't have a matching node + if (!matchingNode) { + return false; } - + + auto nodeData = dynamic_cast(matchingNode->getLinkedData()); + + // don't handle if we don't have node data for this node + if (!nodeData) { + return false; + } + + SharedAssignmentPointer matchingAssignment = _allAssignments.value(nodeData->getAssignmentUUID()); + + // check if we have an assignment that matches this temp UUID, and it is a scripted assignment + if (matchingAssignment && matchingAssignment->getType() == Assignment::AgentType) { + // we have a matching assignment and it is for the right type, have the HTTP manager handle it + // via correct URL for the script so the client can download + + QUrl scriptURL = url; + scriptURL.setPath(URI_ASSIGNMENT + "/scripts/" + + uuidStringWithoutCurlyBraces(matchingAssignment->getUUID())); + + // have the HTTPManager serve the appropriate script file + return _httpManager.handleHTTPRequest(connection, scriptURL, true); + } + // request not handled return false; } From aa836aa4b55d9771ecc5ad0623baf8cfc18c04c7 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Fri, 20 Nov 2015 11:44:04 -0800 Subject: [PATCH 07/10] add a script request timeout --- assignment-client/src/Agent.cpp | 19 ++++++++++++++++--- assignment-client/src/Agent.h | 1 + 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/assignment-client/src/Agent.cpp b/assignment-client/src/Agent.cpp index 5ec4b91fa6..cb3f4d9b05 100644 --- a/assignment-client/src/Agent.cpp +++ b/assignment-client/src/Agent.cpp @@ -163,6 +163,12 @@ void Agent::requestScript() { QNetworkRequest networkRequest = QNetworkRequest(scriptURL); networkRequest.setHeader(QNetworkRequest::UserAgentHeader, HIGH_FIDELITY_USER_AGENT); + // setup a timeout for script request + static const int SCRIPT_TIMEOUT_MS = 10000; + _scriptRequestTimeout = new QTimer(this); + connect(_scriptRequestTimeout, &QTimer::timeout, this, &Agent::scriptRequestFinished); + _scriptRequestTimeout->start(SCRIPT_TIMEOUT_MS); + qDebug() << "Downloading script at" << scriptURL.toString(); QNetworkReply* reply = networkAccessManager.get(networkRequest); connect(reply, &QNetworkReply::finished, this, &Agent::scriptRequestFinished); @@ -170,8 +176,10 @@ void Agent::requestScript() { void Agent::scriptRequestFinished() { auto reply = qobject_cast(sender()); + + _scriptRequestTimeout->stop(); - if (reply->error() != QNetworkReply::NoError) { + if (reply && reply->error() == QNetworkReply::NoError) { _scriptContents = reply->readAll(); qDebug() << "Downloaded script:" << _scriptContents; @@ -179,8 +187,13 @@ void Agent::scriptRequestFinished() { // to return before calling executeScript QMetaObject::invokeMethod(this, "executeScript", Qt::QueuedConnection); } else { - qDebug() << "Failed to download script at" << reply->url().toString() << " - bailing on assignment."; - qDebug() << "QNetworkReply error was" << reply->errorString(); + if (reply) { + qDebug() << "Failed to download script at" << reply->url().toString() << " - bailing on assignment."; + qDebug() << "QNetworkReply error was" << reply->errorString(); + } else { + qDebug() << "Failed to download script - request timed out. Bailing on assignment."; + } + setFinished(true); } diff --git a/assignment-client/src/Agent.h b/assignment-client/src/Agent.h index 205d4d4a80..6819976633 100644 --- a/assignment-client/src/Agent.h +++ b/assignment-client/src/Agent.h @@ -78,6 +78,7 @@ private: void sendAvatarBillboardPacket(); QString _scriptContents; + QTimer* _scriptRequestTimeout { nullptr }; bool _isListeningToAudioStream = false; Sound* _avatarSound = nullptr; int _numAvatarSoundSentBytes = 0; From cf204ad5b07abc3a4db42febb9b861fa24bd68ef Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Fri, 20 Nov 2015 11:44:58 -0800 Subject: [PATCH 08/10] cleanup executeScript comment in Agent --- assignment-client/src/Agent.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assignment-client/src/Agent.cpp b/assignment-client/src/Agent.cpp index cb3f4d9b05..2fdba9e256 100644 --- a/assignment-client/src/Agent.cpp +++ b/assignment-client/src/Agent.cpp @@ -183,7 +183,7 @@ void Agent::scriptRequestFinished() { _scriptContents = reply->readAll(); qDebug() << "Downloaded script:" << _scriptContents; - // we could just call executeScript directly - we use a QueuedConnect to allow scriptRequestFinished + // we could just call executeScript directly - we use a QueuedConnection to allow scriptRequestFinished // to return before calling executeScript QMetaObject::invokeMethod(this, "executeScript", Qt::QueuedConnection); } else { From e97e864de77ae0e3dea74b30b5d122852dcdca81 Mon Sep 17 00:00:00 2001 From: samcake Date: Fri, 20 Nov 2015 15:05:17 -0800 Subject: [PATCH 09/10] Getting the animation playback to work --- libraries/avatars/src/AvatarData.cpp | 23 +++++++++++++++-------- libraries/avatars/src/AvatarData.h | 2 +- 2 files changed, 16 insertions(+), 9 deletions(-) diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index 44a781bc24..3b8623243e 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -801,6 +801,14 @@ void AvatarData::changeReferential(Referential* ref) { _referential = ref; } +void AvatarData::setRawJointData(QVector data) { + if (QThread::currentThread() != thread()) { + QMetaObject::invokeMethod(this, "setRawJointData", Q_ARG(QVector, data)); + return; + } + _jointData = data; +} + void AvatarData::setJointData(int index, const glm::quat& rotation, const glm::vec3& translation) { if (index == -1) { return; @@ -1538,16 +1546,15 @@ void AvatarData::fromFrame(const QByteArray& frameData, AvatarData& result) { QVector jointArray; QJsonArray jointArrayJson = root[JSON_AVATAR_JOINT_ARRAY].toArray(); jointArray.reserve(jointArrayJson.size()); + int i = 0; for (const auto& jointJson : jointArrayJson) { - jointArray.push_back(jointDataFromJsonValue(jointJson)); + auto joint = jointDataFromJsonValue(jointJson); + jointArray.push_back(joint); + result.setJointData(i, joint.rotation, joint.translation); + result._jointData[i].rotationSet = true; // Have to do that yep + i++; } - - QVector jointRotations; - jointRotations.reserve(jointArray.size()); - for (const auto& joint : jointArray) { - jointRotations.push_back(joint.rotation); - } - result.setJointRotations(jointRotations); + result.setRawJointData(jointArray); } #if 0 diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index 846c314e4b..847a369185 100644 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -247,7 +247,7 @@ public: Q_INVOKABLE char getHandState() const { return _handState; } const QVector& getRawJointData() const { return _jointData; } - void setRawJointData(QVector data) { _jointData = data; } + Q_INVOKABLE void setRawJointData(QVector data); Q_INVOKABLE virtual void setJointData(int index, const glm::quat& rotation, const glm::vec3& translation); Q_INVOKABLE virtual void setJointRotation(int index, const glm::quat& rotation); From 0dc551409dcac284d1ace3d7b2ad8f185861edf9 Mon Sep 17 00:00:00 2001 From: samcake Date: Fri, 20 Nov 2015 15:22:01 -0800 Subject: [PATCH 10/10] Fixing a comment --- libraries/avatars/src/AvatarData.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index 3b8623243e..0574f712bc 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -1551,7 +1551,7 @@ void AvatarData::fromFrame(const QByteArray& frameData, AvatarData& result) { auto joint = jointDataFromJsonValue(jointJson); jointArray.push_back(joint); result.setJointData(i, joint.rotation, joint.translation); - result._jointData[i].rotationSet = true; // Have to do that yep + result._jointData[i].rotationSet = true; // Have to do that to broadcast the avatar new pose i++; } result.setRawJointData(jointArray);