// 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> /// do the particle-fitting in another thread /// </summary> public static void Preprocess() { // ############################################################# // # DO NOT LOG FROM A THREAD IN UNITY. IT IS NOT THREAD SAFE. # // ############################################################# // deduce number of particles int inner_count = 150000; int outer_count = 600000; int pause_count = 250000; if (Settings.LowQualityRendering) { inner_count /= 5; outer_count /= 5; pause_count /= 5; } // create all magnetic fields and do particle-fitting List <string> done = new List <string>(); foreach (var pair in models) { // get radiation data RadiationModel mf = pair.Value; // skip if model is already done if (done.Contains(mf.name)) { continue; } // add to the skip list done.Add(mf.name); // particle-fitting for the inner radiation belt if (mf.has_inner) { mf.inner_pmesh = new ParticleMesh(mf.Inner_func, mf.Inner_domain(), mf.Inner_offset(), inner_count, mf.inner_quality); } // particle-fitting for the outer radiation belt if (mf.has_outer) { mf.outer_pmesh = new ParticleMesh(mf.Outer_func, mf.Outer_domain(), mf.Outer_offset(), outer_count, mf.outer_quality); } // particle-fitting for the magnetopause if (mf.has_pause) { mf.pause_pmesh = new ParticleMesh(mf.Pause_func, mf.Pause_domain(), mf.Pause_offset(), pause_count, mf.pause_quality); } } }
// 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); } } }
// do the particle-fitting in another thread public static void Preprocess() { // deduce number of particles int inner_count = 150000; int outer_count = 600000; int pause_count = 250000; if (Settings.LowQualityRendering) { inner_count /= 5; outer_count /= 5; pause_count /= 5; } // start time UInt64 time = Lib.Clocks(); // create all magnetic fields and do particle-fitting List <string> done = new List <string>(); foreach (var pair in models) { // get radiation data RadiationModel mf = pair.Value; // skip if model is already done if (done.Contains(mf.name)) { continue; } // add to the skip list done.Add(mf.name); // if it has a field if (mf.Has_field()) { // some feedback in the log // note: can't use BuildString here, as it is not thread-safe Lib.Log("particle-fitting '" + mf.name + "'..."); } // particle-fitting for the inner radiation belt if (mf.has_inner) { mf.inner_pmesh = new ParticleMesh(mf.Inner_func, mf.Inner_domain(), mf.Inner_offset(), inner_count, mf.inner_quality); } // particle-fitting for the outer radiation belt if (mf.has_outer) { mf.outer_pmesh = new ParticleMesh(mf.Outer_func, mf.Outer_domain(), mf.Outer_offset(), outer_count, mf.outer_quality); } // particle-fitting for the magnetopause if (mf.has_pause) { mf.pause_pmesh = new ParticleMesh(mf.Pause_func, mf.Pause_domain(), mf.Pause_offset(), pause_count, mf.pause_quality); } } // measure time required // note: can't use BuildString here, as it is not thread-safe Lib.Log("particle-fitting completed in " + Lib.Seconds(Lib.Clocks() - time).ToString("F3") + " seconds"); }
// 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); }