// return the surface radiation for the body specified (used by body info panel) public static double ComputeSurface(CelestialBody b, double gamma_transparency) { // store stuff Space gsm; Vector3 p; float D; // transform to local space once Vector3d position = ScaledSpace.LocalToScaledSpace(b.position); // accumulate radiation double radiation = 0.0; CelestialBody body = b; while (body != null) { RadiationBody rb = Info(body); RadiationModel mf = rb.model; if (mf.Has_field()) { // generate radii-normalized GSM space gsm = Gsm_space(rb, true); // move the poing in GSM space p = gsm.Transform_in(position); // accumulate radiation and determine pause/belt flags if (mf.has_inner) { D = mf.Inner_func(p); radiation += Lib.Clamp(D / -0.0666f, 0.0f, 1.0f) * rb.radiation_inner; } if (mf.has_outer) { D = mf.Outer_func(p); radiation += Lib.Clamp(D / -0.0333f, 0.0f, 1.0f) * rb.radiation_outer; } if (mf.has_pause) { gsm = Gsm_space(rb, false); p = gsm.Transform_in(position); D = mf.Pause_func(p); radiation += Lib.Clamp(D / -0.1332f, 0.0f, 1.0f) * rb.radiation_pause; } } // avoid loops in the chain body = (body.referenceBody != null && body.referenceBody.referenceBody == body) ? null : body.referenceBody; } // add extern radiation radiation += PreferencesStorm.Instance.ExternRadiation; // clamp radiation to positive range // note: we avoid radiation going to zero by using a small positive value radiation = Math.Max(radiation, Nominal); // return radiation, scaled by gamma transparency if inside atmosphere return(radiation * gamma_transparency); }
/// <summary>return true if the given body has a belt or a magnetosphere (doesn't matter if visible or not)</summary> public static bool HasMagneticField(CelestialBody body) { if (!Features.Radiation) { return(false); } RadiationBody rb = KERBALISM.Radiation.Info(body); return(rb.model.Has_field()); }
/// <summary>set visibility of the inner radiation belt</summary> public static void SetMagnetopauseVisible(CelestialBody body, bool visible) { if (!Features.Radiation) { return; } RadiationBody rb = KERBALISM.Radiation.Info(body); rb.pause_visible = visible; }
/// <summary>return true if the given body has a magnetopause that is visible</summary> public static bool IsMagnetopauseVisible(CelestialBody body) { if (!Features.Radiation) { return(false); } RadiationBody rb = KERBALISM.Radiation.Info(body); return(rb.model.has_pause && rb.pause_visible); }
/// <summary>set visibility of the inner radiation belt</summary> public static void SetOuterBeltVisible(CelestialBody body, bool visible) { if (!Features.Radiation) { return; } RadiationBody rb = KERBALISM.Radiation.Info(body); rb.outer_visible = visible; }
/// <summary>return true if the given body has an outer radiation belt that is visible</summary> public static bool IsOuterBeltVisible(CelestialBody body) { if (!Features.Radiation) { return(false); } RadiationBody rb = KERBALISM.Radiation.Info(body); return(rb.model.has_outer && rb.outer_visible); }
/// <summary>return true if the given body has an outer radiation belt (doesn't matter if visible or not)</summary> public static bool HasOuterBelt(CelestialBody body) { if (!Features.Radiation) { return(false); } RadiationBody rb = KERBALISM.Radiation.Info(body); return(rb.model.has_outer); }
/// <summary> Returns the radiation emitted by the body at the center, adjusted by solar activity cycle </summary> private static double RadiationR0(RadiationBody rb) { // for easier configuration, the radiation model sets the radiation on the surface of the body. // from there, it decreases according to the inverse square law with distance from the surface. var r0 = rb.radiation_r0; // precomputed // if there is a solar cycle, add a bit of radiation variation relative to current activity if (rb.solar_cycle > 0) { var activity = rb.SolarActivity() * 0.3; r0 = r0 + r0 * activity; } return(r0); }
// return the total environent radiation at position specified public static double Compute(Vessel v, Vector3d position, double gamma_transparency, double sunlight, out bool blackout, out bool magnetosphere, out bool inner_belt, out bool outer_belt, out bool interstellar) { // prepare out parameters blackout = false; magnetosphere = false; inner_belt = false; outer_belt = false; interstellar = false; // no-op when Radiation is disabled if (!Features.Radiation) { return(0.0); } // store stuff Space gsm; Vector3 p; float D; // transform to local space once position = ScaledSpace.LocalToScaledSpace(position); // accumulate radiation double radiation = 0.0; CelestialBody body = v.mainBody; while (body != null) { RadiationBody rb = Info(body); RadiationModel mf = rb.model; if (mf.Has_field()) { // generate radii-normalized GSM space gsm = Gsm_space(rb.body, FlightGlobals.Bodies[rb.reference]); // move the poing in GSM space p = gsm.Transform_in(position); // accumulate radiation and determine pause/belt flags if (mf.has_inner) { D = mf.Inner_func(p); radiation += Lib.Clamp(D / -0.0666f, 0.0f, 1.0f) * rb.radiation_inner; inner_belt |= D < 0.0f; } if (mf.has_outer) { D = mf.Outer_func(p); radiation += Lib.Clamp(D / -0.0333f, 0.0f, 1.0f) * rb.radiation_outer; outer_belt |= D < 0.0f; } if (mf.has_pause) { D = mf.Pause_func(p); radiation += Lib.Clamp(D / -0.1332f, 0.0f, 1.0f) * rb.radiation_pause; magnetosphere |= D < 0.0f && rb.body.flightGlobalsIndex != 0; //< ignore heliopause interstellar |= D > 0.0f && rb.body.flightGlobalsIndex == 0; //< outside heliopause } } // avoid loops in the chain body = (body.referenceBody != null && body.referenceBody.referenceBody == body) ? null : body.referenceBody; } // add extern radiation radiation += PreferencesStorm.Instance.ExternRadiation; // add emitter radiation radiation += Emitter.Total(v); // if there is a storm in progress if (Storm.InProgress(v)) { // inside a magnetopause (except heliosphere), blackout the signal // outside, add storm radiations modulated by sun visibility if (magnetosphere) { blackout = true; } else { radiation += PreferencesStorm.Instance.StormRadiation * sunlight; } } // clamp radiation to positive range // note: we avoid radiation going to zero by using a small positive value radiation = Math.Max(radiation, Nominal); // return radiation, scaled by gamma transparency if inside atmosphere return(radiation * gamma_transparency); }
// render the fields of the interesting body public static void Render() { // get interesting body CelestialBody body = Interesting_body(); // maintain visualization modes if (body == null) { show_inner = false; show_outer = false; show_pause = false; } else { if (Input.GetKeyDown(KeyCode.Keypad0)) { if (show_inner || show_outer || show_pause) { show_inner = false; show_outer = false; show_pause = false; } else { show_inner = true; show_outer = true; show_pause = true; } } if (Input.GetKeyDown(KeyCode.Keypad1)) { show_inner = true; show_outer = false; show_pause = false; } if (Input.GetKeyDown(KeyCode.Keypad2)) { show_inner = false; show_outer = true; show_pause = false; } if (Input.GetKeyDown(KeyCode.Keypad3)) { show_inner = false; show_outer = false; show_pause = true; } } // if there is an active body, and at least one of the modes is active if (body != null && (show_inner || show_outer || show_pause)) { // if we don't know if preprocessing is completed if (preprocess_thread != null) { // if the preprocess thread has not done yet if (preprocess_thread.IsAlive) { // disable all modes show_inner = false; show_outer = false; show_pause = false; // tell the user and do nothing Message.Post("<color=#00ffff><b>Fitting particles to signed distance fields</b></color>", "Come back in a minute"); return; } // wait for particle-fitting thread to cleanup preprocess_thread.Join(); // preprocessing is complete preprocess_thread = null; } // load and configure shader if (mat == null) { if (!Settings.LowQualityRendering) { // load shader mat = Lib.GetShader("MiniParticle"); // configure shader mat.SetColor("POINT_COLOR", new Color(0.33f, 0.33f, 0.33f, 0.1f)); } else { // load shader mat = Lib.GetShader("PointParticle"); // configure shader mat.SetColor("POINT_COLOR", new Color(0.33f, 0.33f, 0.33f, 0.1f)); mat.SetFloat("POINT_SIZE", 4.0f); } } // generate radii-normalized GMS space RadiationBody rb = Info(body); Space gsm = Gsm_space(rb.body, FlightGlobals.Bodies[rb.reference]); #if DEBUG // show axis LineRenderer.Commit(gsm.origin, gsm.origin + gsm.x_axis * gsm.scale * 5.0f, Color.red); LineRenderer.Commit(gsm.origin, gsm.origin + gsm.y_axis * gsm.scale * 5.0f, Color.green); LineRenderer.Commit(gsm.origin, gsm.origin + gsm.z_axis * gsm.scale * 5.0f, Color.blue); #endif // get magnetic field data RadiationModel mf = Info(body).model; // enable material mat.SetPass(0); // render active body fields Matrix4x4 m = gsm.Look_at(); if (show_inner && mf.has_inner) { mf.inner_pmesh.Render(m); } if (show_outer && mf.has_outer) { mf.outer_pmesh.Render(m); } if (show_pause && mf.has_pause) { mf.pause_pmesh.Render(m); } } }
// generate gsm-space frame of reference // - origin is at body position // - the x-axis point to reference body // - the rotation axis is used as y-axis initial guess // - the space is then orthonormalized // - if the reference body is the same as the body, // the galactic rotation vector is used as x-axis instead public static Space Gsm_space(RadiationBody rb, bool tilted) { CelestialBody body = rb.body; CelestialBody reference = FlightGlobals.Bodies[rb.reference]; Space gsm; gsm.origin = ScaledSpace.LocalToScaledSpace(body.position); gsm.scale = ScaledSpace.InverseScaleFactor * (float)body.Radius; if (body != reference) { gsm.x_axis = ((Vector3)ScaledSpace.LocalToScaledSpace(reference.position) - gsm.origin).normalized; if (!tilted) { gsm.y_axis = body.RotationAxis; //< initial guess gsm.z_axis = Vector3.Cross(gsm.x_axis, gsm.y_axis).normalized; gsm.y_axis = Vector3.Cross(gsm.z_axis, gsm.x_axis).normalized; //< orthonormalize } else { /* "Do not try and tilt the planet, that's impossible. * Instead, only try to realize the truth...there is no tilt. * Then you'll see that it is not the planet that tilts, it is * the rest of the universe." * * - The Matrix * * * the orbits are inclined (with respect to the equator of the * Earth), but all axes are parallel. and aligned with the unity * world z axis. or is it y? whatever, KSP uses two conventions * in different places. * if you use Principia, the current main body (or if there is * none, e.g. in the space centre or tracking station, the home * body) is not tilted (its axis is the unity vertical. * you can fetch the full orientation (tilt and rotation) of any * body (including the current main body) in the current unity * frame (which changes of course, because sometimes KSP uses a * rotating frame, and because Principia tilts the universe * differently if the current main body changes) as the * orientation of the scaled space body * * body.scaledBody.transform.rotation or something along those lines * * - egg */ Vector3 pole = rb.geomagnetic_pole; Quaternion rotation = body.scaledBody.transform.rotation; gsm.y_axis = (rotation * pole).normalized; gsm.z_axis = Vector3.Cross(gsm.x_axis, gsm.y_axis).normalized; gsm.x_axis = Vector3.Cross(gsm.y_axis, gsm.z_axis).normalized; //< orthonormalize } } else { // galactic gsm.x_axis = new Vector3(1.0f, 0.0f, 0.0f); gsm.y_axis = new Vector3(0.0f, 1.0f, 0.0f); gsm.z_axis = new Vector3(0.0f, 0.0f, 1.0f); } gsm.origin = gsm.origin + gsm.y_axis * (gsm.scale * rb.geomagnetic_offset); return(gsm); }
// return the surface radiation for the body specified (used by body info panel) public static double ComputeSurface(CelestialBody b, double gamma_transparency) { if (!Features.Radiation) { return(0.0); } // store stuff Space gsm; Vector3 p; double D; // transform to local space once Vector3d position = ScaledSpace.LocalToScaledSpace(b.position); // accumulate radiation double radiation = 0.0; CelestialBody body = b; while (body != null) { RadiationBody rb = Info(body); RadiationModel mf = rb.model; var activity = rb.SolarActivity(false); if (mf.Has_field()) { // generate radii-normalized GSM space gsm = Gsm_space(rb, true); // move the poing in GSM space p = gsm.Transform_in(position); // accumulate radiation and determine pause/belt flags if (mf.has_inner) { D = mf.Inner_func(p); // allow for radiation field to grow/shrink with solar activity D -= activity * 0.25 / mf.inner_radius; var r = RadiationInBelt(D, mf.inner_radius, rb.radiation_inner_gradient); radiation += r * rb.radiation_inner * (1 + activity * 0.3); } if (mf.has_outer) { D = mf.Outer_func(p); // allow for radiation field to grow/shrink with solar activity D -= activity * 0.25 / mf.outer_radius; var r = RadiationInBelt(D, mf.outer_radius, rb.radiation_outer_gradient); radiation += r * rb.radiation_outer * (1 + activity * 0.3); } if (mf.has_pause) { gsm = Gsm_space(rb, false); p = gsm.Transform_in(position); D = mf.Pause_func(p); radiation += Lib.Clamp(D / -0.1332f, 0.0f, 1.0f) * rb.RadiationPause(); } } if (rb.radiation_surface > 0 && body != b) { // add surface radiation emitted from other body double distance = (b.position - body.position).magnitude; var r0 = RadiationR0(rb); var r1 = DistanceRadiation(r0, distance); // Lib.Log("Surface radiation on " + b + " from " + body + ": " + Lib.HumanReadableRadiation(r1) + " distance " + distance); // clamp to max. surface radiation. when loading on a rescaled system, the vessel can appear to be within the sun for a few ticks radiation += Math.Min(r1, rb.radiation_surface); } // avoid loops in the chain body = (body.referenceBody != null && body.referenceBody.referenceBody == body) ? null : body.referenceBody; } // add extern radiation radiation += Settings.ExternRadiation / 3600.0; // Lib.Log("Radiation subtotal on " + b + ": " + Lib.HumanReadableRadiation(radiation) + ", gamma " + gamma_transparency); // scale radiation by gamma transparency if inside atmosphere radiation *= gamma_transparency; // Lib.Log("srf scaled on " + b + ": " + Lib.HumanReadableRadiation(radiation)); // add surface radiation of the body itself RadiationBody bodyInfo = Info(b); // clamp to max. bodyInfo.radiation_surface to avoid extreme radiation effects while loading a vessel on rescaled systems radiation += Math.Min(bodyInfo.radiation_surface, DistanceRadiation(RadiationR0(bodyInfo), b.Radius)); // Lib.Log("Radiation on " + b + ": " + Lib.HumanReadableRadiation(radiation) + ", own surface radiation " + Lib.HumanReadableRadiation(DistanceRadiation(RadiationR0(Info(b)), b.Radius))); // Lib.Log("radiation " + radiation + " nominal " + Nominal); // clamp radiation to positive range // note: we avoid radiation going to zero by using a small positive value radiation = Math.Max(radiation, Nominal); return(radiation); }
// return the total environent radiation at position specified public static double Compute(Vessel v, Vector3d position, double gamma_transparency, double sunlight, out bool blackout, out bool magnetosphere, out bool inner_belt, out bool outer_belt, out bool interstellar, out double shieldedRadiation) { // prepare out parameters blackout = false; magnetosphere = false; inner_belt = false; outer_belt = false; interstellar = false; shieldedRadiation = 0.0; // no-op when Radiation is disabled if (!Features.Radiation) { return(0.0); } // store stuff Space gsm; Vector3 p; double D; double r; // accumulate radiation double radiation = 0.0; CelestialBody body = v.mainBody; while (body != null) { // Compute radiation values from overlapping 3d fields (belts + magnetospheres) RadiationBody rb = Info(body); RadiationModel mf = rb.model; // activity is [-0.15..1.05] var activity = rb.SolarActivity(false); if (mf.Has_field()) { // transform to local space once var scaled_position = ScaledSpace.LocalToScaledSpace(position); // generate radii-normalized GSM space gsm = Gsm_space(rb, true); // move the point in GSM space p = gsm.Transform_in(scaled_position); // accumulate radiation and determine pause/belt flags if (mf.has_inner) { D = mf.Inner_func(p); inner_belt |= D < 0; // allow for radiation field to grow/shrink with solar activity D -= activity * 0.25 / mf.inner_radius; r = RadiationInBelt(D, mf.inner_radius, rb.radiation_inner_gradient); radiation += r * rb.radiation_inner * (1 + activity * 0.3); } if (mf.has_outer) { D = mf.Outer_func(p); outer_belt |= D < 0; // allow for radiation field to grow/shrink with solar activity D -= activity * 0.25 / mf.outer_radius; r = RadiationInBelt(D, mf.outer_radius, rb.radiation_outer_gradient); radiation += r * rb.radiation_outer * (1 + activity * 0.3); } if (mf.has_pause) { gsm = Gsm_space(rb, false); p = gsm.Transform_in(scaled_position); D = mf.Pause_func(p); radiation += Lib.Clamp(D / -0.1332f, 0.0f, 1.0f) * rb.RadiationPause(); magnetosphere |= D < 0.0f && !Lib.IsSun(rb.body); //< ignore heliopause interstellar |= D > 0.0f && Lib.IsSun(rb.body); //< outside heliopause } } if (rb.radiation_surface > 0 && body != v.mainBody) { Vector3d direction; double distance; if (Sim.IsBodyVisible(v, position, body, v.KerbalismData().EnvVisibleBodies, out direction, out distance)) { var r0 = RadiationR0(rb); var r1 = DistanceRadiation(r0, distance); // clamp to max. surface radiation. when loading on a rescaled system, the vessel can appear to be within the sun for a few ticks radiation += Math.Min(r1, rb.radiation_surface); #if DEBUG_RADIATION if (v.loaded) { Lib.Log("Radiation " + v + " from surface of " + body + ": " + Lib.HumanReadableRadiation(radiation) + " gamma: " + Lib.HumanReadableRadiation(r1)); } #endif } } // avoid loops in the chain body = (body.referenceBody != null && body.referenceBody.referenceBody == body) ? null : body.referenceBody; } // add extern radiation radiation += Settings.ExternRadiation / 3600.0; #if DEBUG_RADIATION if (v.loaded) { Lib.Log("Radiation " + v + " extern: " + Lib.HumanReadableRadiation(radiation) + " gamma: " + Lib.HumanReadableRadiation(Settings.ExternRadiation)); } #endif // apply gamma transparency if inside atmosphere radiation *= gamma_transparency; #if DEBUG_RADIATION if (v.loaded) { Lib.Log("Radiation " + v + " after gamma: " + Lib.HumanReadableRadiation(radiation) + " transparency: " + gamma_transparency); } #endif // add surface radiation of the body itself if (Lib.IsSun(v.mainBody) && v.altitude < v.mainBody.Radius) { if (v.altitude > v.mainBody.Radius) { radiation += DistanceRadiation(RadiationR0(Info(v.mainBody)), v.altitude); } } #if DEBUG_RADIATION if (v.loaded) { Lib.Log("Radiation " + v + " from current main body: " + Lib.HumanReadableRadiation(radiation) + " gamma: " + Lib.HumanReadableRadiation(DistanceRadiation(RadiationR0(Info(v.mainBody)), v.altitude))); } #endif shieldedRadiation = radiation; // if there is a storm in progress if (Storm.InProgress(v)) { // inside a magnetopause (except heliosphere), blackout the signal // outside, add storm radiations modulated by sun visibility if (magnetosphere) { blackout = true; } else { var vd = v.KerbalismData(); var activity = Info(vd.EnvMainSun.SunData.body).SolarActivity(false) / 2.0; var strength = PreferencesRadiation.Instance.StormRadiation * sunlight * (activity + 0.5); radiation += strength; shieldedRadiation += vd.EnvHabitatInfo.AverageHabitatRadiation(strength); } } // add emitter radiation after atmosphere transparency var emitterRadiation = Emitter.Total(v); radiation += emitterRadiation; shieldedRadiation += emitterRadiation; #if DEBUG_RADIATION if (v.loaded) { Lib.Log("Radiation " + v + " after emitters: " + Lib.HumanReadableRadiation(radiation) + " shielded " + Lib.HumanReadableRadiation(shieldedRadiation)); } #endif // for EVAs, add the effect of nearby emitters if (v.isEVA) { var nearbyEmitters = Emitter.Nearby(v); radiation += nearbyEmitters; shieldedRadiation += nearbyEmitters; #if DEBUG_RADIATION if (v.loaded) { Lib.Log("Radiation " + v + " nearby emitters " + Lib.HumanReadableRadiation(nearbyEmitters)); } #endif } var passiveShielding = PassiveShield.Total(v); shieldedRadiation -= passiveShielding; #if DEBUG_RADIATION if (v.loaded) { Lib.Log("Radiation " + v + " passiveShielding " + Lib.HumanReadableRadiation(passiveShielding)); } if (v.loaded) { Lib.Log("Radiation " + v + " before clamp: " + Lib.HumanReadableRadiation(radiation) + " shielded " + Lib.HumanReadableRadiation(shieldedRadiation)); } #endif // clamp radiation to positive range // note: we avoid radiation going to zero by using a small positive value radiation = Math.Max(radiation, Nominal); shieldedRadiation = Math.Max(shieldedRadiation, Nominal); #if DEBUG_RADIATION if (v.loaded) { Lib.Log("Radiation " + v + " after clamp: " + Lib.HumanReadableRadiation(radiation) + " shielded " + Lib.HumanReadableRadiation(shieldedRadiation)); } #endif // return radiation return(radiation); }