private void UpdateMove() { if (!MovingVessel) { // EndMove(); return; } MovingVessel.IgnoreGForces(240); // Lerp is animating move if (!_hoverChanged) { MoveHeight = Mathf.Lerp(MoveHeight, _vBounds.BottomLength + HoverHeight, 10 * Time.fixedDeltaTime); } else { double alt = MovingVessel.radarAltitude; // sINCE Lerp is animating move from 0 to hoverheight, we do not want this going below current altitude if (MoveHeight < alt) { MoveHeight = Convert.ToSingle(alt); } MoveHeight = MovingVessel.Splashed ? Mathf.Lerp(MoveHeight, _vBounds.BottomLength + _hoverAdjust, 10 * Time.fixedDeltaTime) : Mathf.Lerp(MoveHeight, _vBounds.BottomLength + (MoveHeight + _hoverAdjust < 0 ? -MoveHeight : _hoverAdjust), 10 * Time.fixedDeltaTime); } MovingVessel.ActionGroups.SetGroup(KSPActionGroup.RCS, false); _up = (MovingVessel.transform.position - FlightGlobals.currentMainBody.transform.position).normalized; Vector3 forward; if (MapView.MapIsEnabled) { forward = North(); } else { forward = Vector3.ProjectOnPlane(MovingVessel.CoM - FlightCamera.fetch.mainCamera.transform.position, _up).normalized; if (Vector3.Dot(-_up, FlightCamera.fetch.mainCamera.transform.up) > 0) { forward = Vector3.ProjectOnPlane(FlightCamera.fetch.mainCamera.transform.up, _up).normalized; } } Vector3 right = Vector3.Cross(_up, forward); Vector3 offsetDirection = Vector3.zero; bool inputting = false; //auto level plane if (GameSettings.TRANSLATE_FWD.GetKey()) { Quaternion targetRot = Quaternion.LookRotation(-_up, forward); _startRotation = Quaternion.RotateTowards(_startRotation, targetRot, RotationSpeed * 2); _hasRotated = true; } else if (GameSettings.TRANSLATE_BACK.GetKey())//auto level rocket { Quaternion targetRot = Quaternion.LookRotation(forward, _up); _startRotation = Quaternion.RotateTowards(_startRotation, targetRot, RotationSpeed * 2); _hasRotated = true; } if (inputting) { _currMoveSpeed = Mathf.Clamp(Mathf.MoveTowards(_currMoveSpeed, MoveSpeed, MoveAccel * Time.fixedDeltaTime), 0, MoveSpeed); } else { _currMoveSpeed = 0; } Vector3 offset = offsetDirection.normalized * _currMoveSpeed; _currMoveVelocity = offset / Time.fixedDeltaTime; Vector3 vSrfPt = MovingVessel.CoM - (MoveHeight * _up); bool srfBelowWater = false; RaycastHit ringHit; bool surfaceDetected = CapsuleCast(out ringHit); Vector3 finalOffset = Vector3.zero; if (surfaceDetected) { if (FlightGlobals.getAltitudeAtPos(ringHit.point) < 0) { srfBelowWater = true; } Vector3 rOffset = Vector3.Project(ringHit.point - vSrfPt, _up); Vector3 mOffset = (vSrfPt + offset) - MovingVessel.CoM; finalOffset = rOffset + mOffset + (MoveHeight * _up); MovingVessel.Translate(finalOffset); } PQS bodyPQS = MovingVessel.mainBody.pqsController; Vector3d geoCoords = WorldPositionToGeoCoords(MovingVessel.GetWorldPos3D() + (_currMoveVelocity * Time.fixedDeltaTime), MovingVessel.mainBody); double lat = geoCoords.x; double lng = geoCoords.y; Vector3d bodyUpVector = new Vector3d(1, 0, 0); bodyUpVector = QuaternionD.AngleAxis(lat, Vector3d.forward /*around Z axis*/) * bodyUpVector; bodyUpVector = QuaternionD.AngleAxis(lng, Vector3d.down /*around -Y axis*/) * bodyUpVector; double srfHeight = bodyPQS.GetSurfaceHeight(bodyUpVector); if (!surfaceDetected || srfBelowWater) { Vector3 terrainPos = MovingVessel.mainBody.position + (float)srfHeight * _up; Vector3 waterSrfPoint = FlightGlobals.currentMainBody.position + ((float)FlightGlobals.currentMainBody.Radius * _up); if (!surfaceDetected) { MovingVessel.SetPosition(terrainPos + (MoveHeight * _up) + offset); } else { MovingVessel.SetPosition(waterSrfPoint + (MoveHeight * _up) + offset); } //update vessel situation to splashed down: //MovingVessel.UpdateLandedSplashed(); } //fix surface rotation Quaternion srfRotFix = Quaternion.FromToRotation(_startingUp, _up); _currRotation = srfRotFix * _startRotation; MovingVessel.SetRotation(_currRotation); if (Vector3.Angle(_startingUp, _up) > 5) { _startRotation = _currRotation; _startingUp = _up; } MovingVessel.SetWorldVelocity(Vector3d.zero); MovingVessel.angularVelocity = Vector3.zero; MovingVessel.angularMomentum = Vector3.zero; }
public static IEnumerator UpdateTextures(CelestialBody celestialBody, TextureOptions options) { // Get time DateTime now = DateTime.Now; // If the user wants to export normals, we need height too if (options.ExportNormal) { options.ExportHeight = true; } // Prepare the PQS PQS pqsVersion = celestialBody.pqsController; // If the PQS is null, abort if (pqsVersion == null) { yield break; } // Tell the PQS that we are going to build maps pqsVersion.SetupExternalRender(); // Get the mod building methods from the PQS #if !KSP131 Action <PQS.VertexBuildData, Boolean> modOnVertexBuildHeight = (Action <PQS.VertexBuildData, Boolean>)Delegate.CreateDelegate( typeof(Action <PQS.VertexBuildData, Boolean>), pqsVersion, typeof(PQS).GetMethod("Mod_OnVertexBuildHeight", BindingFlags.Instance | BindingFlags.NonPublic)); #else Action <PQS.VertexBuildData> modOnVertexBuildHeight = (Action <PQS.VertexBuildData>)Delegate.CreateDelegate( typeof(Action <PQS.VertexBuildData>), pqsVersion, typeof(PQS).GetMethod("Mod_OnVertexBuildHeight", BindingFlags.Instance | BindingFlags.NonPublic)); #endif Action <PQS.VertexBuildData> modOnVertexBuild = (Action <PQS.VertexBuildData>)Delegate.CreateDelegate( typeof(Action <PQS.VertexBuildData>), pqsVersion, typeof(PQS).GetMethod("Mod_OnVertexBuild", BindingFlags.Instance | BindingFlags.NonPublic)); // Get all mods the PQS is connected to PQSMod[] mods = pqsVersion.GetComponentsInChildren <PQSMod>() .Where(m => m.sphere == pqsVersion && m.modEnabled).OrderBy(m => m.order).ToArray(); // Prevent the PQS from updating pqsVersion.enabled = false; // Create the Textures Texture2D colorMap = new Texture2D(options.Resolution, options.Resolution / 2, TextureFormat.ARGB32, true); Texture2D heightMap = new Texture2D(options.Resolution, options.Resolution / 2, TextureFormat.RGB24, true); // Arrays Color[] colorMapValues = new Color[options.Resolution * (options.Resolution / 2)]; Color[] heightMapValues = new Color[options.Resolution * (options.Resolution / 2)]; // Create a VertexBuildData PQS.VertexBuildData data = new PQS.VertexBuildData(); // Display ScreenMessage message = ScreenMessages.PostScreenMessage("Generating Planet-Maps", Single.MaxValue, ScreenMessageStyle.UPPER_CENTER); yield return(null); // Loop through the pixels for (Int32 y = 0; y < options.Resolution / 2; y++) { for (Int32 x = 0; x < options.Resolution; x++) { // Update Message Double percent = (Double)(y * options.Resolution + x) / (options.Resolution / 2 * options.Resolution) * 100; while (CanvasUpdateRegistry.IsRebuildingLayout()) { Thread.Sleep(10); } message.textInstance.text.text = "Generating Planet-Maps: " + percent.ToString("0.00") + "%"; // Update the VertexBuildData data.directionFromCenter = QuaternionD.AngleAxis(360d / options.Resolution * x, Vector3d.up) * QuaternionD.AngleAxis(90d - 180d / (options.Resolution / 2f) * y, Vector3d.right) * Vector3d.forward; data.vertHeight = pqsVersion.radius; // Build from the Mods Double height = Double.MinValue; if (options.ExportHeight) { #if !KSP131 modOnVertexBuildHeight(data, true); #else modOnVertexBuildHeight(data); #endif // Adjust the height height = (data.vertHeight - pqsVersion.radius) * (1d / pqsVersion.radiusDelta); if (height < 0) { height = 0; } else if (height > 1) { height = 1; } // Set the Pixels heightMapValues[y * options.Resolution + x] = new Color((Single)height, (Single)height, (Single)height); } if (options.ExportColor) { modOnVertexBuild(data); // Adjust the Color Color color = data.vertColor; if (!pqsVersion.mapOcean) { color.a = 1f; } else if (height > pqsVersion.mapOceanHeight) { color.a = options.TransparentMaps ? 0f : 1f; } else { color = pqsVersion.mapOceanColor.A(1f); } // Set the Pixels colorMapValues[y * options.Resolution + x] = color; } } yield return(null); } // Serialize the maps to disk String name = "KittopiaTech/PluginData/" + celestialBody.transform.name + "/" + DateTime.Now.ToString("yyyy-MM-dd_hh-mm-ss") + "/"; String path = KSPUtil.ApplicationRootPath + "/GameData/" + name; Directory.CreateDirectory(path); // Colormap if (options.ExportColor) { // Save it colorMap.SetPixels(colorMapValues); yield return(null); if (options.SaveToDisk) { File.WriteAllBytes(path + celestialBody.transform.name + "_Color.png", colorMap.EncodeToPNG()); colorMap.name = name + celestialBody.transform.name + "_Color.png"; yield return(null); } // Apply it if (options.ApplyToScaled) { colorMap.Apply(); ScaledSpaceOnDemand od = celestialBody.scaledBody.GetComponent <ScaledSpaceOnDemand>(); if (od != null) { od.texture = colorMap.name; UnityEngine.Object.DestroyImmediate(colorMap); if (od.isLoaded) { od.UnloadTextures(); od.LoadTextures(); } } else { celestialBody.scaledBody.GetComponent <MeshRenderer>().sharedMaterial.SetTexture("_MainTex", colorMap); } } else { UnityEngine.Object.DestroyImmediate(colorMap); } } if (options.ExportHeight) { heightMap.SetPixels(heightMapValues); yield return(null); if (options.SaveToDisk) { File.WriteAllBytes(path + celestialBody.transform.name + "_Height.png", heightMap.EncodeToPNG()); yield return(null); } if (options.ExportNormal) { // Bump to Normal Map Texture2D normalMap = Utility.BumpToNormalMap(heightMap, pqsVersion, options.NormalStrength / 10); yield return(null); if (options.SaveToDisk) { File.WriteAllBytes(path + celestialBody.transform.name + "_Normal.png", normalMap.EncodeToPNG()); normalMap.name = name + celestialBody.transform.name + "_Normal.png"; yield return(null); } // Apply it if (options.ApplyToScaled) { normalMap.Apply(); ScaledSpaceOnDemand od = celestialBody.scaledBody.GetComponent <ScaledSpaceOnDemand>(); if (od != null) { od.normals = normalMap.name; UnityEngine.Object.DestroyImmediate(normalMap); if (od.isLoaded) { od.UnloadTextures(); od.LoadTextures(); } } else { celestialBody.scaledBody.GetComponent <MeshRenderer>().sharedMaterial .SetTexture("_BumpMap", normalMap); } } else { UnityEngine.Object.DestroyImmediate(normalMap); } } UnityEngine.Object.DestroyImmediate(heightMap); } // Close the Renderer pqsVersion.enabled = true; pqsVersion.CloseExternalRender(); // Declare that we're done ScreenMessages.RemoveMessage(message); ScreenMessages.PostScreenMessage("Operation completed in: " + (DateTime.Now - now).TotalMilliseconds + " ms", 2f, ScreenMessageStyle.UPPER_CENTER); }
public static void DrawGroundMarker(CelestialBody body, double latitude, double longitude, Color c, bool map, double rotation = 0, double radius = 0) { Vector3d up = body.GetSurfaceNVector(latitude, longitude); var height = body.pqsController.GetSurfaceHeight(QuaternionD.AngleAxis(longitude, Vector3d.down) * QuaternionD.AngleAxis(latitude, Vector3d.forward) * Vector3d.right); if (height < body.Radius) { height = body.Radius; } Vector3d center = body.position + height * up; Vector3d camPos = map ? ScaledSpace.ScaledToLocalSpace(PlanetariumCamera.Camera.transform.position) : (Vector3d)FlightCamera.fetch.mainCamera.transform.position; if (IsOccluded(center, body, camPos)) { return; } Vector3d north = Vector3d.Exclude(up, body.transform.up).normalized; if (radius <= 0) { radius = map ? body.Radius / 15 : 5; } if (!map) { Vector3 centerPoint = FlightCamera.fetch.mainCamera.WorldToViewportPoint(center); if (centerPoint.z < 0) { return; } } GLTriangle( center, center + radius * (QuaternionD.AngleAxis(rotation - 10, up) * north), center + radius * (QuaternionD.AngleAxis(rotation + 10, up) * north) , c, map); GLTriangle( center, center + radius * (QuaternionD.AngleAxis(rotation + 110, up) * north), center + radius * (QuaternionD.AngleAxis(rotation + 130, up) * north) , c, map); GLTriangle( center, center + radius * (QuaternionD.AngleAxis(rotation - 110, up) * north), center + radius * (QuaternionD.AngleAxis(rotation - 130, up) * north) , c, map); }
public void Update() { this.impactHappening = false; if (FlightGlobals.ActiveVessel.mainBody.pqsController != null) { //do impact site calculations this.impactHappening = true; this.impactTime = 0; this.impactLongitude = 0; this.impactLatitude = 0; this.impactAltitude = 0; var e = FlightGlobals.ActiveVessel.orbit.eccentricity; //get current position direction vector var currentpos = this.RadiusDirection(FlightGlobals.ActiveVessel.orbit.trueAnomaly * Units.RAD_TO_DEG); //calculate longitude in inertial reference frame from that var currentirflong = 180 * Math.Atan2(currentpos.x, currentpos.y) / Math.PI; //experimentally determined; even for very flat trajectories, the errors go into the sub-millimeter area after 5 iterations or so const int impactiterations = 6; //do a few iterations of impact site calculations for (var i = 0; i < impactiterations; i++) { if (FlightGlobals.ActiveVessel.orbit.PeA >= this.impactAltitude) { //periapsis must be lower than impact alt this.impactHappening = false; } if ((FlightGlobals.ActiveVessel.orbit.eccentricity < 1) && (FlightGlobals.ActiveVessel.orbit.ApA <= this.impactAltitude)) { //apoapsis must be higher than impact alt this.impactHappening = false; } if ((FlightGlobals.ActiveVessel.orbit.eccentricity >= 1) && (FlightGlobals.ActiveVessel.orbit.timeToPe <= 0)) { //if currently escaping, we still need to be before periapsis this.impactHappening = false; } if (!this.impactHappening) { this.impactTime = 0; this.impactLongitude = 0; this.impactLatitude = 0; this.impactAltitude = 0; break; } double impacttheta = 0; if (e > 0) { //in this step, we are using the calculated impact altitude of the last step, to refine the impact site position double costheta = (FlightGlobals.ActiveVessel.orbit.PeR * (1 + e) / (FlightGlobals.ActiveVessel.mainBody.Radius + this.impactAltitude) - 1) / e; if (costheta < -1d) { costheta = -1d; } else if (costheta > 1d) { costheta = 1d; } impacttheta = -180 * Math.Acos(costheta) / Math.PI; } //calculate time to impact this.impactTime = FlightGlobals.ActiveVessel.orbit.timeToPe - this.TimeToPeriapsis(impacttheta); //calculate position vector of impact site var impactpos = this.RadiusDirection(impacttheta); //calculate longitude of impact site in inertial reference frame var impactirflong = 180 * Math.Atan2(impactpos.x, impactpos.y) / Math.PI; var deltairflong = impactirflong - currentirflong; //get body rotation until impact var bodyrot = 360 * this.impactTime / FlightGlobals.ActiveVessel.mainBody.rotationPeriod; //get current longitude in body coordinates var currentlong = FlightGlobals.ActiveVessel.longitude; //finally, calculate the impact longitude in body coordinates this.impactLongitude = this.NormAngle(currentlong - deltairflong - bodyrot); //calculate impact latitude from impact position this.impactLatitude = 180 * Math.Asin(impactpos.z / impactpos.magnitude) / Math.PI; //calculate the actual altitude of the impact site //altitude for long/lat code stolen from some ISA MapSat forum post; who knows why this works, but it seems to. var rad = QuaternionD.AngleAxis(this.impactLongitude, Vector3d.down) * QuaternionD.AngleAxis(this.impactLatitude, Vector3d.forward) * Vector3d.right; this.impactAltitude = FlightGlobals.ActiveVessel.mainBody.pqsController.GetSurfaceHeight(rad) - FlightGlobals.ActiveVessel.mainBody.pqsController.radius; if ((this.impactAltitude < 0) && FlightGlobals.ActiveVessel.mainBody.ocean) { this.impactAltitude = 0; } } } // Set accessable properties. if (this.impactHappening) { ShowDetails = true; Time = this.impactTime; Longitude = this.impactLongitude; Latitude = this.impactLatitude; Altitude = this.impactAltitude; try { Biome = ScienceUtil.GetExperimentBiome(FlightGlobals.ActiveVessel.mainBody, this.impactLatitude, this.impactLongitude); } catch (Exception ex) { MyLogger.Log("GetExperimentBiome(" + FlightGlobals.ActiveVessel.mainBody.name + ", " + this.impactLatitude + ", " + this.impactLongitude + ") died"); MyLogger.Exception(ex); Biome = "<failed>"; } } else { ShowDetails = false; } }
public static double PQSSurfaceHeight(double latitude, double longitude, CelestialBody body) { if (body.pqsController != null) { Vector3d pqsRadialVector = QuaternionD.AngleAxis(longitude, Vector3d.down) * QuaternionD.AngleAxis(latitude, Vector3d.forward) * Vector3d.right; return(body.pqsController.GetSurfaceHeight(pqsRadialVector) - body.pqsController.radius); } else { return(0); } }
public static Coordinates GetMouseCoordinates(CelestialBody body) { Ray mouseRay = PlanetariumCamera.Camera.ScreenPointToRay(Input.mousePosition); mouseRay.origin = ScaledSpace.ScaledToLocalSpace(mouseRay.origin); Vector3d relOrigin = mouseRay.origin - body.position; Vector3d relSurfacePosition; double curRadius = body.pqsController.radiusMax; double lastRadius = 0; double error = 0; int loops = 0; float st = Time.time; while (loops < 50) { if (PQS.LineSphereIntersection(relOrigin, mouseRay.direction, curRadius, out relSurfacePosition)) { Vector3d surfacePoint = body.position + relSurfacePosition; double alt = body.pqsController.GetSurfaceHeight(QuaternionD.AngleAxis(body.GetLongitude(surfacePoint), Vector3d.down) * QuaternionD.AngleAxis(body.GetLatitude(surfacePoint), Vector3d.forward) * Vector3d.right); error = Math.Abs(curRadius - alt); if (error < (body.pqsController.radiusMax - body.pqsController.radiusMin) / 100) { return(new Coordinates(body.GetLatitude(surfacePoint), MuUtils.ClampDegrees180(body.GetLongitude(surfacePoint)))); } else { lastRadius = curRadius; curRadius = alt; loops++; } } else { if (loops == 0) { break; } else { // Went too low, needs to try higher curRadius = (lastRadius * 9 + curRadius) / 10; loops++; } } } return(null); }
/// <summary> /// Generates planet maps from a PQS /// </summary> private void GenerateMaps(Int32 width, Body body, PQSMod[] mods, Boolean hasOcean, Double oceanHeight, Color oceanColor, Single normalStrength, out Texture2D colorMap, out Texture2D heightMap, out Texture2D normalMap) { // Generate the textures Int32 height = width / 2; colorMap = new Texture2D(width, height, TextureFormat.ARGB32, true); heightMap = new Texture2D(width, height, TextureFormat.RGB24, true); // Create a VertexBuildData PQS.VertexBuildData data = new PQS.VertexBuildData(); Vector3 direction; // Build the textures for (Int32 y = 0; y < height; y++) { for (Int32 x = 0; x < width; x++) { // Update the build data data.directionFromCenter = QuaternionD.AngleAxis(360d / width * x, Vector3d.up) * QuaternionD.AngleAxis(90d - 180d / height * y, Vector3d.right) * Vector3d.forward; data.vertHeight = body.pqs.Value.radius; // Map coords data.latitude = Math.Asin(data.directionFromCenter.y); if (double.IsNaN(data.latitude)) { data.latitude = 1.5707963267948966; } direction = new Vector3d(data.directionFromCenter.x, 0.0, data.directionFromCenter.z).normalized; if (direction.magnitude > 0.0) { data.longitude = direction.z >= 0.0 ? Math.Asin(direction.x) : Math.PI - Math.Asin(direction.x); } else { data.longitude = 0.0; } data.v = data.latitude / 3.1415926535897931 + 0.5; data.u = data.longitude / 3.1415926535897931 * 0.5; // Build the height data for (Int32 i = 0; i < mods.Length; i++) { mods[i].OnVertexBuildHeight(data); } // Build the color data for (Int32 i = 0; i < mods.Length; i++) { mods[i].OnVertexBuild(data); } // Process the height Single h = (Single)((data.vertHeight - body.pqs.Value.radius) * (1d / body.pqs.Value.mapMaxHeight)); if (h < 0) { h = 0; } else if (h > 1) { h = 1; } heightMap.SetPixel(x, y, new Color(h, h, h)); // Process the color if (hasOcean) { if (h <= oceanHeight) { colorMap.SetPixel(x, y, oceanColor.A(1f)); } else { colorMap.SetPixel(x, y, data.vertColor.A(0f)); } } else { colorMap.SetPixel(x, y, data.vertColor.A(1f)); } } } // Generate the normalmap normalMap = Utility.BumpToNormalMap(heightMap, normalStrength); }
/// <summary> /// By default, KSP merges all scatters that are on one quad into one gigantic mesh that is then rendered. /// Because Kopernicus wants to know where each scatter actually is, we have to create our own scatter objects /// </summary> private void CreateScatterMeshes(PQSMod_LandClassScatterQuad quad) { Random.InitState(quad.seed); // Redo the density calculation if (useBetterDensity) { Dictionary <String, Double> densities = quad.quad.GetComponent <DensityContainer>().densities; if (densities.ContainsKey(quad.scatter.scatterName)) { Double density = densities[quad.scatter.scatterName]; if (ignoreDensityGameSetting && PQS.Global_ScatterFactor > 0) { density /= PQS.Global_ScatterFactor; } Double scatterN = density * quad.scatter.densityFactor * (quad.quad.quadArea / quad.quad.sphereRoot.radius / 1000.0) * quad.scatter.maxScatter; scatterN += Random.Range(-0.5f, 0.5f); quad.count = Math.Min((Int32)Math.Round(scatterN), quad.scatter.maxScatter); } } for (Int32 i = 0; i < quad.count; i++) { if (useBetterDensity) { // Generate a random number between 0 and 1. If it is above the spawn chance, abort if (Random.value > spawnChance) { continue; } } Int32 num2 = -1; Int32 num3 = -1; while (num3 == num2) { Int32 num4 = Random.Range(1, PQS.cacheRes + 1); Int32 num5 = Random.Range(1, PQS.cacheRes + 1); Int32 x = num4 + Random.Range(-1, 1); Int32 z = num5 + Random.Range(-1, 1); num3 = PQS.vi(num4, num5); num2 = PQS.vi(x, z); } Vector3 scatterPos = Vector3.Lerp(quad.quad.verts[num3], quad.quad.verts[num2], Random.value); Vector3 scatterUp = quad.quad.sphereRoot.surfaceRelativeQuads ? (Vector3)(scatterPos + quad.quad.positionPlanet).normalized : scatterPos.normalized; scatterPos += scatterUp * quad.scatter.verticalOffset; Single scatterAngle = Random.Range(rotation[0], rotation[1]); Quaternion scatterRot = QuaternionD.AngleAxis(scatterAngle, scatterUp) * quad.quad.quadRotation; Single scatterScale = Random.Range(quad.scatter.minScale, quad.scatter.maxScale); // Create a new object for the scatter GameObject scatterObject = new GameObject("Scatter"); scatterObject.transform.parent = quad.obj.transform; scatterObject.transform.localPosition = scatterPos; scatterObject.transform.localRotation = scatterRot; scatterObject.transform.localScale = Vector3.one * scatterScale; scatterObject.AddComponent <KopernicusSurfaceObject>().objectName = quad.scatter.scatterName; MeshFilter filter = scatterObject.AddComponent <MeshFilter>(); filter.sharedMesh = meshes.Count > 0 ? meshes[Random.Range(0, meshes.Count)] : baseMesh; MeshRenderer renderer = scatterObject.AddComponent <MeshRenderer>(); renderer.sharedMaterial = quad.scatter.material; renderer.shadowCastingMode = quad.scatter.castShadows ? ShadowCastingMode.On : ShadowCastingMode.Off; renderer.receiveShadows = quad.scatter.recieveShadows; scatterObject.layer = GameLayers.LOCAL_SPACE; scatterObjects.Add(scatterObject); } quad.obj.name = "Kopernicus-" + quad.scatter.scatterName; quad.obj.AddOrGetComponent <ScatterDistanceCuller>(); }
public static void updatePlanetaryResourceMap() { if (FlightGlobals.currentMainBody.flightGlobalsIndex != current_body) { loadPlanetaryResourceData(FlightGlobals.currentMainBody.flightGlobalsIndex); } if (body_resource_maps.ContainsKey(displayed_resource) && (FlightGlobals.currentMainBody.flightGlobalsIndex != map_body || displayed_resource != map_resource)) { foreach (ORSResourceAbundanceMarker abundance_marker in abundance_markers) { removeAbundanceSphere(abundance_marker.getPlanetarySphere()); removeAbundanceSphere(abundance_marker.getScaledSphere()); } abundance_markers.Clear(); CelestialBody celbody = FlightGlobals.currentMainBody; sphere_texture = body_resource_maps[displayed_resource].getDisplayTexturePath(); Vector2d[] abundance_points_list = body_abudnance_angles[displayed_resource]; if (abundance_points_list != null && celbody.pqsController != null) { foreach (Vector2d abundance_point in abundance_points_list) { double theta = abundance_point.x; double phi = abundance_point.y; Vector3d up = celbody.GetSurfaceNVector(phi, theta).normalized; double surface_height = celbody.pqsController.GetSurfaceHeight(QuaternionD.AngleAxis(theta, Vector3d.down) * QuaternionD.AngleAxis(phi, Vector3d.forward) * Vector3d.right); GameObject resource_prim = createAbundanceSphere(); GameObject resource_prim_scaled = createAbundanceSphere(); Vector3d center = celbody.position + surface_height * up; Vector3d scaledcenter = ScaledSpace.LocalToScaledSpace(celbody.position) + surface_height * up * ScaledSpace.InverseScaleFactor; Transform scaled_trans = PSystemManager.Instance.localBodies.Select(b => b.scaledBody.transform).Single(t => t.name == celbody.name); resource_prim_scaled.transform.position = scaledcenter; resource_prim_scaled.transform.localScale = sphere_scale_scaled * (FlightGlobals.currentMainBody.Radius / FlightGlobals.Bodies[ORSGameConstants.REF_BODY_KERBIN].Radius); resource_prim_scaled.transform.localRotation = Quaternion.identity; resource_prim_scaled.transform.parent = scaled_trans; resource_prim_scaled.layer = 10; resource_prim.transform.position = center; resource_prim.transform.parent = celbody.transform; resource_prim.transform.localScale = sphere_scale * (FlightGlobals.currentMainBody.Radius / FlightGlobals.Bodies[ORSGameConstants.REF_BODY_KERBIN].Radius); resource_prim.transform.localRotation = Quaternion.identity; ORSResourceAbundanceMarker abundance_marker = new ORSResourceAbundanceMarker(resource_prim_scaled, resource_prim); abundance_markers.Add(abundance_marker); } map_body = current_body; map_resource = displayed_resource; stored_scale = ScaledSpace.ScaleFactor; } //celbody.renderer.material.mainTexture. } else { if (body_resource_maps.ContainsKey(displayed_resource) && FlightGlobals.currentMainBody.flightGlobalsIndex == map_body && displayed_resource == map_resource) { CelestialBody celbody = FlightGlobals.currentMainBody; foreach (ORSResourceAbundanceMarker abundance_marker in abundance_markers) { if (lineOfSightToPosition(abundance_marker.getPlanetarySphere().transform.position, celbody)) { if (MapView.MapIsEnabled) { abundance_marker.getScaledSphere().GetComponent <Renderer>().enabled = true; abundance_marker.getPlanetarySphere().GetComponent <Renderer>().enabled = false; } else { abundance_marker.getScaledSphere().GetComponent <Renderer>().enabled = false; abundance_marker.getPlanetarySphere().GetComponent <Renderer>().enabled = true; } } else { abundance_marker.getScaledSphere().GetComponent <Renderer>().enabled = false; abundance_marker.getPlanetarySphere().GetComponent <Renderer>().enabled = false; } } } } }
// Get true altitude above terrain (from MuMech lib) // Also from: http://kerbalspaceprogram.com/forum/index.php?topic=10324.msg161923#msg161923 private static double getTrueAltitude(Vessel vessel) { Vector3 CoM = vessel.findWorldCenterOfMass(); Vector3 up = (CoM - vessel.mainBody.position).normalized; double altitudeASL = vessel.mainBody.GetAltitude(CoM); double altitudeTrue = 0.0; RaycastHit sfc; if (Physics.Raycast(CoM, -up, out sfc, (float)altitudeASL + 10000.0F, 1 << 15)) { altitudeTrue = sfc.distance; } else if (vessel.mainBody.pqsController != null) { altitudeTrue = vessel.mainBody.GetAltitude(CoM) - (vessel.mainBody.pqsController.GetSurfaceHeight(QuaternionD.AngleAxis(vessel.mainBody.GetLongitude(CoM), Vector3d.down) * QuaternionD.AngleAxis(vessel.mainBody.GetLatitude(CoM), Vector3d.forward) * Vector3d.right) - vessel.mainBody.pqsController.radius); } else { altitudeTrue = vessel.mainBody.GetAltitude(CoM); } return(altitudeTrue); }
public void FixedUpdate() { var vessel = GetComponent <Vessel>(); if (vessel != FlightGlobals.ActiveVessel) { KRASHShelter.instance.SetSimActiveNotification(); Destroy(this); return; } KRASHShelter.instance.SetSimNotification("Simulation Setup in Progress"); if (AlreadyTeleported) { if (vessel.LandedOrSplashed) { if (!pausebeforestarting) { pausebeforestarting = true; FlightDriver.SetPause(true); //PopupDialog.SpawnPopupDialog ("Vessel has landed", "Vessel has landed. Click the OK button to continue", "OK", true, HighLogic.Skin); var dialog = new MultiOptionDialog("krash4", "Vessel has landed. Click the OK button to continue", "", HighLogic.UISkin, new DialogGUIBase[] { new DialogGUIButton("OK", () => { FlightDriver.SetPause(false); KRASHShelter.instance.SetSimActiveNotification(); Destroy(this); // _activePopup.Dismiss (); }) }); PopupDialog.SpawnPopupDialog(new Vector2(0.5f, 0.5f), new Vector2(0.5f, 0.5f), dialog, false, HighLogic.UISkin, true); } } else { var accel = (vessel.srf_velocity + vessel.upAxis) * -0.5; vessel.ChangeWorldVelocity(accel); Hyperedit.RateLimitedLogger.Log(accelLogObject, string.Format("(Happening every frame) Soft-lander changed ship velocity this frame by vector {0},{1},{2} (mag {3})", accel.x, accel.y, accel.z, accel.magnitude)); } } else { Log.Info("NotTeleported"); var pqs = Body.pqsController; if (pqs == null) { KRASHShelter.instance.SetSimActiveNotification(); Destroy(this); return; } var alt = pqs.GetSurfaceHeight( QuaternionD.AngleAxis(Longitude, Vector3d.down) * QuaternionD.AngleAxis(Latitude, Vector3d.forward) * Vector3d.right) - pqs.radius; alt = Math.Max(alt, 0); // Underwater! if (TimeWarp.CurrentRateIndex != 0) { TimeWarp.SetRate(0, true); Log.Info("Set time warp to index 0"); } // HoldVesselUnpack is in display frames, not physics frames Log.Info("alt: " + alt.ToString() + " Altitude: " + Altitude.ToString()); Log.Info("Latitude: " + Latitude.ToString() + " Longitude: " + Longitude.ToString()); //var teleportPosition = Body.GetRelSurfacePosition(Latitude, Longitude, alt + Altitude); var teleportPosition = Body.GetWorldSurfacePosition(Latitude, Longitude, alt + Altitude) - Body.position; var teleportVelocity = Body.getRFrmVel(teleportPosition + Body.position); // convert from world space to orbit space teleportPosition = teleportPosition.xzy; teleportVelocity = teleportVelocity.xzy; // counter for the momentary fall when on rails (about one second) teleportVelocity += teleportPosition.normalized * (Body.gravParameter / teleportPosition.sqrMagnitude); // var oldUp = vessel.orbit.pos.xzy.normalized; // can also be vessel.vesselTransform.position, I think // var newUp = teleportPosition.xzy.normalized; // teleportPosition is the orbitspace location (note the .xzy) // var rotation = Quaternion.FromToRotation(oldUp, newUp)*vessel.vesselTransform.rotation; var from = Vector3d.up; var to = teleportPosition.xzy.normalized; var rotation = Quaternion.FromToRotation(from, to); var orbit = vessel.orbitDriver.orbit.Clone(); orbit.UpdateFromStateVectors(teleportPosition, teleportVelocity, Body, Planetarium.GetUniversalTime()); vessel.SetOrbit(orbit); vessel.SetRotation(rotation); AlreadyTeleported = true; } }
public static QuaternionD BodyRotationAtdT(CelestialBody Body, double dT) { var angle = -(dT / Body.rotationPeriod * 360 % 360.0); return(QuaternionD.AngleAxis(angle, Body.zUpAngularVelocity.normalized)); }
public void RandomizePosition(bool waterAllowed = true) { System.Random generator = new System.Random(seed + id); CelestialBody myPlanet = null; foreach (CelestialBody body in FlightGlobals.Bodies) { if (body.GetName() == celestialName) { myPlanet = body; } } if (myPlanet != null) { if (myPlanet.ocean && !waterAllowed) { if (myPlanet.pqsController != null) { while (true) { double rand = generator.NextDouble(); rand = 1.0 - (rand * 2); latitude = Math.Asin(rand) * UnityEngine.Mathf.Rad2Deg; longitude = generator.NextDouble() * 360 - 180; Vector3d pqsRadialVector = QuaternionD.AngleAxis(longitude, Vector3d.down) * QuaternionD.AngleAxis(latitude, Vector3d.forward) * Vector3d.right; double chosenHeight = myPlanet.pqsController.GetSurfaceHeight(pqsRadialVector) - myPlanet.pqsController.radius; if (chosenHeight < 0) { continue; } else { break; } } } } else { double rand = generator.NextDouble(); rand = 1.0 - (rand * 2); latitude = Math.Asin(rand) * UnityEngine.Mathf.Rad2Deg; longitude = generator.NextDouble() * 360 - 180; } } }
public void RandomizeNear(double centerLatitude, double centerLongitude, string celestialName, double searchRadius, bool waterAllowed = true) { CelestialBody myPlanet = null; System.Random generator = new System.Random(this.seed + this.id); foreach (CelestialBody body in FlightGlobals.Bodies) { if (body.GetName() == celestialName) { myPlanet = body; } } if (myPlanet != null) { if (myPlanet.ocean && !waterAllowed) { if (myPlanet.pqsController != null) { while (true) { double distancePerDegree = (myPlanet.Radius * 2 * UnityEngine.Mathf.PI) / 360.0; double lng_min = centerLongitude - searchRadius / UnityEngine.Mathf.Abs(UnityEngine.Mathf.Cos(UnityEngine.Mathf.Deg2Rad * (float)centerLatitude) * (float)distancePerDegree); double lng_max = centerLongitude + searchRadius / UnityEngine.Mathf.Abs(UnityEngine.Mathf.Cos(UnityEngine.Mathf.Deg2Rad * (float)centerLatitude) * (float)distancePerDegree); double lat_min = centerLatitude - (searchRadius / distancePerDegree); double lat_max = centerLatitude + (searchRadius / distancePerDegree); latitude = lat_min + generator.NextDouble() * (lat_max - lat_min); longitude = lng_min + generator.NextDouble() * (lng_max - lng_min); Vector3d pqsRadialVector = QuaternionD.AngleAxis(longitude, Vector3d.down) * QuaternionD.AngleAxis(latitude, Vector3d.forward) * Vector3d.right; double chosenHeight = myPlanet.pqsController.GetSurfaceHeight(pqsRadialVector) - myPlanet.pqsController.radius; if (chosenHeight < 0) { continue; } else { break; } } } } else { double distancePerDegree = (myPlanet.Radius * 2 * UnityEngine.Mathf.PI) / 360.0; double lng_min = centerLongitude - searchRadius / UnityEngine.Mathf.Abs(UnityEngine.Mathf.Cos(UnityEngine.Mathf.Deg2Rad * (float)centerLatitude) * (float)distancePerDegree); double lng_max = centerLongitude + searchRadius / UnityEngine.Mathf.Abs(UnityEngine.Mathf.Cos(UnityEngine.Mathf.Deg2Rad * (float)centerLatitude) * (float)distancePerDegree); double lat_min = centerLatitude - (searchRadius / distancePerDegree); double lat_max = centerLatitude + (searchRadius / distancePerDegree); latitude = lat_min + generator.NextDouble() * (lat_max - lat_min); longitude = lng_min + generator.NextDouble() * (lng_max - lng_min); } } }
//This method is called periodically by the main thread, //then it decides if it is time to start a new thread or not public static void update() { now = Time.time; //double lc = lastCharge; //float lt = lastTime; //lastTime = now; double dt = TimeWarp.fixedDeltaTime; //Log.Info("Starting simulation at " + Math.Round(now, 3)); isReadable = false; //Reset some stuff ship = FlightGlobals.ActiveVessel; body = ship.mainBody; _gravity = FlightGlobals.getGeeForceAtPosition(ship.CoM).magnitude; _electricCharge = 0; _electricMax = 0; _evamaxfuel = 0; _evafuel = 0; _liquidFuel = 0; _liquidMax = 0; _monoFuel = 0; _monoMax = 0; _airin = 0; _airreq = 0; _mass = 0; _max_thrust = 0; _cur_thrust = 0; _isp = 0; _engineaccel = 0; _enginedecel = 0; _max_temp_percent = 0; _max_temp_actual = 0; double _best_temp = 0; double _best_ablation = 0; _ablation = 1; _tarpos = new Vector3(); // int cur_stage = ship.currentStage; //Log.Info("##########################"); //Log.Info("Current Stage: "+cur_stage); //Part loop - look at all the parts and modules for stuff. foreach (Part P in ship.parts) { /*Log.Info("-------------------------------"); * Log.Info(P.name); * Log.Info("inStageIndex: " + P.inStageIndex); * Log.Info("inv Stage: " + P.inverseStage); * Log.Info("Manual Stage Offset: " + P.manualStageOffset); * Log.Info("Computed Stage: " + (P.inverseStage + P.manualStageOffset)); * Log.Info("Original Stage: " + P.originalStage); * //Log.Info("Stage After: " + P.stageAfter); * //Log.Info("Stage Before: " + P.stageBefore); * Log.Info("Stage Offset: " + P.stageOffset); */ //Temperature //First, look at internal temps _best_temp = P.temperature / P.maxTemp; if (_best_temp > _max_temp_percent) { _max_temp_percent = _best_temp; _max_temp_actual = P.temperature; } //Now look at skin temps _best_temp = P.skinTemperature / P.skinMaxTemp; if (_best_temp > _max_temp_percent) { _max_temp_percent = _best_temp; _max_temp_actual = P.skinTemperature; } //if (P.temperature/P.maxTemp > .5) Log.Info(P.partInfo.title + " Temp: " + (int)P.temperature + "⁰/" + (int)P.maxTemp + "⁰"); //Vessel Mass if (P.physicalSignificance == Part.PhysicalSignificance.FULL) { _mass += P.mass; _mass += P.GetResourceMass(); } //Resource loop foreach (PartResource pr in P.Resources) { if (pr.resourceName.Equals("ElectricCharge")) { _electricCharge += pr.amount; _electricMax += pr.maxAmount; } else if (pr.resourceName.Equals("LiquidFuel")) { _liquidFuel += pr.amount; _liquidMax += pr.maxAmount; } else if (pr.resourceName.Equals("MonoPropellant")) { _monoFuel += pr.amount; _monoMax += pr.maxAmount; } else if (pr.resourceName.Equals("Ablator")) { _best_ablation = pr.amount / pr.maxAmount; if (_best_ablation < _ablation) { _ablation = _best_ablation; } } } //Module loop foreach (PartModule pm in P.Modules) { if (pm is KerbalEVA) { KerbalEVA eva = pm as KerbalEVA; _evafuel = eva.Fuel; _evamaxfuel = eva.FuelCapacity; } else if (pm is ModuleWheelDeployment) { _gearstate = 0; //Gear up ModuleWheelDeployment mlg = pm as ModuleWheelDeployment; if (mlg.stateString.Equals("Deployed")) { _gearstate = 3; } if (mlg.stateString.StartsWith("Deploying")) { _gearstate = 2; } if (mlg.stateString.StartsWith("Retracting")) { _gearstate = 1; } } else if (pm is ModuleEngines) { ModuleEngines ME = pm as ModuleEngines; if (ME.engineShutdown || !ME.EngineIgnited) //We don't care about engines that aren't ready to fire { continue; } if (1 / ME.engineAccelerationSpeed > _engineaccel) { _engineaccel = 1 / ME.engineAccelerationSpeed; } if (1 / ME.engineDecelerationSpeed > _enginedecel) { _enginedecel = 1 / ME.engineDecelerationSpeed; } //Thrust/ISP float thrust = ME.maxThrust * ME.thrustPercentage * 0.01f; _isp += thrust / ME.atmosphereCurve.Evaluate((float)ship.staticPressurekPa); _max_thrust += thrust; _cur_thrust += ME.finalThrust; //I think this is the actual thrust being produced by each engine //Intake Air foreach (Propellant Pro in ME.propellants) { if (Pro.name.Equals("IntakeAir")) { _airreq += Pro.currentRequirement; } } //Overheat stuff //if (!ME.flameout) //{ // double temp = P.temperature / P.maxTemp; // if (temp > _max_temp) _max_temp = temp; //} } else if (pm is ModuleEnginesFX) //The newer engines all use this instead of ModuleEngines { ModuleEnginesFX MFX = pm as ModuleEnginesFX; if (MFX.engineShutdown || !MFX.EngineIgnited) { continue; } //Thrust/ISP float thrust = MFX.maxThrust * MFX.thrustPercentage * 0.01f; _isp += thrust / MFX.atmosphereCurve.Evaluate((float)ship.staticPressurekPa); _max_thrust += thrust; _cur_thrust += MFX.finalThrust; //Overheat stuff //if (!MFX.flameout) //{ // double temp = P.temperature / P.maxTemp; // if (temp > _max_temp) _max_temp = temp; //} if (1 / MFX.engineAccelerationSpeed > _engineaccel) { _engineaccel = 1 / MFX.engineAccelerationSpeed; } if (1 / MFX.engineDecelerationSpeed > _enginedecel) { _enginedecel = 1 / MFX.engineDecelerationSpeed; } foreach (Propellant Pro in MFX.propellants) { if (Pro.name.Equals("IntakeAir")) { _airreq += Pro.currentRequirement; } } } else if (pm is ModuleResourceIntake) { ModuleResourceIntake MRI = pm as ModuleResourceIntake; if (MRI.intakeEnabled && MRI.resourceName.Equals("IntakeAir")) { _airin += MRI.airFlow * dt; //Using dt courtesy of FAR } } } } _isp = _max_thrust / _isp; //Complete the average isp if (_engineaccel == 0) { _engineaccel = 2; //Default in case the engines don't define it } //Log.Info("Engine Acceleration: " + Math.Round(_engineaccel, 2)); //Electric charge rate if (now - lastTime > 0.1) { _chargeRate = (_electricCharge - lastCharge) / (now - lastTime); lastCharge = _electricCharge; lastTime = now; } //Clamp to +30/-30 if (_chargeRate > 30) { _chargeRate = 30; } if (_chargeRate < -30) { _chargeRate = -30; } //Maneuver node burn calculations //dv = Isp * ln (m0 / m1) //e^(dv/ISP) = m0/m1 //m1 = m0/e^(dv/ISP) //mass flow = thrust/isp try { ManeuverNode myNode = FlightGlobals.ActiveVessel.patchedConicSolver.maneuverNodes.ToArray()[0]; double deltaVRem = myNode.GetBurnVector(ship.orbit).magnitude; //Remaining ΔV in the burn, per maneuver node //Log.Info("dV: " + Math.Round(deltaVRem, 1)); //Log.Info("Mass: " + Math.Round(_mass, 1) + " ISP: " + Math.Round(_isp, 1)); _final_mass = _mass / Math.Pow(Math.E, (deltaVRem / (_isp * 9.82))); //Mass after burn Changed from 9.821 //Log.Info("Final Mass: " + Math.Round(_final_mass, 1)); _mass_rate = _max_thrust / (_isp * 9.82); //Mass flow rate //Log.Info("Mass Rate: " + Math.Round(_mass_rate, 3)); //Log.Info("Burn Mass: " + Math.Round(_mass - _final_mass, 1)); _burn_time = (_mass - _final_mass);// / _mass_rate; //Time it takes for this burn //Log.Info("Burn Mass: " + _burn_time); _burn_time = _burn_time / _mass_rate; //Log.Info("Burn Time: " + _burn_time); _burn_time += 2d; //_burn_time += _engineaccel + _enginedecel; //Factor in slow throttles } catch { _burn_time = 0; _final_mass = 0; _mass_rate = 0; } //Heading, courtesy of MechJeb /*Vector3d CoM, up, north, east, forward, rootPartPos; * Quaternion rotationSurface, rotationVesselSurface; * CoM = ship.findWorldCenterOfMass(); * up = (CoM - body.position).normalized; * Rigidbody rigidBody = ship.rootPart.rigidbody; * if (rigidBody != null) rootPartPos = rigidBody.position; * north = Vector3d.Exclude(up, (body.position + body.transform.up * (float)body.Radius) - CoM).normalized; * east = body.getRFrmVel(CoM).normalized; * forward = ship.GetTransform().up; * rotationSurface = Quaternion.LookRotation(north, up); * rotationVesselSurface = Quaternion.Inverse(Quaternion.Euler(90, 0, 0) * Quaternion.Inverse(ship.GetTransform().rotation) * rotationSurface); * _heading = rotationVesselSurface.eulerAngles.y; */ _heading = FlightGlobals.ship_heading; //Log.Info("FlightGlobals Heading: " + FlightGlobals.ship_heading + " MechJeb Heading: " + _heading); //good //Log.Info("Ship temp: " + FlightGlobals.ship_temp); //not useful //Log.Info("Get dV: " + FlightGlobals.ActiveVessel.GetDeltaV()); //nothing //Log.Info("Surface Height: " + FlightGlobals.ActiveVessel.GetHeightFromSurface() + " Terrain Height: "+FlightGlobals.ActiveVessel.GetHeightFromTerrain()); //not useful //Log.Info("GetMass(): " + FlightGlobals.ActiveVessel.GetTotalMass()+" myMass: "+_mass); //good //Log.Info("IAS: " + FlightGlobals.ActiveVessel.indicatedAirSpeed + " M: " + FlightGlobals.ActiveVessel.mach); //good //Waypoint distance code //Uses the Spherical Law of Cosines from http://www.movable-type.co.uk/scripts/latlong.html NavWaypoint nW = NavWaypoint.fetch; //The active nav waypoint FinePrint.WaypointManager WM; WM = FinePrint.WaypointManager.Instance(); if (nW != null && nW.IsActive) { //Convert these all to radians to do actual math on them. double Aa = ship.latitude * Math.PI / 180; double Ao = ship.longitude * Math.PI / 180; double Ba = nW.Latitude * Math.PI / 180; double Bo = nW.Longitude * Math.PI / 180; _nav_dist = Math.Acos(Math.Sin(Aa) * Math.Sin(Ba) + Math.Cos(Aa) * Math.Cos(Ba) * Math.Cos(Bo - Ao)) * FlightGlobals.currentMainBody.Radius; //used to be 600000 double y = Math.Sin(Bo - Ao) * Math.Cos(Ba); double x = Math.Cos(Aa) * Math.Sin(Ba) - Math.Sin(Aa) * Math.Cos(Ba) * Math.Cos(Bo - Ao); double b = Math.Atan2(y, x) * 180 / Math.PI; //Converted to degrees. _nav_heading = (b + 360f) % 360f; //Convert to 0-360 from -180 to 180 } else if (_nav_waypoint != 0) { //defaults _nav_heading = -1; _nav_dist = -1; //now try actually calculating things int count = 0; foreach (FinePrint.Waypoint W in WM.Waypoints) { //we clearly only care about waypoints on this planet if (W.celestialName.Equals(body.name)) { count++; if (count == _nav_waypoint) { //Convert these all to radians to do actual math on them. double Aa = ship.latitude * Math.PI / 180; double Ao = ship.longitude * Math.PI / 180; double Ba = W.latitude * Math.PI / 180; double Bo = W.longitude * Math.PI / 180; _nav_dist = Math.Acos(Math.Sin(Aa) * Math.Sin(Ba) + Math.Cos(Aa) * Math.Cos(Ba) * Math.Cos(Bo - Ao)) * FlightGlobals.currentMainBody.Radius; double y = Math.Sin(Bo - Ao) * Math.Cos(Ba); double x = Math.Cos(Aa) * Math.Sin(Ba) - Math.Sin(Aa) * Math.Cos(Ba) * Math.Cos(Bo - Ao); double b = Math.Atan2(y, x) * 180 / Math.PI; //Converted to degrees. _nav_heading = (b + 360f) % 360f; //Convert to 0-360 from -180 to 180 } } } } else { _nav_dist = -1; //Both of these at -1 implies no nav data _nav_heading = -1f; } //Pitch, yaw, roll /*NavBall ball = FlightUIController.fetch.GetComponentInChildren<NavBall>(); * Quaternion vesselRot = Quaternion.Inverse(ball.relativeGymbal); * pitch = (vesselRot.eulerAngles.x > 180) ? (360 - vesselRot.eulerAngles.x) : -vesselRot.eulerAngles.x; * roll = (vesselRot.eulerAngles.z > 180) ? (360 - vesselRot.eulerAngles.z) : -vesselRot.eulerAngles.z; * yaw = vesselRot.eulerAngles.y; //Heading, more or less * _rel_yaw = AngleAroundNormal(ship.GetObtVelocity(), ship.ReferenceTransform.up, ship.ReferenceTransform.forward); * _rel_pitch = AngleAroundNormal(ship.GetObtVelocity(), ship.ReferenceTransform.up, ship.ReferenceTransform.right); * * //Polars * if (pitch > 0f) * { * Quaternion polarRot = Quaternion.Euler(90, 0, 0) * vesselRot; * polar_yaw = (polarRot.eulerAngles.y + 180f) % 360f - 180f; * polar_pitch = (polarRot.eulerAngles.x + 180f) % 360f - 180f; * polar_roll = polarRot.eulerAngles.z; * } * else * { * Quaternion polarRot = Quaternion.Euler(-90, 0, 0) * vesselRot; * polar_yaw = (polarRot.eulerAngles.y + 180f) % 360f - 180f; * polar_pitch = (polarRot.eulerAngles.x + 180f) % 360f - 180f; * polar_roll = polarRot.eulerAngles.z; * }*/ //Airspeed stuff getAirspeedInfo(ship, out _mach, out _tas, out _eas); //Max Altitude if (ship.Landed) { max_altitude = ship.altitude; //Reset max altitude upon landing/takeoff } if (ship.altitude > max_altitude) { max_altitude = ship.altitude; } //RA double terrain = ship.terrainAltitude; if (terrain < 0) { terrain = 0; } _ra = (long)(body.GetAltitude(ship.CoM) - terrain); //In orbit? inOrbit = false; //Minimum periapsis of 5km if (ship.orbit.PeA > 5000) { //Make sure our orbit is out of the atmosphere, if there is one. if (body.atmosphere) { if (ship.orbit.PeA > body.atmosphereDepth) //is this the same as max altitude? { inOrbit = true; } } else { inOrbit = true; } } //Closest approach and time, target distance and speed if (FlightGlobals.fetch.VesselTarget != null) { Orbit TO = FlightGlobals.fetch.VesselTarget.GetOrbit(); Vector3d aPos = ship.ReferenceTransform.position; //Control source's position Vector3d tPos = _tarpos = FlightGlobals.fetch.VesselTarget.GetTransform().position; //Rough distance if (FlightGlobals.fetch.VesselTarget is ModuleDockingNode) //Use more precise distance { ModuleDockingNode targetDockingPort = FlightGlobals.fetch.VesselTarget as ModuleDockingNode; tPos = _tarpos = targetDockingPort.controlTransform.position; } // MechJeb guidance targets _don't have an orbit_! else if (TO != null) { // This is in the wrong coordinate system for _tarpos, and only usable for distance aPos = ship.GetOrbit().pos; tPos = TO.pos; } _target_dist = Vector3d.Distance(aPos, tPos); } else { _target_dist = 0; } var target = FlightGlobals.fetch.VesselTarget; // MechJeb guidance targets _don't have an orbit! // Also, landed targets often have invalid orbit that causes error spam and lag. if (target != null && target.GetOrbit() != null) { Orbit O = ship.GetOrbit(); Orbit TO = target.GetOrbit(); double t = Planetarium.GetUniversalTime(); Func <double, Vector3d> targetPosAt = (d => TO.getPositionAtUT(t + d)); // For landed targets the orbit doesn't make sense, so calculate planet rotation if (target.GetVessel() != null && target.GetVessel().LandedOrSplashed) { Vessel ves = target.GetVessel(); CelestialBody vbody = ves.mainBody; Vector3d pos = vbody.GetRelSurfacePosition(ves.latitude, ves.longitude, ves.altitude); targetPosAt = delegate(double d) { double angle = vbody.rotates ? d * 360.0 / vbody.rotationPeriod : 0; return(vbody.position + QuaternionD.AngleAxis(angle, Vector3d.down) * pos); }; } //I chunck my orbit into 100 pieces, and find between which two chuncks the minimum occurs... double period = O.period; double bestDist = double.MaxValue; double bestTime = 0; for (double d = 0; d < period; d += period / 100) //Look at 100 places around the orbit { if (d == 0) { continue; //skip the first time } double dist = Vector3d.Distance(O.getPositionAtUT(t + d), targetPosAt(d)); if (dist < bestDist) { bestDist = dist; bestTime = d; } } //Now, do it again, but over a small section of the orbit centered on the above double start = bestTime - (period / 100); double end = bestTime + (period / 100); for (double d = start; d < end; d += period / 1000) //Look at 100 places within this chunck of orbit { if (d == start) { continue; //Skip the first time } double dist = Vector3d.Distance(O.getPositionAtUT(t + d), targetPosAt(d)); if (dist < bestDist) { bestDist = dist; bestTime = d; } } //And one last time, which is probably overkill start = bestTime - (period / 1000); end = bestTime + (period / 1000); for (double d = start; d < end; d += period / 10000) //Look at 100 places within this chunck of orbit { if (d == start) { continue; //For ease of computation } double dist = Vector3d.Distance(O.getPositionAtUT(t + d), targetPosAt(d)); if (dist < bestDist) { bestDist = dist; bestTime = d; //previous time is my start time } } _closest_time = bestTime; _closest_approach = bestDist; } else { _closest_approach = 0; _closest_time = 0; } //Time to Impact calculations double vertspeed = FlightGlobals.ActiveVessel.verticalSpeed; if (!ship.Landed && vertspeed < 0 && ship.orbit.PeA <= 0 && !ship.mainBody.atmosphere) { double Vf = Math.Sqrt((vertspeed * vertspeed) + 2 * _ra * _gravity); //t = 2d/(Vi+Vf) _tti = (Math.Abs(Vf) - Math.Abs(vertspeed)) / _gravity; _tti++; //So you hit the ground at 0, not 1 second after 0 } else { _tti = 0; } //Suicide Altitude calculations if (ship.Landed || ship.orbit.PeA > 0 || body.atmosphere) { _sa = -1; //Not calculating this } else { _sa = 0; double avgG = body.gravParameter / ((body.Radius + ship.terrainAltitude) * (body.Radius + ship.terrainAltitude)); avgG += FlightGlobals.getGeeForceAtPosition(ship.CoM).magnitude; avgG /= 2; //Log.Info("Average G: " + Math.Round(avgG, 2)); double vdv = Math.Sqrt((2 * avgG * _ra) + (ship.verticalSpeed * ship.verticalSpeed)); //Log.Info("Vertical Delta V: " + Math.Round(vdv, 1)); //Altitude Fraction = (Vertical dv ^2) / (2 * 1000 * Thrust (kN)) double altFrac = (vdv * vdv) / (2 * 1000 * _max_thrust); //Log.Info("Altitude Fraction: " + Math.Round(altFrac, 2)); //m-avg = (m0 + (m0 / e ^ (dv / (Isp * 9.82)))) / 2 double avgMass = (_mass / Math.Pow(Math.E, (vdv / (_isp * 9.82)))); avgMass = (_mass + avgMass) / 2; //Log.Info("Average mass: " + Math.Round(avgMass, 1)); _sa = (long)Math.Round(altFrac * avgMass * 1000); //Log.Info("Suicide Altitude: " + Math.Round(_sa)); } //Cleanup isReadable = true; //Log.Info("Thread simulation complete at " + Math.Round(Time.time, 3)); }
private static void PlaceWaypointAtCursor() { if (targetBody.pqsController == null) { return; } Ray mouseRay = PlanetariumCamera.Camera.ScreenPointToRay(Input.mousePosition); mouseRay.origin = ScaledSpace.ScaledToLocalSpace(mouseRay.origin); var bodyToOrigin = mouseRay.origin - targetBody.position; double curRadius = targetBody.pqsController.radiusMax; double lastRadius = 0; int loops = 0; while (loops < 50) { Vector3d relSurfacePosition; if (PQS.LineSphereIntersection(bodyToOrigin, mouseRay.direction, curRadius, out relSurfacePosition)) { var surfacePoint = targetBody.position + relSurfacePosition; double alt = targetBody.pqsController.GetSurfaceHeight( QuaternionD.AngleAxis(targetBody.GetLongitude(surfacePoint), Vector3d.down) * QuaternionD.AngleAxis(targetBody.GetLatitude(surfacePoint), Vector3d.forward) * Vector3d.right); double error = Math.Abs(curRadius - alt); if (error < (targetBody.pqsController.radiusMax - targetBody.pqsController.radiusMin) / 100) { latitude = targetBody.GetLatitude(surfacePoint).ToString(); longitude = targetBody.GetLongitude(surfacePoint).ToString(); return; } else { lastRadius = curRadius; curRadius = alt; loops++; } } else { if (loops == 0) { break; } // Went too low, needs to try higher else { curRadius = (lastRadius * 9 + curRadius) / 10; loops++; } } } }