diff --git a/examples/example/games/airHockey.js b/examples/example/games/airHockey.js old mode 100644 new mode 100755 index a703f379bc..ca7f007df6 --- a/examples/example/games/airHockey.js +++ b/examples/example/games/airHockey.js @@ -83,6 +83,30 @@ var puck_name_index = 2; var light_name_index = 3; var floor_name_index = 4; +//Create Spawn and Del. Button Vars. + +function updateButtonPosition() { + Overlays.editOverlay(spawnButton, { + x: screenSize.x / 2 + PADDING, + y: screenSize.y - (BUTTON_SIZE * 2 + PADDING), + }); + Overlays.editOverlay(deleteButton, { + x: screenSize.x / 2 - BUTTON_SIZE, + y: screenSize.y - (BUTTON_SIZE * 2 + PADDING), + }); +} + +function onScriptUpdate() { + var oldScreenSize = screenSize; + + screenSize = Controller.getViewportDimensions(); + + if (screenSize.x !== oldScreenSize.x || screenSize.y !== oldScreenSize.y) { + updateButtonPosition(); + } +} + +screenSize = Controller.getViewportDimensions(); var deleteButton = Overlays.addOverlay("image", { x: screenSize.x / 2 - BUTTON_SIZE, @@ -112,6 +136,7 @@ var spawnButton = Overlays.addOverlay("image", { alpha: 1 }); +Script.update.connect(onScriptUpdate); var floor, edge1, edge2, edge3a, edge3b, edge4a, edge4b, light; diff --git a/interface/src/AbstractLoggerInterface.h b/interface/src/AbstractLoggerInterface.h index fe45346e4c..3823060b13 100644 --- a/interface/src/AbstractLoggerInterface.h +++ b/interface/src/AbstractLoggerInterface.h @@ -24,7 +24,7 @@ public: inline bool extraDebugging() { return _extraDebugging; } inline void setExtraDebugging(bool debugging) { _extraDebugging = debugging; } - virtual void addMessage(QString) = 0; + virtual void addMessage(const QString&) = 0; virtual QString getLogData() = 0; virtual void locateLog() = 0; @@ -32,7 +32,7 @@ signals: void logReceived(QString message); private: - bool _extraDebugging; + bool _extraDebugging{ false }; }; #endif // hifi_AbstractLoggerInterface_h diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index be773d52f3..04ffe64d8d 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -3367,14 +3367,6 @@ void Application::displaySide(RenderArgs* renderArgs, Camera& theCamera, bool se // load the view frustum loadViewFrustum(theCamera, _displayViewFrustum); - // flip x if in mirror mode (also requires reversing winding order for backface culling) - if (theCamera.getMode() == CAMERA_MODE_MIRROR) { - //glScalef(-1.0f, 1.0f, 1.0f); - //glFrontFace(GL_CW); - } else { - glFrontFace(GL_CCW); - } - // transform view according to theCamera // could be myCamera (if in normal mode) // or could be viewFrustumOffsetCamera if in offset mode @@ -3392,9 +3384,6 @@ void Application::displaySide(RenderArgs* renderArgs, Camera& theCamera, bool se Transform viewTransform; viewTransform.setTranslation(theCamera.getPosition()); viewTransform.setRotation(rotation); - if (theCamera.getMode() == CAMERA_MODE_MIRROR) { -// viewTransform.setScale(Transform::Vec3(-1.0f, 1.0f, 1.0f)); - } if (renderArgs->_renderSide != RenderArgs::MONO) { glm::mat4 invView = glm::inverse(_untranslatedViewMatrix); diff --git a/interface/src/FileLogger.cpp b/interface/src/FileLogger.cpp index 4808842036..00da80814b 100644 --- a/interface/src/FileLogger.cpp +++ b/interface/src/FileLogger.cpp @@ -21,11 +21,34 @@ const QString FILENAME_FORMAT = "hifi-log_%1_%2.txt"; const QString DATETIME_FORMAT = "yyyy-MM-dd_hh.mm.ss"; const QString LOGS_DIRECTORY = "Logs"; +class FilePersistThread : public GenericQueueThread < QString > { +public: + FilePersistThread(const FileLogger& logger) : _logger(logger) { + setObjectName("LogFileWriter"); + } + +protected: + virtual bool processQueueItems(const Queue& messages) { + QFile file(_logger._fileName); + if (file.open(QIODevice::WriteOnly | QIODevice::Append | QIODevice::Text)) { + QTextStream out(&file); + foreach(const QString& message, messages) { + out << message; + } + } + return true; + } +private: + const FileLogger& _logger; +}; + +static FilePersistThread* _persistThreadInstance; + FileLogger::FileLogger(QObject* parent) : - AbstractLoggerInterface(parent), - _logData("") + AbstractLoggerInterface(parent) { - setExtraDebugging(false); + _persistThreadInstance = new FilePersistThread(*this); + _persistThreadInstance->initialize(true, QThread::LowestPriority); _fileName = FileUtils::standardPath(LOGS_DIRECTORY); QHostAddress clientAddress = getLocalAddress(); @@ -33,18 +56,24 @@ FileLogger::FileLogger(QObject* parent) : _fileName.append(QString(FILENAME_FORMAT).arg(clientAddress.toString(), now.toString(DATETIME_FORMAT))); } -void FileLogger::addMessage(QString message) { - QMutexLocker locker(&_mutex); - emit logReceived(message); - _logData += message; +FileLogger::~FileLogger() { + _persistThreadInstance->terminate(); +} - QFile file(_fileName); - if (file.open(QIODevice::WriteOnly | QIODevice::Append | QIODevice::Text)) { - QTextStream out(&file); - out << message; - } +void FileLogger::addMessage(const QString& message) { + _persistThreadInstance->queueItem(message); + emit logReceived(message); } void FileLogger::locateLog() { FileUtils::locateFile(_fileName); } + +QString FileLogger::getLogData() { + QString result; + QFile f(_fileName); + if (f.open(QFile::ReadOnly | QFile::Text)) { + result = QTextStream(&f).readAll(); + } + return result; +} diff --git a/interface/src/FileLogger.h b/interface/src/FileLogger.h index 3dbbfd26cd..549654ca5c 100644 --- a/interface/src/FileLogger.h +++ b/interface/src/FileLogger.h @@ -13,23 +13,24 @@ #define hifi_FileLogger_h #include "AbstractLoggerInterface.h" -#include +#include class FileLogger : public AbstractLoggerInterface { Q_OBJECT public: FileLogger(QObject* parent = NULL); + virtual ~FileLogger(); - virtual void addMessage(QString); - virtual QString getLogData() { return _logData; } - virtual void locateLog(); + virtual void addMessage(const QString&) override; + virtual QString getLogData() override; + virtual void locateLog() override; private: - QString _logData; QString _fileName; - QMutex _mutex; - + friend class FilePersistThread; }; + + #endif // hifi_FileLogger_h diff --git a/libraries/physics/src/EntityMotionState.cpp b/libraries/physics/src/EntityMotionState.cpp index 4652005730..1976ddd814 100644 --- a/libraries/physics/src/EntityMotionState.cpp +++ b/libraries/physics/src/EntityMotionState.cpp @@ -248,7 +248,7 @@ bool EntityMotionState::remoteSimulationOutOfSync(uint32_t simulationStep) { btTransform xform = _body->getWorldTransform(); _serverPosition = bulletToGLM(xform.getOrigin()); _serverRotation = bulletToGLM(xform.getRotation()); - _serverVelocity = bulletToGLM(_body->getLinearVelocity()); + _serverVelocity = getBodyLinearVelocity(); _serverAngularVelocity = bulletToGLM(_body->getAngularVelocity()); _lastStep = simulationStep; _serverActionData = _entity->getActionData(); @@ -536,7 +536,7 @@ void EntityMotionState::bump(quint8 priority) { void EntityMotionState::resetMeasuredBodyAcceleration() { _lastMeasureStep = ObjectMotionState::getWorldSimulationStep(); if (_body) { - _lastVelocity = bulletToGLM(_body->getLinearVelocity()); + _lastVelocity = getBodyLinearVelocity(); } else { _lastVelocity = glm::vec3(0.0f); } @@ -555,7 +555,7 @@ void EntityMotionState::measureBodyAcceleration() { // Note: the integration equation for velocity uses damping: v1 = (v0 + a * dt) * (1 - D)^dt // hence the equation for acceleration is: a = (v1 / (1 - D)^dt - v0) / dt - glm::vec3 velocity = bulletToGLM(_body->getLinearVelocity()); + glm::vec3 velocity = getBodyLinearVelocity(); _measuredAcceleration = (velocity / powf(1.0f - _body->getLinearDamping(), dt) - _lastVelocity) * invDt; _lastVelocity = velocity; if (numSubsteps > PHYSICS_ENGINE_MAX_NUM_SUBSTEPS) { diff --git a/libraries/physics/src/ObjectActionOffset.cpp b/libraries/physics/src/ObjectActionOffset.cpp index 7cf35b27d7..2391ded13d 100644 --- a/libraries/physics/src/ObjectActionOffset.cpp +++ b/libraries/physics/src/ObjectActionOffset.cpp @@ -16,7 +16,11 @@ const uint16_t ObjectActionOffset::offsetVersion = 1; ObjectActionOffset::ObjectActionOffset(const QUuid& id, EntityItemPointer ownerEntity) : - ObjectAction(ACTION_TYPE_OFFSET, id, ownerEntity) { + ObjectAction(ACTION_TYPE_OFFSET, id, ownerEntity), + _pointToOffsetFrom(0.0f), + _linearDistance(0.0f), + _linearTimeScale(FLT_MAX), + _positionalTargetSet(false) { #if WANT_DEBUG qDebug() << "ObjectActionOffset::ObjectActionOffset"; #endif @@ -44,6 +48,7 @@ void ObjectActionOffset::updateActionWorker(btScalar deltaTimeStep) { unlock(); return; } + ObjectMotionState* motionState = static_cast(physicsInfo); btRigidBody* rigidBody = motionState->getRigidBody(); if (!rigidBody) { @@ -52,21 +57,32 @@ void ObjectActionOffset::updateActionWorker(btScalar deltaTimeStep) { return; } - if (_positionalTargetSet) { - glm::vec3 offset = _pointToOffsetFrom - bulletToGLM(rigidBody->getCenterOfMassPosition()); - float offsetLength = glm::length(offset); - float offsetError = _linearDistance - offsetLength; - - // if (glm::abs(offsetError) > IGNORE_POSITION_DELTA) { - if (glm::abs(offsetError) > 0.0f) { - float offsetErrorAbs = glm::abs(offsetError); - float offsetErrorDirection = - offsetError / offsetErrorAbs; - glm::vec3 previousVelocity = bulletToGLM(rigidBody->getLinearVelocity()); - - glm::vec3 velocityAdjustment = glm::normalize(offset) * offsetErrorDirection * offsetErrorAbs / _linearTimeScale; - rigidBody->setLinearVelocity(glmToBullet(previousVelocity + velocityAdjustment)); - // rigidBody->setLinearVelocity(glmToBullet(velocityAdjustment)); + const float MAX_LINEAR_TIMESCALE = 600.0f; // 10 minutes is a long time + if (_positionalTargetSet && _linearTimeScale < MAX_LINEAR_TIMESCALE) { + if (_needsActivation) { rigidBody->activate(); + _needsActivation = false; + } + glm::vec3 objectPosition = bulletToGLM(rigidBody->getCenterOfMassPosition()); + glm::vec3 springAxis = objectPosition - _pointToOffsetFrom; // from anchor to object + float distance = glm::length(springAxis); + if (distance > FLT_EPSILON) { + springAxis /= distance; // normalize springAxis + + // compute (critically damped) target velocity of spring relaxation + glm::vec3 offset = (distance - _linearDistance) * springAxis; + glm::vec3 targetVelocity = (-1.0f / _linearTimeScale) * offset; + + // compute current velocity and its parallel component + glm::vec3 currentVelocity = bulletToGLM(rigidBody->getLinearVelocity()); + glm::vec3 parallelVelocity = glm::dot(currentVelocity, springAxis) * springAxis; + + // we blend the parallel component with the spring's target velocity to get the new velocity + float blend = deltaTimeStep / _linearTimeScale; + if (blend > 1.0f) { + blend = 1.0f; + } + rigidBody->setLinearVelocity(glmToBullet(currentVelocity + blend * (targetVelocity - parallelVelocity))); } } @@ -75,45 +91,45 @@ void ObjectActionOffset::updateActionWorker(btScalar deltaTimeStep) { bool ObjectActionOffset::updateArguments(QVariantMap arguments) { - bool pOk0 = true; + bool ok = true; glm::vec3 pointToOffsetFrom = - EntityActionInterface::extractVec3Argument("offset action", arguments, "pointToOffsetFrom", pOk0, true); + EntityActionInterface::extractVec3Argument("offset action", arguments, "pointToOffsetFrom", ok, true); + if (!ok) { + return false; + } - bool pOk1 = true; + ok = true; float linearTimeScale = - EntityActionInterface::extractFloatArgument("offset action", arguments, "linearTimeScale", pOk1, false); + EntityActionInterface::extractFloatArgument("offset action", arguments, "linearTimeScale", ok, false); + if (ok) { + if (linearTimeScale <= 0.0f) { + qDebug() << "offset action -- linearTimeScale must be greater than zero."; + return false; + } + } else { + linearTimeScale = 0.1f; + } - bool pOk2 = true; + ok = true; float linearDistance = - EntityActionInterface::extractFloatArgument("offset action", arguments, "linearDistance", pOk2, false); - - if (!pOk0) { - return false; - } - if (pOk1 && linearTimeScale <= 0.0f) { - qDebug() << "offset action -- linearTimeScale must be greater than zero."; - return false; + EntityActionInterface::extractFloatArgument("offset action", arguments, "linearDistance", ok, false); + if (!ok) { + linearDistance = 0.0f; } - lockForWrite(); - - _pointToOffsetFrom = pointToOffsetFrom; - _positionalTargetSet = true; - - if (pOk1) { + // only change stuff if something actually changed + if (_pointToOffsetFrom != pointToOffsetFrom + || _linearTimeScale != linearTimeScale + || _linearDistance != linearDistance) { + lockForWrite(); + _pointToOffsetFrom = pointToOffsetFrom; _linearTimeScale = linearTimeScale; - } else { - _linearTimeScale = 0.1f; - } - - if (pOk2) { _linearDistance = linearDistance; - } else { - _linearDistance = 1.0f; + _positionalTargetSet = true; + _active = true; + _needsActivation = true; + unlock(); } - - _active = true; - unlock(); return true; } diff --git a/libraries/physics/src/ObjectActionOffset.h b/libraries/physics/src/ObjectActionOffset.h index 1918da6996..a0190f3832 100644 --- a/libraries/physics/src/ObjectActionOffset.h +++ b/libraries/physics/src/ObjectActionOffset.h @@ -36,6 +36,7 @@ public: float _linearDistance; float _linearTimeScale; bool _positionalTargetSet; + bool _needsActivation = true; }; #endif // hifi_ObjectActionOffset_h diff --git a/libraries/physics/src/ObjectMotionState.cpp b/libraries/physics/src/ObjectMotionState.cpp index 7ff119fb79..aeec85f7ea 100644 --- a/libraries/physics/src/ObjectMotionState.cpp +++ b/libraries/physics/src/ObjectMotionState.cpp @@ -80,8 +80,19 @@ void ObjectMotionState::setBodyGravity(const glm::vec3& gravity) const { } glm::vec3 ObjectMotionState::getBodyLinearVelocity() const { - return bulletToGLM(_body->getLinearVelocity()); + // returns the body's velocity unless it is moving too slow in which case returns zero + btVector3 velocity = _body->getLinearVelocity(); + + // NOTE: the threshold to use here relates to the linear displacement threshold (dX) for sending updates + // to objects that are tracked server-side (e.g. entities which use dX = 2mm). Hence an object moving + // just under this velocity threshold would trigger an update about V/dX times per second. + const float MIN_LINEAR_SPEED_SQUARED = 0.0036f; // 6 mm/sec + if (velocity.length2() < MIN_LINEAR_SPEED_SQUARED) { + velocity *= 0.0f; + } + return bulletToGLM(velocity); } + glm::vec3 ObjectMotionState::getObjectLinearVelocityChange() const { return glm::vec3(0.0f); // Subclasses override where meaningful. } diff --git a/libraries/render-utils/src/RenderDeferredTask.cpp b/libraries/render-utils/src/RenderDeferredTask.cpp index 12a6d32ae5..4929d4b5ae 100755 --- a/libraries/render-utils/src/RenderDeferredTask.cpp +++ b/libraries/render-utils/src/RenderDeferredTask.cpp @@ -118,7 +118,7 @@ void DrawOpaqueDeferred::run(const SceneContextPointer& sceneContext, const Rend args->_viewFrustum->evalProjectionMatrix(projMat); args->_viewFrustum->evalViewTransform(viewMat); if (args->_renderMode == RenderArgs::MIRROR_RENDER_MODE) { - viewMat.postScale(glm::vec3(-1.0f, 1.0f, 1.0f)); + viewMat.preScale(glm::vec3(-1.0f, 1.0f, 1.0f)); } batch.setProjectionTransform(projMat); batch.setViewTransform(viewMat); diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index 382f78a6ec..8ef8c244bc 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -493,7 +493,7 @@ void ScriptEngine::evaluate() { QScriptValue result = evaluate(_scriptContents); - // TODO: why do we check this twice? It seems like the call to clearExcpetions() in the lower level evaluate call + // TODO: why do we check this twice? It seems like the call to clearExceptions() in the lower level evaluate call // will cause this code to never actually run... if (hasUncaughtException()) { int line = uncaughtExceptionLineNumber(); diff --git a/libraries/shared/src/GenericQueueThread.h b/libraries/shared/src/GenericQueueThread.h new file mode 100644 index 0000000000..2a48ff7418 --- /dev/null +++ b/libraries/shared/src/GenericQueueThread.h @@ -0,0 +1,72 @@ +// +// Created by Bradley Austin Davis 2015/07/08. +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#pragma once +#ifndef hifi_GenericQueueThread_h +#define hifi_GenericQueueThread_h + +#include + +#include +#include +#include + +#include "GenericThread.h" +#include "NumericalConstants.h" + +template +class GenericQueueThread : public GenericThread { +public: + using Queue = QQueue; + GenericQueueThread(QObject* parent = nullptr) + : GenericThread(parent) {} + + virtual ~GenericQueueThread() {} + + void queueItem(const T& t) { + lock(); + queueItemInternal(t); + unlock(); + _hasItems.wakeAll(); + } + +protected: + virtual void queueItemInternal(const T& t) { + _items.push_back(t); + } + + virtual uint32_t getMaxWait() { + return MSECS_PER_SECOND; + } + + virtual bool process() { + if (!_items.size()) { + _hasItemsMutex.lock(); + _hasItems.wait(&_hasItemsMutex, getMaxWait()); + _hasItemsMutex.unlock(); + } + + if (!_items.size()) { + return isStillRunning(); + } + + Queue processItems; + lock(); + processItems.swap(_items); + unlock(); + return processQueueItems(processItems); + } + + virtual bool processQueueItems(const Queue& items) = 0; + + Queue _items; + QWaitCondition _hasItems; + QMutex _hasItemsMutex; +}; + +#endif // hifi_GenericQueueThread_h diff --git a/libraries/shared/src/GenericThread.cpp b/libraries/shared/src/GenericThread.cpp index 3068ca2ab6..9c1c7c590c 100644 --- a/libraries/shared/src/GenericThread.cpp +++ b/libraries/shared/src/GenericThread.cpp @@ -14,7 +14,8 @@ #include "GenericThread.h" -GenericThread::GenericThread() : +GenericThread::GenericThread(QObject* parent) : + QObject(parent), _stopThread(false), _isThreaded(false) // assume non-threaded, must call initialize() { @@ -27,13 +28,14 @@ GenericThread::~GenericThread() { } } -void GenericThread::initialize(bool isThreaded) { +void GenericThread::initialize(bool isThreaded, QThread::Priority priority) { _isThreaded = isThreaded; if (_isThreaded) { _thread = new QThread(this); // match the thread name to our object name _thread->setObjectName(objectName()); + _thread->setPriority(priority); // when the worker thread is started, call our engine's run.. connect(_thread, SIGNAL(started()), this, SLOT(threadRoutine())); diff --git a/libraries/shared/src/GenericThread.h b/libraries/shared/src/GenericThread.h index b2c0eb13db..f261dc5b37 100644 --- a/libraries/shared/src/GenericThread.h +++ b/libraries/shared/src/GenericThread.h @@ -23,12 +23,12 @@ class GenericThread : public QObject { Q_OBJECT public: - GenericThread(); + GenericThread(QObject* parent = nullptr); virtual ~GenericThread(); /// Call to start the thread. /// \param bool isThreaded true by default. false for non-threaded mode and caller must call threadRoutine() regularly. - void initialize(bool isThreaded = true); + void initialize(bool isThreaded = true, QThread::Priority priority = QThread::NormalPriority); /// Call to stop the thread void terminate(); diff --git a/libraries/shared/src/Transform.h b/libraries/shared/src/Transform.h index e961f379c8..6f841151c4 100644 --- a/libraries/shared/src/Transform.h +++ b/libraries/shared/src/Transform.h @@ -97,6 +97,8 @@ public: const Vec3& getScale() const; void setScale(float scale); void setScale(const Vec3& scale); // [new this] = [this.translation] * [this.rotation] * [scale] + void preScale(float scale); + void preScale(const Vec3& scale); void postScale(float scale); // [new this] = [this] * [scale] equivalent to glScale void postScale(const Vec3& scale); // [new this] = [this] * [scale] equivalent to glScale @@ -322,6 +324,14 @@ inline void Transform::setScale(const Vec3& scale) { } } +inline void Transform::preScale(float scale) { + setScale(getScale() * scale); +} + +inline void Transform::preScale(const Vec3& scale) { + setScale(getScale() * scale); +} + inline void Transform::postScale(float scale) { if (!isValidScale(scale) || scale == 1.0f) { return;