diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index e231a42f92..0f6fe88ce9 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -2094,6 +2094,18 @@ void MyAvatar::setHasScriptedBlendshapes(bool hasScriptedBlendshapes) { _hasScriptedBlendShapes = hasScriptedBlendshapes; } +void MyAvatar::setHasProceduralBlinkFaceMovement(bool hasProceduralBlinkFaceMovement) { + _headData->setHasProceduralBlinkFaceMovement(hasProceduralBlinkFaceMovement); +} + +void MyAvatar::setHasProceduralEyeFaceMovement(bool hasProceduralEyeFaceMovement) { + _headData->setHasProceduralEyeFaceMovement(hasProceduralEyeFaceMovement); +} + +void MyAvatar::setHasAudioEnabledFaceMovement(bool hasAudioEnabledFaceMovement) { + _headData->setHasAudioEnabledFaceMovement(hasAudioEnabledFaceMovement); +} + void MyAvatar::updateOrientation(float deltaTime) { // Smoothly rotate body with arrow keys diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index 55780b56ac..813dddcc98 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -87,6 +87,8 @@ class MyAvatar : public Avatar { * @property {number} audioListenerModeCustom=2 - The audio listening position is at a the position specified by set by the * customListenPosition and customListenOrientation property values. Read-only. * @property {boolean} hasScriptedBlendshapes=false - Blendshapes will be transmitted over the network if set to true. + * @property {boolean} hasProceduralBlinkFaceMovement=true - procedural blinking will be turned on if set to true. + * @property {boolean} hasProceduralEyeFaceMovement=true - procedural eye movement will be turned on if set to true. * @property {boolean} hasAudioEnabledFaceMovement=true - If set to true, voice audio will move the mouth Blendshapes while MyAvatar.hasScriptedBlendshapes is enabled. * @property {Vec3} customListenPosition=Vec3.ZERO - The listening position used when the audioListenerMode * property value is audioListenerModeCustom. @@ -187,6 +189,8 @@ class MyAvatar : public Avatar { Q_PROPERTY(AudioListenerMode audioListenerModeCamera READ getAudioListenerModeCamera) Q_PROPERTY(AudioListenerMode audioListenerModeCustom READ getAudioListenerModeCustom) Q_PROPERTY(bool hasScriptedBlendshapes READ getHasScriptedBlendshapes WRITE setHasScriptedBlendshapes) + Q_PROPERTY(bool hasProceduralBlinkFaceMovement READ getHasProceduralBlinkFaceMovement WRITE setHasProceduralBlinkFaceMovement) + Q_PROPERTY(bool hasProceduralEyeFaceMovement READ getHasProceduralEyeFaceMovement WRITE setHasProceduralEyeFaceMovement) Q_PROPERTY(bool hasAudioEnabledFaceMovement READ getHasAudioEnabledFaceMovement WRITE setHasAudioEnabledFaceMovement) //TODO: make gravity feature work Q_PROPERTY(glm::vec3 gravity READ getGravity WRITE setGravity) @@ -1347,8 +1351,12 @@ private: bool getShouldRenderLocally() const { return _shouldRender; } void setHasScriptedBlendshapes(bool hasScriptedBlendshapes); bool getHasScriptedBlendshapes() const override { return _hasScriptedBlendShapes; } - void setHasAudioEnabledFaceMovement(bool hasAudioEnabledFaceMovement) { _hasAudioEnabledFaceMovement = hasAudioEnabledFaceMovement; } - bool getHasAudioEnabledFaceMovement() const override { return _hasAudioEnabledFaceMovement; } + void setHasProceduralBlinkFaceMovement(bool hasProceduralBlinkFaceMovement); + bool getHasProceduralBlinkFaceMovement() const override { return _headData->getHasProceduralBlinkFaceMovement(); } + void setHasProceduralEyeFaceMovement(bool hasProceduralEyeFaceMovement); + bool getHasProceduralEyeFaceMovement() const override { return _headData->getHasProceduralEyeFaceMovement(); } + void setHasAudioEnabledFaceMovement(bool hasAudioEnabledFaceMovement); + bool getHasAudioEnabledFaceMovement() const override { return _headData->getHasAudioEnabledFaceMovement(); } bool isMyAvatar() const override { return true; } virtual int parseDataFromBuffer(const QByteArray& buffer) override; virtual glm::vec3 getSkeletonPosition() const override; diff --git a/libraries/avatars-renderer/src/avatars-renderer/Head.cpp b/libraries/avatars-renderer/src/avatars-renderer/Head.cpp index 256b3bf8a6..b0707922ea 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/Head.cpp +++ b/libraries/avatars-renderer/src/avatars-renderer/Head.cpp @@ -100,41 +100,51 @@ void Head::simulate(float deltaTime) { } _browAudioLift = glm::clamp(_browAudioLift *= 0.7f, 0.0f, 1.0f); + const float BLINK_SPEED = 10.0f; const float BLINK_SPEED_VARIABILITY = 1.0f; const float BLINK_START_VARIABILITY = 0.25f; const float FULLY_OPEN = 0.0f; const float FULLY_CLOSED = 1.0f; - if (_leftEyeBlinkVelocity == 0.0f && _rightEyeBlinkVelocity == 0.0f) { - // no blinking when brows are raised; blink less with increasing loudness - const float BASE_BLINK_RATE = 15.0f / 60.0f; - const float ROOT_LOUDNESS_TO_BLINK_INTERVAL = 0.25f; - if (forceBlink || (_browAudioLift < EPSILON && shouldDo(glm::max(1.0f, sqrt(fabs(_averageLoudness - _longTermAverageLoudness)) * + if (getHasProceduralBlinkFaceMovement()) { + if (_leftEyeBlinkVelocity == 0.0f && _rightEyeBlinkVelocity == 0.0f) { + // no blinking when brows are raised; blink less with increasing loudness + const float BASE_BLINK_RATE = 15.0f / 60.0f; + const float ROOT_LOUDNESS_TO_BLINK_INTERVAL = 0.25f; + if (forceBlink || (_browAudioLift < EPSILON && shouldDo(glm::max(1.0f, sqrt(fabs(_averageLoudness - _longTermAverageLoudness)) * ROOT_LOUDNESS_TO_BLINK_INTERVAL) / BASE_BLINK_RATE, deltaTime))) { - _leftEyeBlinkVelocity = BLINK_SPEED + randFloat() * BLINK_SPEED_VARIABILITY; - _rightEyeBlinkVelocity = BLINK_SPEED + randFloat() * BLINK_SPEED_VARIABILITY; - if (randFloat() < 0.5f) { - _leftEyeBlink = BLINK_START_VARIABILITY; - } else { - _rightEyeBlink = BLINK_START_VARIABILITY; + _leftEyeBlinkVelocity = BLINK_SPEED + randFloat() * BLINK_SPEED_VARIABILITY; + _rightEyeBlinkVelocity = BLINK_SPEED + randFloat() * BLINK_SPEED_VARIABILITY; + if (randFloat() < 0.5f) { + _leftEyeBlink = BLINK_START_VARIABILITY; + } + else { + _rightEyeBlink = BLINK_START_VARIABILITY; + } + } + } + else { + _leftEyeBlink = glm::clamp(_leftEyeBlink + _leftEyeBlinkVelocity * deltaTime, FULLY_OPEN, FULLY_CLOSED); + _rightEyeBlink = glm::clamp(_rightEyeBlink + _rightEyeBlinkVelocity * deltaTime, FULLY_OPEN, FULLY_CLOSED); + + if (_leftEyeBlink == FULLY_CLOSED) { + _leftEyeBlinkVelocity = -BLINK_SPEED; + + } + else if (_leftEyeBlink == FULLY_OPEN) { + _leftEyeBlinkVelocity = 0.0f; + } + if (_rightEyeBlink == FULLY_CLOSED) { + _rightEyeBlinkVelocity = -BLINK_SPEED; + + } + else if (_rightEyeBlink == FULLY_OPEN) { + _rightEyeBlinkVelocity = 0.0f; } } } else { - _leftEyeBlink = glm::clamp(_leftEyeBlink + _leftEyeBlinkVelocity * deltaTime, FULLY_OPEN, FULLY_CLOSED); - _rightEyeBlink = glm::clamp(_rightEyeBlink + _rightEyeBlinkVelocity * deltaTime, FULLY_OPEN, FULLY_CLOSED); - - if (_leftEyeBlink == FULLY_CLOSED) { - _leftEyeBlinkVelocity = -BLINK_SPEED; - - } else if (_leftEyeBlink == FULLY_OPEN) { - _leftEyeBlinkVelocity = 0.0f; - } - if (_rightEyeBlink == FULLY_CLOSED) { - _rightEyeBlinkVelocity = -BLINK_SPEED; - - } else if (_rightEyeBlink == FULLY_OPEN) { - _rightEyeBlinkVelocity = 0.0f; - } + _rightEyeBlink = FULLY_OPEN; + _leftEyeBlink = FULLY_OPEN; } // use data to update fake Faceshift blendshape coefficients diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index 568c48dd62..2d8ee52ea1 100644 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -502,6 +502,8 @@ public: float getDomainLimitedScale() const; virtual bool getHasScriptedBlendshapes() const { return false; } + virtual bool getHasProceduralBlinkFaceMovement() const { return true; } + virtual bool getHasProceduralEyeFaceMovement() const { return true; } virtual bool getHasAudioEnabledFaceMovement() const { return false; } /**jsdoc diff --git a/libraries/avatars/src/HeadData.h b/libraries/avatars/src/HeadData.h index bcc2cacde5..f8eca0915e 100644 --- a/libraries/avatars/src/HeadData.h +++ b/libraries/avatars/src/HeadData.h @@ -69,6 +69,24 @@ public: } bool lookAtPositionChangedSince(quint64 time) { return _lookAtPositionChanged >= time; } + bool getHasProceduralEyeFaceMovement() const { return _hasProceduralEyeFaceMovement; } + + void setHasProceduralEyeFaceMovement(const bool hasProceduralEyeFaceMovement) { + _hasProceduralEyeFaceMovement = hasProceduralEyeFaceMovement; + } + + bool getHasProceduralBlinkFaceMovement() const { return _hasProceduralBlinkFaceMovement; } + + void setHasProceduralBlinkFaceMovement(const bool hasProceduralBlinkFaceMovement) { + _hasProceduralBlinkFaceMovement = hasProceduralBlinkFaceMovement; + } + + bool getHasAudioEnabledFaceMovement() const { return _hasAudioEnabledFaceMovement; } + + void setHasAudioEnabledFaceMovement(const bool hasAudioEnabledFaceMovement) { + _hasAudioEnabledFaceMovement = hasAudioEnabledFaceMovement; + } + friend class AvatarData; QJsonObject toJson() const; @@ -83,6 +101,11 @@ protected: glm::vec3 _lookAtPosition; quint64 _lookAtPositionChanged { 0 }; + //std::atomic _hasProceduralBlinkFaceMovement{ true }; + //std::atomic _hasProceduralEyeFaceMovement{ true }; + bool _hasAudioEnabledFaceMovement { true }; + bool _hasProceduralBlinkFaceMovement{ true }; + bool _hasProceduralEyeFaceMovement{ true }; bool _isFaceTrackerConnected { false }; bool _isEyeTrackerConnected { false }; float _leftEyeBlink { 0.0f }; diff --git a/scripts/developer/DefaultStylizedFemale_Clothed.fst b/scripts/developer/DefaultStylizedFemale_Clothed.fst new file mode 100644 index 0000000000..3e46d6a15c --- /dev/null +++ b/scripts/developer/DefaultStylizedFemale_Clothed.fst @@ -0,0 +1,139 @@ +name = DefaultStylizedFemale_Clothed +type = body+head +scale = 1 +filename = DefaultStylizedFemale_Clothed/DefaultStylizedFemale_Clothed.fbx +texdir = DefaultStylizedFemale_Clothed/textures +joint = jointLean = Spine +joint = jointRightHand = RightHand +joint = jointEyeLeft = LeftEye +joint = jointHead = HeadTop_End +joint = jointNeck = Neck +joint = jointRoot = Hips +joint = jointEyeRight = RightEye +joint = jointLeftHand = LeftHand +freeJoint = LeftArm +freeJoint = LeftForeArm +freeJoint = RightArm +freeJoint = RightForeArm +bs = EyeBlink_L = Blink_Left = 1 +bs = Sneer = Squint_Right = 0.5 +bs = Sneer = Squint_Left = 0.5 +bs = Sneer = NoseScrunch_Right = 0.75 +bs = Sneer = NoseScrunch_Left = 0.75 +bs = ChinLowerRaise = Jaw_Up = 1 +bs = EyeSquint_R = Squint_Right = 1 +bs = MouthSmile_R = Smile_Right = 1 +bs = ChinUpperRaise = UpperLipUp_Right = 0.5 +bs = ChinUpperRaise = UpperLipUp_Left = 0.5 +bs = LipsLowerOpen = LowerLipOut = 1 +bs = LipsLowerDown = LowerLipDown_Right = 0.7 +bs = LipsLowerDown = LowerLipDown_Left = 0.7 +bs = BrowsU_L = BrowsUp_Left = 1 +bs = MouthRight = Midmouth_Right = 1 +bs = MouthDimple_R = Smile_Right = 0.25 +bs = LipsPucker = MouthNarrow_Right = 1 +bs = LipsPucker = MouthNarrow_Left = 1 +bs = Puff = CheekPuff_Right = 1 +bs = Puff = CheekPuff_Left = 1 +bs = JawFwd = JawForeward = 1 +bs = BrowsD_L = BrowsDown_Left = 1 +bs = LipsFunnel = TongueUp = 1 +bs = LipsFunnel = MouthWhistle_NarrowAdjust_Right = 0.5 +bs = LipsFunnel = MouthWhistle_NarrowAdjust_Left = 0.5 +bs = LipsFunnel = MouthNarrow_Right = 1 +bs = LipsFunnel = MouthNarrow_Left = 1 +bs = LipsFunnel = Jaw_Down = 0.36 +bs = LipsFunnel = JawForeward = 0.39 +bs = LipsUpperOpen = UpperLipOut = 1 +bs = EyeSquint_L = Squint_Left = 1 +bs = MouthDimple_L = Smile_Left = 0.25 +bs = LipsLowerClose = LowerLipIn = 1 +bs = MouthFrown_R = Frown_Right = 1 +bs = MouthFrown_L = Frown_Left = 1 +bs = BrowsU_R = BrowsUp_Right = 1 +bs = JawOpen = MouthOpen = 0.7 +bs = JawRight = Jaw_Right = 1 +bs = MouthLeft = Midmouth_Left = 1 +bs = BrowsU_C = BrowsUp_Right = 1 +bs = BrowsU_C = BrowsUp_Left = 1 +bs = LipsUpperUp = UpperLipUp_Right = 0.7 +bs = LipsUpperUp = UpperLipUp_Left = 0.7 +bs = EyeBlink_R = Blink_Right = 1 +bs = EyeOpen_R = EyesWide_Right = 1 +bs = LipsUpperClose = UpperLipIn = 1 +bs = MouthSmile_L = Smile_Left = 1 +bs = EyeOpen_L = EyesWide_Left = 1 +bs = JawLeft = JawRotateY_Left = 0.5 +bs = BrowsD_R = BrowsDown_Right = 1 +jointIndex = RightHandThumb4 = 21 +jointIndex = Neck = 62 +jointIndex = LeftHandIndex4 = 57 +jointIndex = Body = 71 +jointIndex = LeftHandIndex1 = 54 +jointIndex = RightHand = 17 +jointIndex = RightHandMiddle1 = 26 +jointIndex = Spine = 11 +jointIndex = RightHandRing2 = 31 +jointIndex = RightArm = 15 +jointIndex = RightHandPinky2 = 35 +jointIndex = LeftToeBase = 9 +jointIndex = RightHandIndex3 = 24 +jointIndex = RightHandRing1 = 30 +jointIndex = RightHandPinky1 = 34 +jointIndex = RightEye = 66 +jointIndex = LeftHandRing4 = 49 +jointIndex = LeftHandRing2 = 47 +jointIndex = RightHandMiddle2 = 27 +jointIndex = Head = 63 +jointIndex = LeftHandMiddle4 = 53 +jointIndex = LeftLeg = 7 +jointIndex = LeftHandPinky2 = 43 +jointIndex = LeftHandThumb1 = 58 +jointIndex = LeftHandPinky4 = 45 +jointIndex = RightHandIndex1 = 22 +jointIndex = Tops = 67 +jointIndex = Hips = 0 +jointIndex = LeftUpLeg = 6 +jointIndex = RightShoulder = 14 +jointIndex = Spine2 = 13 +jointIndex = RightHandRing4 = 33 +jointIndex = RightHandThumb3 = 20 +jointIndex = RightHandIndex4 = 25 +jointIndex = LeftFoot = 8 +jointIndex = LeftHandRing3 = 48 +jointIndex = LeftHand = 41 +jointIndex = LeftForeArm = 40 +jointIndex = LeftToe_End = 10 +jointIndex = Bottoms = 68 +jointIndex = RightFoot = 3 +jointIndex = LeftHandMiddle2 = 51 +jointIndex = LeftHandThumb3 = 60 +jointIndex = RightHandPinky3 = 36 +jointIndex = LeftEye = 65 +jointIndex = LeftHandIndex2 = 55 +jointIndex = RightHandIndex2 = 23 +jointIndex = LeftHandPinky1 = 42 +jointIndex = LeftHandMiddle3 = 52 +jointIndex = RightHandMiddle4 = 29 +jointIndex = LeftHandThumb2 = 59 +jointIndex = Shoes = 69 +jointIndex = RightHandThumb1 = 18 +jointIndex = RightToe_End = 5 +jointIndex = RightHandThumb2 = 19 +jointIndex = RightUpLeg = 1 +jointIndex = RightLeg = 2 +jointIndex = LeftHandMiddle1 = 50 +jointIndex = LeftHandIndex3 = 56 +jointIndex = LeftHandThumb4 = 61 +jointIndex = RightHandRing3 = 32 +jointIndex = Hair = 70 +jointIndex = Spine1 = 12 +jointIndex = LeftHandRing1 = 46 +jointIndex = LeftArm = 39 +jointIndex = LeftShoulder = 38 +jointIndex = RightForeArm = 16 +jointIndex = HeadTop_End = 64 +jointIndex = RightHandPinky4 = 37 +jointIndex = LeftHandPinky3 = 44 +jointIndex = RightToeBase = 4 +jointIndex = RightHandMiddle3 = 28 diff --git a/scripts/developer/facialExpressions.js b/scripts/developer/facialExpressions.js new file mode 100644 index 0000000000..84e3966c4a --- /dev/null +++ b/scripts/developer/facialExpressions.js @@ -0,0 +1,374 @@ +// +// facialExpressions.js +// A script to set different emotions using blend shapes +// +// Author: Elisa Lupin-Jimenez +// Copyright High Fidelity 2018 +// +// Licensed under the Apache 2.0 License +// See accompanying license file or http://apache.org/ +// +// All assets are under CC Attribution Non-Commerical +// http://creativecommons.org/licenses/ +// + +(function() { + + var TABLET_BUTTON_NAME = "EMOTIONS"; + // TODO: ADD HTML LANDING PAGE + + var TRANSITION_TIME_SECONDS = 0.25; + + var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system"); + var icon = "https://hifi-content.s3.amazonaws.com/elisalj/emoji_scripts/icons/emoji-i.svg"; + var activeIcon = "https://hifi-content.s3.amazonaws.com/elisalj/emoji_scripts/icons/emoji-a.svg"; + var isActive = true; + + var controllerMappingName; + var controllerMapping; + + var tabletButton = tablet.addButton({ + icon: icon, + activeIcon: activeIcon, + text: TABLET_BUTTON_NAME, + isActive: true + }); + + var toggle = function() { + isActive = !isActive; + tabletButton.editProperties({isActive: isActive}); + if (isActive) { + Controller.enableMapping(controllerMappingName); + } else { + setEmotion(DEFAULT); + Controller.disableMapping(controllerMappingName); + } + }; + + tabletButton.clicked.connect(toggle); + + var DEFAULT = { + "EyeOpen_L": 0.00, + "EyeOpen_R": 0.00, + "EyeBlink_L": 0.00, + "EyeBlink_R": 0.00, + "EyeSquint_L": 0.00, + "EyeSquint_R": 0.00, + "BrowsD_L": 0.00, + "BrowsD_R": 0.00, + "BrowsU_L": 0.00, + "BrowsU_C": 0.00, + "JawOpen": 0.00, + "JawFwd": 0.00, + "MouthFrown_L": 0.00, + "MouthFrown_R": 0.00, + "MouthSmile_L": 0.00, + "MouthSmile_R": 0.00, + "MouthDimple_L": 0.00, + "MouthDimple_R": 0.00, + "LipsUpperClose": 0.00, + "LipsLowerClose": 0.00, + "LipsLowerOpen": 0.00, + "ChinUpperRaise": 0.00, + "Sneer": 0.00, + "Puff": 0.00 + }; + + var SMILE = { + "EyeOpen_L": 0.00, + "EyeOpen_R": 0.00, + "EyeBlink_L": 0.30, + "EyeBlink_R": 0.30, + "EyeSquint_L": 0.90, + "EyeSquint_R": 0.90, + "BrowsD_L": 1.00, + "BrowsD_R": 1.00, + "BrowsU_L": 0.00, + "BrowsU_C": 0.00, + "JawOpen": 0.00, + "JawFwd": 0.00, + "MouthFrown_L": 0.00, + "MouthFrown_R": 0.00, + "MouthSmile_L": 1.00, + "MouthSmile_R": 1.00, + "MouthDimple_L": 1.00, + "MouthDimple_R": 1.00, + "LipsUpperClose": 0.40, + "LipsLowerClose": 0.30, + "LipsLowerOpen": 0.25, + "ChinUpperRaise": 0.35, + "Sneer": 0.00, + "Puff": 0.00 + }; + + var LAUGH = { + "EyeOpen_L": 0.00, + "EyeOpen_R": 0.00, + "EyeBlink_L": 0.45, + "EyeBlink_R": 0.45, + "EyeSquint_L": 0.75, + "EyeSquint_R": 0.75, + "BrowsD_L": 0.00, + "BrowsD_R": 0.00, + "BrowsU_L": 0.00, + "BrowsU_C": 0.50, + "JawOpen": 0.50, + "JawFwd": 0.00, + "MouthFrown_L": 0.00, + "MouthFrown_R": 0.00, + "MouthSmile_L": 1.00, + "MouthSmile_R": 1.00, + "MouthDimple_L": 1.00, + "MouthDimple_R": 1.00, + "LipsUpperClose": 0.00, + "LipsLowerClose": 0.00, + "LipsLowerOpen": 0.00, + "ChinUpperRaise": 0.30, + "Sneer": 1.00, + "Puff": 0.30 + }; + + var FLIRT = { + "EyeOpen_L": 0.00, + "EyeOpen_R": 0.00, + "EyeBlink_L": 0.50, + "EyeBlink_R": 0.50, + "EyeSquint_L": 0.25, + "EyeSquint_R": 0.25, + "BrowsD_L": 0.00, + "BrowsD_R": 1.00, + "BrowsU_L": 0.55, + "BrowsU_C": 0.00, + "JawOpen": 0.00, + "JawFwd": 0.00, + "MouthFrown_L": 0.00, + "MouthFrown_R": 0.00, + "MouthSmile_L": 0.50, + "MouthSmile_R": 0.00, + "MouthDimple_L": 1.00, + "MouthDimple_R": 1.00, + "LipsUpperClose": 0.00, + "LipsLowerClose": 0.00, + "LipsLowerOpen": 0.00, + "ChinUpperRaise": 0.00, + "Sneer": 0.00, + "Puff": 0.00 + }; + + var SAD = { + "EyeOpen_L": 0.00, + "EyeOpen_R": 0.00, + "EyeBlink_L": 0.30, + "EyeBlink_R": 0.30, + "EyeSquint_L": 0.30, + "EyeSquint_R": 0.30, + "BrowsD_L": 0.00, + "BrowsD_R": 0.00, + "BrowsU_L": 0.00, + "BrowsU_C": 0.50, + "JawOpen": 0.00, + "JawFwd": 0.80, + "MouthFrown_L": 0.80, + "MouthFrown_R": 0.80, + "MouthSmile_L": 0.00, + "MouthSmile_R": 0.00, + "MouthDimple_L": 0.00, + "MouthDimple_R": 0.00, + "LipsUpperClose": 0.00, + "LipsLowerClose": 0.50, + "LipsLowerOpen": 0.00, + "ChinUpperRaise": 0.00, + "Sneer": 0.00, + "Puff": 0.00 + }; + + var ANGRY = { + "EyeOpen_L": 1.00, + "EyeOpen_R": 1.00, + "EyeBlink_L": 0.00, + "EyeBlink_R": 0.00, + "EyeSquint_L": 1.00, + "EyeSquint_R": 1.00, + "BrowsD_L": 1.00, + "BrowsD_R": 1.00, + "BrowsU_L": 0.00, + "BrowsU_C": 0.00, + "JawOpen": 0.00, + "JawFwd": 0.00, + "MouthFrown_L": 0.50, + "MouthFrown_R": 0.50, + "MouthSmile_L": 0.00, + "MouthSmile_R": 0.00, + "MouthDimple_L": 0.00, + "MouthDimple_R": 0.00, + "LipsUpperClose": 0.50, + "LipsLowerClose": 0.50, + "LipsLowerOpen": 0.00, + "ChinUpperRaise": 0.00, + "Sneer": 0.50, + "Puff": 0.00 + }; + + var FEAR = { + "EyeOpen_L": 1.00, + "EyeOpen_R": 1.00, + "EyeBlink_L": 0.00, + "EyeBlink_R": 0.00, + "EyeSquint_L": 0.00, + "EyeSquint_R": 0.00, + "BrowsD_L": 0.00, + "BrowsD_R": 0.00, + "BrowsU_L": 0.00, + "BrowsU_C": 1.00, + "JawOpen": 0.15, + "JawFwd": 0.00, + "MouthFrown_L": 0.30, + "MouthFrown_R": 0.30, + "MouthSmile_L": 0.00, + "MouthSmile_R": 0.00, + "MouthDimple_L": 0.00, + "MouthDimple_R": 0.00, + "LipsUpperClose": 0.00, + "LipsLowerClose": 0.00, + "LipsLowerOpen": 0.00, + "ChinUpperRaise": 0.00, + "Sneer": 0.00, + "Puff": 0.00 + }; + + var DISGUST = { + "EyeOpen_L": 0.00, + "EyeOpen_R": 0.00, + "EyeBlink_L": 0.25, + "EyeBlink_R": 0.25, + "EyeSquint_L": 1.00, + "EyeSquint_R": 1.00, + "BrowsD_L": 1.00, + "BrowsD_R": 1.00, + "BrowsU_L": 0.00, + "BrowsU_C": 0.00, + "JawOpen": 0.00, + "JawFwd": 0.00, + "MouthFrown_L": 1.00, + "MouthFrown_R": 1.00, + "MouthSmile_L": 0.00, + "MouthSmile_R": 0.00, + "MouthDimple_L": 0.00, + "MouthDimple_R": 0.00, + "LipsUpperClose": 0.00, + "LipsLowerClose": 0.75, + "LipsLowerOpen": 0.00, + "ChinUpperRaise": 0.75, + "Sneer": 1.00, + "Puff": 0.00 + }; + + + function mixValue(valueA, valueB, percentage) { + return valueA + ((valueB - valueA) * percentage); + } + + var lastEmotionUsed = DEFAULT; + var emotion = DEFAULT; + var isChangingEmotion = false; + var changingEmotionPercentage = 0.0; + + Script.update.connect(function(deltaTime) { + if (!isChangingEmotion) { + return; + } + changingEmotionPercentage += deltaTime / TRANSITION_TIME_SECONDS; + if (changingEmotionPercentage >= 1.0) { + changingEmotionPercentage = 1.0; + isChangingEmotion = false; + if (emotion === DEFAULT) { + MyAvatar.hasScriptedBlendshapes = false; + } + } + for (var blendshape in emotion) { + MyAvatar.setBlendshape(blendshape, + mixValue(lastEmotionUsed[blendshape], emotion[blendshape], changingEmotionPercentage)); + } + }); + + function setEmotion(currentEmotion) { + if (emotion !== lastEmotionUsed) { + lastEmotionUsed = emotion; + } + if (currentEmotion !== lastEmotionUsed) { + changingEmotionPercentage = 0.0; + emotion = currentEmotion; + isChangingEmotion = true; + MyAvatar.hasScriptedBlendshapes = true; + } + } + + + controllerMappingName = 'Hifi-FacialExpressions-Mapping'; + controllerMapping = Controller.newMapping(controllerMappingName); + + controllerMapping.from(Controller.Hardware.Keyboard.H).to(function(value) { + if (value !== 0) { + setEmotion(SMILE); + } + }); + + controllerMapping.from(Controller.Hardware.Keyboard.J).to(function(value) { + if (value !== 0) { + setEmotion(LAUGH); + } + }); + + controllerMapping.from(Controller.Hardware.Keyboard.K).to(function(value) { + if (value !== 0) { + setEmotion(FLIRT); + } + }); + + controllerMapping.from(Controller.Hardware.Keyboard.L).to(function(value) { + if (value !== 0) { + setEmotion(SAD); + } + }); + + controllerMapping.from(Controller.Hardware.Keyboard.V).to(function(value) { + if (value !== 0) { + setEmotion(ANGRY); + } + }); + + controllerMapping.from(Controller.Hardware.Keyboard.B).to(function(value) { + if (value !== 0) { + setEmotion(FEAR); + } + }); + + controllerMapping.from(Controller.Hardware.Keyboard.M).to(function(value) { + if (value !== 0) { + setEmotion(DISGUST); + } + }); + + controllerMapping.from(Controller.Hardware.Keyboard.N).to(function(value) { + if (value !== 0) { + setEmotion(DEFAULT); + } + }); + + Controller.enableMapping(controllerMappingName); + + Script.scriptEnding.connect(function() { + tabletButton.clicked.disconnect(toggle); + tablet.removeButton(tabletButton); + Controller.disableMapping(controllerMappingName); + + if (emotion !== DEFAULT || isChangingEmotion) { + isChangingEmotion = false; + for (var blendshape in DEFAULT) { + MyAvatar.setBlendshape(blendshape, DEFAULT[blendshape]); + } + MyAvatar.hasScriptedBlendshapes = false; + } + }); + +}()); \ No newline at end of file