// Input: direction in ship's frame, doesn't need to be normalized // Approximate positive angular velocity at match point // Applies thrust directly // old: returns whether it can reach that direction in this frame // returns angle to target double Ship::AIFaceDirection(const vector3d &dir, double av) { double maxAccel = m_type->angThrust / GetAngularInertia(); // should probably be in stats anyway vector3d head = (dir * GetOrient()).Normalized(); // create desired object-space heading vector3d dav(0.0, 0.0, 0.0); // desired angular velocity double ang = 0.0; if (head.z > -0.99999999) { ang = acos (Clamp(-head.z, -1.0, 1.0)); // scalar angle from head to curhead double iangvel = av + calc_ivel_pos(ang, 0.0, maxAccel); // ideal angvel at current time // Normalize (head.x, head.y) to give desired angvel direction if (head.z > 0.999999) head.x = 1.0; double head2dnorm = 1.0 / sqrt(head.x*head.x + head.y*head.y); // NAN fix shouldn't be necessary if inputs are normalized dav.x = head.y * head2dnorm * iangvel; dav.y = -head.x * head2dnorm * iangvel; } vector3d cav = GetAngVelocity() * GetOrient(); // current obj-rel angvel double frameAccel = maxAccel * Pi::game->GetTimeStep(); vector3d diff = (dav - cav) / frameAccel; // find diff between current & desired angvel // If the player is pressing a roll key, don't override roll. // XXX this really shouldn't be here. a better way would be to have a // field in Ship describing the wanted angvel adjustment from input. the // baseclass version in Ship would always be 0. the version in Player // would be constructed from user input. that adjustment could then be // considered by this method when computing the required change if (IsType(Object::PLAYER) && (KeyBindings::rollLeft.IsActive() || KeyBindings::rollRight.IsActive())) diff.z = m_angThrusters.z; SetAngThrusterState(diff); return ang; }
double Ship::AIFaceOrient(const vector3d &dir, const vector3d &updir) { double timeStep = Pi::GetTimeStep(); matrix4x4d rot; GetRotMatrix(rot); double maxAccel = GetShipType().angThrust / GetAngularInertia(); // should probably be in stats anyway double frameAccel = maxAccel * timeStep; if (dir.Dot(vector3d(rot[8], rot[9], rot[10])) > -0.999999) { AIFaceDirection(dir); return false; } vector3d uphead = (updir * rot).Normalized(); // create desired object-space updir vector3d dav(0.0, 0.0, 0.0); // desired angular velocity double ang = 0.0; if (uphead.y < 0.999999) { ang = acos(Clamp(uphead.y, -1.0, 1.0)); // scalar angle from head to curhead double iangvel = sqrt(2.0 * maxAccel * ang); // ideal angvel at current time double frameEndAV = iangvel - frameAccel; if (frameEndAV <= 0.0) iangvel = ang / timeStep; // last frame discrete correction else iangvel = (iangvel + frameEndAV) * 0.5; // discrete overshoot correction dav.z = -iangvel; } vector3d cav = (GetAngVelocity() - GetFrame()->GetAngVelocity()) * rot; // current obj-rel angvel // vector3d cav = GetAngVelocity() * rot; // current obj-rel angvel vector3d diff = (dav - cav) / frameAccel; // find diff between current & desired angvel SetAngThrusterState(diff); return ang; // if (diff.x*diff.x > 1.0 || diff.y*diff.y > 1.0 || diff.z*diff.z > 1.0) return false; // else return true; }
// Input in object space void Ship::AIMatchAngVelObjSpace(const vector3d &angvel) { double maxAccel = m_type->angThrust / GetAngularInertia(); double invFrameAccel = 1.0 / (maxAccel * Pi::game->GetTimeStep()); vector3d diff = angvel - GetAngVelocity() * GetOrient(); // find diff between current & desired angvel SetAngThrusterState(diff * invFrameAccel); }
// Input in object space void Ship::AIMatchAngVelObjSpace(const vector3d &angvel) { double maxAccel = GetShipType().angThrust / GetAngularInertia(); double invFrameAccel = 1.0 / (maxAccel * Pi::GetTimeStep()); matrix4x4d rot; GetRotMatrix(rot); vector3d diff = angvel - GetAngVelocity() * rot; // find diff between current & desired angvel SetAngThrusterState(diff * invFrameAccel); }
void Ship::AIModelCoordsMatchAngVel(vector3d desiredAngVel, double softness) { double angAccel = m_type->angThrust / GetAngularInertia(); const double softTimeStep = Pi::game->GetTimeStep() * softness; vector3d angVel = desiredAngVel - GetAngVelocity() * GetOrient(); vector3d thrust; for (int axis=0; axis<3; axis++) { if (angAccel * softTimeStep >= fabs(angVel[axis])) { thrust[axis] = angVel[axis] / (softTimeStep * angAccel); } else { thrust[axis] = (angVel[axis] > 0.0 ? 1.0 : -1.0); } } SetAngThrusterState(thrust); }
void Ship::AIModelCoordsMatchAngVel(vector3d desiredAngVel, double softness) { const ShipType &stype = GetShipType(); double angAccel = stype.angThrust / GetAngularInertia(); const double softTimeStep = Pi::GetTimeStep() * softness; matrix4x4d rot; GetRotMatrix(rot); vector3d angVel = desiredAngVel - rot.InverseOf() * GetAngVelocity(); vector3d thrust; for (int axis=0; axis<3; axis++) { if (angAccel * softTimeStep >= fabs(angVel[axis])) { thrust[axis] = angVel[axis] / (softTimeStep * angAccel); } else { thrust[axis] = (angVel[axis] > 0.0 ? 1.0 : -1.0); } } SetAngThrusterState(thrust); }
// Input: direction in ship's frame, doesn't need to be normalized // Approximate positive angular velocity at match point // Applies thrust directly // old: returns whether it can reach that direction in this frame // returns angle to target double Ship::AIFaceDirection(const vector3d &dir, double av) { double timeStep = Pi::GetTimeStep(); double maxAccel = GetShipType().angThrust / GetAngularInertia(); // should probably be in stats anyway if (maxAccel <= 0.0) // happens if no angular thrust is set for the model eg MISSILE_UNGUIDED return 0.0; double frameAccel = maxAccel * timeStep; matrix4x4d rot; GetRotMatrix(rot); vector3d head = (dir * rot).Normalized(); // create desired object-space heading vector3d dav(0.0, 0.0, 0.0); // desired angular velocity double ang = 0.0; if (head.z > -0.99999999) { ang = acos (Clamp(-head.z, -1.0, 1.0)); // scalar angle from head to curhead double iangvel = av + sqrt (2.0 * maxAccel * ang); // ideal angvel at current time double frameEndAV = iangvel - frameAccel; if (frameEndAV <= 0.0) iangvel = ang / timeStep; // last frame discrete correction else iangvel = (iangvel + frameEndAV) * 0.5; // discrete overshoot correction // Normalize (head.x, head.y) to give desired angvel direction if (head.z > 0.9999) head.x = 1.0; double head2dnorm = 1.0 / sqrt(head.x*head.x + head.y*head.y); // NAN fix shouldn't be necessary if inputs are normalized dav.x = head.y * head2dnorm * iangvel; dav.y = -head.x * head2dnorm * iangvel; } vector3d cav = (GetAngVelocity() - GetFrame()->GetAngVelocity()) * rot; // current obj-rel angvel // vector3d cav = GetAngVelocity() * rot; // current obj-rel angvel vector3d diff = (dav - cav) / frameAccel; // find diff between current & desired angvel SetAngThrusterState(diff); return ang; //if (diff.x*diff.x > 1.0 || diff.y*diff.y > 1.0 || diff.z*diff.z > 1.0) return false; // else return true; }
// get updir as close as possible just using roll thrusters double Ship::AIFaceUpdir(const vector3d &updir, double av) { double maxAccel = m_type->angThrust / GetAngularInertia(); // should probably be in stats anyway double frameAccel = maxAccel * Pi::game->GetTimeStep(); vector3d uphead = updir * GetOrient(); // create desired object-space updir if (uphead.z > 0.99999) return 0; // bail out if facing updir uphead.z = 0; uphead = uphead.Normalized(); // only care about roll axis double ang = 0.0, dav = 0.0; if (uphead.y < 0.99999999) { ang = acos(Clamp(uphead.y, -1.0, 1.0)); // scalar angle from head to curhead double iangvel = av + calc_ivel_pos(ang, 0.0, maxAccel); // ideal angvel at current time dav = uphead.x > 0 ? -iangvel : iangvel; } double cav = (GetAngVelocity() * GetOrient()).z; // current obj-rel angvel double diff = (dav - cav) / frameAccel; // find diff between current & desired angvel SetAngThrusterState(2, diff); return ang; }