diff --git a/src/DynamicBody.cpp b/src/DynamicBody.cpp index 47901f582..b03f2e0c0 100644 --- a/src/DynamicBody.cpp +++ b/src/DynamicBody.cpp @@ -371,7 +371,12 @@ bool DynamicBody::OnCollision(Body *o, Uint32 flags, double relVel) // return parameters for orbit of any body, gives both elliptic and hyperbolic trajectories Orbit DynamicBody::ComputeOrbit() const { - FrameId nrFrameId = Frame::GetFrame(GetFrame())->GetNonRotFrame(); + auto f = Frame::GetFrame(GetFrame()); + // if we are in a rotating frame, then dynamic body currently under the + // influence of a rotational frame, therefore getting the orbital parameters + // is not appropriate, return the orbit as a fixed point + if (f->IsRotFrame()) return Orbit::ForStaticBody(GetPosition()); + FrameId nrFrameId = f->GetId(); const Frame *nrFrame = Frame::GetFrame(nrFrameId); const double mass = nrFrame->GetSystemBody()->GetMass(); diff --git a/src/Orbit.cpp b/src/Orbit.cpp index 6e1308262..128eb1608 100644 --- a/src/Orbit.cpp +++ b/src/Orbit.cpp @@ -166,6 +166,7 @@ double Orbit::MeanAnomalyAtTime(double time) const vector3d Orbit::OrbitalPosAtTime(double t) const { + if (is_zero_general(m_semiMajorAxis)) return m_positionForStaticBody; double cos_v, sin_v, r; calc_position_from_mean_anomaly(MeanAnomalyAtTime(t), m_eccentricity, m_semiMajorAxis, cos_v, sin_v, &r); return m_orient * vector3d(-cos_v * r, sin_v * r, 0); @@ -298,31 +299,44 @@ void Orbit::SetShapeAroundPrimary(double semiMajorAxis, double centralMass, doub m_velocityAreaPerSecond = calc_velocity_area_per_sec(semiMajorAxis, centralMass, eccentricity); } -Orbit Orbit::FromBodyState(const vector3d &pos, const vector3d &vel, double centralMass) +Orbit Orbit::ForStaticBody(const vector3d &position) { Orbit ret; + // just remember the current position of the body, and we will return it, for any t + ret.m_positionForStaticBody = position; + return ret; +} - const double r_now = pos.Length() + 1e-12; - const double v_now = vel.Length() + 1e-12; +Orbit Orbit::FromBodyState(const vector3d &pos, const vector3d &vel_raw, double centralMass) +{ + Orbit ret; // standard gravitational parameter const double u = centralMass * G; + // maybe we will adjust the speed a little now + vector3d vel = vel_raw; // angular momentum - const vector3d ang = pos.Cross(vel); + vector3d ang = pos.Cross(vel); + // quite a rare case - the speed is directed strictly to the star or away from the star + // let's make a small disturbance to the velocity, so as not to calculate the radial orbit + if (is_zero_general(ang.LengthSqr()) && !is_zero_general(centralMass)) { + if (is_zero_general(pos.x) && is_zero_general(pos.y)) // even rarer case, the body lies strictly on the z-axis + vel.x += 0.001; + else + vel.z += 0.001; + ang = pos.Cross(vel); // recalculate angular momentum + } + + const double r_now = pos.Length(); + const double LLSqr = ang.LengthSqr(); // total energy const double EE = vel.LengthSqr() / 2.0 - u / r_now; - if (is_zero_general(centralMass) || is_zero_general(r_now) || is_zero_general(v_now) || is_zero_general(EE) || (ang.z * ang.z / LLSqr > 1.0)) { - ret.m_eccentricity = 0.0; - ret.m_semiMajorAxis = 0.0; - ret.m_velocityAreaPerSecond = 0.0; - ret.m_orbitalPhaseAtStart = 0.0; - ret.m_orient = matrix3x3d::Identity(); - return ret; - } + if (is_zero_general(centralMass) || is_zero_general(EE) || (ang.z * ang.z / LLSqr > 1.0)) + return Orbit::ForStaticBody(pos); // http://en.wikipedia.org/wiki/Orbital_eccentricity ret.m_eccentricity = 1 + 2 * EE * LLSqr / (u * u); diff --git a/src/Orbit.h b/src/Orbit.h index 1a7ef7221..160c50b73 100644 --- a/src/Orbit.h +++ b/src/Orbit.h @@ -16,6 +16,7 @@ public: // note: the resulting Orbit is at the given position at t=0 static Orbit FromBodyState(const vector3d &position, const vector3d &velocity, double central_mass); + static Orbit ForStaticBody(const vector3d &position); Orbit() : m_eccentricity(0.0), @@ -58,6 +59,7 @@ private: double MeanAnomalyFromTrueAnomaly(double trueAnomaly) const; double MeanAnomalyAtTime(double time) const; + vector3d m_positionForStaticBody; double m_eccentricity; double m_semiMajorAxis; double m_orbitalPhaseAtStart; // 0 to 2 pi radians diff --git a/src/SystemView.cpp b/src/SystemView.cpp index eb46bd26b..1a7556fd1 100644 --- a/src/SystemView.cpp +++ b/src/SystemView.cpp @@ -107,6 +107,7 @@ void SystemView::ResetViewpoint() m_zoomTo = 1.0f / float(AU); m_timeStep = 1.0f; m_time = m_game->GetTime(); + m_transTo *= 0.0; m_animateTransition = MAX_TRANSITION_FRAMES; } @@ -263,6 +264,11 @@ void SystemView::GetTransformTo(const SystemBody *b, vector3d &pos) void SystemView::GetTransformTo(Projectable &p, vector3d &pos) { + if (m_selectedObject.type == Projectable::NONE) { + // notning selected + pos *= 0.0; + return; + } // accept only real objects (no orbit icons or lagrange points) assert(p.type == Projectable::OBJECT); pos = vector3d(0., 0., 0.); @@ -285,16 +291,15 @@ void SystemView::CalculateShipPositionAtTime(const Ship *s, Orbit o, double t, v pos = vector3d(0., 0., 0.); FrameId shipFrameId = s->GetFrame(); FrameId shipNonRotFrameId = Frame::GetFrame(shipFrameId)->GetNonRotFrame(); - if (s->GetFlightState() != Ship::FlightState::FLYING) { + // if the ship is in a rotating frame, we will rotate it with the frame + if (Frame::GetFrame(shipFrameId)->IsRotFrame()) { vector3d rpos(0.0); - if (Frame::GetFrame(shipFrameId)->IsRotFrame()) { - Frame *rotframe = Frame::GetFrame(shipFrameId); - if (t == m_game->GetTime()) { - pos = s->GetPositionRelTo(m_game->GetSpace()->GetRootFrame()); - return; - } else - rpos = s->GetPositionRelTo(shipNonRotFrameId) * rotframe->GetOrient() * matrix3x3d::RotateY(rotframe->GetAngSpeed() * (t - m_game->GetTime())) * rotframe->GetOrient().Transpose(); - } + Frame *rotframe = Frame::GetFrame(shipFrameId); + if (t == m_game->GetTime()) { + pos = s->GetPositionRelTo(m_game->GetSpace()->GetRootFrame()); + return; + } else + rpos = s->GetPositionRelTo(shipNonRotFrameId) * rotframe->GetOrient() * matrix3x3d::RotateY(rotframe->GetAngSpeed() * (t - m_game->GetTime())) * rotframe->GetOrient().Transpose(); vector3d fpos(0.0); CalculateFramePositionAtTime(shipNonRotFrameId, t, fpos); pos += fpos + rpos; @@ -388,16 +393,21 @@ void SystemView::Draw3D() m_renderer->SetTransform(m_cameraSpace); // smooth transition animation - m_transTo *= 0.0; - if (m_selectedObject.type != Projectable::NONE) GetTransformTo(m_selectedObject, m_transTo); if (m_animateTransition) { + // since the object being approached can move, we need to compensate for its movement + // making an imprint of the old value (from previous frame) + m_trans -= m_transTo; + // calculate the new value + GetTransformTo(m_selectedObject, m_transTo); + // now the difference between the new and the old value is added to m_trans + m_trans += m_transTo; const float ft = Pi::GetFrameTime(); m_animateTransition--; AnimationCurves::Approach(m_trans.x, m_transTo.x, ft); AnimationCurves::Approach(m_trans.y, m_transTo.y, ft); AnimationCurves::Approach(m_trans.z, m_transTo.z, ft); } else { - m_trans = m_transTo; + GetTransformTo(m_selectedObject, m_trans); } vector3d pos = m_trans; @@ -537,7 +547,7 @@ void SystemView::DrawShips(const double t, const vector3d &offset) AddProjected(Projectable::OBJECT, Projectable::SHIP, static_cast((*s).first), pos); if (m_shipDrawing == ORBITS && (*s).first->GetFlightState() == Ship::FlightState::FLYING) { vector3d framepos(0.0); - CalculateFramePositionAtTime(Frame::GetFrame((*s).first->GetFrame())->GetNonRotFrame(), m_time, framepos); + CalculateFramePositionAtTime((*s).first->GetFrame(), m_time, framepos); PutOrbit(Projectable::SHIP, static_cast((*s).first), &(*s).second, offset + framepos, isSelected ? svColor[SELECTED_SHIP_ORBIT] : svColor[SHIP_ORBIT], 0); } } @@ -649,6 +659,10 @@ void SystemView::SetSelectedObject(Projectable::types type, Projectable::bases b m_selectedObject.type = type; m_selectedObject.base = base; m_selectedObject.ref.sbody = sb; + // we will immediately determine the coordinates of the selected body so that + // there is a correct starting point of the transition animation, otherwise + // there may be an unwanted shift in the next frame + GetTransformTo(m_selectedObject, m_transTo); m_animateTransition = MAX_TRANSITION_FRAMES; } @@ -657,6 +671,10 @@ void SystemView::SetSelectedObject(Projectable::types type, Projectable::bases b m_selectedObject.type = type; m_selectedObject.base = base; m_selectedObject.ref.body = b; + // we will immediately determine the coordinates of the selected body so that + // there is a correct starting point of the transition animation, otherwise + // there may be an unwanted shift in the next frame + GetTransformTo(m_selectedObject, m_transTo); m_animateTransition = MAX_TRANSITION_FRAMES; } diff --git a/src/lua/LuaSystemView.cpp b/src/lua/LuaSystemView.cpp index a4d1ab610..366a1558e 100644 --- a/src/lua/LuaSystemView.cpp +++ b/src/lua/LuaSystemView.cpp @@ -218,7 +218,7 @@ static int l_systemview_get_projected_grouped(lua_State *l) touchedGroups.push_back(&group); //now select the nearest group (if have) if (touchedGroups.size()) { - GroupInfo *nearest; + GroupInfo *nearest = nullptr; double min_length = 1e64; for (GroupInfo *&g : touchedGroups) { double this_length = (g->m_mainObject.screenpos - special_object[object_type]->screenpos).Length();