From c8c8bccbf3b7768c88791ce2e6f37fe10b2a319a Mon Sep 17 00:00:00 2001 From: matsukaze Date: Mon, 9 Jun 2014 17:52:50 -0400 Subject: [PATCH 01/13] Job #19766 BUG: Stop or reload all scripts crashes interface fixed. QUrl(name).toString() does not equal name, therefore removing name from ScriptEngineHash was failing, and a dangling pointer was left in the script engine hash map. --- interface/src/Application.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 574df09ee2..9fb821bff4 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -3494,7 +3494,7 @@ ScriptEngine* Application::loadScript(const QString& scriptName, bool loadScript // start the script on a new thread... QUrl scriptUrl(scriptName); scriptEngine = new ScriptEngine(scriptUrl, &_controllerScriptingInterface); - _scriptEnginesHash.insert(scriptName, scriptEngine); + _scriptEnginesHash.insert(scriptUrl.toString(), scriptEngine); if (!scriptEngine->hasScript()) { qDebug() << "Application::loadScript(), script failed to load..."; From b4e984086505814cbc426226777b74a113da8362 Mon Sep 17 00:00:00 2001 From: matsukaze Date: Mon, 9 Jun 2014 23:15:45 -0400 Subject: [PATCH 02/13] Job #19766 BUG: Stop or reload all scripts crashes interface fix, part 2. Keep the scriptUrl internal to the ScriptEngine class and refer to it externally by the file name string. Change the ScriptEngine constructor to accept a filename QString, instead of a QUrl. Resolve constructor ambiguity in Particle, which creates anonymous ScriptEngine. --- interface/src/Application.cpp | 5 ++--- libraries/particles/src/Particle.cpp | 6 +++--- libraries/script-engine/src/ScriptEngine.cpp | 12 +++++------- libraries/script-engine/src/ScriptEngine.h | 2 +- 4 files changed, 11 insertions(+), 14 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 9fb821bff4..3ec5588c61 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -3492,9 +3492,8 @@ ScriptEngine* Application::loadScript(const QString& scriptName, bool loadScript scriptEngine = new ScriptEngine(NO_SCRIPT, "", &_controllerScriptingInterface); } else { // start the script on a new thread... - QUrl scriptUrl(scriptName); - scriptEngine = new ScriptEngine(scriptUrl, &_controllerScriptingInterface); - _scriptEnginesHash.insert(scriptUrl.toString(), scriptEngine); + scriptEngine = new ScriptEngine(scriptName, &_controllerScriptingInterface); + _scriptEnginesHash.insert(scriptName, scriptEngine); if (!scriptEngine->hasScript()) { qDebug() << "Application::loadScript(), script failed to load..."; diff --git a/libraries/particles/src/Particle.cpp b/libraries/particles/src/Particle.cpp index 59265c00dc..90e59b9422 100644 --- a/libraries/particles/src/Particle.cpp +++ b/libraries/particles/src/Particle.cpp @@ -873,7 +873,7 @@ void Particle::endParticleScriptContext(ScriptEngine& engine, ParticleScriptObje void Particle::executeUpdateScripts() { // Only run this particle script if there's a script attached directly to the particle. if (!_script.isEmpty()) { - ScriptEngine engine(_script); + ScriptEngine engine(_script, QString("")); ParticleScriptObject particleScriptable(this); startParticleScriptContext(engine, particleScriptable); particleScriptable.emitUpdate(); @@ -884,7 +884,7 @@ void Particle::executeUpdateScripts() { void Particle::collisionWithParticle(Particle* other, const glm::vec3& penetration) { // Only run this particle script if there's a script attached directly to the particle. if (!_script.isEmpty()) { - ScriptEngine engine(_script); + ScriptEngine engine(_script, QString("")); ParticleScriptObject particleScriptable(this); startParticleScriptContext(engine, particleScriptable); ParticleScriptObject otherParticleScriptable(other); @@ -896,7 +896,7 @@ void Particle::collisionWithParticle(Particle* other, const glm::vec3& penetrati void Particle::collisionWithVoxel(VoxelDetail* voxelDetails, const glm::vec3& penetration) { // Only run this particle script if there's a script attached directly to the particle. if (!_script.isEmpty()) { - ScriptEngine engine(_script); + ScriptEngine engine(_script, QString("")); ParticleScriptObject particleScriptable(this); startParticleScriptContext(engine, particleScriptable); particleScriptable.emitCollisionWithVoxel(*voxelDetails, penetration); diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index a4aae61248..025f316d29 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -93,7 +93,7 @@ ScriptEngine::ScriptEngine(const QString& scriptContents, const QString& fileNam { } -ScriptEngine::ScriptEngine(const QUrl& scriptURL, +ScriptEngine::ScriptEngine(const QString& fileNameString, AbstractControllerScriptingInterface* controllerScriptingInterface) : _scriptContents(), _isFinished(false), @@ -110,21 +110,19 @@ ScriptEngine::ScriptEngine(const QUrl& scriptURL, _controllerScriptingInterface(controllerScriptingInterface), _avatarData(NULL), _scriptName(), - _fileNameString(), + _fileNameString(fileNameString), _quatLibrary(), _vec3Library(), _uuidLibrary(), _animationCache(this) { - QString scriptURLString = scriptURL.toString(); - _fileNameString = scriptURLString; - - QUrl url(scriptURL); + QUrl url(fileNameString); + QString scriptUrlString = url.toString(); // 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) { - url = QUrl::fromLocalFile(scriptURLString); + url = QUrl::fromLocalFile(scriptUrlString); } // ok, let's see if it's valid... and if so, load it diff --git a/libraries/script-engine/src/ScriptEngine.h b/libraries/script-engine/src/ScriptEngine.h index bf2ac40568..f88017515e 100644 --- a/libraries/script-engine/src/ScriptEngine.h +++ b/libraries/script-engine/src/ScriptEngine.h @@ -40,7 +40,7 @@ const unsigned int SCRIPT_DATA_CALLBACK_USECS = floor(((1.0 / 60.0f) * 1000 * 10 class ScriptEngine : public QObject { Q_OBJECT public: - ScriptEngine(const QUrl& scriptURL, + ScriptEngine(const QString& fileNameString, AbstractControllerScriptingInterface* controllerScriptingInterface = NULL); ScriptEngine(const QString& scriptContents = NO_SCRIPT, From d84beee3e49699454f4b44414ec11910ccf12204 Mon Sep 17 00:00:00 2001 From: wangyix Date: Tue, 10 Jun 2014 11:05:51 -0700 Subject: [PATCH 03/13] VoxelPacketProcessor -> OctreePacketProcessor added nodes bookkeeping in ReceivedPacketProcessor; added check in sendNack() to not send NACKs to nodes that have sent packets that are waiting in the message queue. --- interface/src/Application.cpp | 14 ++++++++++---- interface/src/Application.h | 8 ++++---- interface/src/DatagramProcessor.cpp | 4 ++-- interface/src/ui/ApplicationOverlay.cpp | 4 ++-- ...etProcessor.cpp => OctreePacketProcessor.cpp} | 10 +++++----- ...PacketProcessor.h => OctreePacketProcessor.h} | 10 +++++----- libraries/networking/src/NetworkPacket.cpp | 16 ++++++++-------- libraries/networking/src/NetworkPacket.h | 8 ++++---- libraries/networking/src/PacketSender.cpp | 2 +- .../networking/src/ReceivedPacketProcessor.cpp | 10 ++++++---- .../networking/src/ReceivedPacketProcessor.h | 11 +++++++++-- 11 files changed, 56 insertions(+), 41 deletions(-) rename interface/src/voxels/{VoxelPacketProcessor.cpp => OctreePacketProcessor.cpp} (92%) rename interface/src/voxels/{VoxelPacketProcessor.h => OctreePacketProcessor.h} (76%) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 3cfec3190e..9f62742e6c 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -158,7 +158,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) : _mousePressed(false), _audio(STARTUP_JITTER_SAMPLES), _enableProcessVoxelsThread(true), - _voxelProcessor(), + _octreeProcessor(), _voxelHideShowThread(&_voxels), _packetsPerSecond(0), _bytesPerSecond(0), @@ -418,7 +418,7 @@ Application::~Application() { _audio.thread()->quit(); _audio.thread()->wait(); - _voxelProcessor.terminate(); + _octreeProcessor.terminate(); _voxelHideShowThread.terminate(); _voxelEditSender.terminate(); _particleEditSender.terminate(); @@ -517,7 +517,7 @@ void Application::initializeGL() { qDebug( "init() complete."); // create thread for parsing of voxel data independent of the main network and rendering threads - _voxelProcessor.initialize(_enableProcessVoxelsThread); + _octreeProcessor.initialize(_enableProcessVoxelsThread); _voxelEditSender.initialize(_enableProcessVoxelsThread); _voxelHideShowThread.initialize(_enableProcessVoxelsThread); _particleEditSender.initialize(_enableProcessVoxelsThread); @@ -1884,7 +1884,7 @@ void Application::updateThreads(float deltaTime) { // parse voxel packets if (!_enableProcessVoxelsThread) { - _voxelProcessor.threadRoutine(); + _octreeProcessor.threadRoutine(); _voxelHideShowThread.threadRoutine(); _voxelEditSender.threadRoutine(); _particleEditSender.threadRoutine(); @@ -2125,6 +2125,12 @@ void Application::sendNack() { || node->getType() == NodeType::ModelServer) ) { + // if there are octree packets from this node that are waiting to be processed, + // don't send a NACK since the missing packets may be among those waiting packets. + if (_octreeProcessor.hasPacketsToProcessFrom(node)) { + continue; + } + QUuid nodeUUID = node->getUUID(); _octreeSceneStatsLock.lockForRead(); diff --git a/interface/src/Application.h b/interface/src/Application.h index 170be43493..2889dcb301 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -88,7 +88,7 @@ #include "voxels/VoxelFade.h" #include "voxels/VoxelHideShowThread.h" #include "voxels/VoxelImporter.h" -#include "voxels/VoxelPacketProcessor.h" +#include "voxels/OctreePacketProcessor.h" #include "voxels/VoxelSystem.h" @@ -129,7 +129,7 @@ static const float MIRROR_FIELD_OF_VIEW = 30.0f; class Application : public QApplication { Q_OBJECT - friend class VoxelPacketProcessor; + friend class OctreePacketProcessor; friend class VoxelEditPacketSender; friend class DatagramProcessor; @@ -192,7 +192,7 @@ public: ViewFrustum* getShadowViewFrustum() { return &_shadowViewFrustum; } VoxelSystem* getVoxels() { return &_voxels; } VoxelTree* getVoxelTree() { return _voxels.getTree(); } - const VoxelPacketProcessor& getVoxelPacketProcessor() const { return _voxelProcessor; } + const OctreePacketProcessor& getOctreePacketProcessor() const { return _octreeProcessor; } ParticleTreeRenderer* getParticles() { return &_particles; } MetavoxelSystem* getMetavoxels() { return &_metavoxels; } ModelTreeRenderer* getModels() { return &_models; } @@ -533,7 +533,7 @@ private: Audio _audio; bool _enableProcessVoxelsThread; - VoxelPacketProcessor _voxelProcessor; + OctreePacketProcessor _octreeProcessor; VoxelHideShowThread _voxelHideShowThread; VoxelEditPacketSender _voxelEditSender; ParticleEditPacketSender _particleEditSender; diff --git a/interface/src/DatagramProcessor.cpp b/interface/src/DatagramProcessor.cpp index 56078c1a8d..cdd7e7ef0f 100644 --- a/interface/src/DatagramProcessor.cpp +++ b/interface/src/DatagramProcessor.cpp @@ -71,7 +71,7 @@ void DatagramProcessor::processDatagrams() { case PacketTypeOctreeStats: case PacketTypeEnvironmentData: { PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), - "Application::networkReceive()... _voxelProcessor.queueReceivedPacket()"); + "Application::networkReceive()... _octreeProcessor.queueReceivedPacket()"); bool wantExtraDebugging = application->getLogger()->extraDebugging(); if (wantExtraDebugging && packetTypeForPacket(incomingPacket) == PacketTypeVoxelData) { @@ -92,7 +92,7 @@ void DatagramProcessor::processDatagrams() { if (matchedNode) { // add this packet to our list of voxel packets and process them on the voxel processing - application->_voxelProcessor.queueReceivedPacket(matchedNode, incomingPacket); + application->_octreeProcessor.queueReceivedPacket(matchedNode, incomingPacket); } break; diff --git a/interface/src/ui/ApplicationOverlay.cpp b/interface/src/ui/ApplicationOverlay.cpp index 49ec8ecddb..ec2d6c73b3 100644 --- a/interface/src/ui/ApplicationOverlay.cpp +++ b/interface/src/ui/ApplicationOverlay.cpp @@ -45,7 +45,7 @@ void ApplicationOverlay::renderOverlay(bool renderToTexture) { QGLWidget* glWidget = application->getGLWidget(); MyAvatar* myAvatar = application->getAvatar(); Audio* audio = application->getAudio(); - const VoxelPacketProcessor& voxelPacketProcessor = application->getVoxelPacketProcessor(); + const OctreePacketProcessor& octreePacketProcessor = application->getOctreePacketProcessor(); BandwidthMeter* bandwidthMeter = application->getBandwidthMeter(); NodeBounds& nodeBoundsDisplay = application->getNodeBoundsDisplay(); @@ -200,7 +200,7 @@ void ApplicationOverlay::renderOverlay(bool renderToTexture) { if (Menu::getInstance()->isOptionChecked(MenuOption::Stats)) { // let's set horizontal offset to give stats some margin to mirror int horizontalOffset = MIRROR_VIEW_WIDTH + MIRROR_VIEW_LEFT_PADDING * 2; - int voxelPacketsToProcess = voxelPacketProcessor.packetsToProcessCount(); + int voxelPacketsToProcess = octreePacketProcessor.packetsToProcessCount(); // Onscreen text about position, servers, etc Stats::getInstance()->display(WHITE_TEXT, horizontalOffset, application->getFps(), application->getPacketsPerSecond(), application->getBytesPerSecond(), voxelPacketsToProcess); // Bandwidth meter diff --git a/interface/src/voxels/VoxelPacketProcessor.cpp b/interface/src/voxels/OctreePacketProcessor.cpp similarity index 92% rename from interface/src/voxels/VoxelPacketProcessor.cpp rename to interface/src/voxels/OctreePacketProcessor.cpp index 095c378c04..66190a5689 100644 --- a/interface/src/voxels/VoxelPacketProcessor.cpp +++ b/interface/src/voxels/OctreePacketProcessor.cpp @@ -1,5 +1,5 @@ // -// VoxelPacketProcessor.cpp +// OctreePacketProcessor.cpp // interface/src/voxels // // Created by Brad Hefta-Gaub on 8/12/13. @@ -13,18 +13,18 @@ #include "Application.h" #include "Menu.h" -#include "VoxelPacketProcessor.h" +#include "OctreePacketProcessor.h" -void VoxelPacketProcessor::processPacket(const SharedNodePointer& sendingNode, const QByteArray& packet) { +void OctreePacketProcessor::processPacket(const SharedNodePointer& sendingNode, const QByteArray& packet) { PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), - "VoxelPacketProcessor::processPacket()"); + "OctreePacketProcessor::processPacket()"); QByteArray mutablePacket = packet; const int WAY_BEHIND = 300; if (packetsToProcessCount() > WAY_BEHIND && Application::getInstance()->getLogger()->extraDebugging()) { - qDebug("VoxelPacketProcessor::processPacket() packets to process=%d", packetsToProcessCount()); + qDebug("OctreePacketProcessor::processPacket() packets to process=%d", packetsToProcessCount()); } ssize_t messageLength = mutablePacket.size(); diff --git a/interface/src/voxels/VoxelPacketProcessor.h b/interface/src/voxels/OctreePacketProcessor.h similarity index 76% rename from interface/src/voxels/VoxelPacketProcessor.h rename to interface/src/voxels/OctreePacketProcessor.h index 36456c5cc2..bdf25806f3 100644 --- a/interface/src/voxels/VoxelPacketProcessor.h +++ b/interface/src/voxels/OctreePacketProcessor.h @@ -1,5 +1,5 @@ // -// VoxelPacketProcessor.h +// OctreePacketProcessor.h // interface/src/voxels // // Created by Brad Hefta-Gaub on 8/12/13. @@ -9,16 +9,16 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -#ifndef hifi_VoxelPacketProcessor_h -#define hifi_VoxelPacketProcessor_h +#ifndef hifi_OctreePacketProcessor_h +#define hifi_OctreePacketProcessor_h #include /// Handles processing of incoming voxel packets for the interface application. As with other ReceivedPacketProcessor classes /// the user is responsible for reading inbound packets and adding them to the processing queue by calling queueReceivedPacket() -class VoxelPacketProcessor : public ReceivedPacketProcessor { +class OctreePacketProcessor : public ReceivedPacketProcessor { Q_OBJECT protected: virtual void processPacket(const SharedNodePointer& sendingNode, const QByteArray& packet); }; -#endif // hifi_VoxelPacketProcessor_h +#endif // hifi_OctreePacketProcessor_h diff --git a/libraries/networking/src/NetworkPacket.cpp b/libraries/networking/src/NetworkPacket.cpp index bf18aa9b37..f9d6c7ea7b 100644 --- a/libraries/networking/src/NetworkPacket.cpp +++ b/libraries/networking/src/NetworkPacket.cpp @@ -17,9 +17,9 @@ #include "NetworkPacket.h" -void NetworkPacket::copyContents(const SharedNodePointer& destinationNode, const QByteArray& packet) { +void NetworkPacket::copyContents(const SharedNodePointer& sendingNode, const QByteArray& packet) { if (packet.size() && packet.size() <= MAX_PACKET_SIZE) { - _destinationNode = destinationNode; + _sendingNode = sendingNode; _byteArray = packet; } else { qDebug(">>> NetworkPacket::copyContents() unexpected length = %d", packet.size()); @@ -27,28 +27,28 @@ void NetworkPacket::copyContents(const SharedNodePointer& destinationNode, const } NetworkPacket::NetworkPacket(const NetworkPacket& packet) { - copyContents(packet.getDestinationNode(), packet.getByteArray()); + copyContents(packet.getSendingNode(), packet.getByteArray()); } -NetworkPacket::NetworkPacket(const SharedNodePointer& destinationNode, const QByteArray& packet) { - copyContents(destinationNode, packet); +NetworkPacket::NetworkPacket(const SharedNodePointer& sendingNode, const QByteArray& packet) { + copyContents(sendingNode, packet); }; // copy assignment NetworkPacket& NetworkPacket::operator=(NetworkPacket const& other) { - copyContents(other.getDestinationNode(), other.getByteArray()); + copyContents(other.getSendingNode(), other.getByteArray()); return *this; } #ifdef HAS_MOVE_SEMANTICS // move, same as copy, but other packet won't be used further NetworkPacket::NetworkPacket(NetworkPacket && packet) { - copyContents(packet.getDestinationNode(), packet.getByteArray()); + copyContents(packet.getSendingNode(), packet.getByteArray()); } // move assignment NetworkPacket& NetworkPacket::operator=(NetworkPacket&& other) { - copyContents(other.getDestinationNode(), other.getByteArray()); + copyContents(other.getSendingNode(), other.getByteArray()); return *this; } #endif diff --git a/libraries/networking/src/NetworkPacket.h b/libraries/networking/src/NetworkPacket.h index 94ddf8d56e..deb4cb9fb9 100644 --- a/libraries/networking/src/NetworkPacket.h +++ b/libraries/networking/src/NetworkPacket.h @@ -34,15 +34,15 @@ public: NetworkPacket& operator= (NetworkPacket&& other); // move assignment #endif - NetworkPacket(const SharedNodePointer& destinationNode, const QByteArray& byteArray); + NetworkPacket(const SharedNodePointer& sendingNode, const QByteArray& byteArray); - const SharedNodePointer& getDestinationNode() const { return _destinationNode; } + const SharedNodePointer& getSendingNode() const { return _sendingNode; } const QByteArray& getByteArray() const { return _byteArray; } private: - void copyContents(const SharedNodePointer& destinationNode, const QByteArray& byteArray); + void copyContents(const SharedNodePointer& sendingNode, const QByteArray& byteArray); - SharedNodePointer _destinationNode; + SharedNodePointer _sendingNode; QByteArray _byteArray; }; diff --git a/libraries/networking/src/PacketSender.cpp b/libraries/networking/src/PacketSender.cpp index 5f7502a738..ae844d4f99 100644 --- a/libraries/networking/src/PacketSender.cpp +++ b/libraries/networking/src/PacketSender.cpp @@ -271,7 +271,7 @@ bool PacketSender::nonThreadedProcess() { unlock(); // send the packet through the NodeList... - NodeList::getInstance()->writeDatagram(temporary.getByteArray(), temporary.getDestinationNode()); + NodeList::getInstance()->writeDatagram(temporary.getByteArray(), temporary.getSendingNode()); packetsSentThisCall++; _packetsOverCheckInterval++; _totalPacketsSent++; diff --git a/libraries/networking/src/ReceivedPacketProcessor.cpp b/libraries/networking/src/ReceivedPacketProcessor.cpp index d54e165285..ea613082ce 100644 --- a/libraries/networking/src/ReceivedPacketProcessor.cpp +++ b/libraries/networking/src/ReceivedPacketProcessor.cpp @@ -17,13 +17,14 @@ void ReceivedPacketProcessor::terminating() { _hasPackets.wakeAll(); } -void ReceivedPacketProcessor::queueReceivedPacket(const SharedNodePointer& destinationNode, const QByteArray& packet) { +void ReceivedPacketProcessor::queueReceivedPacket(const SharedNodePointer& sendingNode, const QByteArray& packet) { // Make sure our Node and NodeList knows we've heard from this node. - destinationNode->setLastHeardMicrostamp(usecTimestampNow()); + sendingNode->setLastHeardMicrostamp(usecTimestampNow()); - NetworkPacket networkPacket(destinationNode, packet); + NetworkPacket networkPacket(sendingNode, packet); lock(); _packets.push_back(networkPacket); + _nodePacketCounts[sendingNode->getUUID()]++; unlock(); // Make sure to wake our actual processing thread because we now have packets for it to process. @@ -42,8 +43,9 @@ bool ReceivedPacketProcessor::process() { NetworkPacket& packet = _packets.front(); // get the oldest packet NetworkPacket temporary = packet; // make a copy of the packet in case the vector is resized on us _packets.erase(_packets.begin()); // remove the oldest packet + _nodePacketCounts[temporary.getSendingNode()->getUUID()]--; unlock(); // let others add to the packets - processPacket(temporary.getDestinationNode(), temporary.getByteArray()); // process our temporary copy + processPacket(temporary.getSendingNode(), temporary.getByteArray()); // process our temporary copy } return isStillRunning(); // keep running till they terminate us } diff --git a/libraries/networking/src/ReceivedPacketProcessor.h b/libraries/networking/src/ReceivedPacketProcessor.h index f8306b4896..dea7448af1 100644 --- a/libraries/networking/src/ReceivedPacketProcessor.h +++ b/libraries/networking/src/ReceivedPacketProcessor.h @@ -28,11 +28,16 @@ public: /// \param packetData pointer to received data /// \param ssize_t packetLength size of received data /// \thread network receive thread - void queueReceivedPacket(const SharedNodePointer& destinationNode, const QByteArray& packet); + void queueReceivedPacket(const SharedNodePointer& sendingNode, const QByteArray& packet); /// Are there received packets waiting to be processed bool hasPacketsToProcess() const { return _packets.size() > 0; } + /// Are there received packets waiting to be processed from a certain node + bool hasPacketsToProcessFrom(const SharedNodePointer& sendingNode) const { + return _nodePacketCounts[sendingNode->getUUID()] > 0; + } + /// How many received packets waiting are to be processed int packetsToProcessCount() const { return _packets.size(); } @@ -51,7 +56,9 @@ protected: private: - std::vector _packets; + QVector _packets; + QHash _nodePacketCounts; + QWaitCondition _hasPackets; QMutex _waitingOnPacketsMutex; }; From b96b6c9857086c811b50b1d231b67d0b1c87127b Mon Sep 17 00:00:00 2001 From: wangyix Date: Tue, 10 Jun 2014 12:24:47 -0700 Subject: [PATCH 04/13] added default constructor for NetworkPacket updated param comments in ReceivedPacketProcessor --- interface/src/Application.cpp | 2 +- libraries/networking/src/NetworkPacket.h | 1 + libraries/networking/src/ReceivedPacketProcessor.h | 6 ++---- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 9f62742e6c..eab15ed678 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -2095,7 +2095,7 @@ void Application::updateMyAvatar(float deltaTime) { } } - // sent a nack packet containing missing sequence numbers of received packets + // sent nack packets containing missing sequence numbers of received packets from nodes { quint64 now = usecTimestampNow(); quint64 sinceLastNack = now - _lastNackTime; diff --git a/libraries/networking/src/NetworkPacket.h b/libraries/networking/src/NetworkPacket.h index deb4cb9fb9..52e8a36712 100644 --- a/libraries/networking/src/NetworkPacket.h +++ b/libraries/networking/src/NetworkPacket.h @@ -26,6 +26,7 @@ /// Storage of not-yet processed inbound, or not yet sent outbound generic UDP network packet class NetworkPacket { public: + NetworkPacket() { } NetworkPacket(const NetworkPacket& packet); // copy constructor NetworkPacket& operator= (const NetworkPacket& other); // copy assignment diff --git a/libraries/networking/src/ReceivedPacketProcessor.h b/libraries/networking/src/ReceivedPacketProcessor.h index dea7448af1..096005ffe7 100644 --- a/libraries/networking/src/ReceivedPacketProcessor.h +++ b/libraries/networking/src/ReceivedPacketProcessor.h @@ -43,10 +43,8 @@ public: protected: /// Callback for processing of recieved packets. Implement this to process the incoming packets. - /// \param sockaddr& senderAddress the address of the sender - /// \param packetData pointer to received data - /// \param ssize_t packetLength size of received data - /// \thread "this" individual processing thread + /// \param SharedNodePointer& sendingNode the node that sent this packet + /// \param QByteArray& the packet to be processed virtual void processPacket(const SharedNodePointer& sendingNode, const QByteArray& packet) = 0; /// Implements generic processing behavior for this thread. From 5ab271330e0d9a768adc1685e069b5a7560febd1 Mon Sep 17 00:00:00 2001 From: wangyix Date: Tue, 10 Jun 2014 15:35:48 -0700 Subject: [PATCH 05/13] moved sendNack() into packetDistributor() ...for the purpose of enforcing packets sent per interval in OctreeSendThread. Corrected mistake in keeping track of number of special packets sent where sendSpecialPacket() was assumed to only send one packet per call. --- assignment-client/src/models/ModelServer.cpp | 4 +- assignment-client/src/models/ModelServer.h | 2 +- .../src/octree/OctreeSendThread.cpp | 49 +++++++++---------- .../src/octree/OctreeSendThread.h | 3 -- assignment-client/src/octree/OctreeServer.h | 2 +- .../src/particles/ParticleServer.cpp | 4 +- .../src/particles/ParticleServer.h | 2 +- assignment-client/src/voxels/VoxelServer.cpp | 3 +- assignment-client/src/voxels/VoxelServer.h | 2 +- 9 files changed, 34 insertions(+), 37 deletions(-) diff --git a/assignment-client/src/models/ModelServer.cpp b/assignment-client/src/models/ModelServer.cpp index 07359f001a..ae6ffaf969 100644 --- a/assignment-client/src/models/ModelServer.cpp +++ b/assignment-client/src/models/ModelServer.cpp @@ -86,7 +86,7 @@ bool ModelServer::hasSpecialPacketToSend(const SharedNodePointer& node) { return shouldSendDeletedModels; } -int ModelServer::sendSpecialPacket(OctreeQueryNode* queryNode, const SharedNodePointer& node) { +int ModelServer::sendSpecialPacket(const SharedNodePointer& node, OctreeQueryNode* queryNode, int& packetsSent) { unsigned char outputBuffer[MAX_PACKET_SIZE]; size_t packetLength = 0; @@ -99,6 +99,7 @@ int ModelServer::sendSpecialPacket(OctreeQueryNode* queryNode, const SharedNodeP bool hasMoreToSend = true; // TODO: is it possible to send too many of these packets? what if you deleted 1,000,000 models? + packetsSent = 0; while (hasMoreToSend) { hasMoreToSend = tree->encodeModelsDeletedSince(queryNode->getSequenceNumber(), deletedModelsSentAt, outputBuffer, MAX_PACKET_SIZE, packetLength); @@ -107,6 +108,7 @@ int ModelServer::sendSpecialPacket(OctreeQueryNode* queryNode, const SharedNodeP NodeList::getInstance()->writeDatagram((char*) outputBuffer, packetLength, SharedNodePointer(node)); queryNode->packetSent(outputBuffer, packetLength); + packetsSent++; } nodeData->setLastDeletedModelsSentAt(deletePacketSentAt); diff --git a/assignment-client/src/models/ModelServer.h b/assignment-client/src/models/ModelServer.h index 7e7f239f2a..38acc7f1e1 100644 --- a/assignment-client/src/models/ModelServer.h +++ b/assignment-client/src/models/ModelServer.h @@ -37,7 +37,7 @@ public: // subclass may implement these method virtual void beforeRun(); virtual bool hasSpecialPacketToSend(const SharedNodePointer& node); - virtual int sendSpecialPacket(OctreeQueryNode* queryNode, const SharedNodePointer& node); + virtual int sendSpecialPacket(const SharedNodePointer& node, OctreeQueryNode* queryNode, int& packetsSent); virtual void modelCreated(const ModelItem& newModel, const SharedNodePointer& senderNode); diff --git a/assignment-client/src/octree/OctreeSendThread.cpp b/assignment-client/src/octree/OctreeSendThread.cpp index 30e3011ae8..bc0e84eea9 100644 --- a/assignment-client/src/octree/OctreeSendThread.cpp +++ b/assignment-client/src/octree/OctreeSendThread.cpp @@ -85,7 +85,6 @@ bool OctreeSendThread::process() { if (nodeData && !nodeData->isShuttingDown()) { bool viewFrustumChanged = nodeData->updateCurrentViewFrustum(); packetDistributor(nodeData, viewFrustumChanged); - resendNackedPackets(nodeData); } } } @@ -281,26 +280,6 @@ int OctreeSendThread::handlePacketSend(OctreeQueryNode* nodeData, int& trueBytes return packetsSent; } -int OctreeSendThread::resendNackedPackets(OctreeQueryNode* nodeData) { - - const int MAX_PACKETS_RESEND = 10; - int packetsSent = 0; - - const QByteArray* packet; - while (nodeData->hasNextNackedPacket() && packetsSent < MAX_PACKETS_RESEND) { - packet = nodeData->getNextNackedPacket(); - if (packet) { - NodeList::getInstance()->writeDatagram(*packet, _node); - packetsSent++; - - _totalBytes += packet->size(); - _totalPackets++; - _totalWastedBytes += MAX_PACKET_SIZE - packet->size(); - } - } - return packetsSent; -} - /// Version of voxel distributor that sends the deepest LOD level at once int OctreeSendThread::packetDistributor(OctreeQueryNode* nodeData, bool viewFrustumChanged) { @@ -311,6 +290,11 @@ int OctreeSendThread::packetDistributor(OctreeQueryNode* nodeData, bool viewFrus return 0; } + // calculate max number of packets that can be sent during this interval + int clientMaxPacketsPerInterval = std::max(1, (nodeData->getMaxOctreePacketsPerSecond() / INTERVALS_PER_SECOND)); + int maxPacketsPerInterval = std::min(clientMaxPacketsPerInterval, _myServer->getPacketsPerClientPerInterval()); + + int truePacketsSent = 0; int trueBytesSent = 0; int packetsSentThisInterval = 0; @@ -408,9 +392,6 @@ int OctreeSendThread::packetDistributor(OctreeQueryNode* nodeData, bool viewFrus //quint64 startCompressTimeMsecs = OctreePacketData::getCompressContentTime() / 1000; //quint64 startCompressCalls = OctreePacketData::getCompressContentCalls(); - int clientMaxPacketsPerInterval = std::max(1,(nodeData->getMaxOctreePacketsPerSecond() / INTERVALS_PER_SECOND)); - int maxPacketsPerInterval = std::min(clientMaxPacketsPerInterval, _myServer->getPacketsPerClientPerInterval()); - int extraPackingAttempts = 0; bool completedScene = false; while (somethingToSend && packetsSentThisInterval < maxPacketsPerInterval && !nodeData->isShuttingDown()) { @@ -581,12 +562,26 @@ int OctreeSendThread::packetDistributor(OctreeQueryNode* nodeData, bool viewFrus // send the environment packet // TODO: should we turn this into a while loop to better handle sending multiple special packets if (_myServer->hasSpecialPacketToSend(_node) && !nodeData->isShuttingDown()) { - trueBytesSent += _myServer->sendSpecialPacket(nodeData, _node); + int specialPacketsSent; + trueBytesSent += _myServer->sendSpecialPacket(_node, nodeData, specialPacketsSent); nodeData->resetOctreePacket(); // because nodeData's _sequenceNumber has changed - truePacketsSent++; - packetsSentThisInterval++; + truePacketsSent += specialPacketsSent; + packetsSentThisInterval += specialPacketsSent; } + // Re-send packets that were nacked by the client + while (nodeData->hasNextNackedPacket() && packetsSentThisInterval < maxPacketsPerInterval) { + const QByteArray* packet = nodeData->getNextNackedPacket(); + if (packet) { + NodeList::getInstance()->writeDatagram(*packet, _node); + truePacketsSent++; + packetsSentThisInterval++; + + _totalBytes += packet->size(); + _totalPackets++; + _totalWastedBytes += MAX_PACKET_SIZE - packet->size(); + } + } quint64 end = usecTimestampNow(); int elapsedmsec = (end - start)/USECS_PER_MSEC; diff --git a/assignment-client/src/octree/OctreeSendThread.h b/assignment-client/src/octree/OctreeSendThread.h index e7599ebcd2..d8eed27802 100644 --- a/assignment-client/src/octree/OctreeSendThread.h +++ b/assignment-client/src/octree/OctreeSendThread.h @@ -55,9 +55,6 @@ private: int _nodeMissingCount; bool _isShuttingDown; - - int resendNackedPackets(OctreeQueryNode* nodeData); - }; #endif // hifi_OctreeSendThread_h diff --git a/assignment-client/src/octree/OctreeServer.h b/assignment-client/src/octree/OctreeServer.h index f0db93feb3..5595d139be 100644 --- a/assignment-client/src/octree/OctreeServer.h +++ b/assignment-client/src/octree/OctreeServer.h @@ -72,7 +72,7 @@ public: // subclass may implement these method virtual void beforeRun() { }; virtual bool hasSpecialPacketToSend(const SharedNodePointer& node) { return false; } - virtual int sendSpecialPacket(OctreeQueryNode* queryNode, const SharedNodePointer& node) { return 0; } + virtual int sendSpecialPacket(const SharedNodePointer& node, OctreeQueryNode* queryNode, int& packetsSent) { return 0; } static void attachQueryNodeToNode(Node* newNode); diff --git a/assignment-client/src/particles/ParticleServer.cpp b/assignment-client/src/particles/ParticleServer.cpp index e7a0f75dfd..674d22145f 100644 --- a/assignment-client/src/particles/ParticleServer.cpp +++ b/assignment-client/src/particles/ParticleServer.cpp @@ -86,7 +86,7 @@ bool ParticleServer::hasSpecialPacketToSend(const SharedNodePointer& node) { return shouldSendDeletedParticles; } -int ParticleServer::sendSpecialPacket(OctreeQueryNode* queryNode, const SharedNodePointer& node) { +int ParticleServer::sendSpecialPacket(const SharedNodePointer& node, OctreeQueryNode* queryNode, int& packetsSent) { unsigned char outputBuffer[MAX_PACKET_SIZE]; size_t packetLength = 0; @@ -99,6 +99,7 @@ int ParticleServer::sendSpecialPacket(OctreeQueryNode* queryNode, const SharedNo bool hasMoreToSend = true; // TODO: is it possible to send too many of these packets? what if you deleted 1,000,000 particles? + packetsSent = 0; while (hasMoreToSend) { hasMoreToSend = tree->encodeParticlesDeletedSince(queryNode->getSequenceNumber(), deletedParticlesSentAt, outputBuffer, MAX_PACKET_SIZE, packetLength); @@ -107,6 +108,7 @@ int ParticleServer::sendSpecialPacket(OctreeQueryNode* queryNode, const SharedNo NodeList::getInstance()->writeDatagram((char*) outputBuffer, packetLength, SharedNodePointer(node)); queryNode->packetSent(outputBuffer, packetLength); + packetsSent++; } nodeData->setLastDeletedParticlesSentAt(deletePacketSentAt); diff --git a/assignment-client/src/particles/ParticleServer.h b/assignment-client/src/particles/ParticleServer.h index 3066c5fa98..d444368a9d 100644 --- a/assignment-client/src/particles/ParticleServer.h +++ b/assignment-client/src/particles/ParticleServer.h @@ -37,7 +37,7 @@ public: // subclass may implement these method virtual void beforeRun(); virtual bool hasSpecialPacketToSend(const SharedNodePointer& node); - virtual int sendSpecialPacket(OctreeQueryNode* queryNode, const SharedNodePointer& node); + virtual int sendSpecialPacket(const SharedNodePointer& node, OctreeQueryNode* queryNode, int& packetsSent); virtual void particleCreated(const Particle& newParticle, const SharedNodePointer& senderNode); diff --git a/assignment-client/src/voxels/VoxelServer.cpp b/assignment-client/src/voxels/VoxelServer.cpp index 34b01f529a..b021ddd9f6 100644 --- a/assignment-client/src/voxels/VoxelServer.cpp +++ b/assignment-client/src/voxels/VoxelServer.cpp @@ -40,7 +40,7 @@ bool VoxelServer::hasSpecialPacketToSend(const SharedNodePointer& node) { return shouldSendEnvironments; } -int VoxelServer::sendSpecialPacket(OctreeQueryNode* queryNode, const SharedNodePointer& node) { +int VoxelServer::sendSpecialPacket(const SharedNodePointer& node, OctreeQueryNode* queryNode, int& packetsSent) { unsigned char* copyAt = _tempOutputBuffer; @@ -76,6 +76,7 @@ int VoxelServer::sendSpecialPacket(OctreeQueryNode* queryNode, const SharedNodeP NodeList::getInstance()->writeDatagram((char*) _tempOutputBuffer, envPacketLength, SharedNodePointer(node)); queryNode->packetSent(_tempOutputBuffer, envPacketLength); + packetsSent = 1; return envPacketLength; } diff --git a/assignment-client/src/voxels/VoxelServer.h b/assignment-client/src/voxels/VoxelServer.h index 4e04c48cfd..b13f83b65f 100644 --- a/assignment-client/src/voxels/VoxelServer.h +++ b/assignment-client/src/voxels/VoxelServer.h @@ -46,7 +46,7 @@ public: // subclass may implement these method virtual void beforeRun(); virtual bool hasSpecialPacketToSend(const SharedNodePointer& node); - virtual int sendSpecialPacket(OctreeQueryNode* queryNode, const SharedNodePointer& node); + virtual int sendSpecialPacket(const SharedNodePointer& node, OctreeQueryNode* queryNode, int& packetsSent); private: bool _sendEnvironments; From c6a9a8924dbb847e7bacdac8789c3a484cf8b07e Mon Sep 17 00:00:00 2001 From: wangyix Date: Tue, 10 Jun 2014 16:43:53 -0700 Subject: [PATCH 06/13] changed NetworkPacket::_sendingNode to ::_node --- .../src/octree/OctreeSendThread.cpp | 1 - libraries/networking/src/NetworkPacket.cpp | 16 ++++++++-------- libraries/networking/src/NetworkPacket.h | 8 ++++---- libraries/networking/src/PacketSender.cpp | 2 +- .../networking/src/ReceivedPacketProcessor.cpp | 4 ++-- 5 files changed, 15 insertions(+), 16 deletions(-) diff --git a/assignment-client/src/octree/OctreeSendThread.cpp b/assignment-client/src/octree/OctreeSendThread.cpp index bc0e84eea9..cb149b1d96 100644 --- a/assignment-client/src/octree/OctreeSendThread.cpp +++ b/assignment-client/src/octree/OctreeSendThread.cpp @@ -294,7 +294,6 @@ int OctreeSendThread::packetDistributor(OctreeQueryNode* nodeData, bool viewFrus int clientMaxPacketsPerInterval = std::max(1, (nodeData->getMaxOctreePacketsPerSecond() / INTERVALS_PER_SECOND)); int maxPacketsPerInterval = std::min(clientMaxPacketsPerInterval, _myServer->getPacketsPerClientPerInterval()); - int truePacketsSent = 0; int trueBytesSent = 0; int packetsSentThisInterval = 0; diff --git a/libraries/networking/src/NetworkPacket.cpp b/libraries/networking/src/NetworkPacket.cpp index f9d6c7ea7b..a8110847e1 100644 --- a/libraries/networking/src/NetworkPacket.cpp +++ b/libraries/networking/src/NetworkPacket.cpp @@ -17,9 +17,9 @@ #include "NetworkPacket.h" -void NetworkPacket::copyContents(const SharedNodePointer& sendingNode, const QByteArray& packet) { +void NetworkPacket::copyContents(const SharedNodePointer& node, const QByteArray& packet) { if (packet.size() && packet.size() <= MAX_PACKET_SIZE) { - _sendingNode = sendingNode; + _node = node; _byteArray = packet; } else { qDebug(">>> NetworkPacket::copyContents() unexpected length = %d", packet.size()); @@ -27,28 +27,28 @@ void NetworkPacket::copyContents(const SharedNodePointer& sendingNode, const QBy } NetworkPacket::NetworkPacket(const NetworkPacket& packet) { - copyContents(packet.getSendingNode(), packet.getByteArray()); + copyContents(packet.getNode(), packet.getByteArray()); } -NetworkPacket::NetworkPacket(const SharedNodePointer& sendingNode, const QByteArray& packet) { - copyContents(sendingNode, packet); +NetworkPacket::NetworkPacket(const SharedNodePointer& node, const QByteArray& packet) { + copyContents(node, packet); }; // copy assignment NetworkPacket& NetworkPacket::operator=(NetworkPacket const& other) { - copyContents(other.getSendingNode(), other.getByteArray()); + copyContents(other.getNode(), other.getByteArray()); return *this; } #ifdef HAS_MOVE_SEMANTICS // move, same as copy, but other packet won't be used further NetworkPacket::NetworkPacket(NetworkPacket && packet) { - copyContents(packet.getSendingNode(), packet.getByteArray()); + copyContents(packet.getNode(), packet.getByteArray()); } // move assignment NetworkPacket& NetworkPacket::operator=(NetworkPacket&& other) { - copyContents(other.getSendingNode(), other.getByteArray()); + copyContents(other.getNode(), other.getByteArray()); return *this; } #endif diff --git a/libraries/networking/src/NetworkPacket.h b/libraries/networking/src/NetworkPacket.h index 52e8a36712..0be35f9fff 100644 --- a/libraries/networking/src/NetworkPacket.h +++ b/libraries/networking/src/NetworkPacket.h @@ -35,15 +35,15 @@ public: NetworkPacket& operator= (NetworkPacket&& other); // move assignment #endif - NetworkPacket(const SharedNodePointer& sendingNode, const QByteArray& byteArray); + NetworkPacket(const SharedNodePointer& node, const QByteArray& byteArray); - const SharedNodePointer& getSendingNode() const { return _sendingNode; } + const SharedNodePointer& getNode() const { return _node; } const QByteArray& getByteArray() const { return _byteArray; } private: - void copyContents(const SharedNodePointer& sendingNode, const QByteArray& byteArray); + void copyContents(const SharedNodePointer& node, const QByteArray& byteArray); - SharedNodePointer _sendingNode; + SharedNodePointer _node; QByteArray _byteArray; }; diff --git a/libraries/networking/src/PacketSender.cpp b/libraries/networking/src/PacketSender.cpp index ae844d4f99..3edfc47c04 100644 --- a/libraries/networking/src/PacketSender.cpp +++ b/libraries/networking/src/PacketSender.cpp @@ -271,7 +271,7 @@ bool PacketSender::nonThreadedProcess() { unlock(); // send the packet through the NodeList... - NodeList::getInstance()->writeDatagram(temporary.getByteArray(), temporary.getSendingNode()); + NodeList::getInstance()->writeDatagram(temporary.getByteArray(), temporary.getNode()); packetsSentThisCall++; _packetsOverCheckInterval++; _totalPacketsSent++; diff --git a/libraries/networking/src/ReceivedPacketProcessor.cpp b/libraries/networking/src/ReceivedPacketProcessor.cpp index ea613082ce..46f1515016 100644 --- a/libraries/networking/src/ReceivedPacketProcessor.cpp +++ b/libraries/networking/src/ReceivedPacketProcessor.cpp @@ -43,9 +43,9 @@ bool ReceivedPacketProcessor::process() { NetworkPacket& packet = _packets.front(); // get the oldest packet NetworkPacket temporary = packet; // make a copy of the packet in case the vector is resized on us _packets.erase(_packets.begin()); // remove the oldest packet - _nodePacketCounts[temporary.getSendingNode()->getUUID()]--; + _nodePacketCounts[temporary.getNode()->getUUID()]--; unlock(); // let others add to the packets - processPacket(temporary.getSendingNode(), temporary.getByteArray()); // process our temporary copy + processPacket(temporary.getNode(), temporary.getByteArray()); // process our temporary copy } return isStillRunning(); // keep running till they terminate us } From faa5b1d2f253e3964cc8781fe821cb283d7b171a Mon Sep 17 00:00:00 2001 From: Dev5 Date: Tue, 10 Jun 2014 17:18:32 -0700 Subject: [PATCH 07/13] Added simple tree placement script that has a UI similar to edit voxels. It grows big trees, dont grow near something important. --- examples/growTrees.js | 837 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 837 insertions(+) create mode 100644 examples/growTrees.js diff --git a/examples/growTrees.js b/examples/growTrees.js new file mode 100644 index 0000000000..d853804e77 --- /dev/null +++ b/examples/growTrees.js @@ -0,0 +1,837 @@ +// +// growPlants.js +// examples +// +// Created by Benjamin Arnold on May 29, 2014 +// Copyright 2014 High Fidelity, Inc. +// +// This sample script allows the user to grow different types of plants on the voxels +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +var zFightingSizeAdjust = 0.002; // used to adjust preview voxels to prevent z fighting +var previewLineWidth = 2.0; + +var voxelSize = 1; +var windowDimensions = Controller.getViewportDimensions(); +var toolIconUrl = "http://highfidelity-public.s3-us-west-1.amazonaws.com/images/tools/"; + +var MAX_VOXEL_SCALE_POWER = 5; +var MIN_VOXEL_SCALE_POWER = -8; +var MAX_VOXEL_SCALE = Math.pow(2.0, MAX_VOXEL_SCALE_POWER); +var MIN_VOXEL_SCALE = Math.pow(2.0, MIN_VOXEL_SCALE_POWER); + + +var linePreviewTop = Overlays.addOverlay("line3d", { + position: { x: 0, y: 0, z: 0}, + end: { x: 0, y: 0, z: 0}, + color: { red: 0, green: 255, blue: 0}, + alpha: 1, + visible: false, + lineWidth: previewLineWidth + }); + +var linePreviewBottom = Overlays.addOverlay("line3d", { + position: { x: 0, y: 0, z: 0}, + end: { x: 0, y: 0, z: 0}, + color: { red: 0, green: 255, blue: 0}, + alpha: 1, + visible: false, + lineWidth: previewLineWidth + }); + +var linePreviewLeft = Overlays.addOverlay("line3d", { + position: { x: 0, y: 0, z: 0}, + end: { x: 0, y: 0, z: 0}, + color: { red: 0, green: 255, blue: 0}, + alpha: 1, + visible: false, + lineWidth: previewLineWidth + }); + +var linePreviewRight = Overlays.addOverlay("line3d", { + position: { x: 0, y: 0, z: 0}, + end: { x: 0, y: 0, z: 0}, + color: { red: 0, green: 255, blue: 0}, + alpha: 1, + visible: false, + lineWidth: previewLineWidth + }); + + +var UIColor = { red: 0, green: 160, blue: 0}; +var activeUIColor = { red: 0, green: 255, blue: 0}; + +var toolHeight = 50; +var toolWidth = 50; + +var editToolsOn = true; + +var voxelToolSelected = false; + +var scaleSelectorWidth = 144; +var scaleSelectorHeight = 37; + +var scaleSelectorX = windowDimensions.x / 5.0; +var scaleSelectorY = windowDimensions.y - scaleSelectorHeight; + +var voxelTool = Overlays.addOverlay("image", { + x: scaleSelectorX + scaleSelectorWidth + 1, y: windowDimensions.y - toolHeight, width: toolWidth, height: toolHeight, + subImage: { x: 0, y: toolHeight, width: toolWidth, height: toolHeight }, + imageURL: toolIconUrl + "voxel-tool.svg", + visible: editToolsOn, + color: UIColor, + alpha: 0.9 + }); + +var copyScale = true; +function ScaleSelector() { + this.x = scaleSelectorX; + this.y = scaleSelectorY; + this.width = scaleSelectorWidth; + this.height = scaleSelectorHeight; + + this.displayPower = false; + this.scale = 1.0; + this.power = 0; + + this.FIRST_PART = this.width * 40.0 / 100.0; + this.SECOND_PART = this.width * 37.0 / 100.0; + + this.buttonsOverlay = Overlays.addOverlay("image", { + x: this.x, y: this.y, + width: this.width, height: this.height, + //subImage: { x: 0, y: toolHeight, width: toolWidth, height: toolHeight }, + imageURL: toolIconUrl + "voxel-size-selector.svg", + alpha: 0.9, + visible: editToolsOn, + color: activeUIColor + }); + this.textOverlay = Overlays.addOverlay("text", { + x: this.x + this.FIRST_PART, y: this.y, + width: this.SECOND_PART, height: this.height, + topMargin: 13, + text: this.scale.toString(), + alpha: 0.0, + visible: editToolsOn, + color: activeUIColor + }); + this.powerOverlay = Overlays.addOverlay("text", { + x: this.x + this.FIRST_PART, y: this.y, + width: this.SECOND_PART, height: this.height, + leftMargin: 28, + text: this.power.toString(), + alpha: 0.0, + visible: false, + color: activeUIColor + }); + this.setScale = function(scale) { + if (scale > MAX_VOXEL_SCALE) { + scale = MAX_VOXEL_SCALE; + } + if (scale < MIN_VOXEL_SCALE) { + scale = MIN_VOXEL_SCALE; + } + + this.scale = scale; + this.power = Math.floor(Math.log(scale) / Math.log(2)); + this.update(); + } + + this.show = function(doShow) { + Overlays.editOverlay(this.buttonsOverlay, {visible: doShow}); + Overlays.editOverlay(this.textOverlay, {visible: doShow}); + Overlays.editOverlay(this.powerOverlay, {visible: doShow && this.displayPower}); + } + + this.move = function() { + this.x = swatchesX + swatchesWidth; + this.y = swatchesY; + + Overlays.editOverlay(this.buttonsOverlay, { + x: this.x, y: this.y, + }); + Overlays.editOverlay(this.textOverlay, { + x: this.x + this.FIRST_PART, y: this.y, + }); + Overlays.editOverlay(this.powerOverlay, { + x: this.x + this.FIRST_PART, y: this.y, + }); + } + + + this.switchDisplay = function() { + this.displayPower = !this.displayPower; + + if (this.displayPower) { + Overlays.editOverlay(this.textOverlay, { + leftMargin: 18, + text: "2" + }); + Overlays.editOverlay(this.powerOverlay, { + text: this.power.toString(), + visible: editToolsOn + }); + } else { + Overlays.editOverlay(this.textOverlay, { + leftMargin: 13, + text: this.scale.toString() + }); + Overlays.editOverlay(this.powerOverlay, { + visible: false + }); + } + } + + this.update = function() { + if (this.displayPower) { + Overlays.editOverlay(this.powerOverlay, {text: this.power.toString()}); + } else { + Overlays.editOverlay(this.textOverlay, {text: this.scale.toString()}); + } + } + + this.incrementScale = function() { + copyScale = false; + if (this.power < MAX_VOXEL_SCALE_POWER) { + ++this.power; + this.scale *= 2.0; + this.update(); + } + } + + this.decrementScale = function() { + copyScale = false; + if (MIN_VOXEL_SCALE_POWER < this.power) { + --this.power; + this.scale /= 2.0; + this.update(); + } + } + + this.clicked = function(x, y) { + if (this.x < x && x < this.x + this.width && + this.y < y && y < this.y + this.height) { + + if (x < this.x + this.FIRST_PART) { + this.decrementScale(); + } else if (x < this.x + this.FIRST_PART + this.SECOND_PART) { + this.switchDisplay(); + } else { + this.incrementScale(); + } + return true; + } + return false; + } + + this.cleanup = function() { + Overlays.deleteOverlay(this.buttonsOverlay); + Overlays.deleteOverlay(this.textOverlay); + Overlays.deleteOverlay(this.powerOverlay); + } + +} +var scaleSelector = new ScaleSelector(); + + +function calculateVoxelFromIntersection(intersection, operation) { + + var resultVoxel; + + var x; + var y; + var z; + + // if our "target voxel size" is larger than the voxel we intersected with, then we need to find the closest + // ancestor voxel of our target size that contains our intersected voxel. + if (voxelSize > intersection.voxel.s) { + x = Math.floor(intersection.voxel.x / voxelSize) * voxelSize; + y = Math.floor(intersection.voxel.y / voxelSize) * voxelSize; + z = Math.floor(intersection.voxel.z / voxelSize) * voxelSize; + } else { + // otherwise, calculate the enclosed voxel of size voxelSize that the intersection point falls inside of. + // if you have a voxelSize that's smaller than the voxel you're intersecting, this calculation will result + // in the subvoxel that the intersection point falls in, if the target voxelSize matches the intersecting + // voxel this still works and results in returning the intersecting voxel which is what we want + var adjustToCenter = Vec3.multiply(Voxels.getFaceVector(intersection.face), (voxelSize * -0.5)); + + var centerOfIntersectingVoxel = Vec3.sum(intersection.intersection, adjustToCenter); + x = Math.floor(centerOfIntersectingVoxel.x / voxelSize) * voxelSize; + y = Math.floor(centerOfIntersectingVoxel.y / voxelSize) * voxelSize; + z = Math.floor(centerOfIntersectingVoxel.z / voxelSize) * voxelSize; + } + resultVoxel = { x: x, y: y, z: z, s: voxelSize }; + var highlightAt = { x: x, y: y, z: z, s: voxelSize }; + + + + // we only do the "add to the face we're pointing at" adjustment, if the operation is an add + // operation, and the target voxel size is equal to or smaller than the intersecting voxel. + var wantAddAdjust = (operation == "add" && (voxelSize <= intersection.voxel.s)); + + // now we also want to calculate the "edge square" for the face for this voxel + if (intersection.face == "MIN_X_FACE") { + + highlightAt.x = x - zFightingSizeAdjust; + if (wantAddAdjust) { + resultVoxel.x -= voxelSize; + } + + resultVoxel.bottomLeft = {x: highlightAt.x, y: highlightAt.y + zFightingSizeAdjust, z: highlightAt.z + zFightingSizeAdjust }; + resultVoxel.bottomRight = {x: highlightAt.x, y: highlightAt.y + zFightingSizeAdjust, z: highlightAt.z + voxelSize - zFightingSizeAdjust }; + resultVoxel.topLeft = {x: highlightAt.x, y: highlightAt.y + voxelSize - zFightingSizeAdjust, z: highlightAt.z + zFightingSizeAdjust }; + resultVoxel.topRight = {x: highlightAt.x, y: highlightAt.y + voxelSize - zFightingSizeAdjust, z: highlightAt.z + voxelSize - zFightingSizeAdjust }; + + } else if (intersection.face == "MAX_X_FACE") { + + highlightAt.x = x + voxelSize + zFightingSizeAdjust; + if (wantAddAdjust) { + resultVoxel.x += resultVoxel.s; + } + + resultVoxel.bottomRight = {x: highlightAt.x, y: highlightAt.y + zFightingSizeAdjust, z: highlightAt.z + zFightingSizeAdjust }; + resultVoxel.bottomLeft = {x: highlightAt.x, y: highlightAt.y + zFightingSizeAdjust, z: highlightAt.z + voxelSize - zFightingSizeAdjust }; + resultVoxel.topRight = {x: highlightAt.x, y: highlightAt.y + voxelSize - zFightingSizeAdjust, z: highlightAt.z + zFightingSizeAdjust }; + resultVoxel.topLeft = {x: highlightAt.x, y: highlightAt.y + voxelSize - zFightingSizeAdjust, z: highlightAt.z + voxelSize - zFightingSizeAdjust }; + + } else if (intersection.face == "MIN_Y_FACE") { + + highlightAt.y = y - zFightingSizeAdjust; + if (wantAddAdjust) { + resultVoxel.y -= voxelSize; + } + + resultVoxel.topRight = {x: highlightAt.x + zFightingSizeAdjust , y: highlightAt.y, z: highlightAt.z + zFightingSizeAdjust }; + resultVoxel.topLeft = {x: highlightAt.x + voxelSize - zFightingSizeAdjust, y: highlightAt.y, z: highlightAt.z + zFightingSizeAdjust }; + resultVoxel.bottomRight = {x: highlightAt.x + zFightingSizeAdjust , y: highlightAt.y, z: highlightAt.z + voxelSize - zFightingSizeAdjust }; + resultVoxel.bottomLeft = {x: highlightAt.x + voxelSize - zFightingSizeAdjust , y: highlightAt.y, z: highlightAt.z + voxelSize - zFightingSizeAdjust }; + + } else if (intersection.face == "MAX_Y_FACE") { + + highlightAt.y = y + voxelSize + zFightingSizeAdjust; + if (wantAddAdjust) { + resultVoxel.y += voxelSize; + } + + resultVoxel.bottomRight = {x: highlightAt.x + zFightingSizeAdjust, y: highlightAt.y, z: highlightAt.z + zFightingSizeAdjust }; + resultVoxel.bottomLeft = {x: highlightAt.x + voxelSize - zFightingSizeAdjust, y: highlightAt.y, z: highlightAt.z + zFightingSizeAdjust}; + resultVoxel.topRight = {x: highlightAt.x + zFightingSizeAdjust, y: highlightAt.y, z: highlightAt.z + voxelSize - zFightingSizeAdjust}; + resultVoxel.topLeft = {x: highlightAt.x + voxelSize - zFightingSizeAdjust, y: highlightAt.y, z: highlightAt.z + voxelSize - zFightingSizeAdjust}; + + } else if (intersection.face == "MIN_Z_FACE") { + + highlightAt.z = z - zFightingSizeAdjust; + if (wantAddAdjust) { + resultVoxel.z -= voxelSize; + } + + resultVoxel.bottomRight = {x: highlightAt.x + zFightingSizeAdjust, y: highlightAt.y + zFightingSizeAdjust, z: highlightAt.z }; + resultVoxel.bottomLeft = {x: highlightAt.x + voxelSize - zFightingSizeAdjust, y: highlightAt.y + zFightingSizeAdjust, z: highlightAt.z}; + resultVoxel.topRight = {x: highlightAt.x + zFightingSizeAdjust, y: highlightAt.y + voxelSize - zFightingSizeAdjust, z: highlightAt.z }; + resultVoxel.topLeft = {x: highlightAt.x + voxelSize - zFightingSizeAdjust, y: highlightAt.y + voxelSize - zFightingSizeAdjust, z: highlightAt.z}; + + } else if (intersection.face == "MAX_Z_FACE") { + + highlightAt.z = z + voxelSize + zFightingSizeAdjust; + if (wantAddAdjust) { + resultVoxel.z += voxelSize; + } + + resultVoxel.bottomLeft = {x: highlightAt.x + zFightingSizeAdjust, y: highlightAt.y + zFightingSizeAdjust, z: highlightAt.z }; + resultVoxel.bottomRight = {x: highlightAt.x + voxelSize - zFightingSizeAdjust, y: highlightAt.y + zFightingSizeAdjust, z: highlightAt.z}; + resultVoxel.topLeft = {x: highlightAt.x + zFightingSizeAdjust, y: highlightAt.y + voxelSize - zFightingSizeAdjust, z: highlightAt.z }; + resultVoxel.topRight = {x: highlightAt.x + voxelSize - zFightingSizeAdjust, y: highlightAt.y + voxelSize - zFightingSizeAdjust, z: highlightAt.z}; + + } + return resultVoxel; +} + +var trackLastMouseX = 0; +var trackLastMouseY = 0; + +function showPreviewLines() { + + var pickRay = Camera.computePickRay(trackLastMouseX, trackLastMouseY); + + var intersection = Voxels.findRayIntersection(pickRay); + + if (intersection.intersects) { + var resultVoxel = calculateVoxelFromIntersection(intersection, ""); + Overlays.editOverlay(linePreviewTop, { position: resultVoxel.topLeft, end: resultVoxel.topRight, visible: true }); + Overlays.editOverlay(linePreviewBottom, { position: resultVoxel.bottomLeft, end: resultVoxel.bottomRight, visible: true }); + Overlays.editOverlay(linePreviewLeft, { position: resultVoxel.topLeft, end: resultVoxel.bottomLeft, visible: true }); + Overlays.editOverlay(linePreviewRight, { position: resultVoxel.topRight, end: resultVoxel.bottomRight, visible: true }); + } else { + Overlays.editOverlay(linePreviewTop, { visible: false }); + Overlays.editOverlay(linePreviewBottom, { visible: false }); + Overlays.editOverlay(linePreviewLeft, { visible: false }); + Overlays.editOverlay(linePreviewRight, { visible: false }); + } +} + +function mouseMoveEvent(event) { + trackLastMouseX = event.x; + trackLastMouseY = event.y; + if (!voxelToolSelected) { + return; + } + showPreviewLines(); +} + + +// Array of possible trees, right now there is only one +var treeTypes = []; + +treeTypes.push({ + name: "Tall Green", + // Voxel Colors + wood: { r: 133, g: 81, b: 53 }, + leaves: { r: 22, g: 83, b: 31 }, + + // How tall the tree is + height: { min: 20, max: 60 }, + middleHeight: 0.3, + + // Chance of making a branch + branchChance: { min: 0.01, max: 0.1 }, + branchLength: { min: 30, max: 60 }, + branchThickness: { min: 2, max: 7}, + + // The width of the core, affects width and shape + coreWidth: { min: 1, max: 4 }, + + //TODO: Make this quadratic splines instead of linear + bottomThickness: { min: 2, max: 8 }, + middleThickness: { min: 1, max: 4 }, + topThickness: { min: 3, max: 6 }, + + //Modifies leaves at top + leafCapSizeOffset: 0 +}); + +// Applies noise to color +var colorNoiseRange = 0.2; + + +// Useful constants +var LEFT = 0; +var BACK = 1; +var RIGHT = 2; +var FRONT = 3; +var UP = 4; + +// Interpolates between min and max of treevar based on b +function interpolate(treeVar, b) { + return (treeVar.min + (treeVar.max - treeVar.min) * b); +} + +function makeBranch(x, y, z, step, length, dir, thickness, wood, leaves) { + var moveDir; + + var currentThickness; + //thickness attenuates to thickness - 3 + var finalThickness = thickness - 3; + if (finalThickness < 1) { + finalThickness = 1; + } + //Iterative branch generation + while (true) { + + //If we are at the end, place a ball of leaves + if (step == 0) { + makeSphere(x, y, z, 2 + finalThickness, leaves); + return; + } + //thickness attenuation + currentThickness = Math.round((finalThickness + (thickness - finalThickness) * (step/length))) - 1; + + + // If the branch is thick, grow a vertical slice + if (currentThickness > 0) { + for (var i = -currentThickness; i <= currentThickness; i++) { + var len = currentThickness - Math.abs(i); + switch (dir) { + case 0: //left + case 2: //right + growInDirection(x, y + i * voxelSize, z, len, len, BACK, wood, false, true); + growInDirection(x, y + i * voxelSize, z, len, len, FRONT, wood, false, false) + break; + case 1: //back + case 3: //front + growInDirection(x, y + i * voxelSize, z, len, len, LEFT, wood, false, true); + growInDirection(x, y + i * voxelSize, z, len, len, RIGHT, wood, false, false) + break; + } + } + } else { + //Otherwise place a single voxel + var colorNoise = (colorNoiseRange * Math.random() - colorNoiseRange * 0.5) + 1.0; + Voxels.setVoxel(x, y, z, voxelSize, wood.r * colorNoise, wood.g * colorNoise, wood.b * colorNoise); + } + + // determines random change in direction for branch + var r = Math.floor(Math.random() * 9); + + + if (r >= 6){ + moveDir = dir; //in same direction + } else if (r >= 4) { + moveDir = UP; //up + } + else if (dir == LEFT){ + if (r >= 2){ + moveDir = FRONT; + } + else{ + moveDir = BACK; + } + } + else if (dir == BACK){ + if (r >= 2){ + moveDir = LEFT; + } + else{ + moveDir = RIGHT; + } + } + else if (dir == RIGHT){ + if (r >= 2){ + moveDir = BACK; + } + else{ + moveDir = FRONT; + } + } + else if (dir == FRONT){ + if (r >= 2){ + moveDir = RIGHT; + } + else{ + moveDir = LEFT; + } + } + + //Move the branch by moveDir + switch (moveDir) { + case 0: //left + x = x - voxelSize; + break; + case 1: //back + z = z - voxelSize; + break; + case 2: //right + x = x + voxelSize; + break; + case 3: //front + z = z + voxelSize; + break; + case 4: //up + y = y + voxelSize; + break; + } + + step--; + } +} + +// Places a sphere of voxels +function makeSphere(x, y, z, radius, color) { + if (radius <= 0) { + return; + } + var width = radius * 2 + 1; + var distance; + + for (var i = -radius; i <= radius; i++){ + for (var j = -radius; j <= radius; j++){ + for (var k = -radius; k <= radius; k++){ + distance = Math.sqrt(i * i + j * j + k * k); + if (distance <= radius){ + var colorNoise = (colorNoiseRange * Math.random() - colorNoiseRange * 0.5) + 1.0; + Voxels.setVoxel(x + i * voxelSize, y + j * voxelSize, z + k * voxelSize, voxelSize, color.r * colorNoise, color.g * colorNoise, color.b * colorNoise); + } + } + } + } +} + +function growInDirection(x, y, z, step, length, dir, color, isSideBranching, addVoxel) { + + + if (addVoxel == true) { + var colorNoise = (colorNoiseRange * Math.random() - colorNoiseRange * 0.5) + 1.0; + Voxels.setVoxel(x, y, z, voxelSize, color.r * colorNoise, color.g * colorNoise, color.b * colorNoise); + } + + // If this is a main vein, it will branch outward perpendicular to its motion + if (isSideBranching == true){ + var step2; + if (step >= length - 1){ + step2 = length; + } + else{ + step2 = step + 1; + } + growInDirection(x, y, z, step, length, BACK, color, false, false); + growInDirection(x, y, z, step, length, FRONT, color, false, false); + } + + if (step < 1) return; + + // Recursively move in the direction + if (dir == LEFT) { //left + growInDirection(x - voxelSize, y, z, step - 1, length, dir, color, isSideBranching, true); + } + else if (dir == BACK) { //back + growInDirection(x, y, z - voxelSize, step - 1, length, dir, color, isSideBranching, true); + } + else if (dir == RIGHT) { //right + growInDirection(x + voxelSize, y, z, step - 1, length, dir, color, isSideBranching, true); + } + else if (dir == FRONT) {//front + growInDirection(x, y, z + voxelSize, step - 1, length, dir, color, isSideBranching, true); + } + +} + +// Grows the thickness of the tree +function growHorizontalSlice(x, y, z, thickness, color, side) { + // The side variable determines which directions we should grow in + // it is an optimization that prevents us from visiting voxels multiple + // times for trees with a coreWidth > 1 + + // side: + // 8 == all directions + // 0 1 2 + // 3 -1 4 + // 5 6 7 + + Voxels.setVoxel(x, y, z, voxelSize, color.r, color.g, color.b); + + //We are done if there is no thickness + if (thickness == 0) { + return; + } + + + switch (side) { + case 0: + growInDirection(x, y, z, thickness, thickness, LEFT, color, true, false); + break; + case 1: + growInDirection(x, y, z, thickness, thickness, BACK, color, false, false); + break; + case 2: + growInDirection(x, y, z, thickness, thickness, RIGHT, color, true, false); + break; + case 3: + growInDirection(x, y, z, thickness, thickness, LEFT, color, false, false); + break; + case 4: + growInDirection(x, y, z, thickness, thickness, BACK, color, false, false); + break; + case 5: + growInDirection(x, y, z, thickness, thickness, RIGHT, color, true, false); + break; + case 6: + growInDirection(x, y, z, thickness, thickness, FRONT, color, false, false); + break; + case 7: + growInDirection(x, y, z, thickness, thickness, RIGHT, color, true, false); + break; + case 8: + if (thickness > 1){ + growInDirection(x, y, z, thickness, thickness, LEFT, color, true, false); + growInDirection(x, y, z, thickness, thickness, RIGHT, color, true, false) + } else if (thickness == 1){ + Voxels.setVoxel(x - voxelSize, y, z, voxelSize, color.r, color.g, color.b); + Voxels.setVoxel(x + voxelSize, y, z, voxelSize, color.r, color.g, color.b); + Voxels.setVoxel(x, y, z - voxelSize, voxelSize, color.r, color.g, color.b); + Voxels.setVoxel(x, y, z + voxelSize, voxelSize, color.r, color.g, color.b); + } + break; + } +} + +function computeSide(x, z, coreWidth) { + // side: + // 8 == all directions + // 0 1 2 + // 3 -1 4 + // 5 6 7 + + // if the core is only a single block, we can grow out in all directions + if (coreWidth == 1){ + return 8; + } + + // Back face + if (z == 0) { + if (x == 0) { + return 0; + } else if (x == coreWidth - 1) { + return 2; + } else { + return 1; + } + } + + // Front face + if (z == (coreWidth - 1)) { + if (x == 0) { + return 5; + } else if (x == (coreWidth - 1)) { + return 7; + } else { + return 6; + } + } + + // Left face + if (x == 0) { + return 3; + } + + // Right face + if (x == (coreWidth - 1)) { + return 4; + } + + //Interior + return -1; +} + + +function growTree(x, y, z, tree) { + + // The size of the tree, from 0-1 + var treeSize = Math.random(); + + // Get tree properties by interpolating with the treeSize + var height = interpolate(tree.height, treeSize); + var baseHeight = Math.ceil(tree.middleHeight * height); + var bottomThickness = interpolate(tree.bottomThickness, treeSize); + var middleThickness = interpolate(tree.middleThickness, treeSize); + var topThickness = interpolate(tree.topThickness, treeSize); + var coreWidth = Math.ceil(interpolate(tree.coreWidth, treeSize)); + + var thickness; + var side; + + //Loop upwards through each slice of the tree + for (var i = 0; i < height; i++){ + + //Branch properties are based on current height as well as the overall tree size + var branchChance = interpolate(tree.branchChance, i / height); + var branchLength = Math.ceil(interpolate(tree.branchLength, (i / height) * treeSize)); + var branchThickness = Math.round(interpolate(tree.branchThickness, (i / height) * treeSize)); + + // Get the "thickness" of the tree by doing linear interpolation between the middle thickness + // and the top and bottom thickness. + if (i <= baseHeight && baseHeight != 0){ + thickness = (i / (baseHeight) * (middleThickness - bottomThickness) + bottomThickness); + } else { + var denom = ((height - baseHeight)) * (topThickness - middleThickness) + middleThickness; + if (denom != 0) { + thickness = (i - baseHeight) / denom; + } else { + thickness = 0; + } + } + // The core of the tree is a vertical rectangular prism through the middle of the tree + + //Loop through the "core", which helps shape the trunk + var startX = x - Math.floor(coreWidth / 2) * voxelSize; + var startZ = z - Math.floor(coreWidth / 2) * voxelSize; + for (var j = 0; j < coreWidth; j++) { + for (var k = 0; k < coreWidth; k++) { + //determine which side of the tree we are on + side = computeSide(j, k, coreWidth); + //grow a horizontal slice of the tree + growHorizontalSlice(startX + j * voxelSize, y + i * voxelSize, startZ + k * voxelSize, Math.floor(thickness), tree.wood, side); + + // Branches + if (side != -1) { + var r = Math.random(); + if (r <= branchChance){ + var dir = Math.floor((Math.random() * 4)); + makeBranch(startX + j * voxelSize, y + i * voxelSize, startZ + k * voxelSize, branchLength, branchLength, dir, branchThickness, tree.wood, tree.leaves); + + } + } + } + } + } + + makeSphere(x, y + height * voxelSize, z, topThickness + coreWidth + tree.leafCapSizeOffset, tree.leaves); +} + +function mousePressEvent(event) { + var mouseX = event.x; + var mouseY = event.y; + + var clickedOnSomething = false; + // Check if we clicked an overlay + var clickedOverlay = Overlays.getOverlayAtPoint({x: mouseX, y: mouseY}); + + if (clickedOverlay == voxelTool) { + voxelToolSelected = !voxelToolSelected; + + if (voxelToolSelected == true) { + Overlays.editOverlay(voxelTool, { + color: activeUIColor + }); + } else { + Overlays.editOverlay(voxelTool, { + color: UIColor + }); + } + + clickedOnSomething = true; + } else if (scaleSelector.clicked(event.x, event.y)) { + clickedOnSomething = true; + voxelSize = scaleSelector.scale; + } + + // Return if we clicked on the UI or the voxel tool is disabled + if (clickedOnSomething || !voxelToolSelected) { + return; + } + + // Compute the picking ray for the click + var pickRay = Camera.computePickRay(event.x, event.y); + var intersection = Voxels.findRayIntersection(pickRay); + var resultVoxel = calculateVoxelFromIntersection(intersection, "add"); + + // Currently not in use, could randomly select a tree + var treeIndex = Math.floor(Math.random() * treeTypes.length); + + // Grow the first tree type + growTree(resultVoxel.x, resultVoxel.y, resultVoxel.z, treeTypes[0]); + +} + +function update() { + +} + +function scriptEnding() { + Overlays.deleteOverlay(linePreviewTop); + Overlays.deleteOverlay(linePreviewBottom); + Overlays.deleteOverlay(linePreviewLeft); + Overlays.deleteOverlay(linePreviewRight); + scaleSelector.cleanup(); + Overlays.deleteOverlay(voxelTool); +} + +Controller.mousePressEvent.connect(mousePressEvent); +Controller.mouseMoveEvent.connect(mouseMoveEvent); + +Script.scriptEnding.connect(scriptEnding); + +Script.update.connect(update); + +Voxels.setPacketsPerSecond(10000); From a48f38b1d263a04f0fba5fce7a3502175e2078b9 Mon Sep 17 00:00:00 2001 From: matsukaze Date: Tue, 10 Jun 2014 21:32:23 -0400 Subject: [PATCH 08/13] Revert "Job #19766 BUG: Stop or reload all scripts crashes interface fix, part 2." This reverts commit b4e984086505814cbc426226777b74a113da8362. --- interface/src/Application.cpp | 5 +++-- libraries/particles/src/Particle.cpp | 6 +++--- libraries/script-engine/src/ScriptEngine.cpp | 12 +++++++----- libraries/script-engine/src/ScriptEngine.h | 2 +- 4 files changed, 14 insertions(+), 11 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 3ec5588c61..9fb821bff4 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -3492,8 +3492,9 @@ ScriptEngine* Application::loadScript(const QString& scriptName, bool loadScript scriptEngine = new ScriptEngine(NO_SCRIPT, "", &_controllerScriptingInterface); } else { // start the script on a new thread... - scriptEngine = new ScriptEngine(scriptName, &_controllerScriptingInterface); - _scriptEnginesHash.insert(scriptName, scriptEngine); + QUrl scriptUrl(scriptName); + scriptEngine = new ScriptEngine(scriptUrl, &_controllerScriptingInterface); + _scriptEnginesHash.insert(scriptUrl.toString(), scriptEngine); if (!scriptEngine->hasScript()) { qDebug() << "Application::loadScript(), script failed to load..."; diff --git a/libraries/particles/src/Particle.cpp b/libraries/particles/src/Particle.cpp index 90e59b9422..59265c00dc 100644 --- a/libraries/particles/src/Particle.cpp +++ b/libraries/particles/src/Particle.cpp @@ -873,7 +873,7 @@ void Particle::endParticleScriptContext(ScriptEngine& engine, ParticleScriptObje void Particle::executeUpdateScripts() { // Only run this particle script if there's a script attached directly to the particle. if (!_script.isEmpty()) { - ScriptEngine engine(_script, QString("")); + ScriptEngine engine(_script); ParticleScriptObject particleScriptable(this); startParticleScriptContext(engine, particleScriptable); particleScriptable.emitUpdate(); @@ -884,7 +884,7 @@ void Particle::executeUpdateScripts() { void Particle::collisionWithParticle(Particle* other, const glm::vec3& penetration) { // Only run this particle script if there's a script attached directly to the particle. if (!_script.isEmpty()) { - ScriptEngine engine(_script, QString("")); + ScriptEngine engine(_script); ParticleScriptObject particleScriptable(this); startParticleScriptContext(engine, particleScriptable); ParticleScriptObject otherParticleScriptable(other); @@ -896,7 +896,7 @@ void Particle::collisionWithParticle(Particle* other, const glm::vec3& penetrati void Particle::collisionWithVoxel(VoxelDetail* voxelDetails, const glm::vec3& penetration) { // Only run this particle script if there's a script attached directly to the particle. if (!_script.isEmpty()) { - ScriptEngine engine(_script, QString("")); + ScriptEngine engine(_script); ParticleScriptObject particleScriptable(this); startParticleScriptContext(engine, particleScriptable); particleScriptable.emitCollisionWithVoxel(*voxelDetails, penetration); diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index 025f316d29..a4aae61248 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -93,7 +93,7 @@ ScriptEngine::ScriptEngine(const QString& scriptContents, const QString& fileNam { } -ScriptEngine::ScriptEngine(const QString& fileNameString, +ScriptEngine::ScriptEngine(const QUrl& scriptURL, AbstractControllerScriptingInterface* controllerScriptingInterface) : _scriptContents(), _isFinished(false), @@ -110,19 +110,21 @@ ScriptEngine::ScriptEngine(const QString& fileNameString, _controllerScriptingInterface(controllerScriptingInterface), _avatarData(NULL), _scriptName(), - _fileNameString(fileNameString), + _fileNameString(), _quatLibrary(), _vec3Library(), _uuidLibrary(), _animationCache(this) { - QUrl url(fileNameString); - QString scriptUrlString = url.toString(); + QString scriptURLString = scriptURL.toString(); + _fileNameString = scriptURLString; + + QUrl url(scriptURL); // 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) { - url = QUrl::fromLocalFile(scriptUrlString); + url = QUrl::fromLocalFile(scriptURLString); } // ok, let's see if it's valid... and if so, load it diff --git a/libraries/script-engine/src/ScriptEngine.h b/libraries/script-engine/src/ScriptEngine.h index f88017515e..bf2ac40568 100644 --- a/libraries/script-engine/src/ScriptEngine.h +++ b/libraries/script-engine/src/ScriptEngine.h @@ -40,7 +40,7 @@ const unsigned int SCRIPT_DATA_CALLBACK_USECS = floor(((1.0 / 60.0f) * 1000 * 10 class ScriptEngine : public QObject { Q_OBJECT public: - ScriptEngine(const QString& fileNameString, + ScriptEngine(const QUrl& scriptURL, AbstractControllerScriptingInterface* controllerScriptingInterface = NULL); ScriptEngine(const QString& scriptContents = NO_SCRIPT, From 46f2ab73bccf6fa8669856534f0c20f96ee8c416 Mon Sep 17 00:00:00 2001 From: matsukaze Date: Tue, 10 Jun 2014 22:29:58 -0400 Subject: [PATCH 09/13] Job #19766 BUG: Stop or reload all scripts crashes interface fixed, part 3 Move the conversion of scriptName to QUrl to the beginning of Application::loadScript. Use the scriptURLString to query the _scriptEngineHash map. --- CMakeLists.txt | 4 + assignment-client/src/AssignmentClient.cpp | 123 +++++++++++++++------ assignment-client/src/AssignmentClient.h | 48 ++++++++ interface/src/Application.cpp | 9 +- interface/src/Audio.cpp | 29 ++++- 5 files changed, 175 insertions(+), 38 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index cb1e4224cf..298b9a791e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -20,6 +20,10 @@ if (NOT QT_CMAKE_PREFIX_PATH) set(QT_CMAKE_PREFIX_PATH $ENV{QT_CMAKE_PREFIX_PATH}) endif () +if (NOT ZLIB_ROOT) + set(ZLIB_ROOT $ENV{ZLIB_ROOT}) +endif () + set(CMAKE_PREFIX_PATH ${CMAKE_PREFIX_PATH} ${QT_CMAKE_PREFIX_PATH}) # set our Base SDK to 10.8 diff --git a/assignment-client/src/AssignmentClient.cpp b/assignment-client/src/AssignmentClient.cpp index 009bd42e88..a2faaab651 100644 --- a/assignment-client/src/AssignmentClient.cpp +++ b/assignment-client/src/AssignmentClient.cpp @@ -33,6 +33,64 @@ SharedAssignmentPointer AssignmentClient::_currentAssignment; int hifiSockAddrMeta = qRegisterMetaType("HifiSockAddr"); +template +class Parse : public Parser { +public: + Parse(const QString& option, bool required, T* value) : Parser(option, required), _value(value) {} + bool parse(const QStringList& argumentList); +private: + T* _value; +}; + +template<> +bool Parse::parse(const QStringList& argumentList) { + int argumentIndex = findToken(argumentList); + if (argumentIndex != -1) { + argumentIndex++; + if (argumentList.size() > argumentIndex) { + bool ok; + int value = argumentList[argumentIndex].toInt(&ok); + if (ok) { + *_value = static_cast(value); + } + validateValue(ok); + } else { + valueNotSpecified(); + } + } + return isValid(); +} + +template<> +bool Parse::parse(const QStringList& argumentList) { + int argumentIndex = findToken(argumentList); + if (argumentIndex != -1) { + argumentIndex++; + if (argumentList.size() > argumentIndex) { + *_value = QString(argumentList[argumentIndex]); + validateValue(!_value->isNull()); + } else { + valueNotSpecified(); + } + } + return isValid(); +} + +template<> +bool Parse::parse(const QStringList& argumentList) { + int argumentIndex = findToken(argumentList); + if (argumentIndex != -1) { + argumentIndex++; + if (argumentList.size() > argumentIndex) { + *_value = argumentList[argumentIndex]; + validateValue(true); + } else { + valueNotSpecified(); + } + } + return isValid(); +} + AssignmentClient::AssignmentClient(int &argc, char **argv) : QCoreApplication(argc, argv), _assignmentServerHostname(DEFAULT_ASSIGNMENT_SERVER_HOSTNAME) @@ -43,40 +101,42 @@ AssignmentClient::AssignmentClient(int &argc, char **argv) : QSettings::setDefaultFormat(QSettings::IniFormat); QStringList argumentList = arguments(); - + + // Define parser tokens + const QString ASSIGNMENT_TYPE_OVERRIDE_OPTION = "-t"; + const QString ASSIGNMENT_POOL_OPTION = "--pool"; + const QString ASSIGNMENT_WALLET_DESTINATION_ID_OPTION = "--wallet"; + const QString CUSTOM_ASSIGNMENT_SERVER_HOSTNAME_OPTION = "-a"; + + // Define parser results + Assignment::Type requestAssignmentType = Assignment::AllTypes; + QString assignmentPool; + QUuid walletUUID; + + // Define parsers + _parsers.insert(ASSIGNMENT_TYPE_OVERRIDE_OPTION, new Parse(ASSIGNMENT_TYPE_OVERRIDE_OPTION, false, &requestAssignmentType)); + _parsers.insert(ASSIGNMENT_POOL_OPTION, new Parse(ASSIGNMENT_POOL_OPTION, false, &assignmentPool)); + _parsers.insert(ASSIGNMENT_WALLET_DESTINATION_ID_OPTION, new Parse(ASSIGNMENT_WALLET_DESTINATION_ID_OPTION, false, &walletUUID)); + _parsers.insert(CUSTOM_ASSIGNMENT_SERVER_HOSTNAME_OPTION, new Parse(CUSTOM_ASSIGNMENT_SERVER_HOSTNAME_OPTION, false, &_assignmentServerHostname)); + // register meta type is required for queued invoke method on Assignment subclasses // set the logging target to the the CHILD_TARGET_NAME Logging::setTargetName(ASSIGNMENT_CLIENT_TARGET_NAME); - const QString ASSIGNMENT_TYPE_OVVERIDE_OPTION = "-t"; - int argumentIndex = argumentList.indexOf(ASSIGNMENT_TYPE_OVVERIDE_OPTION); - - Assignment::Type requestAssignmentType = Assignment::AllTypes; - - if (argumentIndex != -1) { - requestAssignmentType = (Assignment::Type) argumentList[argumentIndex + 1].toInt(); + foreach (Parser* option, _parsers) { + if (option) { + option->parse(argumentList); + } } - - const QString ASSIGNMENT_POOL_OPTION = "--pool"; - - argumentIndex = argumentList.indexOf(ASSIGNMENT_POOL_OPTION); - - QString assignmentPool; - - if (argumentIndex != -1) { - assignmentPool = argumentList[argumentIndex + 1]; - } - + // setup our _requestAssignment member variable from the passed arguments _requestAssignment = Assignment(Assignment::RequestCommand, requestAssignmentType, assignmentPool); // check if we were passed a wallet UUID on the command line // this would represent where the user running AC wants funds sent to - - const QString ASSIGNMENT_WALLET_DESTINATION_ID_OPTION = "--wallet"; - if ((argumentIndex = argumentList.indexOf(ASSIGNMENT_WALLET_DESTINATION_ID_OPTION)) != -1) { - QUuid walletUUID = QString(argumentList[argumentIndex + 1]); + if (_parsers[ASSIGNMENT_WALLET_DESTINATION_ID_OPTION]->exists() && + _parsers[ASSIGNMENT_WALLET_DESTINATION_ID_OPTION]->isValid()) { qDebug() << "The destination wallet UUID for credits is" << uuidStringWithoutCurlyBraces(walletUUID); _requestAssignment.setWalletUUID(walletUUID); } @@ -84,14 +144,9 @@ AssignmentClient::AssignmentClient(int &argc, char **argv) : // create a NodeList as an unassigned client NodeList* nodeList = NodeList::createInstance(NodeType::Unassigned); - // check for an overriden assignment server hostname - const QString CUSTOM_ASSIGNMENT_SERVER_HOSTNAME_OPTION = "-a"; - - argumentIndex = argumentList.indexOf(CUSTOM_ASSIGNMENT_SERVER_HOSTNAME_OPTION); - - if (argumentIndex != -1) { - _assignmentServerHostname = argumentList[argumentIndex + 1]; - + // check for an overriden assignment server hostname + if (_parsers[CUSTOM_ASSIGNMENT_SERVER_HOSTNAME_OPTION]->exists() && + _parsers[CUSTOM_ASSIGNMENT_SERVER_HOSTNAME_OPTION]->isValid()) { // set the custom assignment socket on our NodeList HifiSockAddr customAssignmentSocket = HifiSockAddr(_assignmentServerHostname, DEFAULT_DOMAIN_SERVER_PORT); @@ -113,6 +168,12 @@ AssignmentClient::AssignmentClient(int &argc, char **argv) : this, &AssignmentClient::handleAuthenticationRequest); } +AssignmentClient::~AssignmentClient() { + foreach (Parser* option, _parsers) { + delete option; + } +} + void AssignmentClient::sendAssignmentRequest() { if (!_currentAssignment) { NodeList::getInstance()->sendAssignment(_requestAssignment); diff --git a/assignment-client/src/AssignmentClient.h b/assignment-client/src/AssignmentClient.h index 9834402dbf..8951d44135 100644 --- a/assignment-client/src/AssignmentClient.h +++ b/assignment-client/src/AssignmentClient.h @@ -16,11 +16,57 @@ #include "ThreadedAssignment.h" +class Parser { +public: + Parser(const QString& option, bool required) : _option(option), _required(required), _exists(false), _isValid(false) {} + virtual bool parse(const QStringList& argumentList) { return false; } + bool exists() { return _exists; } + bool isValid() { return _isValid; } + +protected: + int findToken(const QStringList& argumentList) { + int argumentIndex = argumentList.indexOf(_option); + validateOption(argumentIndex); + return argumentIndex; + } + + void validateOption(int index) { + _exists = true; + if (index == -1) { + _exists = false; + if (_required) { + qDebug() << "Command line option " << _option << " is missing"; + } + } + } + + void valueNotSpecified() { + _isValid = false; + qDebug() << "Command line option " << _option << " value is missing"; + } + + void validateValue(bool ok) { + _isValid = ok; + if (!ok) { + qDebug() << "Command line option " << _option << " value is invalid"; + } + } + +private: + QString _option; + bool _required; + bool _exists; + bool _isValid; +}; + class AssignmentClient : public QCoreApplication { Q_OBJECT public: AssignmentClient(int &argc, char **argv); + ~AssignmentClient(); + static const SharedAssignmentPointer& getCurrentAssignment() { return _currentAssignment; } + private slots: void sendAssignmentRequest(); void readPendingDatagrams(); @@ -31,6 +77,8 @@ private: Assignment _requestAssignment; static SharedAssignmentPointer _currentAssignment; QString _assignmentServerHostname; + + QMap _parsers; }; #endif // hifi_AssignmentClient_h diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 9fb821bff4..25a5ba8007 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -3483,8 +3483,10 @@ void Application::saveScripts() { } ScriptEngine* Application::loadScript(const QString& scriptName, bool loadScriptFromEditor) { - if(loadScriptFromEditor && _scriptEnginesHash.contains(scriptName) && !_scriptEnginesHash[scriptName]->isFinished()){ - return _scriptEnginesHash[scriptName]; + QUrl scriptUrl(scriptName); + const QString& scriptURLString = scriptUrl.toString(); + if(loadScriptFromEditor && _scriptEnginesHash.contains(scriptURLString) && !_scriptEnginesHash[scriptURLString]->isFinished()){ + return _scriptEnginesHash[scriptURLString]; } ScriptEngine* scriptEngine; @@ -3492,9 +3494,8 @@ ScriptEngine* Application::loadScript(const QString& scriptName, bool loadScript scriptEngine = new ScriptEngine(NO_SCRIPT, "", &_controllerScriptingInterface); } else { // start the script on a new thread... - QUrl scriptUrl(scriptName); scriptEngine = new ScriptEngine(scriptUrl, &_controllerScriptingInterface); - _scriptEnginesHash.insert(scriptUrl.toString(), scriptEngine); + _scriptEnginesHash.insert(scriptURLString, scriptEngine); if (!scriptEngine->hasScript()) { qDebug() << "Application::loadScript(), script failed to load..."; diff --git a/interface/src/Audio.cpp b/interface/src/Audio.cpp index ebc228a13d..1ecfad9007 100644 --- a/interface/src/Audio.cpp +++ b/interface/src/Audio.cpp @@ -219,8 +219,9 @@ QAudioDeviceInfo defaultAudioDeviceForMode(QAudio::Mode mode) { pPropertyStore->Release(); pPropertyStore = NULL; //QAudio devices seems to only take the 31 first characters of the Friendly Device Name. - const DWORD QT_WIN_MAX_AUDIO_DEVICENAME_LEN = 31; - deviceName = QString::fromWCharArray((wchar_t*)pv.pwszVal).left(QT_WIN_MAX_AUDIO_DEVICENAME_LEN); + //const DWORD QT_WIN_MAX_AUDIO_DEVICENAME_LEN = 31; + // deviceName = QString::fromWCharArray((wchar_t*)pv.pwszVal).left(QT_WIN_MAX_AUDIO_DEVICENAME_LEN); + deviceName = QString::fromWCharArray((wchar_t*)pv.pwszVal); qDebug() << (mode == QAudio::AudioOutput ? "output" : "input") << " device:" << deviceName; PropVariantClear(&pv); } @@ -1077,13 +1078,35 @@ void Audio::renderToolBox(int x, int y, bool boxed) { } _iconBounds = QRect(x, y, MUTE_ICON_SIZE, MUTE_ICON_SIZE); + + static quint64 last = 0; // Hold the last time sample + if (!_muted) { + last = 0; + glColor3f(1,1,1); glBindTexture(GL_TEXTURE_2D, _micTextureId); } else { + quint64 usecTimestampNow(); + static const float CYCLE_DURATION = 3.0f; // Seconds + quint64 now = usecTimestampNow(); + float delta = (float)(now - last) / 1000000.0f; + float from = 1.0f; // Linear fade down (upper bound) + float to = 0.3f; // Linear fade down (lower bound) + + if (delta > CYCLE_DURATION) { + last = now; + delta -= (int)delta; + } else if (delta > (CYCLE_DURATION * 0.5f)) { + // Linear fade up + from = 0.3f; // lower bound + to = 1.0f; // upper bound + } + // Compute a linear ramp to fade the color from full to partial saturation + float linearRamp = (from - to) * delta / CYCLE_DURATION + to; + glColor3f(linearRamp, linearRamp, linearRamp); glBindTexture(GL_TEXTURE_2D, _muteTextureId); } - glColor3f(1,1,1); glBegin(GL_QUADS); glTexCoord2f(1, 1); From 1e1cc69287d8cdaaf304bbfc05da56ae9564016f Mon Sep 17 00:00:00 2001 From: matsukaze Date: Tue, 10 Jun 2014 22:37:34 -0400 Subject: [PATCH 10/13] Revert "Job #19766 BUG: Stop or reload all scripts crashes interface fixed, part 3" This reverts commit 46f2ab73bccf6fa8669856534f0c20f96ee8c416. --- CMakeLists.txt | 4 - assignment-client/src/AssignmentClient.cpp | 123 ++++++--------------- assignment-client/src/AssignmentClient.h | 48 -------- interface/src/Application.cpp | 9 +- interface/src/Audio.cpp | 29 +---- 5 files changed, 38 insertions(+), 175 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 298b9a791e..cb1e4224cf 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -20,10 +20,6 @@ if (NOT QT_CMAKE_PREFIX_PATH) set(QT_CMAKE_PREFIX_PATH $ENV{QT_CMAKE_PREFIX_PATH}) endif () -if (NOT ZLIB_ROOT) - set(ZLIB_ROOT $ENV{ZLIB_ROOT}) -endif () - set(CMAKE_PREFIX_PATH ${CMAKE_PREFIX_PATH} ${QT_CMAKE_PREFIX_PATH}) # set our Base SDK to 10.8 diff --git a/assignment-client/src/AssignmentClient.cpp b/assignment-client/src/AssignmentClient.cpp index a2faaab651..009bd42e88 100644 --- a/assignment-client/src/AssignmentClient.cpp +++ b/assignment-client/src/AssignmentClient.cpp @@ -33,64 +33,6 @@ SharedAssignmentPointer AssignmentClient::_currentAssignment; int hifiSockAddrMeta = qRegisterMetaType("HifiSockAddr"); -template -class Parse : public Parser { -public: - Parse(const QString& option, bool required, T* value) : Parser(option, required), _value(value) {} - bool parse(const QStringList& argumentList); -private: - T* _value; -}; - -template<> -bool Parse::parse(const QStringList& argumentList) { - int argumentIndex = findToken(argumentList); - if (argumentIndex != -1) { - argumentIndex++; - if (argumentList.size() > argumentIndex) { - bool ok; - int value = argumentList[argumentIndex].toInt(&ok); - if (ok) { - *_value = static_cast(value); - } - validateValue(ok); - } else { - valueNotSpecified(); - } - } - return isValid(); -} - -template<> -bool Parse::parse(const QStringList& argumentList) { - int argumentIndex = findToken(argumentList); - if (argumentIndex != -1) { - argumentIndex++; - if (argumentList.size() > argumentIndex) { - *_value = QString(argumentList[argumentIndex]); - validateValue(!_value->isNull()); - } else { - valueNotSpecified(); - } - } - return isValid(); -} - -template<> -bool Parse::parse(const QStringList& argumentList) { - int argumentIndex = findToken(argumentList); - if (argumentIndex != -1) { - argumentIndex++; - if (argumentList.size() > argumentIndex) { - *_value = argumentList[argumentIndex]; - validateValue(true); - } else { - valueNotSpecified(); - } - } - return isValid(); -} - AssignmentClient::AssignmentClient(int &argc, char **argv) : QCoreApplication(argc, argv), _assignmentServerHostname(DEFAULT_ASSIGNMENT_SERVER_HOSTNAME) @@ -101,42 +43,40 @@ AssignmentClient::AssignmentClient(int &argc, char **argv) : QSettings::setDefaultFormat(QSettings::IniFormat); QStringList argumentList = arguments(); - - // Define parser tokens - const QString ASSIGNMENT_TYPE_OVERRIDE_OPTION = "-t"; - const QString ASSIGNMENT_POOL_OPTION = "--pool"; - const QString ASSIGNMENT_WALLET_DESTINATION_ID_OPTION = "--wallet"; - const QString CUSTOM_ASSIGNMENT_SERVER_HOSTNAME_OPTION = "-a"; - - // Define parser results - Assignment::Type requestAssignmentType = Assignment::AllTypes; - QString assignmentPool; - QUuid walletUUID; - - // Define parsers - _parsers.insert(ASSIGNMENT_TYPE_OVERRIDE_OPTION, new Parse(ASSIGNMENT_TYPE_OVERRIDE_OPTION, false, &requestAssignmentType)); - _parsers.insert(ASSIGNMENT_POOL_OPTION, new Parse(ASSIGNMENT_POOL_OPTION, false, &assignmentPool)); - _parsers.insert(ASSIGNMENT_WALLET_DESTINATION_ID_OPTION, new Parse(ASSIGNMENT_WALLET_DESTINATION_ID_OPTION, false, &walletUUID)); - _parsers.insert(CUSTOM_ASSIGNMENT_SERVER_HOSTNAME_OPTION, new Parse(CUSTOM_ASSIGNMENT_SERVER_HOSTNAME_OPTION, false, &_assignmentServerHostname)); - + // register meta type is required for queued invoke method on Assignment subclasses // set the logging target to the the CHILD_TARGET_NAME Logging::setTargetName(ASSIGNMENT_CLIENT_TARGET_NAME); - foreach (Parser* option, _parsers) { - if (option) { - option->parse(argumentList); - } + const QString ASSIGNMENT_TYPE_OVVERIDE_OPTION = "-t"; + int argumentIndex = argumentList.indexOf(ASSIGNMENT_TYPE_OVVERIDE_OPTION); + + Assignment::Type requestAssignmentType = Assignment::AllTypes; + + if (argumentIndex != -1) { + requestAssignmentType = (Assignment::Type) argumentList[argumentIndex + 1].toInt(); } - + + const QString ASSIGNMENT_POOL_OPTION = "--pool"; + + argumentIndex = argumentList.indexOf(ASSIGNMENT_POOL_OPTION); + + QString assignmentPool; + + if (argumentIndex != -1) { + assignmentPool = argumentList[argumentIndex + 1]; + } + // setup our _requestAssignment member variable from the passed arguments _requestAssignment = Assignment(Assignment::RequestCommand, requestAssignmentType, assignmentPool); // check if we were passed a wallet UUID on the command line // this would represent where the user running AC wants funds sent to - if (_parsers[ASSIGNMENT_WALLET_DESTINATION_ID_OPTION]->exists() && - _parsers[ASSIGNMENT_WALLET_DESTINATION_ID_OPTION]->isValid()) { + + const QString ASSIGNMENT_WALLET_DESTINATION_ID_OPTION = "--wallet"; + if ((argumentIndex = argumentList.indexOf(ASSIGNMENT_WALLET_DESTINATION_ID_OPTION)) != -1) { + QUuid walletUUID = QString(argumentList[argumentIndex + 1]); qDebug() << "The destination wallet UUID for credits is" << uuidStringWithoutCurlyBraces(walletUUID); _requestAssignment.setWalletUUID(walletUUID); } @@ -144,9 +84,14 @@ AssignmentClient::AssignmentClient(int &argc, char **argv) : // create a NodeList as an unassigned client NodeList* nodeList = NodeList::createInstance(NodeType::Unassigned); - // check for an overriden assignment server hostname - if (_parsers[CUSTOM_ASSIGNMENT_SERVER_HOSTNAME_OPTION]->exists() && - _parsers[CUSTOM_ASSIGNMENT_SERVER_HOSTNAME_OPTION]->isValid()) { + // check for an overriden assignment server hostname + const QString CUSTOM_ASSIGNMENT_SERVER_HOSTNAME_OPTION = "-a"; + + argumentIndex = argumentList.indexOf(CUSTOM_ASSIGNMENT_SERVER_HOSTNAME_OPTION); + + if (argumentIndex != -1) { + _assignmentServerHostname = argumentList[argumentIndex + 1]; + // set the custom assignment socket on our NodeList HifiSockAddr customAssignmentSocket = HifiSockAddr(_assignmentServerHostname, DEFAULT_DOMAIN_SERVER_PORT); @@ -168,12 +113,6 @@ AssignmentClient::AssignmentClient(int &argc, char **argv) : this, &AssignmentClient::handleAuthenticationRequest); } -AssignmentClient::~AssignmentClient() { - foreach (Parser* option, _parsers) { - delete option; - } -} - void AssignmentClient::sendAssignmentRequest() { if (!_currentAssignment) { NodeList::getInstance()->sendAssignment(_requestAssignment); diff --git a/assignment-client/src/AssignmentClient.h b/assignment-client/src/AssignmentClient.h index 8951d44135..9834402dbf 100644 --- a/assignment-client/src/AssignmentClient.h +++ b/assignment-client/src/AssignmentClient.h @@ -16,57 +16,11 @@ #include "ThreadedAssignment.h" -class Parser { -public: - Parser(const QString& option, bool required) : _option(option), _required(required), _exists(false), _isValid(false) {} - virtual bool parse(const QStringList& argumentList) { return false; } - bool exists() { return _exists; } - bool isValid() { return _isValid; } - -protected: - int findToken(const QStringList& argumentList) { - int argumentIndex = argumentList.indexOf(_option); - validateOption(argumentIndex); - return argumentIndex; - } - - void validateOption(int index) { - _exists = true; - if (index == -1) { - _exists = false; - if (_required) { - qDebug() << "Command line option " << _option << " is missing"; - } - } - } - - void valueNotSpecified() { - _isValid = false; - qDebug() << "Command line option " << _option << " value is missing"; - } - - void validateValue(bool ok) { - _isValid = ok; - if (!ok) { - qDebug() << "Command line option " << _option << " value is invalid"; - } - } - -private: - QString _option; - bool _required; - bool _exists; - bool _isValid; -}; - class AssignmentClient : public QCoreApplication { Q_OBJECT public: AssignmentClient(int &argc, char **argv); - ~AssignmentClient(); - static const SharedAssignmentPointer& getCurrentAssignment() { return _currentAssignment; } - private slots: void sendAssignmentRequest(); void readPendingDatagrams(); @@ -77,8 +31,6 @@ private: Assignment _requestAssignment; static SharedAssignmentPointer _currentAssignment; QString _assignmentServerHostname; - - QMap _parsers; }; #endif // hifi_AssignmentClient_h diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 25a5ba8007..9fb821bff4 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -3483,10 +3483,8 @@ void Application::saveScripts() { } ScriptEngine* Application::loadScript(const QString& scriptName, bool loadScriptFromEditor) { - QUrl scriptUrl(scriptName); - const QString& scriptURLString = scriptUrl.toString(); - if(loadScriptFromEditor && _scriptEnginesHash.contains(scriptURLString) && !_scriptEnginesHash[scriptURLString]->isFinished()){ - return _scriptEnginesHash[scriptURLString]; + if(loadScriptFromEditor && _scriptEnginesHash.contains(scriptName) && !_scriptEnginesHash[scriptName]->isFinished()){ + return _scriptEnginesHash[scriptName]; } ScriptEngine* scriptEngine; @@ -3494,8 +3492,9 @@ ScriptEngine* Application::loadScript(const QString& scriptName, bool loadScript scriptEngine = new ScriptEngine(NO_SCRIPT, "", &_controllerScriptingInterface); } else { // start the script on a new thread... + QUrl scriptUrl(scriptName); scriptEngine = new ScriptEngine(scriptUrl, &_controllerScriptingInterface); - _scriptEnginesHash.insert(scriptURLString, scriptEngine); + _scriptEnginesHash.insert(scriptUrl.toString(), scriptEngine); if (!scriptEngine->hasScript()) { qDebug() << "Application::loadScript(), script failed to load..."; diff --git a/interface/src/Audio.cpp b/interface/src/Audio.cpp index 1ecfad9007..ebc228a13d 100644 --- a/interface/src/Audio.cpp +++ b/interface/src/Audio.cpp @@ -219,9 +219,8 @@ QAudioDeviceInfo defaultAudioDeviceForMode(QAudio::Mode mode) { pPropertyStore->Release(); pPropertyStore = NULL; //QAudio devices seems to only take the 31 first characters of the Friendly Device Name. - //const DWORD QT_WIN_MAX_AUDIO_DEVICENAME_LEN = 31; - // deviceName = QString::fromWCharArray((wchar_t*)pv.pwszVal).left(QT_WIN_MAX_AUDIO_DEVICENAME_LEN); - deviceName = QString::fromWCharArray((wchar_t*)pv.pwszVal); + const DWORD QT_WIN_MAX_AUDIO_DEVICENAME_LEN = 31; + deviceName = QString::fromWCharArray((wchar_t*)pv.pwszVal).left(QT_WIN_MAX_AUDIO_DEVICENAME_LEN); qDebug() << (mode == QAudio::AudioOutput ? "output" : "input") << " device:" << deviceName; PropVariantClear(&pv); } @@ -1078,35 +1077,13 @@ void Audio::renderToolBox(int x, int y, bool boxed) { } _iconBounds = QRect(x, y, MUTE_ICON_SIZE, MUTE_ICON_SIZE); - - static quint64 last = 0; // Hold the last time sample - if (!_muted) { - last = 0; - glColor3f(1,1,1); glBindTexture(GL_TEXTURE_2D, _micTextureId); } else { - quint64 usecTimestampNow(); - static const float CYCLE_DURATION = 3.0f; // Seconds - quint64 now = usecTimestampNow(); - float delta = (float)(now - last) / 1000000.0f; - float from = 1.0f; // Linear fade down (upper bound) - float to = 0.3f; // Linear fade down (lower bound) - - if (delta > CYCLE_DURATION) { - last = now; - delta -= (int)delta; - } else if (delta > (CYCLE_DURATION * 0.5f)) { - // Linear fade up - from = 0.3f; // lower bound - to = 1.0f; // upper bound - } - // Compute a linear ramp to fade the color from full to partial saturation - float linearRamp = (from - to) * delta / CYCLE_DURATION + to; - glColor3f(linearRamp, linearRamp, linearRamp); glBindTexture(GL_TEXTURE_2D, _muteTextureId); } + glColor3f(1,1,1); glBegin(GL_QUADS); glTexCoord2f(1, 1); From 679846267e32a0929045dd86cd914f8c4c80ff91 Mon Sep 17 00:00:00 2001 From: matsukaze Date: Tue, 10 Jun 2014 22:42:01 -0400 Subject: [PATCH 11/13] Job #19766 BUG: Stop or reload all scripts crashes interface fixed, part 3 Same comments as before, but without the local files changes mistakenly checked in. --- interface/src/Application.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 9fb821bff4..25a5ba8007 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -3483,8 +3483,10 @@ void Application::saveScripts() { } ScriptEngine* Application::loadScript(const QString& scriptName, bool loadScriptFromEditor) { - if(loadScriptFromEditor && _scriptEnginesHash.contains(scriptName) && !_scriptEnginesHash[scriptName]->isFinished()){ - return _scriptEnginesHash[scriptName]; + QUrl scriptUrl(scriptName); + const QString& scriptURLString = scriptUrl.toString(); + if(loadScriptFromEditor && _scriptEnginesHash.contains(scriptURLString) && !_scriptEnginesHash[scriptURLString]->isFinished()){ + return _scriptEnginesHash[scriptURLString]; } ScriptEngine* scriptEngine; @@ -3492,9 +3494,8 @@ ScriptEngine* Application::loadScript(const QString& scriptName, bool loadScript scriptEngine = new ScriptEngine(NO_SCRIPT, "", &_controllerScriptingInterface); } else { // start the script on a new thread... - QUrl scriptUrl(scriptName); scriptEngine = new ScriptEngine(scriptUrl, &_controllerScriptingInterface); - _scriptEnginesHash.insert(scriptUrl.toString(), scriptEngine); + _scriptEnginesHash.insert(scriptURLString, scriptEngine); if (!scriptEngine->hasScript()) { qDebug() << "Application::loadScript(), script failed to load..."; From 41b9c3b7049cc46732cafa9bf833c5c1537b0fd5 Mon Sep 17 00:00:00 2001 From: Dev5 Date: Wed, 11 Jun 2014 09:06:00 -0700 Subject: [PATCH 12/13] Removed empty update function --- examples/growTrees.js | 5 ----- 1 file changed, 5 deletions(-) diff --git a/examples/growTrees.js b/examples/growTrees.js index d853804e77..142c72300a 100644 --- a/examples/growTrees.js +++ b/examples/growTrees.js @@ -814,9 +814,6 @@ function mousePressEvent(event) { } -function update() { - -} function scriptEnding() { Overlays.deleteOverlay(linePreviewTop); @@ -832,6 +829,4 @@ Controller.mouseMoveEvent.connect(mouseMoveEvent); Script.scriptEnding.connect(scriptEnding); -Script.update.connect(update); - Voxels.setPacketsPerSecond(10000); From fc07ceb412d1975d3b3fc07374073e3eb55bc1c9 Mon Sep 17 00:00:00 2001 From: wangyix Date: Wed, 11 Jun 2014 10:54:44 -0700 Subject: [PATCH 13/13] added killNode() slot for _packetCounts garbage collection --- interface/src/Application.cpp | 1 + libraries/networking/src/ReceivedPacketProcessor.cpp | 4 ++++ libraries/networking/src/ReceivedPacketProcessor.h | 3 +++ 3 files changed, 8 insertions(+) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index eab15ed678..2b3256141e 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -244,6 +244,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) : connect(nodeList, SIGNAL(nodeKilled(SharedNodePointer)), SLOT(nodeKilled(SharedNodePointer))); connect(nodeList, SIGNAL(nodeAdded(SharedNodePointer)), &_voxels, SLOT(nodeAdded(SharedNodePointer))); connect(nodeList, SIGNAL(nodeKilled(SharedNodePointer)), &_voxels, SLOT(nodeKilled(SharedNodePointer))); + connect(nodeList, SIGNAL(nodeKilled(SharedNodePointer)), &_octreeProcessor, SLOT(nodeKilled(SharedNodePointer))); connect(nodeList, &NodeList::uuidChanged, this, &Application::updateWindowTitle); connect(nodeList, SIGNAL(uuidChanged(const QUuid&)), _myAvatar, SLOT(setSessionUUID(const QUuid&))); connect(nodeList, &NodeList::limitOfSilentDomainCheckInsReached, nodeList, &NodeList::reset); diff --git a/libraries/networking/src/ReceivedPacketProcessor.cpp b/libraries/networking/src/ReceivedPacketProcessor.cpp index 46f1515016..d556e8a059 100644 --- a/libraries/networking/src/ReceivedPacketProcessor.cpp +++ b/libraries/networking/src/ReceivedPacketProcessor.cpp @@ -49,3 +49,7 @@ bool ReceivedPacketProcessor::process() { } return isStillRunning(); // keep running till they terminate us } + +void ReceivedPacketProcessor::killNode(const SharedNodePointer& node) { + _nodePacketCounts.remove(node->getUUID()); +} diff --git a/libraries/networking/src/ReceivedPacketProcessor.h b/libraries/networking/src/ReceivedPacketProcessor.h index 096005ffe7..80fe75aaa7 100644 --- a/libraries/networking/src/ReceivedPacketProcessor.h +++ b/libraries/networking/src/ReceivedPacketProcessor.h @@ -41,6 +41,9 @@ public: /// How many received packets waiting are to be processed int packetsToProcessCount() const { return _packets.size(); } +public slots: + void killNode(const SharedNodePointer& node); + protected: /// Callback for processing of recieved packets. Implement this to process the incoming packets. /// \param SharedNodePointer& sendingNode the node that sent this packet