void Robot::lookAtBehind(const Math::Angle &angle) { float targetAngle; if (angle.rad() > 0.0f) targetAngle = angle.rad() - Math::PI; else targetAngle = angle.rad() + Math::PI; lookAt(Math::Rad(targetAngle)); }
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(); }
bool KeyframeAnim::KeyframeNode::animate(ModelNode &node, float frame, float fade, bool useDelta) const { if (_numEntries == 0) return false; // Do a binary search for the nearest previous frame // Loop invariant: entries_[low].frame_ <= frame < entries_[high].frame_ int low = 0, high = _numEntries; while (high > low + 1) { int mid = (low + high) / 2; if (_entries[mid]._frame <= frame) low = mid; else high = mid; } float dt = frame - _entries[low]._frame; Math::Vector3d pos = _entries[low]._pos; Math::Angle pitch = _entries[low]._pitch; Math::Angle yaw = _entries[low]._yaw; Math::Angle roll = _entries[low]._roll; /** @bug Interpolating between two orientations specified by Euler angles (yaw/pitch/roll) * by linearly interpolating the YPR values does not compute proper in-between * poses, i.e. the rotation from start to finish does not go via the shortest arc. * Though, if the start and end poses are very similar to each other, this can look * acceptable without visual artifacts. */ if (useDelta) { pos += dt * _entries[low]._dpos; pitch += dt * _entries[low]._dpitch; yaw += dt * _entries[low]._dyaw; roll += dt * _entries[low]._droll; } node._animPos += (pos - node._pos) * fade; Math::Angle dpitch = pitch - node._pitch; node._animPitch += dpitch.normalize(-180) * fade; Math::Angle dyaw = yaw - node._yaw; node._animYaw += dyaw.normalize(-180) * fade; Math::Angle droll = roll - node._roll; node._animRoll += droll.normalize(-180) * fade; return true; }
void Robot::lookAt(const Math::Angle &angle) { setTargetOmega(Math::limit(angle.rad() * Config::lookAtP, Math::degToRad(Config::lookAtMaxSpeedAngle) * Config::lookAtP)); }
void Robot::setTargetDir(const Math::Angle &dir, float speed, float omega) { Math::Vector dirVector = Math::Vector::createForwardVec(dir.rad(), speed); setTargetDir(dirVector.x, dirVector.y, omega); }
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); } }