void Head::simulate(float deltaTime, bool isMine) { // Update audio trailing average for rendering facial animations Faceshift* faceshift = Application::getInstance()->getFaceshift(); if (isMine) { _isFaceshiftConnected = faceshift->isActive(); } if (isMine && faceshift->isActive()) { const float EYE_OPEN_SCALE = 0.5f; _leftEyeBlink = faceshift->getLeftBlink() - EYE_OPEN_SCALE * faceshift->getLeftEyeOpen(); _rightEyeBlink = faceshift->getRightBlink() - EYE_OPEN_SCALE * faceshift->getRightEyeOpen(); // set these values based on how they'll be used. if we use faceshift in the long term, we'll want a complete // mapping between their blendshape coefficients and our avatar features const float MOUTH_SIZE_SCALE = 2500.0f; _averageLoudness = faceshift->getMouthSize() * faceshift->getMouthSize() * MOUTH_SIZE_SCALE; const float BROW_HEIGHT_SCALE = 0.005f; _browAudioLift = faceshift->getBrowUpCenter() * BROW_HEIGHT_SCALE; _blendshapeCoefficients = faceshift->getBlendshapeCoefficients(); } else if (!_isFaceshiftConnected) { // Update eye saccades const float AVERAGE_MICROSACCADE_INTERVAL = 0.50f; const float AVERAGE_SACCADE_INTERVAL = 4.0f; const float MICROSACCADE_MAGNITUDE = 0.002f; const float SACCADE_MAGNITUDE = 0.04f; if (randFloat() < deltaTime / AVERAGE_MICROSACCADE_INTERVAL) { _saccadeTarget = MICROSACCADE_MAGNITUDE * randVector(); } else if (randFloat() < deltaTime / AVERAGE_SACCADE_INTERVAL) { _saccadeTarget = SACCADE_MAGNITUDE * randVector(); } _saccade += (_saccadeTarget - _saccade) * 0.50f; const float AUDIO_AVERAGING_SECS = 0.05f; _averageLoudness = (1.f - deltaTime / AUDIO_AVERAGING_SECS) * _averageLoudness + (deltaTime / AUDIO_AVERAGING_SECS) * _audioLoudness; // Detect transition from talking to not; force blink after that and a delay bool forceBlink = false; const float TALKING_LOUDNESS = 100.0f; const float BLINK_AFTER_TALKING = 0.25f; if (_averageLoudness > TALKING_LOUDNESS) { _timeWithoutTalking = 0.0f; } else if (_timeWithoutTalking < BLINK_AFTER_TALKING && (_timeWithoutTalking += deltaTime) >= BLINK_AFTER_TALKING) { forceBlink = true; } // Update audio attack data for facial animation (eyebrows and mouth) _audioAttack = 0.9f * _audioAttack + 0.1f * fabs(_audioLoudness - _lastLoudness); _lastLoudness = _audioLoudness; const float BROW_LIFT_THRESHOLD = 100.0f; if (_audioAttack > BROW_LIFT_THRESHOLD) { _browAudioLift += sqrtf(_audioAttack) * 0.00005f; } const float CLAMP = 0.01f; if (_browAudioLift > CLAMP) { _browAudioLift = CLAMP; } _browAudioLift *= 0.7f; const float BLINK_SPEED = 10.0f; 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(_averageLoudness) * ROOT_LOUDNESS_TO_BLINK_INTERVAL) / BASE_BLINK_RATE, deltaTime))) { _leftEyeBlinkVelocity = BLINK_SPEED; _rightEyeBlinkVelocity = BLINK_SPEED; } } 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; } } // use data to update fake Faceshift blendshape coefficients const float BROW_LIFT_SCALE = 500.0f; const float JAW_OPEN_SCALE = 0.01f; const float JAW_OPEN_DEAD_ZONE = 0.75f; faceshift->updateFakeCoefficients(_leftEyeBlink, _rightEyeBlink, min(1.0f, _browAudioLift * BROW_LIFT_SCALE), glm::clamp(sqrt(_averageLoudness * JAW_OPEN_SCALE) - JAW_OPEN_DEAD_ZONE, 0.0f, 1.0f), _blendshapeCoefficients); } _faceModel.simulate(deltaTime); // the blend face may have custom eye meshes if (!_faceModel.getEyePositions(_leftEyePosition, _rightEyePosition)) { _leftEyePosition = _rightEyePosition = getPosition(); } _eyePosition = calculateAverageEyePosition(); }
void PerlinFace::updatePositions() { const float BROWS_UP_MAX = 3; const float BROWS_DOWN_MAX = 1; const float BROWS_UP_CENTER_MAX = 1; Faceshift* faceshift = Application::getInstance()->getFaceshift(); if (faceshift->isActive()) { _browsD_L = faceshift->getBrowDownLeft(); _browsD_R = faceshift->getBrowDownRight(); _browsU_C = faceshift->getBrowUpCenter(); _browsU_L = faceshift->getBrowUpLeft(); _browsU_R = faceshift->getBrowUpRight(); _mouthSize = faceshift->getMouthSize(); _mouthSmileLeft = faceshift->getMouthSmileLeft(); _mouthSmileRight = faceshift->getMouthSmileRight(); _eyeBlink_L = faceshift->getLeftBlink(); _eyeBlink_R = faceshift->getRightBlink(); _eyeOpen_L = faceshift->getLeftEyeOpen(); _eyeOpen_R = faceshift->getRightEyeOpen(); } // Update left brow _vertices[BROW_LEFT].y = getVec3(BROW_LEFT).y + _browsU_L * BROWS_UP_MAX - _browsD_L * BROWS_DOWN_MAX - _browsU_C * BROWS_UP_CENTER_MAX; _vertices[BROW_MID_TOP].y = getVec3(BROW_MID_TOP).y + _browsU_L * BROWS_UP_MAX - _browsD_L * BROWS_DOWN_MAX; _vertices[BROW_MID_BOTTOM].y = getVec3(BROW_MID_BOTTOM).y + _browsU_L * BROWS_UP_MAX - _browsD_L * BROWS_DOWN_MAX; _vertices[BROW_RIGHT_TOP].y = getVec3(BROW_RIGHT_TOP).y + _browsU_L * BROWS_UP_MAX - _browsD_L * BROWS_DOWN_MAX + _browsU_C * BROWS_UP_CENTER_MAX; _vertices[BROW_RIGHT_BOTTOM].y = getVec3(BROW_RIGHT_BOTTOM).y + _browsU_L * BROWS_UP_MAX - _browsD_L * BROWS_DOWN_MAX + _browsU_C * BROWS_UP_CENTER_MAX; // Update right brow _vertices[NUM_VERTICES + BROW_LEFT].y = getVec3(NUM_VERTICES + BROW_LEFT).y + _browsU_R * BROWS_UP_MAX - _browsD_R * BROWS_DOWN_MAX - _browsU_C * BROWS_UP_CENTER_MAX; _vertices[NUM_VERTICES + BROW_MID_TOP].y = getVec3(NUM_VERTICES + BROW_MID_TOP).y + _browsU_R * BROWS_UP_MAX - _browsD_R * BROWS_DOWN_MAX; _vertices[NUM_VERTICES + BROW_MID_BOTTOM].y = getVec3(NUM_VERTICES + BROW_MID_BOTTOM).y + _browsU_R * BROWS_UP_MAX - _browsD_R * BROWS_DOWN_MAX; _vertices[NUM_VERTICES + BROW_RIGHT_TOP].y = getVec3(NUM_VERTICES + BROW_RIGHT_TOP).y + _browsU_R * BROWS_UP_MAX - _browsD_R * BROWS_DOWN_MAX + _browsU_C * BROWS_UP_CENTER_MAX; _vertices[NUM_VERTICES + BROW_RIGHT_BOTTOM].y = getVec3(NUM_VERTICES + BROW_RIGHT_BOTTOM).y + _browsU_R * BROWS_UP_MAX - _browsD_R * BROWS_DOWN_MAX + _browsU_C * BROWS_UP_CENTER_MAX; const float MOUTH_UP_MAX = 6.5f; const float MOUTH_SIDE_UP_MAX = 4.0f; const float SMILE_FACTOR = 1.0f / 3.0f; // 0 = No smile, 1 = The Jocker. // Mouth _vertices[MOUTH_BOTTOM_IN].y = getVec3(MOUTH_BOTTOM_IN).y + (1.0 - _mouthSize) * MOUTH_UP_MAX; _vertices[MOUTH_BOTTOM_OUT].y = getVec3(MOUTH_BOTTOM_OUT).y + (1.0 - _mouthSize) * MOUTH_UP_MAX; _vertices[MOUTH_MID_IN] = (1.0f - (_mouthSmileLeft * SMILE_FACTOR)) * (getVec3(MOUTH_MID_IN) + glm::vec3(0, (1.0 - _mouthSize) * MOUTH_SIDE_UP_MAX, 0)) + (_mouthSmileLeft * SMILE_FACTOR) * getVec3(CHICK_MID); _vertices[MOUTH_MID_OUT] = (1.0f - (_mouthSmileLeft * SMILE_FACTOR)) * (getVec3(MOUTH_MID_OUT) + glm::vec3(0, (1.0 - _mouthSize) * MOUTH_SIDE_UP_MAX, 0)) + (_mouthSmileLeft * SMILE_FACTOR) * getVec3(CHICK_MID); _vertices[NUM_VERTICES + MOUTH_BOTTOM_IN].y = getVec3(NUM_VERTICES + MOUTH_BOTTOM_IN).y + (1.0 - _mouthSize) * MOUTH_UP_MAX; _vertices[NUM_VERTICES + MOUTH_BOTTOM_OUT].y = getVec3(NUM_VERTICES + MOUTH_BOTTOM_OUT).y + (1.0 - _mouthSize) * MOUTH_UP_MAX; _vertices[NUM_VERTICES + MOUTH_MID_IN] = (1.0f - (_mouthSmileLeft * SMILE_FACTOR)) * (getVec3(NUM_VERTICES + MOUTH_MID_IN) + glm::vec3(0, (1.0 - _mouthSize) * MOUTH_SIDE_UP_MAX, 0)) + (_mouthSmileLeft * SMILE_FACTOR) * getVec3(NUM_VERTICES + CHICK_MID); _vertices[NUM_VERTICES + MOUTH_MID_OUT] = (1.0f - (_mouthSmileLeft * SMILE_FACTOR)) * (getVec3(NUM_VERTICES + MOUTH_MID_OUT) + glm::vec3(0, (1.0 - _mouthSize) * MOUTH_SIDE_UP_MAX, 0)) + (_mouthSmileLeft * SMILE_FACTOR) * getVec3(NUM_VERTICES + CHICK_MID); // Jaw _vertices[CHIN_IN].y = getVec3(CHIN_IN).y + (1.0 - _mouthSize) * MOUTH_UP_MAX; _vertices[CHIN_TIP].y = getVec3(CHIN_TIP).y + (1.0 - _mouthSize) * MOUTH_UP_MAX; _vertices[CHIN_BOTTOM].y = getVec3(CHIN_BOTTOM).y + (1.0 - _mouthSize) * MOUTH_UP_MAX; _vertices[NUM_VERTICES +CHIN_IN].y = getVec3(NUM_VERTICES + CHIN_IN).y + (1.0 - _mouthSize) * MOUTH_UP_MAX; _vertices[NUM_VERTICES +CHIN_TIP].y = getVec3(NUM_VERTICES + CHIN_TIP).y + (1.0 - _mouthSize) * MOUTH_UP_MAX; _vertices[NUM_VERTICES +CHIN_BOTTOM].y = getVec3(NUM_VERTICES + CHIN_BOTTOM).y + (1.0 - _mouthSize) * MOUTH_UP_MAX; // Eyelids glm::vec3 topLeftEyelid = getVec3(EYE_MID_TOP); glm::vec3 bottomLeftEyelid = getVec3(EYE_MID_BOTTOM); glm::vec3 topRightEyelid = getVec3(NUM_VERTICES + EYE_MID_TOP); glm::vec3 bottomRightEyelid = getVec3(NUM_VERTICES + EYE_MID_BOTTOM); _vertices[EYE_MID_TOP] = (1.0f - (_eyeBlink_L - _eyeOpen_L / 2.0f)) * topLeftEyelid + (_eyeBlink_L - _eyeOpen_L / 2.0f) * (topLeftEyelid + bottomLeftEyelid) / 2.0f; _vertices[EYE_MID_BOTTOM] = (1.0f - (_eyeBlink_L - _eyeOpen_L / 2.0f)) * bottomLeftEyelid + (_eyeBlink_L - _eyeOpen_L / 2.0f) * (topLeftEyelid + bottomLeftEyelid) / 2.0f; _vertices[NUM_VERTICES + EYE_MID_TOP] = (1.0f - (_eyeBlink_R - _eyeOpen_R / 2.0f)) * topRightEyelid + (_eyeBlink_R - _eyeOpen_R / 2.0f) * (topRightEyelid + bottomRightEyelid) / 2.0f; _vertices[NUM_VERTICES + EYE_MID_BOTTOM] = (1.0f - (_eyeBlink_R - _eyeOpen_R / 2.0f)) * bottomRightEyelid + (_eyeBlink_R - _eyeOpen_R / 2.0f) * (topRightEyelid + bottomRightEyelid) / 2.0f; }
void Head::simulate(float deltaTime, bool isMine, bool billboard) { // Update audio trailing average for rendering facial animations Faceshift* faceshift = Application::getInstance()->getFaceshift(); Visage* visage = Application::getInstance()->getVisage(); if (isMine) { _isFaceshiftConnected = false; if (faceshift->isActive()) { _blendshapeCoefficients = faceshift->getBlendshapeCoefficients(); _isFaceshiftConnected = true; } else if (visage->isActive()) { _blendshapeCoefficients = visage->getBlendshapeCoefficients(); _isFaceshiftConnected = true; } } if (!(_isFaceshiftConnected || billboard)) { // Update eye saccades const float AVERAGE_MICROSACCADE_INTERVAL = 0.50f; const float AVERAGE_SACCADE_INTERVAL = 4.0f; const float MICROSACCADE_MAGNITUDE = 0.002f; const float SACCADE_MAGNITUDE = 0.04f; if (randFloat() < deltaTime / AVERAGE_MICROSACCADE_INTERVAL) { _saccadeTarget = MICROSACCADE_MAGNITUDE * randVector(); } else if (randFloat() < deltaTime / AVERAGE_SACCADE_INTERVAL) { _saccadeTarget = SACCADE_MAGNITUDE * randVector(); } _saccade += (_saccadeTarget - _saccade) * 0.50f; const float AUDIO_AVERAGING_SECS = 0.05f; _averageLoudness = (1.f - deltaTime / AUDIO_AVERAGING_SECS) * _averageLoudness + (deltaTime / AUDIO_AVERAGING_SECS) * _audioLoudness; // Detect transition from talking to not; force blink after that and a delay bool forceBlink = false; const float TALKING_LOUDNESS = 100.0f; const float BLINK_AFTER_TALKING = 0.25f; if (_averageLoudness > TALKING_LOUDNESS) { _timeWithoutTalking = 0.0f; } else if (_timeWithoutTalking < BLINK_AFTER_TALKING && (_timeWithoutTalking += deltaTime) >= BLINK_AFTER_TALKING) { forceBlink = true; } // Update audio attack data for facial animation (eyebrows and mouth) _audioAttack = 0.9f * _audioAttack + 0.1f * fabs(_audioLoudness - _lastLoudness); _lastLoudness = _audioLoudness; const float BROW_LIFT_THRESHOLD = 100.0f; if (_audioAttack > BROW_LIFT_THRESHOLD) { _browAudioLift += sqrtf(_audioAttack) * 0.00005f; } const float CLAMP = 0.01f; if (_browAudioLift > CLAMP) { _browAudioLift = CLAMP; } _browAudioLift *= 0.7f; const float BLINK_SPEED = 10.0f; 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(_averageLoudness) * ROOT_LOUDNESS_TO_BLINK_INTERVAL) / BASE_BLINK_RATE, deltaTime))) { _leftEyeBlinkVelocity = BLINK_SPEED; _rightEyeBlinkVelocity = BLINK_SPEED; } } 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; } } // use data to update fake Faceshift blendshape coefficients const float BROW_LIFT_SCALE = 500.0f; const float JAW_OPEN_SCALE = 0.01f; const float JAW_OPEN_DEAD_ZONE = 0.75f; faceshift->updateFakeCoefficients(_leftEyeBlink, _rightEyeBlink, min(1.0f, _browAudioLift * BROW_LIFT_SCALE), glm::clamp(sqrt(_averageLoudness * JAW_OPEN_SCALE) - JAW_OPEN_DEAD_ZONE, 0.0f, 1.0f), _blendshapeCoefficients); } if (!isMine) { _faceModel.setLODDistance(static_cast<Avatar*>(_owningAvatar)->getLODDistance()); } _leftEyePosition = _rightEyePosition = getPosition(); if (!billboard) { _faceModel.simulate(deltaTime); _faceModel.getEyePositions(_leftEyePosition, _rightEyePosition); } _eyePosition = calculateAverageEyePosition(); }