float Movement::computeAngleBetweenVectorsXYPlane(const Math::Vector3d &v1, const Math::Vector3d &v2) const { Math::Vector3d v1XY = v1; v1XY.z() = 0.0; Math::Vector3d v2XY = v2; v2XY.z() = 0.0; Math::Angle angle = Math::Vector3d::angle(v1XY, v2XY); Math::Vector3d cross = Math::Vector3d::crossProduct(v1XY, v2XY); if (cross.z() < 0) { angle = -angle; } return angle.getDegrees(); }
void Head::lookAt(bool entering, const Math::Vector3d &point, float rate, const Math::Matrix4 &matrix) { if (_joint1Node) { float step = g_grim->getPerSecond(rate); float yawStep = step; float pitchStep = step / 3.f; if (!entering) { //animate yaw if (_headYaw > yawStep) { _headYaw -= yawStep; } else if (_headYaw < -yawStep) { _headYaw += yawStep; } else { _headYaw = 0; } //animate pitch if (_headPitch > pitchStep) { _headPitch -= pitchStep; } else if (_headPitch < -pitchStep) { _headPitch += pitchStep; } else { _headPitch = 0; } _joint1Node->_animYaw = _headYaw; Math::Angle pi = _headPitch / 3.f; _joint1Node->_animPitch += pi; _joint2Node->_animPitch += pi; _joint3Node->_animPitch += pi; _joint1Node->_animRoll = (_joint1Node->_animYaw.getDegrees() / 20.f) * _headPitch.getDegrees() / -5.f; if (_joint1Node->_animRoll > _maxRoll) _joint1Node->_animRoll = _maxRoll; if (_joint1Node->_animRoll < -_maxRoll) _joint1Node->_animRoll = -_maxRoll; return; } ModelNode *p = _joint3Node; while (p->_parent) { p = p->_parent; } p->setMatrix(matrix); p->update(); Math::Vector3d v = point - _joint3Node->_matrix.getPosition(); if (v.isZero()) { return; } float magnitude = sqrt(v.x() * v.x() + v.y() * v.y()); float a = v.x() / magnitude; float b = v.y() / magnitude; float yaw; yaw = acos(a) * (180.0f / LOCAL_PI); if (b < 0.0f) yaw = 360.0f - yaw; Math::Angle bodyYaw = matrix.getYaw(); p = _joint1Node->_parent; while (p) { bodyYaw += p->_yaw + p->_animYaw; p = p->_parent; } _joint1Node->_animYaw = (- 90 + yaw - bodyYaw); if (_joint1Node->_animYaw < -180.) { _joint1Node->_animYaw += 360; } if (_joint1Node->_animYaw > 180.) { _joint1Node->_animYaw -= 360; } if (_joint1Node->_animYaw > _maxYaw) _joint1Node->_animYaw = _maxYaw; if (_joint1Node->_animYaw < -_maxYaw) _joint1Node->_animYaw = -_maxYaw; float sqLenght = v.x() * v.x() + v.y() * v.y(); float h; if (sqLenght > 0) { h = sqrt(sqLenght); } else { h = -sqrt(sqLenght); } magnitude = sqrt(v.z() * v.z() + h * h); a = h / magnitude; b = v.z() / magnitude; Math::Angle pitch; pitch = acos(a) * (180.0f / LOCAL_PI); if (b < 0.0f) pitch = 360.0f - pitch; if (pitch > 180) pitch -= 360; if (pitch > _maxPitch) pitch = _maxPitch; if (pitch < -_maxPitch) pitch = -_maxPitch; if ((_joint1Node->_animYaw > 0 && pitch < 0) || (_joint1Node->_animYaw < 0 && pitch > 0)) { pitch += _joint1Node->_animYaw / 10.f; } else { pitch -= _joint1Node->_animYaw / 10.f; } //animate pitch if (pitch - _headPitch > pitchStep) pitch = _headPitch + pitchStep; if (_headPitch - pitch > pitchStep) pitch = _headPitch - pitchStep; Math::Angle pi = pitch / 3.f; _joint1Node->_animPitch += pi; _joint2Node->_animPitch += pi; _joint3Node->_animPitch += pi; //animate yaw if (_joint1Node->_animYaw - _headYaw > yawStep) _joint1Node->_animYaw = _headYaw + yawStep; if (_headYaw - _joint1Node->_animYaw > yawStep) _joint1Node->_animYaw = _headYaw - yawStep; _joint1Node->_animRoll = (_joint1Node->_animYaw.getDegrees() / 20.f) * pitch.getDegrees() / -5.f; if (_joint1Node->_animRoll > _maxRoll) _joint1Node->_animRoll = _maxRoll; if (_joint1Node->_animRoll < -_maxRoll) _joint1Node->_animRoll = -_maxRoll; _headPitch = pitch; _headYaw = _joint1Node->_animYaw; } }
void Actor::update(uint frameTime) { // Snap actor to walkboxes if following them. This might be // necessary for example after activating/deactivating // walkboxes, etc. if (_constrain && !_walking) { g_grim->getCurrSet()->findClosestSector(_pos, NULL, &_pos); } if (_turning) { float turnAmt = g_grim->getPerSecond(_turnRate) * 5.f; Math::Angle dyaw = _moveYaw - _yaw; dyaw.normalize(-180); // If the actor won't turn because the rate is set to zero then // have the actor turn all the way to the destination yaw. // Without this some actors will lock the interface on changing // scenes, this affects the Bone Wagon in particular. if (turnAmt == 0 || turnAmt >= fabsf(dyaw.getDegrees())) { setYaw(_moveYaw); _turning = false; } else if (dyaw > 0) { setYaw(_yaw + turnAmt); } else { setYaw(_yaw - turnAmt); } _currTurnDir = (dyaw > 0 ? 1 : -1); } if (_walking) { updateWalk(); } if (_walkChore.isValid()) { if (_walkedCur) { if (!_walkChore.isPlaying()) { _walkChore.playLooping(true); } if (_restChore.isPlaying()) { _restChore.stop(true); } } else { if (_walkedLast && _walkChore.isPlaying()) { _walkChore.stop(true); if (!_restChore.isPlaying()) { _restChore.playLooping(true); } } } } if (_leftTurnChore.isValid()) { if (_walkedCur || _walkedLast) _currTurnDir = 0; if (_restChore.isValid()) { if (_currTurnDir != 0) { if (getTurnChore(_currTurnDir)->isPlaying() && _restChore.isPlaying()) { _restChore.stop(true, 500); } } else if (_lastTurnDir != 0) { if (!_walkedCur && getTurnChore(_lastTurnDir)->isPlaying()) { _restChore.playLooping(true); } } } if (_lastTurnDir != 0 && _lastTurnDir != _currTurnDir) { getTurnChore(_lastTurnDir)->stop(true); } if (_currTurnDir != 0 && _currTurnDir != _lastTurnDir) { getTurnChore(_currTurnDir)->playLooping(true, 500); } } else { _currTurnDir = 0; } // The rest chore might have been stopped because of a // StopActorChore(nil). Restart it if so. if (!_walkedCur && _currTurnDir == 0 && !_restChore.isPlaying()) { _restChore.playLooping(true); } _walkedLast = _walkedCur; _walkedCur = false; _lastTurnDir = _currTurnDir; _currTurnDir = 0; // Update lip syncing if (_lipSync) { int posSound; // While getPosIn60HzTicks will return "-1" to indicate that the // sound is no longer playing, it is more appropriate to check first if (g_grim->getSpeechMode() != GrimEngine::TextOnly && g_sound->getSoundStatus(_talkSoundName.c_str())) posSound = g_sound->getPosIn60HzTicks(_talkSoundName.c_str()); else posSound = -1; if (posSound != -1) { int anim = _lipSync->getAnim(posSound); if (_talkAnim != anim) { if (anim != -1) { if (_talkChore[anim].isValid()) { stopMumbleChore(); if (_talkAnim != -1) { _talkChore[_talkAnim].stop(); } // Run the stop_talk chore so that it resets the components // to the right visibility. stopTalking(); _talkAnim = anim; _talkChore[_talkAnim].play(); } else if (_mumbleChore.isValid() && !_mumbleChore.isPlaying()) { _mumbleChore.playLooping(); _talkAnim = -1; } } else { stopMumbleChore(); if (_talkAnim != -1) _talkChore[_talkAnim].stop(); _talkAnim = 0; stopTalking(); } } } } frameTime = (uint)(frameTime * _timeScale); for (Common::List<Costume *>::iterator i = _costumeStack.begin(); i != _costumeStack.end(); ++i) { Costume *c = *i; c->setPosRotate(_pos, _pitch, _yaw, _roll); int marker = c->update(frameTime); if (marker > 0) { costumeMarkerCallback(marker); } } for (Common::List<Costume *>::iterator i = _costumeStack.begin(); i != _costumeStack.end(); ++i) { Costume *c = *i; c->animate(); } for (Common::List<Costume *>::iterator i = _costumeStack.begin(); i != _costumeStack.end(); ++i) { Costume *c = *i; c->moveHead(_lookingMode, _lookAtVector); } }