From 1d7420d0dc908cabca046ceba5b4b8c6836710bf Mon Sep 17 00:00:00 2001 From: David Rowe Date: Tue, 28 Apr 2015 12:53:11 -0700 Subject: [PATCH 1/9] Adapt DDE control of eyes to be predominantly open or closed --- interface/src/devices/DdeFaceTracker.cpp | 155 ++++++++++++++++------- interface/src/devices/DdeFaceTracker.h | 21 ++- 2 files changed, 127 insertions(+), 49 deletions(-) diff --git a/interface/src/devices/DdeFaceTracker.cpp b/interface/src/devices/DdeFaceTracker.cpp index be25c0794d..ece9667816 100644 --- a/interface/src/devices/DdeFaceTracker.cpp +++ b/interface/src/devices/DdeFaceTracker.cpp @@ -65,16 +65,16 @@ static const int DDE_TO_FACESHIFT_MAPPING[] = { // The DDE coefficients, overall, range from -0.2 to 1.5 or so. However, individual coefficients typically vary much // less than this. static const float DDE_COEFFICIENT_SCALES[] = { - 4.0f, // EyeBlink_L - 4.0f, // EyeBlink_R + 1.0f, // EyeBlink_L + 1.0f, // EyeBlink_R 1.0f, // EyeSquint_L 1.0f, // EyeSquint_R 1.0f, // EyeDown_L 1.0f, // EyeDown_R 1.0f, // EyeIn_L 1.0f, // EyeIn_R - 4.0f, // EyeOpen_L - 4.0f, // EyeOpen_R + 1.0f, // EyeOpen_L + 1.0f, // EyeOpen_R 1.0f, // EyeOut_L 1.0f, // EyeOut_R 1.0f, // EyeUp_L @@ -136,6 +136,16 @@ struct Packet { const float STARTING_DDE_MESSAGE_TIME = 0.033f; +const int FPS_TIMER_DELAY = 2000; // ms +const int FPS_TIMER_DURATION = 2000; // ms + +#ifdef WIN32 +// warning C4351: new behavior: elements of array 'DdeFaceTracker::_lastEyeBlinks' will be default initialized +// warning C4351: new behavior: elements of array 'DdeFaceTracker::_filteredEyeBlinks' will be default initialized +// warning C4351: new behavior: elements of array 'DdeFaceTracker::_lastEyeCoefficients' will be default initialized +#pragma warning(disable:4351) +#endif + DdeFaceTracker::DdeFaceTracker() : DdeFaceTracker(QHostAddress::Any, DDE_SERVER_PORT, DDE_CONTROL_PORT) { @@ -166,24 +176,33 @@ DdeFaceTracker::DdeFaceTracker(const QHostAddress& host, quint16 serverPort, qui _averageMessageTime(STARTING_DDE_MESSAGE_TIME), _lastHeadTranslation(glm::vec3(0.0f)), _filteredHeadTranslation(glm::vec3(0.0f)), - _lastLeftEyeBlink(0.0f), - _filteredLeftEyeBlink(0.0f), - _lastRightEyeBlink(0.0f), - _filteredRightEyeBlink(0.0f) + _lastEyeBlinks(), + _filteredEyeBlinks(), + _lastEyeCoefficients(), + _isCalculatingFPS(false), + _frameCount(0) { _coefficients.resize(NUM_FACESHIFT_BLENDSHAPES); _blendshapeCoefficients.resize(NUM_FACESHIFT_BLENDSHAPES); - + + _eyeStates[0] = EYE_OPEN; + _eyeStates[1] = EYE_OPEN; + connect(&_udpSocket, SIGNAL(readyRead()), SLOT(readPendingDatagrams())); connect(&_udpSocket, SIGNAL(error(QAbstractSocket::SocketError)), SLOT(socketErrorOccurred(QAbstractSocket::SocketError))); - connect(&_udpSocket, SIGNAL(stateChanged(QAbstractSocket::SocketState)), SLOT(socketStateChanged(QAbstractSocket::SocketState))); + connect(&_udpSocket, SIGNAL(stateChanged(QAbstractSocket::SocketState)), + SLOT(socketStateChanged(QAbstractSocket::SocketState))); } DdeFaceTracker::~DdeFaceTracker() { setEnabled(false); } +#ifdef WIN32 +#pragma warning(default:4351) +#endif + void DdeFaceTracker::setEnabled(bool enabled) { #ifdef HAVE_DDE // isOpen() does not work as one might expect on QUdpSocket; don't test isOpen() before closing socket. @@ -354,40 +373,6 @@ void DdeFaceTracker::decodePacket(const QByteArray& buffer) { _coefficients[DDE_TO_FACESHIFT_MAPPING[i]] = packet.expressions[i]; } - // Use EyeBlink values to control both EyeBlink and EyeOpen - static const float RELAXED_EYE_VALUE = 0.1f; - float leftEye = _coefficients[_leftBlinkIndex]; - float rightEye = _coefficients[_rightBlinkIndex]; - if (isFiltering) { - const float BLINK_VELOCITY_FILTER_STRENGTH = 0.3f; - - float velocity = fabs(leftEye - _lastLeftEyeBlink) / _averageMessageTime; - float velocityFilter = glm::clamp(velocity * BLINK_VELOCITY_FILTER_STRENGTH, 0.0f, 1.0f); - _filteredLeftEyeBlink = velocityFilter * leftEye + (1.0f - velocityFilter) * _filteredLeftEyeBlink; - _lastLeftEyeBlink = leftEye; - leftEye = _filteredLeftEyeBlink; - - velocity = fabs(rightEye - _lastRightEyeBlink) / _averageMessageTime; - velocityFilter = glm::clamp(velocity * BLINK_VELOCITY_FILTER_STRENGTH, 0.0f, 1.0f); - _filteredRightEyeBlink = velocityFilter * rightEye + (1.0f - velocityFilter) * _filteredRightEyeBlink; - _lastRightEyeBlink = rightEye; - rightEye = _filteredRightEyeBlink; - } - if (leftEye > RELAXED_EYE_VALUE) { - _coefficients[_leftBlinkIndex] = leftEye - RELAXED_EYE_VALUE; - _coefficients[_leftEyeOpenIndex] = 0.0f; - } else { - _coefficients[_leftBlinkIndex] = 0.0f; - _coefficients[_leftEyeOpenIndex] = RELAXED_EYE_VALUE - leftEye; - } - if (rightEye > RELAXED_EYE_VALUE) { - _coefficients[_rightBlinkIndex] = rightEye - RELAXED_EYE_VALUE; - _coefficients[_rightEyeOpenIndex] = 0.0f; - } else { - _coefficients[_rightBlinkIndex] = 0.0f; - _coefficients[_rightEyeOpenIndex] = RELAXED_EYE_VALUE - rightEye; - } - // Use BrowsU_C to control both brows' up and down _coefficients[_browDownLeftIndex] = -_coefficients[_browUpCenterIndex]; _coefficients[_browDownRightIndex] = -_coefficients[_browUpCenterIndex]; @@ -403,6 +388,88 @@ void DdeFaceTracker::decodePacket(const QByteArray& buffer) { _coefficients[_mouthSmileLeftIndex] = _coefficients[_mouthSmileLeftIndex] - SMILE_THRESHOLD; _coefficients[_mouthSmileRightIndex] = _coefficients[_mouthSmileRightIndex] - SMILE_THRESHOLD; + // Velocity filter EyeBlink values + const float DDE_EYEBLINK_SCALE = 3.0f; + float eyeBlinks[] = { DDE_EYEBLINK_SCALE * _coefficients[_leftBlinkIndex], DDE_EYEBLINK_SCALE * _coefficients[_rightBlinkIndex] }; + if (isFiltering) { + const float BLINK_VELOCITY_FILTER_STRENGTH = 0.3f; + for (int i = 0; i < 2; i += 1) { + float velocity = fabs(eyeBlinks[i] - _lastEyeBlinks[i]) / _averageMessageTime; + float velocityFilter = glm::clamp(velocity * BLINK_VELOCITY_FILTER_STRENGTH, 0.0f, 1.0f); + _filteredEyeBlinks[i] = velocityFilter * eyeBlinks[i] + (1.0f - velocityFilter) * _filteredEyeBlinks[i]; + _lastEyeBlinks[i] = eyeBlinks[i]; + } + } + + // Finesse EyeBlink values + float eyeCoefficients[2]; + for (int i = 0; i < 2; i += 1) { + // Scale EyeBlink values so that they can be used to control both EyeBlink and EyeOpen + // -ve values control EyeOpen; +ve values control EyeBlink + static const float EYE_CONTROL_THRESHOLD = 0.5f; // Resting eye value + eyeCoefficients[i] = (_filteredEyeBlinks[i] - EYE_CONTROL_THRESHOLD) / (1.0f - EYE_CONTROL_THRESHOLD); + + // Change to closing or opening states + const float EYE_CONTROL_HYSTERISIS = 0.25f; + const float EYE_CLOSING_THRESHOLD = 0.95f; + const float EYE_OPENING_THRESHOLD = EYE_CONTROL_THRESHOLD - EYE_CONTROL_HYSTERISIS; + if ((_eyeStates[i] == EYE_OPEN || _eyeStates[i] == EYE_OPENING) && eyeCoefficients[i] > EYE_CLOSING_THRESHOLD) { + _eyeStates[i] = EYE_CLOSING; + } else if ((_eyeStates[i] == EYE_CLOSED || _eyeStates[i] == EYE_CLOSING) + && eyeCoefficients[i] < EYE_OPENING_THRESHOLD) { + _eyeStates[i] = EYE_OPENING; + } + + const float EYELID_MOVEMENT_RATE = 10.0f; // units/second + const float EYE_OPEN_SCALE = 0.2f; + if (_eyeStates[i] == EYE_CLOSING) { + // Close eyelid until it's fully closed + float closingValue = _lastEyeCoefficients[i] + EYELID_MOVEMENT_RATE * _averageMessageTime; + if (closingValue >= 1.0) { + _eyeStates[i] = EYE_CLOSED; + eyeCoefficients[i] = 1.0; + } else { + eyeCoefficients[i] = closingValue; + } + } else if (_eyeStates[i] == EYE_OPENING) { + // Open eyelid until it meets the current adjusted value + float openingValue = _lastEyeCoefficients[i] - EYELID_MOVEMENT_RATE * _averageMessageTime; + if (openingValue < eyeCoefficients[i] * EYE_OPEN_SCALE) { + _eyeStates[i] = EYE_OPEN; + eyeCoefficients[i] = eyeCoefficients[i] * EYE_OPEN_SCALE; + } else { + eyeCoefficients[i] = openingValue; + } + } else if (_eyeStates[i] == EYE_OPEN) { + // Reduce eyelid movement + eyeCoefficients[i] = eyeCoefficients[i] * EYE_OPEN_SCALE; + } else if (_eyeStates[i] == EYE_CLOSED) { + // Keep eyelid fully closed + eyeCoefficients[i] = 1.0; + } + } + if (_eyeStates[0] == EYE_OPEN && _eyeStates[1] == EYE_OPEN) { + // Couple eyelids + eyeCoefficients[0] = eyeCoefficients[1] = (eyeCoefficients[0] + eyeCoefficients[0]) / 2.0f; + } + _lastEyeCoefficients[0] = eyeCoefficients[0]; + _lastEyeCoefficients[1] = eyeCoefficients[1]; + + // Use EyeBlink values to control both EyeBlink and EyeOpen + if (eyeCoefficients[0] > 0) { + _coefficients[_leftBlinkIndex] = eyeCoefficients[0]; + _coefficients[_leftEyeOpenIndex] = 0.0f; + } else { + _coefficients[_leftBlinkIndex] = 0.0f; + _coefficients[_leftEyeOpenIndex] = -eyeCoefficients[0]; + } + if (eyeCoefficients[1] > 0) { + _coefficients[_rightBlinkIndex] = eyeCoefficients[1]; + _coefficients[_rightEyeOpenIndex] = 0.0f; + } else { + _coefficients[_rightBlinkIndex] = 0.0f; + _coefficients[_rightEyeOpenIndex] = -eyeCoefficients[1]; + } // Scale all coefficients for (int i = 0; i < NUM_EXPRESSIONS; i += 1) { diff --git a/interface/src/devices/DdeFaceTracker.h b/interface/src/devices/DdeFaceTracker.h index d9df5723cf..7ae4a07fea 100644 --- a/interface/src/devices/DdeFaceTracker.h +++ b/interface/src/devices/DdeFaceTracker.h @@ -103,12 +103,23 @@ private: quint64 _lastMessageReceived; float _averageMessageTime; + glm::vec3 _lastHeadTranslation; glm::vec3 _filteredHeadTranslation; - float _lastLeftEyeBlink; - float _filteredLeftEyeBlink; - float _lastRightEyeBlink; - float _filteredRightEyeBlink; + + enum EyeState { + EYE_OPEN, + EYE_CLOSING, + EYE_CLOSED, + EYE_OPENING + }; + EyeState _eyeStates[2]; + float _lastEyeBlinks[2]; + float _filteredEyeBlinks[2]; + float _lastEyeCoefficients[2]; + + bool _isCalculatingFPS; + int _frameCount; }; -#endif // hifi_DdeFaceTracker_h \ No newline at end of file +#endif // hifi_DdeFaceTracker_h From 81e23eab28ae151f6ddd5f16bd222787663f7e29 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Wed, 29 Apr 2015 22:15:49 -0700 Subject: [PATCH 2/9] Coding standard --- interface/src/devices/DdeFaceTracker.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/interface/src/devices/DdeFaceTracker.cpp b/interface/src/devices/DdeFaceTracker.cpp index ece9667816..e28496335f 100644 --- a/interface/src/devices/DdeFaceTracker.cpp +++ b/interface/src/devices/DdeFaceTracker.cpp @@ -369,7 +369,7 @@ void DdeFaceTracker::decodePacket(const QByteArray& buffer) { } // Translate DDE coefficients to Faceshift compatible coefficients - for (int i = 0; i < NUM_EXPRESSIONS; i += 1) { + for (int i = 0; i < NUM_EXPRESSIONS; i++) { _coefficients[DDE_TO_FACESHIFT_MAPPING[i]] = packet.expressions[i]; } @@ -393,7 +393,7 @@ void DdeFaceTracker::decodePacket(const QByteArray& buffer) { float eyeBlinks[] = { DDE_EYEBLINK_SCALE * _coefficients[_leftBlinkIndex], DDE_EYEBLINK_SCALE * _coefficients[_rightBlinkIndex] }; if (isFiltering) { const float BLINK_VELOCITY_FILTER_STRENGTH = 0.3f; - for (int i = 0; i < 2; i += 1) { + for (int i = 0; i < 2; i++) { float velocity = fabs(eyeBlinks[i] - _lastEyeBlinks[i]) / _averageMessageTime; float velocityFilter = glm::clamp(velocity * BLINK_VELOCITY_FILTER_STRENGTH, 0.0f, 1.0f); _filteredEyeBlinks[i] = velocityFilter * eyeBlinks[i] + (1.0f - velocityFilter) * _filteredEyeBlinks[i]; @@ -403,7 +403,7 @@ void DdeFaceTracker::decodePacket(const QByteArray& buffer) { // Finesse EyeBlink values float eyeCoefficients[2]; - for (int i = 0; i < 2; i += 1) { + for (int i = 0; i < 2; i++) { // Scale EyeBlink values so that they can be used to control both EyeBlink and EyeOpen // -ve values control EyeOpen; +ve values control EyeBlink static const float EYE_CONTROL_THRESHOLD = 0.5f; // Resting eye value @@ -472,7 +472,7 @@ void DdeFaceTracker::decodePacket(const QByteArray& buffer) { } // Scale all coefficients - for (int i = 0; i < NUM_EXPRESSIONS; i += 1) { + for (int i = 0; i < NUM_EXPRESSIONS; i++) { _blendshapeCoefficients[i] = glm::clamp(DDE_COEFFICIENT_SCALES[i] * _coefficients[i], 0.0f, 1.0f); } From 45ba6786869aad82fb07f6f9f9a279158e299a37 Mon Sep 17 00:00:00 2001 From: Eric Levin Date: Wed, 29 Apr 2015 23:40:53 -0700 Subject: [PATCH 3/9] can now fling up and down as well as in xz plane --- examples/grab.js | 1 - 1 file changed, 1 deletion(-) diff --git a/examples/grab.js b/examples/grab.js index 4e0c536a1b..dc250b9d27 100644 --- a/examples/grab.js +++ b/examples/grab.js @@ -100,7 +100,6 @@ function flingObject() { flingVelocity = Vec3.subtract(entityProps.position, prevPosition); flingVelocity = Vec3.multiply(flingMultiplier, flingVelocity); - flingVelocity.y = 0; Entities.editEntity(grabbedEntity, { velocity: flingVelocity }); From 42a4fd8884604a6af192bffa4517d5877cedc744 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Wed, 29 Apr 2015 22:07:10 -0700 Subject: [PATCH 4/9] Add velocity filtering of DDE eyebrows --- interface/src/devices/DdeFaceTracker.cpp | 22 +++++++++++++++++----- interface/src/devices/DdeFaceTracker.h | 2 ++ 2 files changed, 19 insertions(+), 5 deletions(-) diff --git a/interface/src/devices/DdeFaceTracker.cpp b/interface/src/devices/DdeFaceTracker.cpp index be25c0794d..d6cc644db1 100644 --- a/interface/src/devices/DdeFaceTracker.cpp +++ b/interface/src/devices/DdeFaceTracker.cpp @@ -169,7 +169,9 @@ DdeFaceTracker::DdeFaceTracker(const QHostAddress& host, quint16 serverPort, qui _lastLeftEyeBlink(0.0f), _filteredLeftEyeBlink(0.0f), _lastRightEyeBlink(0.0f), - _filteredRightEyeBlink(0.0f) + _filteredRightEyeBlink(0.0f), + _lastBrowUp(0.0f), + _filteredBrowUp(0.0f) { _coefficients.resize(NUM_FACESHIFT_BLENDSHAPES); @@ -389,10 +391,20 @@ void DdeFaceTracker::decodePacket(const QByteArray& buffer) { } // Use BrowsU_C to control both brows' up and down - _coefficients[_browDownLeftIndex] = -_coefficients[_browUpCenterIndex]; - _coefficients[_browDownRightIndex] = -_coefficients[_browUpCenterIndex]; - _coefficients[_browUpLeftIndex] = _coefficients[_browUpCenterIndex]; - _coefficients[_browUpRightIndex] = _coefficients[_browUpCenterIndex]; + float browUp = _coefficients[_browUpCenterIndex]; + if (isFiltering) { + const float BROW_VELOCITY_FILTER_STRENGHT = 0.8f; + float velocity = fabs(browUp - _lastBrowUp) / _averageMessageTime; + float velocityFilter = glm::clamp(velocity * BROW_VELOCITY_FILTER_STRENGHT, 0.0f, 1.0f); + _filteredBrowUp = velocityFilter * browUp + (1.0f - velocityFilter) * _filteredBrowUp; + _lastBrowUp = browUp; + browUp = _filteredBrowUp; + _coefficients[_browUpCenterIndex] = browUp; + } + _coefficients[_browUpLeftIndex] = browUp; + _coefficients[_browUpRightIndex] = browUp; + _coefficients[_browDownLeftIndex] = -browUp; + _coefficients[_browDownRightIndex] = -browUp; // Offset jaw open coefficient static const float JAW_OPEN_THRESHOLD = 0.16f; diff --git a/interface/src/devices/DdeFaceTracker.h b/interface/src/devices/DdeFaceTracker.h index d9df5723cf..d4572c8430 100644 --- a/interface/src/devices/DdeFaceTracker.h +++ b/interface/src/devices/DdeFaceTracker.h @@ -109,6 +109,8 @@ private: float _filteredLeftEyeBlink; float _lastRightEyeBlink; float _filteredRightEyeBlink; + float _lastBrowUp; + float _filteredBrowUp; }; #endif // hifi_DdeFaceTracker_h \ No newline at end of file From b6890be73d26709e854d1fab40d77a88dd0f4b6d Mon Sep 17 00:00:00 2001 From: David Rowe Date: Thu, 30 Apr 2015 08:06:46 -0700 Subject: [PATCH 5/9] Adjust eyebrow filter strength --- interface/src/devices/DdeFaceTracker.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/devices/DdeFaceTracker.cpp b/interface/src/devices/DdeFaceTracker.cpp index d6cc644db1..652254d718 100644 --- a/interface/src/devices/DdeFaceTracker.cpp +++ b/interface/src/devices/DdeFaceTracker.cpp @@ -393,7 +393,7 @@ void DdeFaceTracker::decodePacket(const QByteArray& buffer) { // Use BrowsU_C to control both brows' up and down float browUp = _coefficients[_browUpCenterIndex]; if (isFiltering) { - const float BROW_VELOCITY_FILTER_STRENGHT = 0.8f; + const float BROW_VELOCITY_FILTER_STRENGHT = 0.75f; float velocity = fabs(browUp - _lastBrowUp) / _averageMessageTime; float velocityFilter = glm::clamp(velocity * BROW_VELOCITY_FILTER_STRENGHT, 0.0f, 1.0f); _filteredBrowUp = velocityFilter * browUp + (1.0f - velocityFilter) * _filteredBrowUp; From f75b599288b29c1f9073b38c21de5a88e27f0568 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Thu, 30 Apr 2015 11:28:27 -0700 Subject: [PATCH 6/9] don't save default values in json file --- interface/resources/qml/controls/Dialog.qml | 2 +- .../entities/src/EntityItemProperties.cpp | 71 +++++++++++-------- libraries/entities/src/EntityItemProperties.h | 3 +- .../entities/src/EntityItemPropertiesMacros.h | 35 ++++++--- libraries/entities/src/EntityTree.cpp | 4 +- libraries/entities/src/EntityTree.h | 2 +- .../src/RecurseOctreeToMapOperator.cpp | 17 ++++- .../entities/src/RecurseOctreeToMapOperator.h | 3 +- libraries/fbx/src/FBXReader.h | 10 +++ libraries/octree/src/Octree.cpp | 6 +- libraries/octree/src/Octree.h | 2 +- libraries/shared/src/SharedUtil.h | 11 +++ 12 files changed, 115 insertions(+), 51 deletions(-) diff --git a/interface/resources/qml/controls/Dialog.qml b/interface/resources/qml/controls/Dialog.qml index 46add1dc07..f32b4e2e66 100644 --- a/interface/resources/qml/controls/Dialog.qml +++ b/interface/resources/qml/controls/Dialog.qml @@ -66,7 +66,7 @@ DialogBase { // our close function performs the same way as the OffscreenUI class: // don't do anything but manipulate the enabled flag and let the other - // mechanisms decide if the window should be destoryed after the close + // mechanisms decide if the window should be destroyed after the close // animation completes function close() { if (destroyOnCloseButton) { diff --git a/libraries/entities/src/EntityItemProperties.cpp b/libraries/entities/src/EntityItemProperties.cpp index 734f7897bb..267cf75a11 100644 --- a/libraries/entities/src/EntityItemProperties.cpp +++ b/libraries/entities/src/EntityItemProperties.cpp @@ -295,20 +295,23 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { return changedProperties; } -QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine) const { +QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine, bool skipDefaults) const { QScriptValue properties = engine->newObject(); + EntityItemProperties defaultEntityProperties; if (_idSet) { COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(id, _id.toString()); - COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(isKnownID, (_id != UNKNOWN_ENTITY_ID)); + COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER_NO_SKIP(isKnownID, (_id != UNKNOWN_ENTITY_ID)); } else { - COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(isKnownID, false); + COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER_NO_SKIP(isKnownID, false); } COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(type, EntityTypes::getEntityTypeName(_type)); COPY_PROPERTY_TO_QSCRIPTVALUE_VEC3(position); COPY_PROPERTY_TO_QSCRIPTVALUE_VEC3(dimensions); - COPY_PROPERTY_TO_QSCRIPTVALUE_VEC3(naturalDimensions); // gettable, but not settable + if (!skipDefaults) { + COPY_PROPERTY_TO_QSCRIPTVALUE_VEC3(naturalDimensions); // gettable, but not settable + } COPY_PROPERTY_TO_QSCRIPTVALUE_QUAT(rotation); COPY_PROPERTY_TO_QSCRIPTVALUE_VEC3(velocity); COPY_PROPERTY_TO_QSCRIPTVALUE_VEC3(gravity); @@ -316,8 +319,10 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine) cons COPY_PROPERTY_TO_QSCRIPTVALUE(damping); COPY_PROPERTY_TO_QSCRIPTVALUE(density); COPY_PROPERTY_TO_QSCRIPTVALUE(lifetime); - COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(age, getAge()); // gettable, but not settable - COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(ageAsText, formatSecondsElapsed(getAge())); // gettable, but not settable + if (!skipDefaults) { + COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER_NO_SKIP(age, getAge()); // gettable, but not settable + COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER_NO_SKIP(ageAsText, formatSecondsElapsed(getAge())); // gettable, but not settable + } COPY_PROPERTY_TO_QSCRIPTVALUE(script); COPY_PROPERTY_TO_QSCRIPTVALUE_VEC3(registrationPoint); COPY_PROPERTY_TO_QSCRIPTVALUE_VEC3(angularVelocity); @@ -369,31 +374,37 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine) cons COPY_PROPERTY_TO_QSCRIPTVALUE(stageHour); // Sitting properties support - QScriptValue sittingPoints = engine->newObject(); - for (int i = 0; i < _sittingPoints.size(); ++i) { - QScriptValue sittingPoint = engine->newObject(); - sittingPoint.setProperty("name", _sittingPoints.at(i).name); - sittingPoint.setProperty("position", vec3toScriptValue(engine, _sittingPoints.at(i).position)); - sittingPoint.setProperty("rotation", quatToScriptValue(engine, _sittingPoints.at(i).rotation)); - sittingPoints.setProperty(i, sittingPoint); + if (!skipDefaults) { + QScriptValue sittingPoints = engine->newObject(); + for (int i = 0; i < _sittingPoints.size(); ++i) { + QScriptValue sittingPoint = engine->newObject(); + sittingPoint.setProperty("name", _sittingPoints.at(i).name); + sittingPoint.setProperty("position", vec3toScriptValue(engine, _sittingPoints.at(i).position)); + sittingPoint.setProperty("rotation", quatToScriptValue(engine, _sittingPoints.at(i).rotation)); + sittingPoints.setProperty(i, sittingPoint); + } + sittingPoints.setProperty("length", _sittingPoints.size()); + COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(sittingPoints, sittingPoints); // gettable, but not settable } - sittingPoints.setProperty("length", _sittingPoints.size()); - COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(sittingPoints, sittingPoints); // gettable, but not settable - AABox aaBox = getAABox(); - QScriptValue boundingBox = engine->newObject(); - QScriptValue bottomRightNear = vec3toScriptValue(engine, aaBox.getCorner()); - QScriptValue topFarLeft = vec3toScriptValue(engine, aaBox.calcTopFarLeft()); - QScriptValue center = vec3toScriptValue(engine, aaBox.calcCenter()); - QScriptValue boundingBoxDimensions = vec3toScriptValue(engine, aaBox.getDimensions()); - boundingBox.setProperty("brn", bottomRightNear); - boundingBox.setProperty("tfl", topFarLeft); - boundingBox.setProperty("center", center); - boundingBox.setProperty("dimensions", boundingBoxDimensions); - COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(boundingBox, boundingBox); // gettable, but not settable + if (!skipDefaults) { + AABox aaBox = getAABox(); + QScriptValue boundingBox = engine->newObject(); + QScriptValue bottomRightNear = vec3toScriptValue(engine, aaBox.getCorner()); + QScriptValue topFarLeft = vec3toScriptValue(engine, aaBox.calcTopFarLeft()); + QScriptValue center = vec3toScriptValue(engine, aaBox.calcCenter()); + QScriptValue boundingBoxDimensions = vec3toScriptValue(engine, aaBox.getDimensions()); + boundingBox.setProperty("brn", bottomRightNear); + boundingBox.setProperty("tfl", topFarLeft); + boundingBox.setProperty("center", center); + boundingBox.setProperty("dimensions", boundingBoxDimensions); + COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER_NO_SKIP(boundingBox, boundingBox); // gettable, but not settable + } QString textureNamesList = _textureNames.join(",\n"); - COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(originalTextures, textureNamesList); // gettable, but not settable + if (!skipDefaults) { + COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER_NO_SKIP(originalTextures, textureNamesList); // gettable, but not settable + } return properties; } @@ -467,7 +478,11 @@ void EntityItemProperties::copyFromScriptValue(const QScriptValue& object) { } QScriptValue EntityItemPropertiesToScriptValue(QScriptEngine* engine, const EntityItemProperties& properties) { - return properties.copyToScriptValue(engine); + return properties.copyToScriptValue(engine, false); +} + +QScriptValue EntityItemNonDefaultPropertiesToScriptValue(QScriptEngine* engine, const EntityItemProperties& properties) { + return properties.copyToScriptValue(engine, true); } void EntityItemPropertiesFromScriptValue(const QScriptValue &object, EntityItemProperties& properties) { diff --git a/libraries/entities/src/EntityItemProperties.h b/libraries/entities/src/EntityItemProperties.h index faec9e1206..9a1c8ad5a6 100644 --- a/libraries/entities/src/EntityItemProperties.h +++ b/libraries/entities/src/EntityItemProperties.h @@ -159,7 +159,7 @@ public: EntityTypes::EntityType getType() const { return _type; } void setType(EntityTypes::EntityType type) { _type = type; } - virtual QScriptValue copyToScriptValue(QScriptEngine* engine) const; + virtual QScriptValue copyToScriptValue(QScriptEngine* engine, bool skipDefaults) const; virtual void copyFromScriptValue(const QScriptValue& object); // editing related features supported by all entities @@ -307,6 +307,7 @@ private: }; Q_DECLARE_METATYPE(EntityItemProperties); QScriptValue EntityItemPropertiesToScriptValue(QScriptEngine* engine, const EntityItemProperties& properties); +QScriptValue EntityItemNonDefaultPropertiesToScriptValue(QScriptEngine* engine, const EntityItemProperties& properties); void EntityItemPropertiesFromScriptValue(const QScriptValue &object, EntityItemProperties& properties); diff --git a/libraries/entities/src/EntityItemPropertiesMacros.h b/libraries/entities/src/EntityItemPropertiesMacros.h index 33714f4ea2..3fc6cfa85f 100644 --- a/libraries/entities/src/EntityItemPropertiesMacros.h +++ b/libraries/entities/src/EntityItemPropertiesMacros.h @@ -198,26 +198,41 @@ #define COPY_PROPERTY_TO_QSCRIPTVALUE_VEC3(P) \ - QScriptValue P = vec3toScriptValue(engine, _##P); \ - properties.setProperty(#P, P); + if (!skipDefaults || defaultEntityProperties._##P != _##P) { \ + QScriptValue P = vec3toScriptValue(engine, _##P); \ + properties.setProperty(#P, P); \ + } #define COPY_PROPERTY_TO_QSCRIPTVALUE_QUAT(P) \ - QScriptValue P = quatToScriptValue(engine, _##P); \ - properties.setProperty(#P, P); + if (!skipDefaults || defaultEntityProperties._##P != _##P) { \ + QScriptValue P = quatToScriptValue(engine, _##P); \ + properties.setProperty(#P, P); \ + } #define COPY_PROPERTY_TO_QSCRIPTVALUE_COLOR(P) \ - QScriptValue P = xColorToScriptValue(engine, _##P); \ - properties.setProperty(#P, P); + if (!skipDefaults || defaultEntityProperties._##P != _##P) { \ + QScriptValue P = xColorToScriptValue(engine, _##P); \ + properties.setProperty(#P, P); \ + } #define COPY_PROPERTY_TO_QSCRIPTVALUE_COLOR_GETTER(P,G) \ - QScriptValue P = xColorToScriptValue(engine, G); \ - properties.setProperty(#P, P); + if (!skipDefaults || defaultEntityProperties._##P != _##P) { \ + QScriptValue P = xColorToScriptValue(engine, G); \ + properties.setProperty(#P, P); \ + } -#define COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(P, G) \ +#define COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER_NO_SKIP(P, G) \ properties.setProperty(#P, G); +#define COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(P, G) \ + if (!skipDefaults || defaultEntityProperties._##P != _##P) { \ + properties.setProperty(#P, G); \ + } + #define COPY_PROPERTY_TO_QSCRIPTVALUE(P) \ - properties.setProperty(#P, _##P); + if (!skipDefaults || defaultEntityProperties._##P != _##P) { \ + properties.setProperty(#P, _##P); \ + } #define COPY_PROPERTY_FROM_QSCRIPTVALUE_FLOAT(P, S) \ QScriptValue P = object.property(#P); \ diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index 436339b3fe..f8f94f8d17 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -1137,10 +1137,10 @@ bool EntityTree::sendEntitiesOperation(OctreeElement* element, void* extraData) return true; } -bool EntityTree::writeToMap(QVariantMap& entityDescription, OctreeElement* element) { +bool EntityTree::writeToMap(QVariantMap& entityDescription, OctreeElement* element, bool skipDefaultValues) { entityDescription["Entities"] = QVariantList(); QScriptEngine scriptEngine; - RecurseOctreeToMapOperator theOperator(entityDescription, element, &scriptEngine); + RecurseOctreeToMapOperator theOperator(entityDescription, element, &scriptEngine, skipDefaultValues); recurseTreeWithOperator(&theOperator); return true; } diff --git a/libraries/entities/src/EntityTree.h b/libraries/entities/src/EntityTree.h index 3f01dec408..3880d67eda 100644 --- a/libraries/entities/src/EntityTree.h +++ b/libraries/entities/src/EntityTree.h @@ -163,7 +163,7 @@ public: bool wantEditLogging() const { return _wantEditLogging; } void setWantEditLogging(bool value) { _wantEditLogging = value; } - bool writeToMap(QVariantMap& entityDescription, OctreeElement* element); + bool writeToMap(QVariantMap& entityDescription, OctreeElement* element, bool skipDefaultValues); bool readFromMap(QVariantMap& entityDescription); signals: diff --git a/libraries/entities/src/RecurseOctreeToMapOperator.cpp b/libraries/entities/src/RecurseOctreeToMapOperator.cpp index afe28e17e0..daa01c203e 100644 --- a/libraries/entities/src/RecurseOctreeToMapOperator.cpp +++ b/libraries/entities/src/RecurseOctreeToMapOperator.cpp @@ -12,11 +12,15 @@ #include "RecurseOctreeToMapOperator.h" -RecurseOctreeToMapOperator::RecurseOctreeToMapOperator(QVariantMap& map, OctreeElement *top, QScriptEngine *engine) : +RecurseOctreeToMapOperator::RecurseOctreeToMapOperator(QVariantMap& map, + OctreeElement *top, + QScriptEngine *engine, + bool skipDefaultValues) : RecurseOctreeOperator(), _map(map), _top(top), - _engine(engine) + _engine(engine), + _skipDefaultValues(skipDefaultValues) { // if some element "top" was given, only save information for that element and it's children. if (_top) { @@ -36,6 +40,8 @@ bool RecurseOctreeToMapOperator::preRecursion(OctreeElement* element) { bool RecurseOctreeToMapOperator::postRecursion(OctreeElement* element) { + EntityItemProperties defaultProperties; + EntityTreeElement* entityTreeElement = static_cast(element); const QList& entities = entityTreeElement->getEntities(); @@ -43,7 +49,12 @@ bool RecurseOctreeToMapOperator::postRecursion(OctreeElement* element) { foreach (EntityItem* entityItem, entities) { EntityItemProperties properties = entityItem->getProperties(); - QScriptValue qScriptValues = EntityItemPropertiesToScriptValue(_engine, properties); + QScriptValue qScriptValues; + if (_skipDefaultValues) { + qScriptValues = EntityItemNonDefaultPropertiesToScriptValue(_engine, properties); + } else { + qScriptValues = EntityItemPropertiesToScriptValue(_engine, properties); + } entitiesQList << qScriptValues.toVariant(); } _map["Entities"] = entitiesQList; diff --git a/libraries/entities/src/RecurseOctreeToMapOperator.h b/libraries/entities/src/RecurseOctreeToMapOperator.h index 6bd44f3cbf..bfa5024b09 100644 --- a/libraries/entities/src/RecurseOctreeToMapOperator.h +++ b/libraries/entities/src/RecurseOctreeToMapOperator.h @@ -13,7 +13,7 @@ class RecurseOctreeToMapOperator : public RecurseOctreeOperator { public: - RecurseOctreeToMapOperator(QVariantMap& map, OctreeElement *top, QScriptEngine *engine); + RecurseOctreeToMapOperator(QVariantMap& map, OctreeElement *top, QScriptEngine *engine, bool skipDefaultValues); bool preRecursion(OctreeElement* element); bool postRecursion(OctreeElement* element); private: @@ -21,4 +21,5 @@ public: OctreeElement *_top; QScriptEngine *_engine; bool _withinTop; + bool _skipDefaultValues; }; diff --git a/libraries/fbx/src/FBXReader.h b/libraries/fbx/src/FBXReader.h index 871f3d0581..648c47c975 100644 --- a/libraries/fbx/src/FBXReader.h +++ b/libraries/fbx/src/FBXReader.h @@ -203,6 +203,16 @@ public: glm::quat rotation; // relative orientation }; +inline bool operator==(const SittingPoint& lhs, const SittingPoint& rhs) +{ + return (lhs.name == rhs.name) && (lhs.position == rhs.position) && (lhs.rotation == rhs.rotation); +} + +inline bool operator!=(const SittingPoint& lhs, const SittingPoint& rhs) +{ + return (lhs.name != rhs.name) || (lhs.position != rhs.position) || (lhs.rotation != rhs.rotation); +} + /// A set of meshes extracted from an FBX document. class FBXGeometry { public: diff --git a/libraries/octree/src/Octree.cpp b/libraries/octree/src/Octree.cpp index 059c0e5bbc..d53d29e444 100644 --- a/libraries/octree/src/Octree.cpp +++ b/libraries/octree/src/Octree.cpp @@ -2087,7 +2087,7 @@ void Octree::writeToJSONFile(const char* fileName, OctreeElement* element) { QFile persistFile(fileName); QVariantMap entityDescription; - qCDebug(octree, "Saving to file %s...", fileName); + qCDebug(octree, "Saving JSON SVO to file %s...", fileName); OctreeElement* top; if (element) { @@ -2096,7 +2096,7 @@ void Octree::writeToJSONFile(const char* fileName, OctreeElement* element) { top = _rootElement; } - bool entityDescriptionSuccess = writeToMap(entityDescription, top); + bool entityDescriptionSuccess = writeToMap(entityDescription, top, true); if (entityDescriptionSuccess && persistFile.open(QIODevice::WriteOnly)) { persistFile.write(QJsonDocument::fromVariant(entityDescription).toJson()); } else { @@ -2108,7 +2108,7 @@ void Octree::writeToSVOFile(const char* fileName, OctreeElement* element) { std::ofstream file(fileName, std::ios::out|std::ios::binary); if(file.is_open()) { - qCDebug(octree, "Saving to file %s...", fileName); + qCDebug(octree, "Saving binary SVO to file %s...", fileName); PacketType expectedType = expectedDataPacketType(); PacketVersion expectedVersion = versionForPacketType(expectedType); diff --git a/libraries/octree/src/Octree.h b/libraries/octree/src/Octree.h index 21c3efc01d..d7fc58699f 100644 --- a/libraries/octree/src/Octree.h +++ b/libraries/octree/src/Octree.h @@ -331,7 +331,7 @@ public: void writeToFile(const char* filename, OctreeElement* element = NULL, QString persistAsFileType = "svo"); void writeToJSONFile(const char* filename, OctreeElement* element = NULL); void writeToSVOFile(const char* filename, OctreeElement* element = NULL); - virtual bool writeToMap(QVariantMap& entityDescription, OctreeElement* element) = 0; + virtual bool writeToMap(QVariantMap& entityDescription, OctreeElement* element, bool skipDefaultValues) = 0; // Octree importers bool readFromFile(const char* filename); diff --git a/libraries/shared/src/SharedUtil.h b/libraries/shared/src/SharedUtil.h index 9cf76dd1dc..8d55a0f82b 100644 --- a/libraries/shared/src/SharedUtil.h +++ b/libraries/shared/src/SharedUtil.h @@ -43,6 +43,17 @@ inline QDebug& operator<<(QDebug& dbg, const xColor& c) { return dbg; } +inline bool operator==(const xColor& lhs, const xColor& rhs) +{ + return (lhs.red == rhs.red) && (lhs.green == rhs.green) && (lhs.blue == rhs.blue); +} + +inline bool operator!=(const xColor& lhs, const xColor& rhs) +{ + return (lhs.red != rhs.red) || (lhs.green != rhs.green) || (lhs.blue != rhs.blue); +} + + static const float ZERO = 0.0f; static const float ONE = 1.0f; static const float ONE_HALF = 0.5f; From a9c5f044c88ee82742bad322c6728545a22637af Mon Sep 17 00:00:00 2001 From: Eric Levin Date: Thu, 30 Apr 2015 11:39:27 -0700 Subject: [PATCH 7/9] changed grab script so user can only grab with left mouse click and we update velocityof grabbed object every frame instead of flinging it onb release --- examples/grab.js | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/examples/grab.js b/examples/grab.js index dc250b9d27..71264da509 100644 --- a/examples/grab.js +++ b/examples/grab.js @@ -50,6 +50,9 @@ var dropLine = Overlays.addOverlay("line3d", { function mousePressEvent(event) { + if(!event.isLeftButton){ + return; + } var pickRay = Camera.computePickRay(event.x, event.y); var intersection = Entities.findRayIntersection(pickRay); if (intersection.intersects && intersection.properties.collisionsWillMove) { @@ -83,7 +86,7 @@ function mousePressEvent(event) { function mouseReleaseEvent() { if (isGrabbing) { - flingObject(); + // flingObject(); Entities.editEntity(grabbedEntity, { gravity: savedGravity }); @@ -108,7 +111,6 @@ function flingObject() { function mouseMoveEvent(event) { if (isGrabbing) { entityProps = Entities.getEntityProperties(grabbedEntity); - prevPosition = entityProps.position; avatarEntityDistance = Vec3.distance(MyAvatar.position, entityProps.position); finalMoveMultiplier = baseMoveFactor * Math.pow(avatarEntityDistance, 1.5); deltaMouse.x = event.x - prevMouse.x; @@ -133,9 +135,17 @@ function mouseMoveEvent(event) { z: 0 }) }); + + flingVelocity = Vec3.subtract(entityProps.position, prevPosition); + flingVelocity = Vec3.multiply(flingMultiplier, flingVelocity); + Entities.editEntity(grabbedEntity, { + velocity: flingVelocity + }); + prevPosition = entityProps.position; } prevMouse.x = event.x; prevMouse.y = event.y; + } function keyReleaseEvent(event) { From 807eab9fc494535e3ef90f4b03dba49ab0a11db7 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Thu, 30 Apr 2015 12:59:09 -0700 Subject: [PATCH 8/9] Fix typo --- interface/src/devices/DdeFaceTracker.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/interface/src/devices/DdeFaceTracker.cpp b/interface/src/devices/DdeFaceTracker.cpp index 652254d718..f47d4e35be 100644 --- a/interface/src/devices/DdeFaceTracker.cpp +++ b/interface/src/devices/DdeFaceTracker.cpp @@ -393,9 +393,9 @@ void DdeFaceTracker::decodePacket(const QByteArray& buffer) { // Use BrowsU_C to control both brows' up and down float browUp = _coefficients[_browUpCenterIndex]; if (isFiltering) { - const float BROW_VELOCITY_FILTER_STRENGHT = 0.75f; + const float BROW_VELOCITY_FILTER_STRENGTH = 0.75f; float velocity = fabs(browUp - _lastBrowUp) / _averageMessageTime; - float velocityFilter = glm::clamp(velocity * BROW_VELOCITY_FILTER_STRENGHT, 0.0f, 1.0f); + float velocityFilter = glm::clamp(velocity * BROW_VELOCITY_FILTER_STRENGTH, 0.0f, 1.0f); _filteredBrowUp = velocityFilter * browUp + (1.0f - velocityFilter) * _filteredBrowUp; _lastBrowUp = browUp; browUp = _filteredBrowUp; From 4c80633a41f5e72c3cf89cbe6256f9c2f2b6c022 Mon Sep 17 00:00:00 2001 From: Eric Levin Date: Thu, 30 Apr 2015 16:42:32 -0700 Subject: [PATCH 9/9] fixed bug where non-script related content would be deleted on script ending --- examples/grab.js | 6 ------ 1 file changed, 6 deletions(-) diff --git a/examples/grab.js b/examples/grab.js index 71264da509..88e23c9a46 100644 --- a/examples/grab.js +++ b/examples/grab.js @@ -160,11 +160,6 @@ function keyPressEvent(event) { } } -function cleanup() { - Entities.deleteEntity(box); - Entities.deleteEntity(box2); - Entities.deleteEntity(ground); -} function setUpTestObjects() { var distance = 4; @@ -235,4 +230,3 @@ Controller.mousePressEvent.connect(mousePressEvent); Controller.mouseReleaseEvent.connect(mouseReleaseEvent); Controller.keyPressEvent.connect(keyPressEvent); Controller.keyReleaseEvent.connect(keyReleaseEvent); -Script.scriptEnding.connect(cleanup); \ No newline at end of file