/// <summary> /// Returns the star the given body orbits /// </summary> public static KopernicusStar GetNearest(CelestialBody body) { KopernicusStar nearestStar = Stars.OrderBy(s => Vector3.Distance(body.position, s.sun.position)).First(); double greatestDistance = 0; for (Int32 i = 0; i < KopernicusStar.Stars.Count; i++) { KopernicusStar star = KopernicusStar.Stars[i]; double distance = Vector3d.Distance(body.position, star.sun.position); if (star.shifter.givesOffLight && distance > greatestDistance) { greatestDistance = distance; nearestStar = star; } } return nearestStar; }
void Start() { _star = GetComponent <KopernicusStar>(); Mesh mesh = _star.sun.scaledBody.GetComponent <MeshFilter>().sharedMesh; _colliders = new OcclusionNetComponent[mesh.vertexCount / Resolution]; for (Int32 i = 0; i < mesh.vertexCount; i += Resolution) { OcclusionNetComponent component = new GameObject().AddComponent <OcclusionNetComponent>(); component.transform.parent = _star.transform.parent; component.Body = _star.sun; component.Target = _star.target; component.Vertex = mesh.vertices[i]; _colliders[i / Resolution] = component; } }
private static Double Flux(ModularFlightIntegrator fi, KopernicusStar star) { // Nullchecks try { if (fi == null) { return 0; } if (fi.Vessel == null || fi.Vessel.state == Vessel.State.DEAD || fi.CurrentMainBody == null) { return 0; } if (star == null) { return 0; } // Get sunVector Boolean directSunlight = false; Vector3 integratorPosition = fi.transform.position; Vector3d scaledSpace = ScaledSpace.LocalToScaledSpace(integratorPosition); Vector3 position = star.sun.scaledBody.transform.position; Double scale = Math.Max((position - scaledSpace).magnitude, 1); Vector3 sunVector = (position - scaledSpace) / scale; Ray ray = new Ray(ScaledSpace.LocalToScaledSpace(integratorPosition), sunVector); // Get Solar Flux Double realDistanceToSun = 0; if (!Physics.Raycast(ray, out RaycastHit raycastHit, Single.MaxValue, ModularFlightIntegrator.SunLayerMask)) { directSunlight = true; realDistanceToSun = scale * ScaledSpace.ScaleFactor - star.sun.Radius; } else if (raycastHit.transform.GetComponent<ScaledMovement>().celestialBody == star.sun) { realDistanceToSun = ScaledSpace.ScaleFactor * raycastHit.distance; directSunlight = true; } if (directSunlight) { return PhysicsGlobals.SolarLuminosity / (12.5663706143592 * realDistanceToSun * realDistanceToSun); } return 0; }
/// <summary> /// Small method to handle flux /// </summary> public static void Flux(ModularFlightIntegrator fi, KopernicusStar star) { // Nullchecks if (fi.Vessel == null || fi.Vessel.state == Vessel.State.DEAD || fi.CurrentMainBody == null) { return; } // Get sunVector RaycastHit raycastHit; Vector3d scaledSpace = ScaledSpace.LocalToScaledSpace(fi.IntegratorTransform.position); double scale = Math.Max((star.sun.scaledBody.transform.position - scaledSpace).magnitude, 1); Vector3 sunVector = (star.sun.scaledBody.transform.position - scaledSpace) / scale; Ray ray = new Ray(ScaledSpace.LocalToScaledSpace(fi.IntegratorTransform.position), sunVector); // Get Body flux Vector3d scaleFactor = ((Vector3d)star.sun.scaledBody.transform.position - fi.CurrentMainBody.scaledBody.transform.position) * (double)ScaledSpace.ScaleFactor; fi.bodySunFlux = star.sunFlare == fi.CurrentMainBody ? 0 : PhysicsGlobals.SolarLuminosity / Math.PI * 4 * scaleFactor.sqrMagnitude; // Get Solar Flux double realDistanceToSun = 0; bool localDirectSunLight = false; if (!Physics.Raycast(ray, out raycastHit, Single.MaxValue, ModularFI.ModularFlightIntegrator.SunLayerMask)) { localDirectSunLight = true; realDistanceToSun = scale * ScaledSpace.ScaleFactor - star.sun.Radius; } else if (raycastHit.transform.GetComponent <ScaledMovement>().celestialBody == star.sun) { realDistanceToSun = ScaledSpace.ScaleFactor * raycastHit.distance; localDirectSunLight = true; } if (localDirectSunLight) { fi.solarFlux = PhysicsGlobals.SolarLuminosity / (12.5663706143592 * realDistanceToSun * realDistanceToSun); if (!fi.Vessel.directSunlight) { fi.Vessel.directSunlight = true; } } }
/// <summary> /// Small method to handle flux /// </summary> public static void Flux(ModularFlightIntegrator fi, KopernicusStar star) { // Nullchecks if (fi.Vessel == null || fi.Vessel.state == Vessel.State.DEAD || fi.CurrentMainBody == null) { return; } // Get sunVector RaycastHit raycastHit; fi.Vessel.directSunlight = false; Vector3d scaledSpace = ScaledSpace.LocalToScaledSpace(fi.IntegratorTransform.position); double scale = Math.Max((star.sun.scaledBody.transform.position - scaledSpace).magnitude, 1); Vector3 sunVector = (star.sun.scaledBody.transform.position - scaledSpace) / scale; Ray ray = new Ray(ScaledSpace.LocalToScaledSpace(fi.IntegratorTransform.position), sunVector); // Get Body flux fi.solarFlux = 0; fi.sunDot = Vector3d.Dot(fi.sunVector, fi.Vessel.upAxis); fi.CurrentMainBody.GetAtmoThermalStats(true, FlightIntegrator.sunBody, fi.sunVector, fi.sunDot, fi.Vessel.upAxis, fi.altitude, out fi.atmosphereTemperatureOffset, out fi.bodyEmissiveFlux, out fi.bodyAlbedoFlux); Vector3d scaleFactor = ((Vector3d)star.sun.scaledBody.transform.position - fi.CurrentMainBody.scaledBody.transform.position) * (double)ScaledSpace.ScaleFactor; // Get Solar Flux double realDistanceToSun = 0; if (!Physics.Raycast(ray, out raycastHit, Single.MaxValue, ModularFI.ModularFlightIntegrator.SunLayerMask)) { fi.Vessel.directSunlight = true; realDistanceToSun = scale * ScaledSpace.ScaleFactor - star.sun.Radius; } else if (raycastHit.transform.GetComponent <ScaledMovement>().celestialBody == star.sun) { realDistanceToSun = ScaledSpace.ScaleFactor * raycastHit.distance; fi.Vessel.directSunlight = true; } if (fi.Vessel.directSunlight) { fi.solarFlux = PhysicsGlobals.SolarLuminosity / (12.5663706143592 * realDistanceToSun * realDistanceToSun); } }
/// <summary> /// Returns the brightest star near the given body. /// </summary> public static KopernicusStar GetBrightest(CelestialBody body) { double greatestLuminosity = 0; KopernicusStar BrightestStar = GetNearest(body); for (Int32 i = 0; i < KopernicusStar.Stars.Count; i++) { KopernicusStar star = KopernicusStar.Stars[i]; double distance = Vector3d.Distance(body.position, star.sun.position); double aparentLuminosity = 0; if (star.shifter.givesOffLight) { aparentLuminosity = star.shifter.solarLuminosity * (1 / (distance * distance)); } if (aparentLuminosity > greatestLuminosity) { greatestLuminosity = aparentLuminosity; BrightestStar = star; } } return BrightestStar; }
public override void PostCalculateTracking(Boolean trackingLOS, Vector3 trackingDirection) { // Maximum values Double maxEnergy = 0; Double maxFlowRate = 0; KopernicusStar maxStar = null; // Override layer mask planetLayerMask = ModularFlightIntegrator.SunLayerMask; // Efficiency modifier _efficMult = (temperatureEfficCurve.Evaluate((Single)part.skinTemperature) * timeEfficCurve.Evaluate((Single)((Planetarium.GetUniversalTime() - launchUT) * 1.15740740740741E-05)) * efficiencyMult); _flowRate = 0; sunAOA = 0; // Go through all stars foreach (KopernicusStar star in KopernicusStar.Stars) { // Calculate stuff Vector3 trackDir = (star.sun.transform.position - panelRotationTransform.position).normalized; CelestialBody old = trackingBody; trackingTransformLocal = star.sun.transform; trackingTransformScaled = star.sun.scaledBody.transform; trackingLOS = CalculateTrackingLOS(trackDir, ref blockingObject); trackingTransformLocal = old.transform; trackingTransformScaled = old.scaledBody.transform; // Calculate sun AOA Single _sunAOA = 0f; if (!trackingLOS) { _sunAOA = 0f; status = "Blocked by " + blockingObject; } else { status = "Direct Sunlight"; if (panelType == PanelType.FLAT) { _sunAOA = Mathf.Clamp(Vector3.Dot(trackingDotTransform.forward, trackDir), 0f, 1f); } else if (panelType != PanelType.CYLINDRICAL) { _sunAOA = 0.25f; } else { Vector3 direction; if (alignType == PanelAlignType.PIVOT) { direction = trackingDotTransform.forward; } else if (alignType != PanelAlignType.X) { direction = alignType != PanelAlignType.Y ? part.partTransform.forward : part.partTransform.up; } else { direction = part.partTransform.right; } _sunAOA = (1f - Mathf.Abs(Vector3.Dot(direction, trackDir))) * 0.318309873f; } } // Calculate distance multiplier Double __distMult = 1; if (!useCurve) { if (!KopernicusStar.SolarFlux.ContainsKey(star.name)) { continue; } __distMult = (Single)(KopernicusStar.SolarFlux[star.name] / stockLuminosity); } else { __distMult = powerCurve.Evaluate((star.sun.transform.position - panelRotationTransform.position).magnitude); } // Calculate flow rate Double __flowRate = _sunAOA * _efficMult * __distMult; if (part.submergedPortion > 0) { Double altitudeAtPos = -FlightGlobals.getAltitudeAtPos((Vector3d)secondaryTransform.position, vessel.mainBody); altitudeAtPos = (altitudeAtPos * 3 + part.maxDepth) * 0.25; if (altitudeAtPos < 0.5) { altitudeAtPos = 0.5; } Double num = 1 / (1 + altitudeAtPos * part.vessel.mainBody.oceanDensity); if (part.submergedPortion >= 1) { __flowRate = __flowRate * num; } else { __flowRate = __flowRate * UtilMath.LerpUnclamped(1, num, part.submergedPortion); } status += ", Underwater"; } sunAOA += _sunAOA; Double energy = __distMult * _efficMult; if (energy > maxEnergy) { maxFlowRate = __flowRate; maxEnergy = energy; maxStar = star; } // Apply the flow rate _flowRate += __flowRate; } // Sun AOA sunAOA /= relativeSunAOA ? KopernicusStar.Stars.Count : 1; _distMult = _flowRate != 0 ? _flowRate / _efficMult / sunAOA : 0; // We got the best star to use if (maxStar != null && maxStar.sun != trackingBody) { if (!manualTracking) { trackingBody = maxStar.sun; GetTrackingBodyTransforms(); } } // Use the flow rate flowRate = (Single)(resHandler.UpdateModuleResourceOutputs(_flowRate) * flowMult); }
public override void FixedUpdate() { base.FixedUpdate(); frameTimer++; if (HighLogic.LoadedSceneIsFlight) { if ((deployState == ModuleDeployablePart.DeployState.EXTENDED)) { if (frameTimer > (50 * Kopernicus.RuntimeUtility.RuntimeUtility.KopernicusConfig.SolarRefreshRate)) { CelestialBody trackingStar = trackingBody; frameTimer = 0; KopernicusStar bestStar = KopernicusStar.CelestialBodies[trackingStar]; Double totalFlux = 0; Double totalFlow = 0; flowRate = 0; _flowRate = 0; Double bestFlux = 0; for (Int32 s = 0; s < KopernicusStar.Stars.Count; s++) { KopernicusStar star = KopernicusStar.Stars[s]; // Use this star star.shifter.ApplyPhysics(); // Set Tracking Speed to zero Single oldTrackingSpeed = trackingSpeed; trackingSpeed = 0; // Change the tracking body trackingBody = star.sun; GetTrackingBodyTransforms(); CalculateTracking(); //Calculate flux double starFlux = star.CalculateFluxAt(vessel) * 1360 / PhysicsGlobals.SolarLuminosityAtHome; //Check if star has better flux if (bestFlux < starFlux) { bestFlux = starFlux; bestStar = star; } // Add to TotalFlux and EC tally totalFlux += starFlux; float panelEffectivness = 0; //Now for some fancy atmospheric math float atmoDensityMult = 1; float atmoAngleMult = 1; float tempMult = 1; Vector3d localSpace = ScaledSpace.ScaledToLocalSpace(star.target.position); if (this.vessel.atmDensity > 0) { float horizonAngle = (float)Math.Acos(FlightGlobals.currentMainBody.Radius / (FlightGlobals.currentMainBody.Radius + FlightGlobals.ship_altitude)); Vector3 horizonVector = new Vector3(0, Mathf.Sin(Mathf.Deg2Rad * horizonAngle), Mathf.Cos(Mathf.Deg2Rad * horizonAngle)); float sunZenithAngleDeg = Vector3.Angle(FlightGlobals.upAxis, star.sun.position); Double gravAccelParameter = (vessel.mainBody.gravParameter / Math.Pow(vessel.mainBody.Radius + FlightGlobals.ship_altitude, 2)); float massOfAirColumn = (float)(FlightGlobals.getStaticPressure() / gravAccelParameter); tempMult = this.temperatureEfficCurve.Evaluate((float)this.vessel.atmosphericTemperature); atmoDensityMult = AtmosphericAttenutationAirMassMultiplier.Evaluate(massOfAirColumn); atmoAngleMult = AtmosphericAttenutationSolarAngleMultiplier.Evaluate(sunZenithAngleDeg); } panelEffectivness = (chargeRate / 24.4f) / 56.37091313591871f * sunAOA * tempMult * atmoAngleMult * atmoDensityMult; //56.blabla is a weird constant we use to turn flux into EC totalFlow += (starFlux * panelEffectivness) / (1360 / PhysicsGlobals.SolarLuminosityAtHome); // Restore Tracking Speed trackingSpeed = oldTrackingSpeed; } // Restore the starting star trackingBody = trackingStar; KopernicusStar.CelestialBodies[trackingStar].shifter.ApplyPhysics(); GetTrackingBodyTransforms(); CalculateTracking(); vessel.solarFlux = totalFlux; //Add to new output flowRate = (float)totalFlow; _flowRate = totalFlow / chargeRate; resHandler.UpdateModuleResourceOutputs(_flowRate); //caching logic cachedFlowRate = flowRate; _cachedFlowRate = _flowRate; // Setup next tracking body if ((bestStar != null && bestStar != trackingBody) && (!_manualTracking)) { trackingBody = bestStar.sun; GetTrackingBodyTransforms(); CalculateTracking(); } } else { //inbetween timings logic flowRate = cachedFlowRate; _flowRate = _cachedFlowRate; resHandler.UpdateModuleResourceOutputs(_flowRate); } //see if tracked star is blocked or not if (sunAOA > 0) { //this ensures the "blocked" GUI option is set right, if we're exposed to you we're not blocked vessel.directSunlight = true; } // Restore The Current Star KopernicusStar.Current.shifter.ApplyPhysics(); } } else { //Packed logic flowRate = cachedFlowRate; _flowRate = _cachedFlowRate; resHandler.UpdateModuleResourceOutputs(_flowRate); } }
public void LatePostCalculateTracking(Boolean trackingLos, Vector3 trackingDirection, int panelId) { ModuleDeployableSolarPanel SP = SPs[panelId]; // Maximum values Double maxEnergy = 0; KopernicusStar maxStar = null; // Override layer mask typeof(ModuleDeployableSolarPanel).GetFields(BindingFlags.Instance | BindingFlags.NonPublic).FirstOrDefault(f => f.Name == "planetLayerMask").SetValue(SP, ModularFlightIntegrator.SunLayerMask); // Efficiency modifier SP._efficMult = SP.temperatureEfficCurve.Evaluate((Single)part.skinTemperature) * SP.timeEfficCurve.Evaluate( (Single)((Planetarium.GetUniversalTime() - SP.launchUT) * 1.15740740740741E-05)) * SP.efficiencyMult; SP._flowRate = 0; SP.sunAOA = 0; // Go through all stars Int32 stars = KopernicusStar.Stars.Count; for (Int32 i = 0; i < stars; i++) { KopernicusStar star = KopernicusStar.Stars[i]; // Calculate stuff Transform sunTransform = star.sun.transform; Vector3 trackDir = (sunTransform.position - SP.panelRotationTransform.position).normalized; CelestialBody old = SP.trackingBody; SP.trackingTransformLocal = sunTransform; SP.trackingTransformScaled = star.sun.scaledBody.transform; SP.trackingTransformLocal = old.transform; SP.trackingTransformScaled = old.scaledBody.transform; // Calculate sun AOA Single sunAoa; if (!trackingLos) { FieldInfo blockingObject = typeof(ModuleDeployableSolarPanel).GetFields(BindingFlags.Instance | BindingFlags.NonPublic).FirstOrDefault(f => f.Name == "blockingObject"); string blockingObjectName = (string)blockingObject.GetValue(SP); SP.status = Localizer.Format("#Kopernicus_UI_PanelBlocked", blockingObjectName);//"Blocked by " + blockingObjectName } else { SP.status = SP_status_DirectSunlight;//"Direct Sunlight" } if (SP.panelType == ModuleDeployableSolarPanel.PanelType.FLAT) { sunAoa = Mathf.Clamp(Vector3.Dot(SP.trackingDotTransform.forward, trackDir), 0f, 1f); } else if (SP.panelType != ModuleDeployableSolarPanel.PanelType.CYLINDRICAL) { sunAoa = 0.25f; } else { Vector3 direction; if (SP.alignType == ModuleDeployableSolarPanel.PanelAlignType.PIVOT) { direction = SP.trackingDotTransform.forward; } else if (SP.alignType != ModuleDeployableSolarPanel.PanelAlignType.X) { direction = SP.alignType != ModuleDeployableSolarPanel.PanelAlignType.Y ? part.partTransform.forward : part.partTransform.up; } else { direction = part.partTransform.right; } sunAoa = (1f - Mathf.Abs(Vector3.Dot(direction, trackDir))) * 0.318309873f; } // Calculate distance multiplier Double distMult; if (!SP.useCurve) { if (!KopernicusStar.SolarFlux.ContainsKey(star.name)) { continue; } distMult = (Single)(KopernicusStar.SolarFlux[star.name] / StockLuminosity); } else { distMult = SP.powerCurve.Evaluate((star.sun.transform.position - SP.panelRotationTransform.position).magnitude); } // Calculate flow rate Double panelFlowRate = sunAoa * SP._efficMult * distMult; if (part.submergedPortion > 0) { Double altitudeAtPos = -FlightGlobals.getAltitudeAtPos ( (Vector3d)(((Transform)typeof(ModuleDeployableSolarPanel).GetFields(BindingFlags.Instance | BindingFlags.NonPublic).FirstOrDefault(f => f.Name == "secondaryTransform").GetValue(SP)).position), vessel.mainBody ); altitudeAtPos = (altitudeAtPos * 3 + part.maxDepth) * 0.25; if (altitudeAtPos < 0.5) { altitudeAtPos = 0.5; } Double num = 1 / (1 + altitudeAtPos * part.vessel.mainBody.oceanDensity); if (part.submergedPortion >= 1) { panelFlowRate *= num; } else { panelFlowRate *= UtilMath.LerpUnclamped(1, num, part.submergedPortion); } SP.status += ", " + SP_status_Underwater;//Underwater } SP.sunAOA += sunAoa; Double energy = distMult * SP._efficMult; if (energy > maxEnergy) { maxEnergy = energy; maxStar = star; } // Apply the flow rate SP._flowRate += panelFlowRate; } // Sun AOA SP.sunAOA /= _relativeSunAoa ? stars : 1; SP._distMult = Math.Abs(SP._flowRate) > 0.01 ? SP._flowRate / SP._efficMult / SP.sunAOA : 0; // We got the best star to use if (maxStar != null && maxStar.sun != SP.trackingBody) { if (!_manualTracking) { SP.trackingBody = maxStar.sun; SP.GetTrackingBodyTransforms(); } } // Use the flow rate SP.flowRate = (Single)(SP.resHandler.UpdateModuleResourceOutputs(SP._flowRate) * SP.flowMult); }
/// <summary> /// Runs before <see cref="ModuleDeployableSolarPanel.FixedUpdate"/>. /// </summary> void FixedUpdate() { if (HighLogic.LoadedSceneIsFlight) { for (Int32 n = 0; n < SPs.Length; n++) { ModuleDeployableSolarPanel SP = SPs[n]; if (SP.deployState == ModuleDeployablePart.DeployState.EXTENDED) { KopernicusStar trackingStar = KopernicusStar.CelestialBodies[SP.trackingBody]; Double bestFlux = vessel.solarFlux * 1360 / PhysicsGlobals.SolarLuminosityAtHome; KopernicusStar bestStar = trackingStar; Double totalFlux = 0; Single totalAoA = SP.sunAOA; Double _totalFlow = SP._flowRate; Single totalFlow = SP.flowRate; for (Int32 s = 0; s < KopernicusStar.Stars.Count; s++) { KopernicusStar star = KopernicusStar.Stars[s]; if (star != trackingStar) { // Use this star star.shifter.ApplyPhysics(); double flux = star.CalculateFluxAt(vessel); vessel.solarFlux += flux * PhysicsGlobals.SolarLuminosityAtHome / 1360; // Change the tracking body SP.trackingBody = star.sun; SP.GetTrackingBodyTransforms(); // Set Tracking Speed to zero Single trackingSpeed = SP.trackingSpeed; SP.trackingSpeed = 0; // Run The MDSP CalculateTracking SP.CalculateTracking(); // Add to TotalFlux and TotalAoA totalFlux += vessel.solarFlux; totalAoA += SP.sunAOA; _totalFlow += SP._flowRate; totalFlow += SP.flowRate; if (bestFlux < flux) { bestFlux = flux; bestStar = star; } // Restore Tracking Speed SP.trackingSpeed = trackingSpeed; } } // Restore the tracking body SP.trackingBody = trackingStar.sun; SP.GetTrackingBodyTransforms(); // Restore the starting star trackingStar.shifter.ApplyPhysics(); totalFlux += trackingStar.CalculateFluxAt(vessel) * PhysicsGlobals.SolarLuminosityAtHome / 1360; vessel.solarFlux = totalFlux; SP.sunAOA = totalAoA; SP.sunAOA /= _relativeSunAoa ? KopernicusStar.Stars.Count : 1; SP._flowRate = _totalFlow; SP.flowRate = totalFlow; // We got the best star to use if (bestStar != null && bestStar.sun != SP.trackingBody) { if (!_manualTracking) { SP.trackingBody = bestStar.sun; SP.GetTrackingBodyTransforms(); continue; } } } } // Restore The Current Star KopernicusStar.Current.shifter.ApplyPhysics(); } }