private void UpdateParallel( double t, ImagingParallel parallel, Vector3d vesselInSurfaceFrame, Vector3d swathNormal, ImagingProduct[] products, out bool parallelVisibleAtRelevantResolution) { parallelVisibleAtRelevantResolution = false; for (int x = parallel.x_begin; x != parallel.x_end; ++x) { SunSurfaceSatelliteGeometry geometry = products[0].pushbroom ? new SunSurfaceSatelliteGeometry( vesselInSurfaceFrame, swathNormal, parallel.surface[x], parallel.sun[x]) : new SunSurfaceSatelliteGeometry( vesselInSurfaceFrame, parallel.surface[x], parallel.sun[x]); if (geometry.surfaceSatelliteGeometry.Visible) { foreach (var product in products) { bool sunlit = parallel.sun[x].cosSolarZenithAngle > cos75degrees; bool night = parallel.sun[x].cosSolarZenithAngle < cos105degrees; if (product.band == OpticalBand.VisNIR && !sunlit && !night) { continue; } double resolution = product.HorizontalResolution(geometry.surfaceSatelliteGeometry); for (int i = 0; i < resolutionThresholds.Length; ++i) { if (product.HorizontalResolution(geometry.surfaceSatelliteGeometry) < resolutionThresholds[i]) { parallelVisibleAtRelevantResolution = true; for (; i < resolutionThresholds.Length; ++i) { switch (product.band) { case OpticalBand.MidInfrared: parallel.midInfrared[x].lastImagingTime[i] = Math.Max(parallel.midInfrared[x].lastImagingTime[i], t); break; case OpticalBand.VisNIR: if (sunlit) { if (geometry.cosGlintAngle > cos15degrees) { parallel.glintedReflectiveVisNIR[x].lastImagingTime[i] = Math.Max(parallel.glintedReflectiveVisNIR[x].lastImagingTime[i], t); } else { parallel.unglintedReflectiveVisNIR[x].lastImagingTime[i] = Math.Max(parallel.unglintedReflectiveVisNIR[x].lastImagingTime[i], t); } } else if (night) { parallel.nightVisNIR[x].lastImagingTime[i] = Math.Max(parallel.nightVisNIR[x].lastImagingTime[i], t); } break; } } } } } } } }
public void Update() { timeSpentInIllumination = TimeSpan.Zero; timeSpentInTextureUpdate = TimeSpan.Zero; DateTime start = DateTime.UtcNow; CelestialBody kerbin = FlightGlobals.GetHomeBody(); if (reset) { reset = false; kerbin_imaging = null; lastUpdateUT = null; window.width = 0; window.height = 0; return; } if (pause) { return; } if (kerbin_imaging == null) { UnityEngine.Debug.LogWarning("Rebuilding map..."); x_size = small ? 256 : 512; y_size = small ? 128 : 256; kerbin_imaging = new ImagingParallel[y_size]; minimap = new UnityEngine.Texture2D(x_size, y_size); for (int y = 0; y < y_size; ++y) { // Mollweide on [-2, 2] × [-1, 1]. double y_normalized = 2.0 * y / y_size - 1; double θ = Math.Asin(y_normalized); double sinφ = (2 * θ + Math.Sin(2 * θ)) / Math.PI; double φ = Math.Asin(sinφ); double φ_deg = φ * (180 / Math.PI); var parallel = kerbin_imaging[y] = new ImagingParallel { cosLatitude = Math.Cos(φ), sinLatitude = sinφ, map = new MapPoint[x_size], midInfrared = new ImagingStatus[x_size], nightVisNIR = new ImagingStatus[x_size], unglintedReflectiveVisNIR = new ImagingStatus[x_size], glintedReflectiveVisNIR = new ImagingStatus[x_size], sun = new SunSurfaceGeometry[x_size], surface = new SurfacePoint[x_size] }; bool entered_map = false; parallel.x_end = x_size; for (int x = 0; x < x_size; ++x) { double x_normalized = 4.0 * x / x_size - 2; double λ = Math.PI * x_normalized / (2 * Math.Cos(θ)); double λ_deg = λ * (180 / Math.PI); if (double.IsNaN(φ) || double.IsNaN(λ) || Math.Abs(λ) > Math.PI) { parallel.map[x].on_map = false; if (entered_map && parallel.x_end == x_size) { parallel.x_end = x; } } else { parallel.map[x].on_map = true; if (!entered_map) { parallel.x_begin = x; } entered_map = true; double altitude = kerbin.TerrainAltitude(φ_deg, λ_deg); parallel.surface[x] = new SurfacePoint( kerbin.scaledBody.transform.rotation.Inverse() * (kerbin.GetWorldSurfacePosition(φ_deg, λ_deg, altitude) - kerbin.position)); parallel.map[x].ocean = altitude == 0; parallel.midInfrared[x].lastImagingTime = new double[resolutionThresholds.Length]; parallel.nightVisNIR[x].lastImagingTime = new double[resolutionThresholds.Length]; parallel.glintedReflectiveVisNIR[x].lastImagingTime = new double[resolutionThresholds.Length]; parallel.unglintedReflectiveVisNIR[x].lastImagingTime = new double[resolutionThresholds.Length]; for (int i = 0; i < resolutionThresholds.Length; ++i) { parallel.midInfrared[x].lastImagingTime[i] = double.NegativeInfinity; parallel.nightVisNIR[x].lastImagingTime[i] = double.NegativeInfinity; parallel.glintedReflectiveVisNIR[x].lastImagingTime[i] = double.NegativeInfinity; parallel.unglintedReflectiveVisNIR[x].lastImagingTime[i] = double.NegativeInfinity; } } } } } var imagers = activeImagers.ToArray(); double Δt = 30; if (lastUpdateUT == null || kerbin.scaledBody.transform.rotation == null) { lastUpdateUT = Planetarium.GetUniversalTime(); lastKerbinRotation = kerbin.scaledBody.transform.rotation; } var current_kerbin_to_world = kerbin.scaledBody.transform.rotation; for (int n = (int)Math.Floor((Planetarium.GetUniversalTime() - lastUpdateUT.Value) / Δt); n >= 0; --n) { double t = Planetarium.GetUniversalTime() - n * Δt; // TODO(egg): This will fail hilariously if the interval between updates is greater than half a day. // It is probably broken in other ways. var kerbin_to_world = UnityEngine.Quaternion.Slerp( lastKerbinRotation.Value, current_kerbin_to_world, (float)((t - lastUpdateUT) / (Planetarium.GetUniversalTime() - lastUpdateUT))); var world_to_kerbin = kerbin_to_world.Inverse(); double kerbin_radius = kerbin.Radius; var kerbin_world_position = kerbin.orbit.getPositionAtUT(t); Vector3d sunInSurfaceFrame = world_to_kerbin * (kerbin.referenceBody.getPositionAtUT(t) - kerbin_world_position); Vector3d sunDirection = sunInSurfaceFrame.normalized; for (int y = 0; y != y_size; ++y) { var parallel = kerbin_imaging[y]; for (int x = parallel.x_begin; x != parallel.x_end; ++x) { if (solarParallax) { parallel.sun[x] = SunSurfaceGeometry.FromSunPosition( parallel.surface[x], sunInSurfaceFrame); } else { parallel.sun[x] = SunSurfaceGeometry.FromSunDirection( parallel.surface[x], sunDirection); } } } foreach (var imager in imagers) { Vessel platform = FlightGlobals.FindVessel(imager.Key); ImagingProduct[] products = imager.Value; Vector3d vesselFromKerbinInWorld; Vector3d kerbinCentredVesselVelocityInWorld; if (platform.orbit.referenceBody == kerbin) { var vesselFromKerbinInAlice = platform.orbit.getRelativePositionAtUT(t); var kerbinCentredVesselVelocityInAlice = platform.orbit.getOrbitalVelocityAtUT(t); vesselFromKerbinInWorld = vesselFromKerbinInAlice.xzy; kerbinCentredVesselVelocityInWorld = kerbinCentredVesselVelocityInAlice.xzy; } else { Vector3d vesselFromSunInAlice = Vector3d.zero; Vector3d kerbinFromSunInAlice = Vector3d.zero; Vector3d heliocentricVesselVelocityInAlice = Vector3d.zero; Vector3d heliocentricKerbinVelocityInAlice = Vector3d.zero; for (var orbit = platform.orbit; orbit != null; orbit = orbit.referenceBody?.orbit) { vesselFromSunInAlice += orbit.getRelativePositionAtUT(t); heliocentricVesselVelocityInAlice += orbit.getOrbitalVelocityAtUT(t); } for (var orbit = kerbin.orbit; orbit != null; orbit = orbit.referenceBody?.orbit) { kerbinFromSunInAlice += orbit.getRelativePositionAtUT(t); heliocentricKerbinVelocityInAlice += orbit.getOrbitalVelocityAtUT(t); } vesselFromKerbinInWorld = (vesselFromSunInAlice - kerbinFromSunInAlice).xzy; kerbinCentredVesselVelocityInWorld = (heliocentricVesselVelocityInAlice - heliocentricKerbinVelocityInAlice).xzy; } Vector3d swathNormal; if (platform.loaded) { // The rotation transforms from part coordinates to // world coordinates. // TODO(egg): that products[0] is a hack. swathNormal = world_to_kerbin * (products[0].part.rb.rotation * Vector3d.up); } else { // TODO(egg): Fetch from Principia if available. swathNormal = world_to_kerbin * kerbinCentredVesselVelocityInWorld.normalized; } Vector3d vesselInSurfaceFrame = world_to_kerbin * vesselFromKerbinInWorld; UnityEngine.Vector2d subsatellitePoint = kerbin.GetLatitudeAndLongitude( kerbin.position + current_kerbin_to_world * world_to_kerbin * vesselFromKerbinInWorld); double latitude = subsatellitePoint.x; double longitude = subsatellitePoint.y; double xVessel = (0.5 + longitude / 360) % 1; double yVessel = (0.5 + latitude / 180) % 1; for (int y = (int)(yVessel * y_size); y < y_size; ++y) { var parallel = kerbin_imaging[y]; UpdateParallel( t, parallel, vesselInSurfaceFrame, swathNormal, products, out bool parallelVisibleAtRelevantResolution); if (!parallelVisibleAtRelevantResolution) { break; } } for (int y = (int)(yVessel * y_size) - 1; y >= 0; --y) { var parallel = kerbin_imaging[y]; UpdateParallel( t, parallel, vesselInSurfaceFrame, swathNormal, products, out bool parallelVisibleAtRelevantResolution); if (!parallelVisibleAtRelevantResolution) { break; } } } lastUpdateUT = t; lastKerbinRotation = current_kerbin_to_world; } RefreshMap(); minimap.Apply(updateMipmaps: false); timeSpentInUpdate = DateTime.UtcNow - start; }