diff --git a/benchmarks/planetarium_plot_methods.cpp b/benchmarks/planetarium_plot_methods.cpp index 8f6275b9cc..39574d8497 100644 --- a/benchmarks/planetarium_plot_methods.cpp +++ b/benchmarks/planetarium_plot_methods.cpp @@ -36,6 +36,8 @@ using ksp_plugin::Barycentric; using ksp_plugin::Navigation; using ksp_plugin::NavigationFrame; using ksp_plugin::Planetarium; +using ksp_plugin::ScaledSpacePoint; +using ksp_plugin::World; using physics::BodyCentredNonRotatingDynamicFrame; using physics::DegreesOfFreedom; using physics::DiscreteTrajectory; @@ -171,7 +173,13 @@ class Satellites { return Planetarium(parameters, perspective, ephemeris_.get(), - earth_centred_inertial_.get()); + earth_centred_inertial_.get(), + [](Position const& plotted_point) { + constexpr auto inverse_scale_factor = 1 / (6000 * Metre); + return ScaledSpacePoint::FromCoordinates( + ((plotted_point - Navigation::origin) * + inverse_scale_factor).coordinates()); + }); } private: diff --git a/journal/profiles.hpp b/journal/profiles.hpp index 6bca553708..9caa99d9e9 100644 --- a/journal/profiles.hpp +++ b/journal/profiles.hpp @@ -47,6 +47,7 @@ using ksp_plugin::PileUpFuture; using ksp_plugin::Planetarium; using ksp_plugin::Plugin; using ksp_plugin::Vessel; +using ksp_plugin::ScaledSpacePoint; namespace journal { diff --git a/ksp_plugin/interface.hpp b/ksp_plugin/interface.hpp index 92da81d73f..fd5696016e 100644 --- a/ksp_plugin/interface.hpp +++ b/ksp_plugin/interface.hpp @@ -56,6 +56,7 @@ using ksp_plugin::PileUp; using ksp_plugin::PileUpFuture; using ksp_plugin::Planetarium; using ksp_plugin::Plugin; +using ksp_plugin::ScaledSpacePoint; using ksp_plugin::Vessel; using ksp_plugin::World; using physics::DegreesOfFreedom; diff --git a/ksp_plugin/interface_planetarium.cpp b/ksp_plugin/interface_planetarium.cpp index cd96303852..0e62b61e4b 100644 --- a/ksp_plugin/interface_planetarium.cpp +++ b/ksp_plugin/interface_planetarium.cpp @@ -52,7 +52,9 @@ Planetarium* __cdecl principia__PlanetariumCreate( XYZ const xyz_opengl_camera_z_in_world, XYZ const xyz_camera_position_in_world, double const focal, - double const field_of_view) { + double const field_of_view, + double const inverse_scale_factor, + XYZ const scaled_space_origin) { journal::Method m({plugin, sun_world_position, xyz_opengl_camera_x_in_world, @@ -60,7 +62,9 @@ Planetarium* __cdecl principia__PlanetariumCreate( xyz_opengl_camera_z_in_world, xyz_camera_position_in_world, focal, - field_of_view}); + field_of_view, + inverse_scale_factor, + scaled_space_origin}); Renderer const& renderer = CHECK_NOTNULL(plugin)->renderer(); Multivector const opengl_camera_x_in_world( @@ -96,7 +100,20 @@ Planetarium* __cdecl principia__PlanetariumCreate( world_to_plotting_affine_map * camera_to_world_affine_map, focal * Metre); - return m.Return(plugin->NewPlanetarium(parameters, perspective).release()); + auto const plotting_to_scaled_space = + [plotting_to_world = world_to_plotting_affine_map.Inverse(), + scaled_space_origin = FromXYZ>(scaled_space_origin), + inverse_scale_factor = inverse_scale_factor * (1 / Metre)]( + Position const& plotted_point) { + return ScaledSpacePoint::FromCoordinates( + ((plotting_to_world(plotted_point) - scaled_space_origin) * + inverse_scale_factor).coordinates()); + }; + return m.Return( + plugin->NewPlanetarium( + parameters, + perspective, + plotting_to_scaled_space).release()); } void __cdecl principia__PlanetariumDelete( @@ -107,21 +124,27 @@ void __cdecl principia__PlanetariumDelete( return m.Return(); } -Iterator* __cdecl principia__PlanetariumPlotFlightPlanSegment( +// Fills the array of size |vertices_size| at |vertices| with vertices for the +// rendering of the segment with the given index in the flight plan of the +// vessel with the given GUID. +void __cdecl principia__PlanetariumPlotFlightPlanSegment( Planetarium const* const planetarium, Plugin const* const plugin, char const* const vessel_guid, - int const index) { - journal::Method m({planetarium, - plugin, - vessel_guid, - index}); + int const index, + ScaledSpacePoint* const vertices, + int const vertices_size, + int* const vertex_count) { + journal::Method m( + {planetarium, plugin, vessel_guid, index, vertices, vertices_size}, + {vertex_count}); CHECK_NOTNULL(plugin); CHECK_NOTNULL(planetarium); + *vertex_count = 0; + Vessel const& vessel = *plugin->GetVessel(vessel_guid); CHECK(vessel.has_flight_plan()) << vessel_guid; auto const segment = vessel.flight_plan().GetSegment(index); - RP2Lines rp2_lines; // TODO(egg): this is ugly; we should centralize rendering. // If this is a burn and we cannot render the beginning of the burn, we // render none of it, otherwise we try to render the Frenet trihedron at the @@ -129,46 +152,74 @@ Iterator* __cdecl principia__PlanetariumPlotFlightPlanSegment( if (index % 2 == 0 || segment->empty() || segment->front().time >= plugin->renderer().GetPlottingFrame()->t_min()) { - rp2_lines = planetarium->PlotMethod2(*segment, - segment->begin(), segment->end(), - plugin->CurrentTime(), - /*reverse=*/false); + planetarium->PlotMethod3( + *segment, segment->begin(), segment->end(), + plugin->CurrentTime(), + /*reverse=*/false, + [vertices, vertex_count](ScaledSpacePoint const& vertex) { + vertices[(*vertex_count)++] = vertex; + }, + vertices_size); } - return m.Return(new TypedIterator>(rp2_lines)); + return m.Return(); } -Iterator* __cdecl principia__PlanetariumPlotPrediction( +// Fills the array of size |vertices_size| at |vertices| with vertices for the +// rendered prediction of the vessel with the given GUID. +void __cdecl principia__PlanetariumPlotPrediction( Planetarium const* const planetarium, Plugin const* const plugin, - char const* const vessel_guid) { - journal::Method m({planetarium, - plugin, - vessel_guid}); + char const* const vessel_guid, + ScaledSpacePoint* const vertices, + int const vertices_size, + int* const vertex_count) { + journal::Method m( + {planetarium, plugin, vessel_guid, vertices, vertices_size}, + {vertex_count}); CHECK_NOTNULL(plugin); CHECK_NOTNULL(planetarium); + *vertex_count = 0; + auto const prediction = plugin->GetVessel(vessel_guid)->prediction(); - auto const rp2_lines = planetarium->PlotMethod2(*prediction, - prediction->begin(), - prediction->end(), - plugin->CurrentTime(), - /*reverse=*/false); - return m.Return(new TypedIterator>(rp2_lines)); + planetarium->PlotMethod3( + *prediction, prediction->begin(), prediction->end(), + plugin->CurrentTime(), + /*reverse=*/false, + [vertices, vertex_count](ScaledSpacePoint const& vertex) { + vertices[(*vertex_count)++] = vertex; + }, + vertices_size); + return m.Return(); } -Iterator* __cdecl principia__PlanetariumPlotPsychohistory( +// Fills the array of size |vertices_size| at |vertices| with vertices for the +// rendered past trajectory of the vessel with the given GUID; the +// trajectory goes back |max_history_length| seconds before the present time (or +// to the earliest time available if the relevant |t_min| is more recent). +void __cdecl principia__PlanetariumPlotPsychohistory( Planetarium const* const planetarium, Plugin const* const plugin, char const* const vessel_guid, - double const max_history_length) { + double const max_history_length, + ScaledSpacePoint* const vertices, + int const vertices_size, + int* const vertex_count) { journal::Method m( - {planetarium, plugin, vessel_guid, max_history_length}); + {planetarium, + plugin, + vessel_guid, + max_history_length, + vertices, + vertices_size}, + {vertex_count}); CHECK_NOTNULL(plugin); CHECK_NOTNULL(planetarium); + *vertex_count = 0; // Do not plot the psychohistory when there is a target vessel as it is // misleading. if (plugin->renderer().HasTargetVessel()) { - return m.Return(new TypedIterator>({})); + return m.Return(); } else { auto const vessel = plugin->GetVessel(vessel_guid); auto const& trajectory = vessel->trajectory(); @@ -182,42 +233,49 @@ Iterator* __cdecl principia__PlanetariumPlotPsychohistory( // time the history will be shorter than desired. vessel->RequestReanimation(desired_first_time); - auto const rp2_lines = - planetarium->PlotMethod2( - trajectory, - trajectory.lower_bound(desired_first_time), - psychohistory->end(), - plugin->CurrentTime(), - /*reverse=*/true); - return m.Return(new TypedIterator>(rp2_lines)); + planetarium->PlotMethod3( + trajectory, + trajectory.lower_bound(desired_first_time), + psychohistory->end(), + /*now=*/plugin->CurrentTime(), + /*reverse=*/true, + [vertices, vertex_count](ScaledSpacePoint const& vertex) { + vertices[(*vertex_count)++] = vertex; + }, + vertices_size); + return m.Return(); } } -// Returns an iterator for the rendered past trajectory of the celestial with -// the given index; the trajectory goes back |max_history_length| seconds before -// the present time (or to the earliest time available if the relevant |t_min| -// is more recent). -Iterator* __cdecl principia__PlanetariumPlotCelestialTrajectoryForPsychohistory( +// Fills the array of size |vertices_size| at |vertices| with vertices for the +// rendered past trajectory of the celestial with the given index; the +// trajectory goes back |max_history_length| seconds before the present time (or +// to the earliest time available if the relevant |t_min| is more recent). +void __cdecl principia__PlanetariumPlotCelestialPastTrajectory( Planetarium const* const planetarium, Plugin const* const plugin, int const celestial_index, - char const* const vessel_guid, double const max_history_length, - double* const minimal_distance_from_camera) { - journal::Method - m({planetarium, - plugin, - celestial_index, - vessel_guid, - max_history_length}, - {minimal_distance_from_camera}); + ScaledSpacePoint* const vertices, + int const vertices_size, + double* const minimal_distance_from_camera, + int* const vertex_count) { + journal::Method m( + {planetarium, + plugin, + celestial_index, + max_history_length, + vertices, + vertices_size}, + {minimal_distance_from_camera, vertex_count}); CHECK_NOTNULL(plugin); CHECK_NOTNULL(planetarium); + *vertex_count = 0; // Do not plot the past when there is a target vessel as it is misleading. if (plugin->renderer().HasTargetVessel()) { *minimal_distance_from_camera = std::numeric_limits::infinity(); - return m.Return(new TypedIterator>({})); + return m.Return(); } else { auto const& celestial_trajectory = plugin->GetCelestial(celestial_index).trajectory(); @@ -232,39 +290,52 @@ Iterator* __cdecl principia__PlanetariumPlotCelestialTrajectoryForPsychohistory( Instant const first_time = std::max(desired_first_time, celestial_trajectory.t_min()); Length minimal_distance; - auto const rp2_lines = - planetarium->PlotMethod2(celestial_trajectory, - first_time, - /*last_time=*/plugin->CurrentTime(), - /*now=*/plugin->CurrentTime(), - /*reverse=*/true, - &minimal_distance); + planetarium->PlotMethod3( + celestial_trajectory, + first_time, + /*last_time=*/plugin->CurrentTime(), + /*now=*/plugin->CurrentTime(), + /*reverse=*/true, + [vertices, vertex_count](ScaledSpacePoint const& vertex) { + vertices[(*vertex_count)++] = vertex; + }, + vertices_size, + &minimal_distance); *minimal_distance_from_camera = minimal_distance / Metre; - return m.Return(new TypedIterator>(rp2_lines)); + return m.Return(); } } -// Returns an iterator for the rendered future trajectory of the celestial with -// the given index; the trajectory goes as far as the furthest of the final time -// of the prediction or that of the flight plan. -Iterator* __cdecl -principia__PlanetariumPlotCelestialTrajectoryForPredictionOrFlightPlan( +// Fills the array of size |vertices_size| at |vertices| with vertices for the +// rendered future trajectory of the celestial with the given index; the +// trajectory goes as far as the furthest of the final time of the prediction or +// that of the flight plan. +void __cdecl principia__PlanetariumPlotCelestialFutureTrajectory( Planetarium const* const planetarium, Plugin const* const plugin, int const celestial_index, char const* const vessel_guid, - double* const minimal_distance_from_camera) { - journal::Method< - journal::PlanetariumPlotCelestialTrajectoryForPredictionOrFlightPlan> - m({planetarium, plugin, celestial_index, vessel_guid}, - {minimal_distance_from_camera}); + ScaledSpacePoint* const vertices, + int const vertices_size, + double* const minimal_distance_from_camera, + int* const vertex_count) { + journal::Method m( + {planetarium, + plugin, + celestial_index, + vessel_guid, + vertices, + vertices_size}, + {minimal_distance_from_camera, vertex_count}); CHECK_NOTNULL(plugin); CHECK_NOTNULL(planetarium); + *vertex_count = 0; // Do not plot the past when there is a target vessel as it is misleading. + // TODO(egg): This is the future, not the past! if (plugin->renderer().HasTargetVessel()) { *minimal_distance_from_camera = std::numeric_limits::infinity(); - return m.Return(new TypedIterator>({})); + return m.Return(); } else { auto const& vessel = *plugin->GetVessel(vessel_guid); Instant const prediction_final_time = vessel.prediction()->t_max(); @@ -278,15 +349,19 @@ principia__PlanetariumPlotCelestialTrajectoryForPredictionOrFlightPlan( // No need to request reanimation here because the current time of the // plugin is necessarily covered. Length minimal_distance; - auto const rp2_lines = - planetarium->PlotMethod2(celestial_trajectory, - /*first_time=*/plugin->CurrentTime(), - /*last_time=*/final_time, - /*now=*/plugin->CurrentTime(), - /*reverse=*/false, - &minimal_distance); + planetarium->PlotMethod3( + celestial_trajectory, + /*first_time=*/plugin->CurrentTime(), + /*last_time=*/final_time, + /*now=*/plugin->CurrentTime(), + /*reverse=*/false, + [vertices, vertex_count](ScaledSpacePoint const& vertex) { + vertices[(*vertex_count)++] = vertex; + }, + vertices_size, + &minimal_distance); *minimal_distance_from_camera = minimal_distance / Metre; - return m.Return(new TypedIterator>(rp2_lines)); + return m.Return(); } } diff --git a/ksp_plugin/planetarium.cpp b/ksp_plugin/planetarium.cpp index 2d0b026f38..44a73b3e45 100644 --- a/ksp_plugin/planetarium.cpp +++ b/ksp_plugin/planetarium.cpp @@ -15,6 +15,7 @@ namespace ksp_plugin { namespace internal_planetarium { using geometry::Position; +using geometry::R3Element; using geometry::RP2Line; using geometry::Sign; using geometry::Velocity; @@ -43,11 +44,13 @@ Planetarium::Planetarium( Parameters const& parameters, Perspective perspective, not_null const*> const ephemeris, - not_null const plotting_frame) + not_null const plotting_frame, + PlottingToScaledSpaceConversion plotting_to_scaled_space) : parameters_(parameters), perspective_(std::move(perspective)), ephemeris_(ephemeris), - plotting_frame_(plotting_frame) {} + plotting_frame_(plotting_frame), + plotting_to_scaled_space_(std::move(plotting_to_scaled_space)) {} RP2Lines Planetarium::PlotMethod0( DiscreteTrajectory const& trajectory, @@ -268,6 +271,119 @@ RP2Lines Planetarium::PlotMethod2( return lines; } +void Planetarium::PlotMethod3( + Trajectory const& trajectory, + DiscreteTrajectory::iterator begin, + DiscreteTrajectory::iterator end, + Instant const& now, + bool const reverse, + std::function const& add_point, + int max_points) const { + if (begin == end) { + return; + } + auto last = std::prev(end); + auto const begin_time = std::max(begin->time, plotting_frame_->t_min()); + auto const last_time = std::min(last->time, plotting_frame_->t_max()); + PlotMethod3( + trajectory, begin_time, last_time, now, reverse, add_point, max_points); +} + +void Planetarium::PlotMethod3( + Trajectory const& trajectory, + Instant const& first_time, + Instant const& last_time, + Instant const& now, + bool const reverse, + std::function const& add_point, + int const max_points, + Length* const minimal_distance) const { + double const tan²_angular_resolution = + Pow<2>(parameters_.tan_angular_resolution_); + auto const final_time = reverse ? first_time : last_time; + auto previous_time = reverse ? last_time : first_time; + + if (minimal_distance != nullptr) { + *minimal_distance = Infinity; + } + + Sign const direction = reverse ? Sign::Negative() : Sign::Positive(); + if (direction * (final_time - previous_time) <= Time{}) { + return; + } + RigidMotion to_plotting_frame_at_t = + plotting_frame_->ToThisFrameAtTime(previous_time); + DegreesOfFreedom const initial_degrees_of_freedom = + to_plotting_frame_at_t( + trajectory.EvaluateDegreesOfFreedom(previous_time)); + Position previous_position = + initial_degrees_of_freedom.position(); + Velocity previous_velocity = + initial_degrees_of_freedom.velocity(); + Time Δt = final_time - previous_time; + + add_point(plotting_to_scaled_space_(previous_position)); + int points_added = 1; + + Instant t; + double estimated_tan²_error; + std::optional> + degrees_of_freedom_in_barycentric; + Position position; + Square minimal_squared_distance = Infinity>; + + goto estimate_tan²_error; + + while (points_added < max_points && + direction * (previous_time - final_time) < Time{}) { + do { + // One square root because we have squared errors, another one because the + // errors are quadratic in time (in other words, two square roots because + // the squared errors are quartic in time). + // A safety factor prevents catastrophic retries. + Δt *= 0.9 * Sqrt(Sqrt(tan²_angular_resolution / estimated_tan²_error)); + estimate_tan²_error: + t = previous_time + Δt; + if (direction * (t - final_time) > Time{}) { + t = final_time; + Δt = t - previous_time; + } + Position const extrapolated_position = + previous_position + previous_velocity * Δt; + to_plotting_frame_at_t = plotting_frame_->ToThisFrameAtTime(t); + degrees_of_freedom_in_barycentric = + trajectory.EvaluateDegreesOfFreedom(t); + position = to_plotting_frame_at_t.rigid_transformation()( + degrees_of_freedom_in_barycentric->position()); + + // The quadratic term of the error between the linear interpolation and + // the actual function is maximized halfway through the segment, so it is + // 1/2 (Δt/2)² f″(t-Δt) = (1/2 Δt² f″(t-Δt)) / 4; the squared error is + // thus (1/2 Δt² f″(t-Δt))² / 16. + estimated_tan²_error = + perspective_.Tan²AngularDistance(extrapolated_position, position) / + 16; + } while (estimated_tan²_error > tan²_angular_resolution); + + previous_time = t; + previous_position = position; + previous_velocity = + to_plotting_frame_at_t(*degrees_of_freedom_in_barycentric).velocity(); + + add_point(plotting_to_scaled_space_(position)); + ++points_added; + + if (minimal_distance != nullptr) { + minimal_squared_distance = + std::min(minimal_squared_distance, + perspective_.SquaredDistanceFromCamera(position)); + } + } + if (minimal_distance != nullptr) { + *minimal_distance = Sqrt(minimal_squared_distance); + } +} + std::vector> Planetarium::ComputePlottableSpheres( Instant const& now) const { RigidMotion const rigid_motion_at_now = diff --git a/ksp_plugin/planetarium.hpp b/ksp_plugin/planetarium.hpp index 93868a0b54..57f067f2f7 100644 --- a/ksp_plugin/planetarium.hpp +++ b/ksp_plugin/planetarium.hpp @@ -25,6 +25,8 @@ using geometry::Displacement; using geometry::Instant; using geometry::OrthogonalMap; using geometry::Perspective; +using geometry::Position; +using geometry::R3Element; using geometry::RP2Lines; using geometry::RP2Point; using geometry::Segment; @@ -38,6 +40,20 @@ using physics::Trajectory; using quantities::Angle; using quantities::Length; +// Corresponds to a UnityEngine.Vector3 representing a position in KSP’s +// ScaledSpace. +extern "C" struct ScaledSpacePoint { + static inline ScaledSpacePoint FromCoordinates( + R3Element const& coordinates); + + float x; + float y; + float z; +}; + +static_assert(std::is_pod::value, + "NavigationFrameParameters is used for interfacing"); + // A planetarium is an ephemeris together with a perspective. In this setting // it is possible to draw trajectories in the projective plane. class Planetarium { @@ -60,12 +76,16 @@ class Planetarium { friend class Planetarium; }; + using PlottingToScaledSpaceConversion = + std::function const&)>; + // TODO(phl): All this Navigation is weird. Should it be named Plotting? // In particular Navigation vs. NavigationFrame is a mess. Planetarium(Parameters const& parameters, Perspective perspective, not_null const*> ephemeris, - not_null plotting_frame); + not_null plotting_frame, + PlottingToScaledSpaceConversion plotting_to_scaled_space); // A no-op method that just returns all the points in the trajectory defined // by |begin| and |end|. @@ -104,6 +124,28 @@ class Planetarium { bool reverse, Length* minimal_distance = nullptr) const; + // A method similar to PlotMethod2, but which produces a three-dimensional + // trajectory in scaled space instead of projecting and hiding. + void PlotMethod3( + Trajectory const& trajectory, + DiscreteTrajectory::iterator begin, + DiscreteTrajectory::iterator end, + Instant const& now, + bool reverse, + std::function const& add_point, + int max_points) const; + + // The same method, operating on the |Trajectory| interface. + void PlotMethod3( + Trajectory const& trajectory, + Instant const& first_time, + Instant const& last_time, + Instant const& now, + bool reverse, + std::function const& add_point, + int max_points, + Length* minimal_distance = nullptr) const; + private: // Computes the coordinates of the spheres that represent the |ephemeris_| // bodies. These coordinates are in the |plotting_frame_| at time |now|. @@ -118,14 +160,23 @@ class Planetarium { DiscreteTrajectory::iterator end) const; Parameters const parameters_; + PlottingToScaledSpaceConversion plotting_to_scaled_space_; Perspective const perspective_; not_null const*> const ephemeris_; not_null const plotting_frame_; }; +inline ScaledSpacePoint ScaledSpacePoint::FromCoordinates( + R3Element const& coordinates) { + return ScaledSpacePoint{static_cast(coordinates.x), + static_cast(coordinates.y), + static_cast(coordinates.z)}; +} + } // namespace internal_planetarium using internal_planetarium::Planetarium; +using internal_planetarium::ScaledSpacePoint; } // namespace ksp_plugin } // namespace principia diff --git a/ksp_plugin/plugin.cpp b/ksp_plugin/plugin.cpp index af98e05a06..33c36a7e35 100644 --- a/ksp_plugin/plugin.cpp +++ b/ksp_plugin/plugin.cpp @@ -1085,12 +1085,15 @@ void Plugin::ClearOrbitAnalysersOfVesselsOtherThan(Vessel const& vessel) { not_null> Plugin::NewPlanetarium( Planetarium::Parameters const& parameters, - Perspective const& perspective) + Perspective const& perspective, + std::function const&)> + plotting_to_scaled_space) const { return make_not_null_unique(parameters, perspective, ephemeris_.get(), - renderer_->GetPlottingFrame()); + renderer_->GetPlottingFrame(), + std::move(plotting_to_scaled_space)); } not_null> diff --git a/ksp_plugin/plugin.hpp b/ksp_plugin/plugin.hpp index 9b0a08a8e7..055c89c499 100644 --- a/ksp_plugin/plugin.hpp +++ b/ksp_plugin/plugin.hpp @@ -390,7 +390,9 @@ class Plugin { virtual not_null> NewPlanetarium( Planetarium::Parameters const& parameters, - Perspective const& perspective) const; + Perspective const& perspective, + std::function const&)> + plotting_to_scaled_space) const; virtual not_null> NewBarycentricRotatingNavigationFrame(Index primary_index, diff --git a/ksp_plugin_adapter/gl_lines.cs b/ksp_plugin_adapter/gl_lines.cs index 88323600d5..f392d62b2f 100644 --- a/ksp_plugin_adapter/gl_lines.cs +++ b/ksp_plugin_adapter/gl_lines.cs @@ -66,67 +66,17 @@ public static DisposablePlanetarium NewPlanetarium(IntPtr plugin, double m11 = camera.projectionMatrix[1, 1]; double field_of_view = Math.Atan2(Math.Sqrt(m00 * m00 + m11 * m11), m00 * m11); - return plugin.PlanetariumCreate(sun_world_position, - (XYZ)(Vector3d)opengl_camera_x_in_world, - (XYZ)(Vector3d)opengl_camera_y_in_world, - (XYZ)(Vector3d)opengl_camera_z_in_world, - (XYZ)(Vector3d)camera_position_in_world, - focal:1, - field_of_view); - } - - public static void PlotRP2Lines(DisposableIterator rp2_lines_iterator, - UnityEngine.Color colour, - Style style) { - UnityEngine.GL.Color(colour); - - // First evaluate the total size of the lines. - int size = 0; - for (; - !rp2_lines_iterator.IteratorAtEnd(); - rp2_lines_iterator.IteratorIncrement()) { - using (DisposableIterator rp2_line_iterator = - rp2_lines_iterator.IteratorGetRP2LinesIterator()) { - size += rp2_line_iterator.IteratorSize(); - } - } - - // Reset the iterator and do the actual plotting. - rp2_lines_iterator.IteratorReset(); - int index = 0; - for (; - !rp2_lines_iterator.IteratorAtEnd(); - rp2_lines_iterator.IteratorIncrement()) { - using (DisposableIterator rp2_line_iterator = - rp2_lines_iterator.IteratorGetRP2LinesIterator()) { - XY? previous_rp2_point = null; - for (; - !rp2_line_iterator.IteratorAtEnd(); - rp2_line_iterator.IteratorIncrement()) { - XY current_rp2_point = ToScreen( - rp2_line_iterator.IteratorGetRP2LineXY()); - if (previous_rp2_point.HasValue) { - if (style == Style.Faded) { - var faded_colour = colour; - // Fade from the opacity of |colour| (when index = 0) down to 1/4 - // of that opacity. - faded_colour.a *= 1 - (float)(4 * index) / (float)(5 * size); - UnityEngine.GL.Color(faded_colour); - } - if (style != Style.Dashed || index % 2 == 1) { - UnityEngine.GL.Vertex3((float)previous_rp2_point.Value.x, - (float)previous_rp2_point.Value.y, - 0); - UnityEngine.GL.Vertex3((float)current_rp2_point.x, - (float)current_rp2_point.y, - 0); - } - } - previous_rp2_point = current_rp2_point; - ++index; - } - } - } + return plugin.PlanetariumCreate( + sun_world_position, + (XYZ)(Vector3d)opengl_camera_x_in_world, + (XYZ)(Vector3d)opengl_camera_y_in_world, + (XYZ)(Vector3d)opengl_camera_z_in_world, + (XYZ)(Vector3d)camera_position_in_world, + focal:1, + field_of_view, + ScaledSpace.InverseScaleFactor, + scaled_space_origin: (XYZ)ScaledSpace.ScaledToLocalSpace( + Vector3d.zero)); } private static UnityEngine.Vector3 WorldToMapScreen(Vector3d world) { @@ -134,21 +84,9 @@ private static UnityEngine.Vector3 WorldToMapScreen(Vector3d world) { ScaledSpace.LocalToScaledSpace(world)); } - private static XY ToScreen(XY rp2_point) { - UnityEngine.Camera camera = PlanetariumCamera.Camera; - return new XY{ - x = (rp2_point.x * camera.projectionMatrix[0, 0] + 1.0) * - 0.5 * - camera.pixelWidth, - y = (rp2_point.y * camera.projectionMatrix[1, 1] + 1.0) * - 0.5 * - camera.pixelHeight - }; - } - private static UnityEngine.Material line_material_; - private static UnityEngine.Material line_material { + public static UnityEngine.Material line_material { get { if (line_material_ == null) { line_material_ = new UnityEngine.Material( diff --git a/ksp_plugin_adapter/ksp_plugin_adapter.cs b/ksp_plugin_adapter/ksp_plugin_adapter.cs index 9cc6207b43..948e1d7ea6 100644 --- a/ksp_plugin_adapter/ksp_plugin_adapter.cs +++ b/ksp_plugin_adapter/ksp_plugin_adapter.cs @@ -18,7 +18,7 @@ public partial class PrincipiaPluginAdapter : ScenarioModule, // From https://forum.kerbalspaceprogram.com/index.php?/topic/84273--/, // edited 2017-03-09. Where the name of the layer is not CamelCase, the // actual name is commented. - private enum UnityLayers { + public enum UnityLayers { TransparentFX = 1, IgnoreRaycast = 2, // Ignore Raycast Water = 3, @@ -109,18 +109,21 @@ internal IntPtr Plugin() { private bool navball_changed_ = true; private FlightGlobals.SpeedDisplayModes? previous_display_mode_; - private UnityEngine.Color history_colour = XKCDColors.Lime; - private GLLines.Style history_style = GLLines.Style.Faded; - private UnityEngine.Color prediction_colour = XKCDColors.Fuchsia; - private GLLines.Style prediction_style = GLLines.Style.Solid; - private UnityEngine.Color flight_plan_colour = XKCDColors.PeriwinkleBlue; - private GLLines.Style flight_plan_style = GLLines.Style.Dashed; - private UnityEngine.Color burn_colour = XKCDColors.Pink; - private GLLines.Style burn_style = GLLines.Style.Solid; - private UnityEngine.Color target_history_colour = XKCDColors.Goldenrod; - private GLLines.Style target_history_style = GLLines.Style.Faded; - private UnityEngine.Color target_prediction_colour = XKCDColors.LightMauve; - private GLLines.Style target_prediction_style = GLLines.Style.Solid; + // TODO(egg): move all of that to the plotter. + internal UnityEngine.Color history_colour = XKCDColors.Lime; + internal GLLines.Style history_style = GLLines.Style.Faded; + internal UnityEngine.Color prediction_colour = XKCDColors.Fuchsia; + internal GLLines.Style prediction_style = GLLines.Style.Solid; + internal UnityEngine.Color flight_plan_colour = XKCDColors.PeriwinkleBlue; + internal GLLines.Style flight_plan_style = GLLines.Style.Dashed; + internal UnityEngine.Color burn_colour = XKCDColors.Pink; + internal GLLines.Style burn_style = GLLines.Style.Solid; + internal UnityEngine.Color target_history_colour = XKCDColors.Goldenrod; + internal GLLines.Style target_history_style = GLLines.Style.Faded; + internal UnityEngine.Color target_prediction_colour = XKCDColors.LightMauve; + internal GLLines.Style target_prediction_style = GLLines.Style.Solid; + + private Plotter plotter_; private readonly List vessel_futures_ = new List(); @@ -262,9 +265,9 @@ private readonly Dictionary [KSPField(isPersistant = true)] private readonly OrbitAnalyser orbit_analyser_; [KSPField(isPersistant = true)] - internal ReferenceFrameSelector plotting_frame_selector_; + internal readonly ReferenceFrameSelector plotting_frame_selector_; [KSPField(isPersistant = true)] - internal MainWindow main_window_; + private MainWindow main_window_; public event Action LockClearing; public event Action WindowsDisposal; @@ -331,6 +334,7 @@ private readonly Dictionary plotting_frame_selector_ = new ReferenceFrameSelector(this, UpdateRenderingFrame, L10N.CacheFormat("#Principia_PlottingFrame")); + plotter_ = new Plotter(this); main_window_ = new MainWindow(this, flight_planner_, orbit_analyser_, @@ -870,7 +874,8 @@ private void LateUpdate() { if (map_renderer_ == null) { map_renderer_ = PlanetariumCamera.Camera.gameObject. AddComponent(); - map_renderer_.post_render = RenderTrajectories; + map_renderer_.pre_cull = () => { RenderTrajectories(); }; + map_renderer_.post_render = () => { RenderManœuvreMarkers(); }; } if (galaxy_cube_rotator_ == null) { @@ -2121,57 +2126,24 @@ private void RenderTrajectories() { XYZ sun_world_position = (XYZ)Planetarium.fetch.Sun.position; using (DisposablePlanetarium planetarium = GLLines.NewPlanetarium(plugin_, sun_world_position)) { - GLLines.Draw(() => { - PlotCelestialTrajectories(planetarium, main_vessel_guid); + plotter_.PlotTrajectories(planetarium, main_vessel_guid, main_window_.history_length); + } + } + } - // Vessel trajectories. + private void RenderManœuvreMarkers() { + if (!PluginRunning()) { + return; + } + string main_vessel_guid = PredictedVessel()?.id.ToString(); + if (MapView.MapIsEnabled) { + XYZ sun_world_position = (XYZ)Planetarium.fetch.Sun.position; + using (DisposablePlanetarium planetarium = + GLLines.NewPlanetarium(plugin_, sun_world_position)) { + GLLines.Draw(() => { if (main_vessel_guid == null) { return; } - // Main vessel psychohistory and prediction. - using (DisposableIterator rp2_lines_iterator = - planetarium.PlanetariumPlotPsychohistory( - plugin_, - main_vessel_guid, - main_window_.history_length)) { - GLLines.PlotRP2Lines(rp2_lines_iterator, - history_colour, - history_style); - } - using (DisposableIterator rp2_lines_iterator = - planetarium.PlanetariumPlotPrediction( - plugin_, - main_vessel_guid)) { - GLLines.PlotRP2Lines(rp2_lines_iterator, - prediction_colour, - prediction_style); - } - // Target psychohistory and prediction. - string target_id = FlightGlobals.fetch.VesselTarget?.GetVessel()?.id. - ToString(); - if (FlightGlobals.ActiveVessel != null && - !plotting_frame_selector_.target_frame_selected && - target_id != null && - plugin_.HasVessel(target_id)) { - using (DisposableIterator rp2_lines_iterator = - planetarium.PlanetariumPlotPsychohistory( - plugin_, - target_id, - main_window_.history_length)) { - GLLines.PlotRP2Lines(rp2_lines_iterator, - target_history_colour, - target_history_style); - } - using (DisposableIterator rp2_lines_iterator = - planetarium.PlanetariumPlotPrediction( - plugin_, - target_id)) { - GLLines.PlotRP2Lines(rp2_lines_iterator, - target_prediction_colour, - target_prediction_style); - } - } - // Main vessel flight plan. if (plugin_.FlightPlanExists(main_vessel_guid)) { int number_of_anomalous_manœuvres = plugin_.FlightPlanNumberOfAnomalousManoeuvres(main_vessel_guid); @@ -2191,19 +2163,6 @@ private void RenderTrajectories() { } Vector3d position_at_start = (Vector3d)rendered_segments. IteratorGetDiscreteTrajectoryXYZ(); - using (DisposableIterator rp2_lines_iterator = - planetarium.PlanetariumPlotFlightPlanSegment( - plugin_, - main_vessel_guid, - i)) { - GLLines.PlotRP2Lines(rp2_lines_iterator, - is_burn - ? burn_colour - : flight_plan_colour, - is_burn - ? burn_style - : flight_plan_style); - } if (is_burn) { int manœuvre_index = i / 2; if (manœuvre_index < @@ -2237,70 +2196,6 @@ private void RenderTrajectories() { } } - private void PlotCelestialTrajectories(DisposablePlanetarium planetarium, - string main_vessel_guid) { - const double degree = Math.PI / 180; - UnityEngine.Camera camera = PlanetariumCamera.Camera; - float vertical_fov = camera.fieldOfView; - float horizontal_fov = - UnityEngine.Camera.VerticalToHorizontalFieldOfView( - vertical_fov, camera.aspect); - // The angle subtended by the pixel closest to the centre of the viewport. - double tan_angular_resolution = Math.Min( - Math.Tan(vertical_fov * degree / 2) / (camera.pixelHeight / 2), - Math.Tan(horizontal_fov * degree / 2) / (camera.pixelWidth / 2)); - PlotSubtreeTrajectories(planetarium, main_vessel_guid, - Planetarium.fetch.Sun, - tan_angular_resolution); - } - - private void PlotSubtreeTrajectories(DisposablePlanetarium planetarium, - string main_vessel_guid, - CelestialBody root, - double tan_angular_resolution) { - var colour = root.orbitDriver?.Renderer?.orbitColor ?? - XKCDColors.SunshineYellow; - var camera_world_position = ScaledSpace.ScaledToLocalSpace( - PlanetariumCamera.fetch.transform.position); - double min_distance_from_camera = - (root.position - camera_world_position).magnitude; - if (!plotting_frame_selector_.FixesBody(root)) { - using (DisposableIterator rp2_lines_iterator = - planetarium.PlanetariumPlotCelestialTrajectoryForPsychohistory( - plugin_, - root.flightGlobalsIndex, - main_vessel_guid, - main_window_.history_length, - out double min_past_distance)) { - min_distance_from_camera = - Math.Min(min_distance_from_camera, min_past_distance); - GLLines.PlotRP2Lines(rp2_lines_iterator, colour, GLLines.Style.Faded); - } - if (main_vessel_guid != null) { - using (DisposableIterator rp2_lines_iterator = - planetarium. - PlanetariumPlotCelestialTrajectoryForPredictionOrFlightPlan( - plugin_, - root.flightGlobalsIndex, - main_vessel_guid, - out double min_future_distance)) { - min_distance_from_camera = - Math.Min(min_distance_from_camera, min_future_distance); - GLLines.PlotRP2Lines(rp2_lines_iterator, colour, GLLines.Style.Solid); - } - } - } - foreach (CelestialBody child in root.orbitingBodies) { - // Plot the trajectory of an orbiting body if it could be separated from - // that of its parent by a pixel of empty space, instead of merely making - // the line wider. - if (child.orbit.ApR / min_distance_from_camera > 2 * tan_angular_resolution) { - PlotSubtreeTrajectories(planetarium, main_vessel_guid, child, - tan_angular_resolution); - } - } - } - private void RenderPredictionMarkers(string vessel_guid, XYZ sun_world_position) { if (plotting_frame_selector_.target_frame_selected) { diff --git a/ksp_plugin_adapter/ksp_plugin_adapter.csproj b/ksp_plugin_adapter/ksp_plugin_adapter.csproj index 223c5df529..260e113fe5 100644 --- a/ksp_plugin_adapter/ksp_plugin_adapter.csproj +++ b/ksp_plugin_adapter/ksp_plugin_adapter.csproj @@ -20,7 +20,7 @@ TRACE;DEBUG;KSP_VERSION_1_12_2 prompt 4 - false + true false v4.7.2 @@ -32,7 +32,7 @@ prompt 4 true - false + true false v4.7.2 @@ -44,7 +44,7 @@ prompt 4 true - false + true false v3.5 @@ -131,6 +131,7 @@ + diff --git a/ksp_plugin_adapter/plotter.cs b/ksp_plugin_adapter/plotter.cs new file mode 100644 index 0000000000..08b5e1a21e --- /dev/null +++ b/ksp_plugin_adapter/plotter.cs @@ -0,0 +1,264 @@ +using System; +using System.Collections.Generic; +using System.Runtime.InteropServices; + +namespace principia { +namespace ksp_plugin_adapter { + +class Plotter { + public Plotter(PrincipiaPluginAdapter adapter) { + adapter_ = adapter; + } + + public void PlotTrajectories(DisposablePlanetarium planetarium, + string main_vessel_guid, + double history_length) { + PlotCelestialTrajectories(planetarium, main_vessel_guid, history_length); + if (main_vessel_guid == null) { + return; + } + PlotVesselTrajectories(planetarium, main_vessel_guid, history_length); + } + + private void PlotVesselTrajectories(DisposablePlanetarium planetarium, + string main_vessel_guid, + double history_length) { + { + planetarium.PlanetariumPlotPsychohistory( + Plugin, + main_vessel_guid, + history_length, + VertexBuffer.data, + VertexBuffer.size, + out int vertex_count); + DrawLineMesh(psychohistory_mesh_, vertex_count, adapter_.history_colour, + adapter_.history_style); + } + { + planetarium.PlanetariumPlotPrediction( + Plugin, + main_vessel_guid, + VertexBuffer.data, + VertexBuffer.size, + out int vertex_count); + DrawLineMesh(prediction_mesh_, vertex_count, adapter_.prediction_colour, + adapter_.prediction_style); + } + + // Target psychohistory and prediction. + string target_id = FlightGlobals.fetch.VesselTarget?.GetVessel()?.id. + ToString(); + if (FlightGlobals.ActiveVessel != null && + !adapter_.plotting_frame_selector_.target_frame_selected && + target_id != null && + Plugin.HasVessel(target_id)) { + { + planetarium.PlanetariumPlotPsychohistory( + Plugin, + target_id, + history_length, + VertexBuffer.data, + VertexBuffer.size, + out int vertex_count); + DrawLineMesh(target_psychohistory_mesh_, vertex_count, + adapter_.target_history_colour, + adapter_.target_history_style); + } + { + planetarium.PlanetariumPlotPrediction( + Plugin, + target_id, + VertexBuffer.data, + VertexBuffer.size, + out int vertex_count); + DrawLineMesh(target_prediction_mesh_, vertex_count, + adapter_.target_prediction_colour, + adapter_.target_prediction_style); + } + } + + // Main vessel flight plan. + if (Plugin.FlightPlanExists(main_vessel_guid)) { + int number_of_segments = + Plugin.FlightPlanNumberOfSegments(main_vessel_guid); + for (int i = flight_plan_segment_meshes_.Count; i < number_of_segments; ++i) { + flight_plan_segment_meshes_.Add(MakeDynamicMesh()); + } + for (int i = 0; i < number_of_segments; ++i) { + bool is_burn = i % 2 == 1; + planetarium.PlanetariumPlotFlightPlanSegment( + Plugin, + main_vessel_guid, + i, + VertexBuffer.data, + VertexBuffer.size, + out int vertex_count); + DrawLineMesh(flight_plan_segment_meshes_[i], + vertex_count, + is_burn ? adapter_.burn_colour + : adapter_.flight_plan_colour, + is_burn ? adapter_.burn_style + : adapter_.flight_plan_style); + } + } + } + + private void PlotCelestialTrajectories(DisposablePlanetarium planetarium, + string main_vessel_guid, + double history_length) { + const double degree = Math.PI / 180; + UnityEngine.Camera camera = PlanetariumCamera.Camera; + float vertical_fov = camera.fieldOfView; + float horizontal_fov = + UnityEngine.Camera.VerticalToHorizontalFieldOfView( + vertical_fov, camera.aspect); + // The angle subtended by the pixel closest to the centre of the viewport. + double tan_angular_resolution = Math.Min( + Math.Tan(vertical_fov * degree / 2) / (camera.pixelHeight / 2), + Math.Tan(horizontal_fov * degree / 2) / (camera.pixelWidth / 2)); + PlotSubtreeTrajectories(planetarium, main_vessel_guid, history_length, + Planetarium.fetch.Sun, tan_angular_resolution); + } + + // Plots the trajectories of |root| and its natural satellites. + private void PlotSubtreeTrajectories(DisposablePlanetarium planetarium, + string main_vessel_guid, + double history_length, + CelestialBody root, + double tan_angular_resolution) { + CelestialTrajectories trajectories; + if (!celestial_trajectory_meshes_.TryGetValue(root, out trajectories)) { + trajectories = celestial_trajectory_meshes_[root] = + new CelestialTrajectories(); + } + var colour = root.orbitDriver?.Renderer?.orbitColor ?? + XKCDColors.SunshineYellow; + var camera_world_position = ScaledSpace.ScaledToLocalSpace( + PlanetariumCamera.fetch.transform.position); + double min_distance_from_camera = + (root.position - camera_world_position).magnitude; + if (!adapter_.plotting_frame_selector_.FixesBody(root)) { + { + planetarium.PlanetariumPlotCelestialPastTrajectory( + Plugin, + root.flightGlobalsIndex, + history_length, + VertexBuffer.data, + VertexBuffer.size, + out double min_past_distance, + out int vertex_count); + min_distance_from_camera = + Math.Min(min_distance_from_camera, min_past_distance); + DrawLineMesh(trajectories.past, vertex_count, colour, + GLLines.Style.Faded); + } + + if (main_vessel_guid != null) { + planetarium.PlanetariumPlotCelestialFutureTrajectory( + Plugin, + root.flightGlobalsIndex, + main_vessel_guid, + VertexBuffer.data, + VertexBuffer.size, + out double min_future_distance, + out int vertex_count); + min_distance_from_camera = + Math.Min(min_distance_from_camera, min_future_distance); + DrawLineMesh(trajectories.future, vertex_count, colour, + GLLines.Style.Solid); + } + } + foreach (CelestialBody child in root.orbitingBodies) { + // Plot the trajectory of an orbiting body if it could be separated from + // that of its parent by a pixel of empty space, instead of merely making + // the line wider. + if (child.orbit.ApR / min_distance_from_camera > + 2 * tan_angular_resolution) { + PlotSubtreeTrajectories(planetarium, main_vessel_guid, history_length, + child, tan_angular_resolution); + } + } + } + + private void DrawLineMesh(UnityEngine.Mesh mesh, + int vertex_count, + UnityEngine.Color colour, + GLLines.Style style) { + mesh.vertices = VertexBuffer.vertices; + var indices = new int[vertex_count]; + for (int i = 0; i < vertex_count; ++i) { + indices[i] = i; + } + var colours = new UnityEngine.Color[VertexBuffer.size]; + if (style == GLLines.Style.Faded) { + for (int i = 0; i < vertex_count; ++i) { + var faded_colour = colour; + // Fade from the opacity of |colour| (when i = 0) down to 20% of that + // opacity. + faded_colour.a *= 1 - 0.8f * (i / (float)vertex_count); + colours[i] = faded_colour; + } + } else { + for (int i = 0; i < vertex_count; ++i) { + colours[i] = colour; + } + } + mesh.colors = colours; + mesh.SetIndices( + indices, + style == GLLines.Style.Dashed ? UnityEngine.MeshTopology.Lines + : UnityEngine.MeshTopology.LineStrip, + submesh: 0); + mesh.RecalculateBounds(); + // If the lines are drawn in layer 31 (Vectors), which sounds more + // appropriate, they vanish when zoomed out. Layer 9 works; pay no + // attention to its name. + UnityEngine.Graphics.DrawMesh( + mesh, + UnityEngine.Vector3.zero, + UnityEngine.Quaternion.identity, + GLLines.line_material, + (int)PrincipiaPluginAdapter.UnityLayers.Atmosphere, + PlanetariumCamera.Camera); + } + + private static UnityEngine.Mesh MakeDynamicMesh() { + var result = new UnityEngine.Mesh(); + result.MarkDynamic(); + return result; + } + + private readonly PrincipiaPluginAdapter adapter_; + + private IntPtr Plugin => adapter_.Plugin(); + + private static class VertexBuffer { + public static IntPtr data => handle_.AddrOfPinnedObject(); + public static int size => vertices_.Length; + + public static UnityEngine.Vector3[] vertices => vertices_; + + private static UnityEngine.Vector3[] vertices_ = + new UnityEngine.Vector3[10_000]; + private static GCHandle handle_ = + GCHandle.Alloc(vertices_, GCHandleType.Pinned); + } + + private class CelestialTrajectories { + public UnityEngine.Mesh future = MakeDynamicMesh(); + public UnityEngine.Mesh past = MakeDynamicMesh(); + } + + private Dictionary + celestial_trajectory_meshes_ = + new Dictionary(); + private UnityEngine.Mesh psychohistory_mesh_ = MakeDynamicMesh(); + private UnityEngine.Mesh prediction_mesh_ = MakeDynamicMesh(); + private List flight_plan_segment_meshes_ = + new List(); + private UnityEngine.Mesh target_psychohistory_mesh_ = MakeDynamicMesh(); + private UnityEngine.Mesh target_prediction_mesh_ = MakeDynamicMesh(); +} + +} // namespace ksp_plugin_adapter +} // namespace principia diff --git a/ksp_plugin_test/interface_planetarium_test.cpp b/ksp_plugin_test/interface_planetarium_test.cpp index c1d05b46b5..56ae1c168f 100644 --- a/ksp_plugin_test/interface_planetarium_test.cpp +++ b/ksp_plugin_test/interface_planetarium_test.cpp @@ -62,7 +62,7 @@ TEST_F(InterfacePlanetariumTest, ConstructionDestruction) { Permutation( Permutation::CoordinatePermutation::YXZ) .Forget()))); - EXPECT_CALL(*plugin_, NewPlanetarium(_, _)) + EXPECT_CALL(*plugin_, NewPlanetarium(_, _, _)) .WillOnce(Return(ByMove(std::make_unique()))); Planetarium const* planetarium = principia__PlanetariumCreate(plugin_.get(), @@ -72,7 +72,9 @@ TEST_F(InterfacePlanetariumTest, ConstructionDestruction) { {0, 0, 1}, {1, 2, 3}, 10, - 90); + 90, + 1.0 / 6000, + {4, 5, 6}); principia__PlanetariumDelete(&planetarium); EXPECT_THAT(planetarium, IsNull()); } diff --git a/ksp_plugin_test/mock_planetarium.hpp b/ksp_plugin_test/mock_planetarium.hpp index 1332f97af8..9f6710f26f 100644 --- a/ksp_plugin_test/mock_planetarium.hpp +++ b/ksp_plugin_test/mock_planetarium.hpp @@ -30,7 +30,13 @@ class MockPlanetarium : public Planetarium { .Forget()), 1 * Metre), make_not_null const*>(), - make_not_null()) {} + make_not_null(), + [](Position const& plotted_point) { + constexpr auto inverse_scale_factor = 1 / (6000 * Metre); + return ScaledSpacePoint::FromCoordinates( + ((plotted_point - Navigation::origin) * + inverse_scale_factor).coordinates()); + }) {} }; } // namespace internal_planetarium diff --git a/ksp_plugin_test/mock_plugin.hpp b/ksp_plugin_test/mock_plugin.hpp index b7d088ce1a..ddea647ea1 100644 --- a/ksp_plugin_test/mock_plugin.hpp +++ b/ksp_plugin_test/mock_plugin.hpp @@ -100,7 +100,9 @@ class MockPlugin : public Plugin { MOCK_METHOD(not_null>, NewPlanetarium, (Planetarium::Parameters const& parameters, - (Perspective const& perspective)), + (Perspective const& perspective), + std::function const&)> + plotting_to_scaled_space), (const, override)); MOCK_METHOD(not_null>, NewBodyCentredNonRotatingNavigationFrame, diff --git a/ksp_plugin_test/planetarium_test.cpp b/ksp_plugin_test/planetarium_test.cpp index e2efc01ac5..ef1999a710 100644 --- a/ksp_plugin_test/planetarium_test.cpp +++ b/ksp_plugin_test/planetarium_test.cpp @@ -49,6 +49,7 @@ using geometry::Instant; using geometry::LinearMap; using geometry::OrthogonalMap; using geometry::Perspective; +using geometry::Position; using geometry::RigidTransformation; using geometry::Rotation; using geometry::Sign; @@ -107,6 +108,13 @@ class PlanetariumTest : public ::testing::Test { Sign::Positive(), DeduceSignReversingOrientation{}).Forget()), /*focal=*/5 * Metre), + plotting_to_scaled_space_( + [](Position const& plotted_point) { + constexpr auto inverse_scale_factor = 1 / (6000 * Metre); + return ScaledSpacePoint::FromCoordinates( + ((plotted_point - Navigation::origin) * + inverse_scale_factor).coordinates()); + }), // A body of radius 1 m located at the origin. body_(MassiveBody::Parameters(1 * Kilogram), RotatingBody::Parameters( @@ -134,6 +142,8 @@ class PlanetariumTest : public ::testing::Test { Instant const t0_; Perspective const perspective_; MockDynamicFrame plotting_frame_; + std::function const&)> + plotting_to_scaled_space_; RotatingBody const body_; std::vector> const bodies_; MockContinuousTrajectory continuous_trajectory_; @@ -155,8 +165,11 @@ TEST_F(PlanetariumTest, PlotMethod0) { /*sphere_radius_multiplier=*/1, /*angular_resolution=*/0 * Degree, /*field_of_view=*/90 * Degree); - Planetarium planetarium( - parameters, perspective_, &ephemeris_, &plotting_frame_); + Planetarium planetarium(parameters, + perspective_, + &ephemeris_, + &plotting_frame_, + plotting_to_scaled_space_); auto const rp2_lines = planetarium.PlotMethod0(discrete_trajectory, discrete_trajectory.begin(), @@ -201,8 +214,11 @@ TEST_F(PlanetariumTest, PlotMethod1) { /*sphere_radius_multiplier=*/1, /*angular_resolution=*/0.4 * ArcMinute, /*field_of_view=*/90 * Degree); - Planetarium planetarium( - parameters, perspective_, &ephemeris_, &plotting_frame_); + Planetarium planetarium(parameters, + perspective_, + &ephemeris_, + &plotting_frame_, + plotting_to_scaled_space_); auto const rp2_lines = planetarium.PlotMethod1(discrete_trajectory, discrete_trajectory.begin(), @@ -237,8 +253,11 @@ TEST_F(PlanetariumTest, PlotMethod2) { /*sphere_radius_multiplier=*/1, /*angular_resolution=*/0.4 * ArcMinute, /*field_of_view=*/90 * Degree); - Planetarium planetarium( - parameters, perspective_, &ephemeris_, &plotting_frame_); + Planetarium planetarium(parameters, + perspective_, + &ephemeris_, + &plotting_frame_, + plotting_to_scaled_space_); auto const rp2_lines = planetarium.PlotMethod2(discrete_trajectory, discrete_trajectory.begin(), @@ -293,7 +312,8 @@ TEST_F(PlanetariumTest, RealSolarSystem) { Perspective(rigid_transformation, /*focal=*/1 * Metre), ephemeris.get(), - plotting_frame.get()); + plotting_frame.get(), + plotting_to_scaled_space_); auto const rp2_lines = planetarium.PlotMethod2(discrete_trajectory, discrete_trajectory.begin(), diff --git a/serialization/journal.proto b/serialization/journal.proto index 99f9ce20df..b681717209 100644 --- a/serialization/journal.proto +++ b/serialization/journal.proto @@ -1781,6 +1781,8 @@ message PlanetariumCreate { required XYZ xyz_camera_position_in_world = 6; required double focal = 7; required double field_of_view = 8; + required double inverse_scale_factor = 9; + required XYZ scaled_space_origin = 10; } message Return { required fixed64 result = 1 [(pointer_to) = "Planetarium", @@ -1806,9 +1808,9 @@ message PlanetariumDelete { optional Out out = 2; } -message PlanetariumPlotCelestialTrajectoryForPsychohistory { +message PlanetariumPlotCelestialFutureTrajectory { extend Method { - optional PlanetariumPlotCelestialTrajectoryForPsychohistory extension = 5161; + optional PlanetariumPlotCelestialFutureTrajectory extension = 5161; } message In { required fixed64 planetarium = 1 [(pointer_to) = "Planetarium const", @@ -1816,25 +1818,21 @@ message PlanetariumPlotCelestialTrajectoryForPsychohistory { (is_subject) = true]; required fixed64 plugin = 2 [(pointer_to) = "Plugin const"]; required int32 celestial_index = 3; - optional string vessel_guid = 4; - required double max_history_length = 5; + required string vessel_guid = 4; + required fixed64 vertices = 5 [(pointer_to) = "ScaledSpacePoint"]; + required int32 vertices_size = 6; } message Out { required double minimal_distance_from_camera = 1; - } - message Return { - required fixed64 rp2_lines = 1 [(pointer_to) = "Iterator", - (disposable) = "DisposableIterator", - (is_produced) = true]; + required int32 vertex_count = 2; } optional In in = 1; optional Out out = 2; - optional Return return = 3; } -message PlanetariumPlotCelestialTrajectoryForPredictionOrFlightPlan { +message PlanetariumPlotCelestialPastTrajectory { extend Method { - optional PlanetariumPlotCelestialTrajectoryForPredictionOrFlightPlan extension = 5162; + optional PlanetariumPlotCelestialPastTrajectory extension = 5162; } message In { required fixed64 planetarium = 1 [(pointer_to) = "Planetarium const", @@ -1842,19 +1840,16 @@ message PlanetariumPlotCelestialTrajectoryForPredictionOrFlightPlan { (is_subject) = true]; required fixed64 plugin = 2 [(pointer_to) = "Plugin const"]; required int32 celestial_index = 3; - required string vessel_guid = 4; + required double max_history_length = 4; + required fixed64 vertices = 5 [(pointer_to) = "ScaledSpacePoint"]; + required int32 vertices_size = 6; } message Out { required double minimal_distance_from_camera = 1; - } - message Return { - required fixed64 rp2_lines = 1 [(pointer_to) = "Iterator", - (disposable) = "DisposableIterator", - (is_produced) = true]; + required int32 vertex_count = 2; } optional In in = 1; optional Out out = 2; - optional Return return = 3; } message PlanetariumPlotFlightPlanSegment { @@ -1868,14 +1863,14 @@ message PlanetariumPlotFlightPlanSegment { required fixed64 plugin = 2 [(pointer_to) = "Plugin const"]; required string vessel_guid = 4; required int32 index = 5; + required fixed64 vertices = 6 [(pointer_to) = "ScaledSpacePoint"]; + required int32 vertices_size = 7; } - message Return { - required fixed64 rp2_lines = 1 [(pointer_to) = "Iterator", - (disposable) = "DisposableIterator", - (is_produced) = true]; + message Out { + required int32 vertex_count = 1; } optional In in = 1; - optional Return return = 3; + optional Out out = 2; } message PlanetariumPlotPrediction { @@ -1888,14 +1883,14 @@ message PlanetariumPlotPrediction { (is_subject) = true]; required fixed64 plugin = 2 [(pointer_to) = "Plugin const"]; required string vessel_guid = 4; + required fixed64 vertices = 5 [(pointer_to) = "ScaledSpacePoint"]; + required int32 vertices_size = 6; } - message Return { - required fixed64 rp2_lines = 1 [(pointer_to) = "Iterator", - (disposable) = "DisposableIterator", - (is_produced) = true]; + message Out { + required int32 vertex_count = 1; } optional In in = 1; - optional Return return = 3; + optional Out out = 2; } message PlanetariumPlotPsychohistory { @@ -1909,14 +1904,14 @@ message PlanetariumPlotPsychohistory { required fixed64 plugin = 2 [(pointer_to) = "Plugin const"]; required string vessel_guid = 4; required double max_history_length = 5; + required fixed64 vertices = 6 [(pointer_to) = "ScaledSpacePoint"]; + required int32 vertices_size = 7; } - message Return { - required fixed64 rp2_lines = 1 [(pointer_to) = "Iterator", - (disposable) = "DisposableIterator", - (is_produced) = true]; + message Out { + required int32 vertex_count = 1; } optional In in = 1; - optional Return return = 3; + optional Out out = 2; } message PrepareToReportCollisions {