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