private float CalcPeriod(float a, NBody centerMass) { // this equation has a G but we set G=1 float _period = 2f * Mathf.PI * Mathf.Sqrt(a * a * a) / Mathf.Sqrt(centerMass.mass); return(GravityScaler.ScalePeriod(_period));; }
private void UpdateSoiPosition() { targetPoint = new Vector3d(soiRadius * Mathf.Cos(soiAngle) + moonRadius, soiRadius * Mathf.Sin(soiAngle), 0); targetPoint = GravityScaler.ScaleVector3dPhyToScene(targetPoint); }
void Awake() { // Convert from real world units // Rocket engine thrust is given in SI units, so acceleration is in SI units // For Orbital scale need to convert to km/hr^2 accelerationConversion = GravityScaler.AccelSItoGEUnits(); // and convert to the physics scale used in the engine accelerationConversion = accelerationConversion * GravityScaler.AccelGEtoInternalUnits(); burnStart = new double[MAX_STAGES]; burnRateScale = GravityScaler.GetGameSecondPerPhysicsSecond(); fuelLevel = new double[MAX_STAGES]; for (int i = 0; i < numStages; i++) { fuelLevel[i] = massFuel[i]; } // acceleration will be opposite to thrust direction accelDirection = -thrustAxis; activeStage = 0; activeState = GravityEngine.Instance().GetWorldState(); SetEngine(engineOn); }
private void UpdateStartPosition() { startPoint = new Vector3d(shipRadius * Mathf.Cos(shipAngle), shipRadius * Mathf.Sin(shipAngle), 0); startPoint = GravityScaler.ScaleVector3dPhyToScene(startPoint); }
/// <summary> /// Callback to compute the launch transfer times and display them in the scene. /// </summary> public void Compute() { int itemNo = destDropdown.value; NBody toNbody = destinations[itemNo].GetComponent <NBody>(); OrbitData fromOrbit = new OrbitData(); fromOrbit.SetOrbit(fromNbody, centerNbody); OrbitData toOrbit = new OrbitData(); toOrbit.SetOrbit(toNbody, centerNbody); HohmannXfer hohmannXfer = new HohmannXfer(fromOrbit, toOrbit, true); // Find launch windows double[] times = hohmannXfer.LaunchTimes(numWindows); List <Dropdown.OptionData> items = new List <Dropdown.OptionData>(); foreach (double t in times) { Dropdown.OptionData item = new Dropdown.OptionData(); item.text = GravityScaler.GetWorldTimeFormatted(t, GravityScaler.Units.SOLAR); items.Add(item); } launchTimes.ClearOptions(); launchTimes.AddOptions(items); }
public override void OnInspectorGUI() { GUI.changed = false; BinaryPair bPair = (BinaryPair)target; Vector3 velocity = Vector3.zero; GravityScaler.Units units = GravityEngine.Instance().units; string prompt = string.Format("Velocity ({0})", GravityScaler.VelocityUnits(units)); velocity = EditorGUILayout.Vector3Field(new GUIContent(prompt, "velocity of binary center of mass"), bPair.velocity); if (GUI.changed) { Undo.RecordObject(bPair, "EllipseBase Change"); bPair.velocity = velocity; EditorUtility.SetDirty(bPair); } base.OnInspectorGUI(); if (axisUpdated) { bPair.ApplyScale(GravityEngine.Instance().GetLengthScale()); } }
void Update() { // space toggles evolution if (Input.GetKeyDown(KeyCode.Space)) { GravityEngine.Instance().SetEvolve(!GravityEngine.Instance().GetEvolve()); } DateTime newTime = SolarUtils.DateForEpoch(solarSystem.GetStartEpochTime()); newTime += GravityScaler.GetTimeSpan(GravityEngine.Instance().GetPhysicalTimeDouble(), GravityScaler.Units.SOLAR); //currentTime.text = newTime.ToString("yyyy:MM:dd"); // 24h format //currentTime.text = newTime.ToString("yyyy:MM:dd HH:mm:ss"); CurrentDate.SetText(newTime.ToString("yyyy:MM:dd HH:mm:ss")); CurrentDateHighlighted.SetText(newTime.ToString("yyyy:MM:dd HH:mm:ss")); // ICK! Need to wait until GE moves these objects before can clear the trail if (clearTrail) { if (clearTrailDelay-- <= 0) { ResetTrails(); clearTrail = false; } } }
// Use this for initialization (must Awake, since start of GameLoop will set states) void Awake() { state = State.SELECT_OBJECTIVE; intercepts = null; time_to_moon_phys = TIME_TO_MOON_SEC / GravityScaler.GetGameSecondPerPhysicsSecond(); if (targets.Length == 0) { Debug.LogError("No targets configured"); } // Player is spaceship 1, others are objectives // take first ship to tbe the player target = targets[0]; // Need to configure objective chooser SetObjectiveOptions(targets); SetState(state); // add a trajectory intercepts component (it need to handle markers so it has // a monobehaviour base class). // The pair of spaceships to be checked will be selected dynamically trajIntercepts = gameObject.AddComponent <TrajectoryIntercepts>(); trajIntercepts.interceptSymbol = interceptMarker; trajIntercepts.rendezvousSymbol = rendezvousMarker; spaceshipGO = spaceshipCtrl.transform.parent.gameObject; // optional spaceshipOrbit = spaceshipGO.GetComponent <OrbitUniversal>(); // only record the elements that are active at the start of the scene orbitPredictors = new List <OrbitPredictor>(); foreach (OrbitPredictor op in (OrbitPredictor[])Object.FindObjectsOfType(typeof(OrbitPredictor))) { if (op.gameObject.activeInHierarchy) { orbitPredictors.Add(op); if (op.transform.parent == spaceshipGO.transform) { shipOrbitPredictor = op; } } } if (shipOrbitPredictor == null) { Debug.LogError("Did not find orbit predictor for ship"); } orbitRenderers = new List <OrbitRenderer>(); foreach (OrbitRenderer or in (OrbitRenderer[])Object.FindObjectsOfType(typeof(OrbitRenderer))) { if (or.gameObject.activeInHierarchy) { orbitRenderers.Add(or); } } }
// Update is called once per frame void Update() { if (!frame1Hack) { frame1Hack = true; AddGhostBodies(); ComputeBaseTransferTime(); // need GE to process these updates Debug.LogFormat("Moon period={0:0.0}", ghostMoonOrbit[MOON_SOI_ENTER].GetPeriod()); } if (!running) { // Getting user input for FR AdjustTimeOfFlight(); UpdateManeuverSymbols(); HandleMouseSOIInput(); ComputeTransfer(); if (freeReturnInfo != null) { freeReturnInfo.text = string.Format("Perilune = {0:0.0}\nReturn Perigee={1:0.0}\nTime to SOI = {2:0.0}\n{3}", ghostShipOrbit[SOI_HYPER].GetPerigee(), ghostShipOrbit[EXIT_SOI].GetPerigee(), timeHohmann * tflightFactor, GravityScaler.GetWorldTimeFormatted(timeHohmann * tflightFactor, ge.units)); } } else { // RUNNING if (Input.GetKeyUp(KeyCode.C)) { // Circularize (if in the vicinity of the moon) CircularizeAroundMoon(); } else if (Input.GetKeyUp(KeyCode.R)) { // Raise circular orbit but Hohmann Xfer NewCircularOrbit(1.3f); } return; } if (Input.GetKeyUp(KeyCode.X)) { // execute the transfer ExecuteTransfer(); if (instructions != null) { instructions.gameObject.SetActive(false); } running = true; } else if (Input.GetKeyUp(KeyCode.Space)) { ge.SetEvolve(!ge.GetEvolve()); } }
public override void OnInspectorGUI() { GUI.changed = false; GravityParticles nbp = (GravityParticles)target; Vector3 initialV = nbp.initialVelocity; bool addNBodyVelocity = nbp.addNBodyVelocity; IGravityParticlesInit particlesInit = nbp.GetComponent <IGravityParticlesInit>(); GravityScaler.Units units = GravityEngine.Instance().units; string prompt = string.Format("Initial Vel. ({0})", GravityScaler.VelocityUnits(units)); if (particlesInit == null) { EditorGUILayout.LabelField("No init script", EditorStyles.boldLabel); bool hasNBody = false; if (nbp.GetComponent <NBody>() != null) { hasNBody = true; } else if (nbp.transform.parent != null && nbp.transform.parent.gameObject.GetComponent <NBody>() != null) { hasNBody = true; } if (hasNBody) { addNBodyVelocity = EditorGUILayout.Toggle(new GUIContent("Use velocity from NBody", velTip), nbp.addNBodyVelocity); if (addNBodyVelocity) { EditorGUILayout.LabelField("Initial Velocity: (from NBody)", EditorStyles.boldLabel); } else { initialV = EditorGUILayout.Vector3Field(new GUIContent(prompt, iTip), nbp.initialVelocity); } } else { initialV = EditorGUILayout.Vector3Field(new GUIContent(prompt, iTip), nbp.initialVelocity); } } else { EditorGUILayout.LabelField("Initalize with: " + particlesInit.GetType().ToString(), EditorStyles.boldLabel); } if (GUI.changed) { Undo.RecordObject(nbp, "GravityParticles Change"); nbp.initialVelocity = initialV; nbp.addNBodyVelocity = addNBodyVelocity; EditorUtility.SetDirty(nbp); } }
public override void OnInspectorGUI() { GUI.changed = false; NBodyCollision nbc = (NBodyCollision)target; GameObject explodePF = nbc.explosionPrefab; int precedence = nbc.collisionPrecedence; float explodeOrBounceVelocity = nbc.explodeOrBounceVelocity; float bounceFactor = 1f; string layer = null; float oldWidth = EditorGUIUtility.labelWidth; EditorGUIUtility.labelWidth = 200; NBodyCollision.CollisionType type = NBodyCollision.CollisionType.ABSORB_IMMEDIATE; type = (NBodyCollision.CollisionType)EditorGUILayout.EnumPopup(new GUIContent("Collision Type", collTip), nbc.collisionType); precedence = EditorGUILayout.IntField(new GUIContent("Collision Precedence", cTip), nbc.collisionPrecedence); layer = EditorGUILayout.TextField(new GUIContent("Collision Layer (Optional)", layerTip), nbc.collisionLayer); if (type == NBodyCollision.CollisionType.EXPLODE) { explodePF = (GameObject)EditorGUILayout.ObjectField( new GUIContent("Explosion Prefab", "Particle System with NBodyParticles"), explodePF, typeof(GameObject), true); } else if (type == NBodyCollision.CollisionType.BOUNCE) { EditorGUIUtility.labelWidth = oldWidth; bounceFactor = EditorGUILayout.Slider(new GUIContent("Bounce", bTip), nbc.bounceFactor, 0f, 1f); } else if (type == NBodyCollision.CollisionType.EXPLODE_OR_BOUNCE) { explodePF = (GameObject)EditorGUILayout.ObjectField( new GUIContent("Explosion Prefab", "Particle System with NBodyParticles"), explodePF, typeof(GameObject), true); bounceFactor = EditorGUILayout.Slider(new GUIContent("Bounce", bTip), nbc.bounceFactor, 0f, 1f); GravityScaler.Units units = GravityEngine.Instance().units; EditorGUIUtility.labelWidth = oldWidth; string prompt = string.Format("Relative Velocity to Explode ({0})", GravityScaler.VelocityUnits(units)); explodeOrBounceVelocity = EditorGUILayout.FloatField(new GUIContent(prompt, dTip), nbc.explodeOrBounceVelocity); } if (GUI.changed) { Undo.RecordObject(nbc, "NBodyCollision Change"); nbc.explosionPrefab = explodePF; nbc.collisionType = type; nbc.bounceFactor = bounceFactor; nbc.explodeOrBounceVelocity = explodeOrBounceVelocity; nbc.collisionPrecedence = precedence; nbc.collisionLayer = layer; EditorUtility.SetDirty(nbc); } }
private void DoTranslation(Vector3 axis) { if (rigidBodyEnabled) { rigidBody.AddRelativeForce(deltaV * axis, ForceMode.VelocityChange); } else { // for a massless (wrt e.g. Earth) ship, applyimpulse will just do a change in velocity ge.ApplyImpulse(nbody, GravityScaler.ScaleVelSceneToPhys(deltaV * axis)); } }
// Check can go through phys time and still get a sensible DateTime (same day) public void SolarTimeNow() { SolarSystem solar = GameObject.Find("SolarSystem").GetComponent <SolarSystem>(); Assert.That(solar != null, "Could not get Solar system. Did you open SolarUnitTest scene?"); System.DateTime newTime = SolarUtils.DateForEpoch(solar.GetStartEpochTime()); newTime += GravityScaler.GetTimeSpan(GravityEngine.Instance().GetPhysicalTimeDouble(), GravityScaler.Units.SOLAR); Assert.AreEqual(newTime.Year, 2016); Assert.AreEqual(newTime.Month, 1); Assert.AreEqual(newTime.Day, 1); }
void Start() { SetEngine(engineOn); // Convert from real world units // Rocket engine thrust is given in SI units, so acceleration is in SI units // For Orbital scale need to convert to km/hr^2 accelerationConversion = GravityScaler.AccelSItoGEUnits(); // and convert to the physics scale used in the engine accelerationConversion = accelerationConversion * GravityScaler.AccelGEtoInternalUnits(); burnRateScaled = burnRate * GravityScaler.GetGameSecondPerPhysicsSecond(); thrustDirection = thrustAxis; }
/// <summary> /// Calculate velocity for binary members and assign. /// reflect: One of the bodies positions and velocities must be reflected to opposite side of ellipse /// </summary> private void SetupBody(NBody nbody, float a_n, float mu, bool reflect) { float a_phy = a_n / GravityEngine.instance.physToWorldFactor; // Phase is TRUE anomoly f float f = phase * Mathf.Deg2Rad; float reflect_in_y = 1f; if (reflect) { reflect_in_y = -1f; //f = f + Mathf.PI; } // Murray and Dermott (2.20) float r = a_phy * (1f - ecc * ecc) / (1f + ecc * Mathf.Cos(f)); // (2.26) mu = n^2 a^3 (n is period, aka T) float n = Mathf.Sqrt(mu * GravityEngine.Instance().massScale / (a_phy * a_phy * a_phy)); // (2.36) float denom = Mathf.Sqrt(1f - ecc * ecc); float xdot = -1f * n * a_phy * Mathf.Sin(f) / denom; float ydot = n * a_phy * (ecc + Mathf.Cos(f)) / denom; Vector3 position_xy = new Vector3(reflect_in_y * r * Mathf.Cos(f), r * Mathf.Sin(f), 0); // move from XY plane to the orbital plane and scale to world space // orbit position is WRT center Vector3 position = ellipse_orientation * position_xy; // ISSUE: Problematic in case where added after the fact position += cmPosition; nbody.initialPhysPosition = position; Vector3 v_xy = new Vector3(xdot, ydot, 0); v_xy *= reflect_in_y; // velocity will be scaled when NBody is scaled Vector3 vel_phys = Vector3.zero; if (binaryNbody != null) { // use CM velocity from parent for e.g. binary in orbit around something vel_phys = binaryNbody.vel_phys; } else { vel_phys = velocity * GravityScaler.GetVelocityScale(); } nbody.vel_phys = ellipse_orientation * v_xy + vel_phys; }
// Update is called once per frame void Update() { if (Input.GetKey(KeyCode.A)) { fromPhase += PHASE_PER_KEY; } else if (Input.GetKey(KeyCode.S)) { fromPhase -= PHASE_PER_KEY; } else if (Input.GetKey(KeyCode.Q)) { toPhase += PHASE_PER_KEY; } else if (Input.GetKey(KeyCode.W)) { toPhase -= PHASE_PER_KEY; } fromPhase = NUtils.DegreesMod360(fromPhase); toPhase = NUtils.DegreesMod360(toPhase); SetMarkers(); shipOrbitData.SetOrbitForVelocity(spaceshipNBody, shipOrbit.centerNbody); // Determine TOF Vector3d from = new Vector3d(shipOrbitData.GetPhysicsPositionforEllipse(fromPhase)); Vector3d to = new Vector3d(shipOrbitData.GetPhysicsPositionforEllipse(toPhase)); Vector3d shipPos = GravityEngine.instance.GetPositionDoubleV3(spaceshipNBody); double tPeri = shipOrbit.TimeOfFlight(shipPos, new Vector3d(shipOrbitData.GetPhysicsPositionforEllipse(0f))); double tApo = shipOrbit.TimeOfFlight(shipPos, new Vector3d(shipOrbitData.GetPhysicsPositionforEllipse(180f))); double tof = shipOrbit.TimeOfFlight(from, to); // Scale to game time //tApo = GravityScaler.ScaleToGameSeconds((float) tApo); //tPeri = GravityScaler.ScaleToGameSeconds((float)tPeri); //tof = GravityScaler.ScaleToGameSeconds((float)tof); //tofText.text = string.Format("Time of Flight = {0:#.#}\nTime to Apoapsis = {1:#.#}\nTime to Periapsis = {2:#.#}\ntau = {3}", // tof, tApo, tPeri, shipOrbitData.tau); GravityScaler.Units units = GravityEngine.instance.units; tofText.text = string.Format("Time of Flight = {0}\nTime to Apoapsis = {1}\nTime to Periapsis = {2}\ntau = {3}", GravityScaler.GetWorldTimeFormatted(tof, units), GravityScaler.GetWorldTimeFormatted(tApo, units), GravityScaler.GetWorldTimeFormatted(tPeri, units), GravityScaler.GetWorldTimeFormatted(shipOrbitData.tau, units)); }
private static void GetMajorAxis(OrbitUniversal orbitU, ref double p_inspector, ref bool sizeUpdate, GravityScaler.Units units) { float oldLabelWidth = EditorGUIUtility.labelWidth; EditorGUIUtility.labelWidth = oldLabelWidth + 50f; string prompt = string.Format("Semi-Major Axis (a) [{0}]", GravityScaler.LengthUnits(units)); double old_a = orbitU.GetMajorAxisInspector(); double a = EditorGUILayout.DelayedDoubleField(new GUIContent(prompt, aTip), old_a); if (!EditorApplication.isPlaying && (a != old_a)) { orbitU.SetMajorAxisInspector(a); sizeUpdate = true; // Need to update ecc and p with new values p_inspector = orbitU.p_inspector; } EditorGUILayout.LabelField(string.Format("Axis result in: p={0:0.00}", p_inspector)); EditorGUIUtility.labelWidth = oldLabelWidth; }
//***************************************************************** // Run-time methods //***************************************************************** /// <summary> /// Run-time update of the solar system time. The new time must be a time later /// than the initial start time of the solar system. /// /// This API makes use of @GravityEngine#SetPhysicalTime. This requires that ALL /// objects in the scene be "on-rails" i.e. have an OrbitXXX component and be in Kepler /// evolution mode. /// /// </summary> /// <param name="year"></param> /// <param name="month"></param> /// <param name="day"></param> /// <param name="dayFrac"></param> public void SetTime(int year, int month, int day, float dayFrac) { // TODO - use dayFrac // C# DateTime/TimeSpan to find the number of seconds to add System.DateTime newTime = new System.DateTime(year, month, day); System.DateTime startTime = SolarUtils.DateForEpoch(_epochTime); double secsFromStart = (newTime - startTime).Ticks / (1E7); if (secsFromStart < 0) { Debug.LogError("Cannot evolve earlier than solar system start time."); } Debug.LogFormat("Secs from start={0} phys={1} newPhys={2}", secsFromStart, GravityEngine.Instance().GetPhysicalTimeDouble(), GravityScaler.WorldSecsToPhysTime(secsFromStart)); GravityEngine.Instance().SetPhysicalTime(GravityScaler.WorldSecsToPhysTime(secsFromStart)); }
/// <summary> /// Validation: /// - confirmed velocity in SI units matches expected orbital velocity of ISS /// - checked that SI acceleration = -g at terminal velocity (9.73 m/s^2 at 28 km) /// </summary> // Use this for initialization void Start() { if (inertialMassKg == 0) { Debug.LogError("Mass is zero. Drag calculation will fail."); } if (spaceship == null) { spaceship = GetComponent <NBody>(); } geDistanceToKm = 1; v_ship = new double[] { 0, 0, 0 }; v_earth = new double[] { 0, 0, 0 }; velocityScaleInternalToSI = GravityScaler.VelocityScaletoSIUnits() / GravityScaler.GetVelocityScale(); accelSItoGE = GravityScaler.AccelSItoGEUnits() / GravityScaler.AccelerationScaleInternalToGEUnits(); LoadDensityProfile(); liveState = GravityEngine.Instance().GetWorldState(); }
private void Deactivate() { // Get CM object pos & vel from GE GravityEngine ge = GravityEngine.Instance(); Vector3d cmPos = ge.GetPositionDoubleV3(cmNbody); Vector3d cmVel = ge.GetVelocityDoubleV3(cmNbody); int i = 0; foreach (NBody nbody in nbodies) { Rigidbody rb = nbody.GetComponentInChildren <Rigidbody>(); if (rb == null) { Debug.LogWarning("could not find rigidbody for " + nbody.gameObject.name); continue; } // rb.isKinematic = true; // set position and velocity Vector3d nbodyVel = new Vector3d(GravityScaler.ScaleVelSceneToPhys(rb.velocity)); ge.SetVelocityDoubleV3(nbody, cmVel + nbodyVel); Vector3d nbodyPos = new Vector3d(GravityScaler.ScalePositionSceneToPhys(nbody.transform.localPosition)); ge.SetPositionDoubleV3(nbody, cmPos + nbodyPos); ge.ActivateBody(nbody.gameObject); // restore parent nbody.transform.parent = priorParents[i]; i++; } ge.RemoveBody(cmObject); Destroy(cmObject); // de-activate any RCS elements foreach (ReactionControlSystem r in rcs) { if (r != null) { r.SetRigidBodyEnabled(false); } } }
public void InitFromOrbitData(OrbitData od) { // a is inited from perihilion perihelion_phys = od.perihelion; // @TODO: This should be in selected units! perihelion = GravityScaler.ScaleDistancePhyToScene(od.perihelion); ecc = od.ecc; omega_lc = od.omega_lc; omega_uc = od.omega_uc; inclination = od.inclination; phase_nu = od.phase * Mathf.Deg2Rad; // normally a should be negative, but we made it positive... p = od.a * (1 - ecc * ecc); // float denom = 1 + ecc * Mathf.Cos(phase_nu); Vector3 r_pqw = new Vector3(p * Mathf.Cos(phase_nu) / denom, p * Mathf.Sin(phase_nu) / denom, 0); r_initial_phy = r_pqw.magnitude; initFromOrbitData = true; Init(); PreEvolve(GravityEngine.Instance().physToWorldFactor, GravityEngine.Instance().massScale); }
/// <summary> /// Draw a circle at SOI radius around the moon /// </summary> /// <param name="physRadius">radius in physics units</param> private void Draw(float physRadius) { const int numPoints = 200; Vector3[] points = new Vector3[numPoints]; float radius = GravityScaler.ScaleDistancePhyToScene(physRadius); float dtheta = 2f * Mathf.PI / (float)numPoints; float theta = 0; // add a fudge factor to ensure we go all the way around the circle for (int i = 0; i < numPoints; i++) { points[i] = new Vector3(radius * Mathf.Cos(theta), radius * Mathf.Sin(theta), 0); points[i] = Quaternion.AngleAxis(inclination, Vector3.right) * points[i]; points[i] += moonBody.transform.position; theta += dtheta; } // close the path (credit for fix to R. Vincent) points[numPoints - 1] = points[0]; soiRenderer.positionCount = numPoints; soiRenderer.SetPositions(points); }
/// <summary> /// Take the Nbody objects in the nbodies list set them inactive and make them children of a new /// NBody object moving as the CM of the nbodies. This allows RigidBody mechanics during close /// encounters. /// </summary> private void Activate() { if (nbodies.Length < 2) { Debug.LogError("Need two or more nbodies"); return; } GravityEngine ge = GravityEngine.Instance(); // Step 1: calculate CM position and velocity Vector3d cmPos = new Vector3d(0, 0, 0); Vector3d cmVel = new Vector3d(0, 0, 0); float mass = 0f; rigidBodies = new Rigidbody[nbodies.Length]; // RigidBody is assumed to be attached to one of the children (to keep model scale independent) int i = 0; foreach (NBody nbody in nbodies) { rigidBodies[i] = nbody.GetComponentInChildren <Rigidbody>(); //rigidBodies[i] = nbody.GetComponent<Rigidbody>(); if (rigidBodies[i] == null) { Debug.LogError("Abort - No rigidbody detected on " + nbody.gameObject.name); return; } mass += rigidBodies[i].mass; cmPos += rigidBodies[i].mass * ge.GetPositionDoubleV3(nbody); cmVel += rigidBodies[i].mass * ge.GetVelocityDoubleV3(nbody); i++; } cmPos /= mass; cmVel /= mass; Debug.LogFormat("CM p={0} v={1} mass={2}", cmPos.ToVector3(), cmVel.ToVector3(), mass); // Step2: Inactivate the NBodies and make children of a new NBody object priorParents = new Transform[nbodies.Length]; cmObject = new GameObject("DockingGroupCM"); cmNbody = cmObject.AddComponent <NBody>(); // Set cm pos/vel // NBody InitPosition will use transform or initialPos base on units. Set both. cmNbody.initialPos = cmPos.ToVector3() * ge.physToWorldFactor; cmNbody.transform.position = cmNbody.initialPos; ge.AddBody(cmObject); ge.SetVelocity(cmNbody, cmVel.ToVector3()); Debug.LogFormat("set pos={0} actual={1}", cmPos.ToVector3(), ge.GetPhysicsPosition(cmNbody)); i = 0; foreach (NBody nbody in nbodies) { Vector3d pos = ge.GetPositionDoubleV3(nbody); Vector3d vel = ge.GetVelocityDoubleV3(nbody); priorParents[i] = nbody.gameObject.transform.parent; ge.InactivateBody(nbody.gameObject); nbody.gameObject.transform.parent = cmObject.transform; // position wrt to CM. Need to convert to Unity scene units from GE Internal pos = (pos - cmPos) * ge.physToWorldFactor; vel = GravityScaler.ScaleVelPhysToScene(vel - cmVel); nbody.transform.localPosition = pos.ToVector3(); rigidBodies[i].velocity = vel.ToVector3(); // rigidBodies[i].isKinematic = false; i++; Debug.LogFormat("body {0} p={1} v={2}", nbody.gameObject.name, pos.ToVector3(), vel.ToVector3()); } // activate any RCS elements foreach (ReactionControlSystem r in rcs) { if (r != null) { r.SetRigidBodyEnabled(true); } } }
void Start() { worldState = GravityEngine.Instance().GetWorldState(); accelGEtoSI = GravityScaler.AccelerationScaleInternalToGEUnits() / GravityScaler.AccelSItoGEUnits(); }
public override void OnInspectorGUI() { GUI.changed = false; NBody nbody = (NBody)target; float mass = 0f; float size = 0.1f; bool autoSize = true; Vector3 velocity = Vector3.zero; Vector3 initialPos = Vector3.zero; bool rotateFrame = false; if (GravityEngine.Instance() == null) { EditorGUILayout.LabelField("Require a GravityEngine in the scene to", EditorStyles.boldLabel); EditorGUILayout.LabelField("display NBody component.", EditorStyles.boldLabel); } GravityScaler.Units units = GravityEngine.Instance().units; string mass_prompt = string.Format("Mass ({0})", GravityScaler.MassUnits(units)); mass = EditorGUILayout.DelayedFloatField(new GUIContent(mass_prompt, mTip), (float)nbody.mass); // If the velocity is controlled by an EllipseBase, or this NBody is the direct child of // BinaryPair or ThreeeBodySolution then don't allowit to be controlled. string controlledBy = null; if (nbody.transform.gameObject.GetComponent <OrbitEllipse>() != null) { controlledBy = "Initial position/Velocity is set by ellipse parameters."; } else if (nbody.transform.gameObject.GetComponent <OrbitHyper>() != null) { controlledBy = "Initial position/Velocity is set by hyperbola parameters."; } else if (nbody.transform.parent != null) { if (nbody.transform.parent.gameObject.GetComponent <BinaryPair>() != null) { controlledBy = "Initial position/Velocity is set by BinaryPair parent."; } else if (nbody.transform.parent.gameObject.GetComponent <ThreeBodySolution>() != null) { controlledBy = "Initial position/Velocity is set by ThreeBodySolution parent."; } } if (controlledBy == null) { switch (units) { case GravityScaler.Units.DIMENSIONLESS: EditorGUILayout.LabelField("Initial position set via transform"); velocity = EditorGUILayout.Vector3Field(new GUIContent("Velocity", velTip), nbody.vel); initialPos = nbody.transform.position; break; default: string prompt = string.Format("Initial Pos ({0})", GravityScaler.LengthUnits(units)); initialPos = EditorGUILayout.Vector3Field(new GUIContent(prompt, iposTip), nbody.initialPos); prompt = string.Format("Velocity ({0})", GravityScaler.VelocityUnits(units)); velocity = EditorGUILayout.Vector3Field(new GUIContent(prompt, velTip), nbody.vel); break; } } else { EditorGUILayout.LabelField(controlledBy, EditorStyles.boldLabel); //EditorGUILayout.LabelField(string.Format("vel= {0:F2} {1:F2} {2:F2}", nbody.vel.x, nbody.vel.y, nbody.vel.z)); } // particle capture size EditorGUIUtility.labelWidth = 200f; EditorGUIUtility.fieldWidth = 20f; rotateFrame = EditorGUILayout.Toggle(new GUIContent("Rotate frame in orbit", rotateTip), nbody.rotateFrame); autoSize = EditorGUILayout.Toggle(new GUIContent("Automatic particle capture size", autoTip), nbody.automaticParticleCapture); EditorGUIUtility.labelWidth = 0; EditorGUIUtility.fieldWidth = 0; if (!autoSize) { EditorGUIUtility.labelWidth = 200f; EditorGUIUtility.fieldWidth = 40f; size = EditorGUILayout.FloatField(new GUIContent("Particle capture radius", sizeTip), (float)nbody.size); EditorGUIUtility.labelWidth = 0; EditorGUIUtility.fieldWidth = 0; } else { float detectedSize = nbody.CalculateSize(); if (detectedSize < 0) { EditorGUILayout.LabelField("Did not detect a child with a MeshFilter.", EditorStyles.boldLabel); EditorGUILayout.LabelField("Using size=" + size); } else { EditorGUILayout.LabelField("Particle Capture radius=" + detectedSize); size = detectedSize; } } if (mass < 0) { mass = 0; } if (nbody.transform.hasChanged) { // User has dragged the object and the transform has changed, need // to change the initial Pos to correspond to this position in the correct units if (units != GravityScaler.Units.DIMENSIONLESS) { initialPos = nbody.transform.position / GravityEngine.Instance().GetLengthScale(); } nbody.initialPos = initialPos; nbody.transform.hasChanged = false; } if (GUI.changed) { Undo.RecordObject(nbody, "NBody Change"); nbody.mass = FixNaN.FixIfNaN(mass); nbody.vel = FixNaN.FixIfNaN(velocity); nbody.size = size; nbody.rotateFrame = rotateFrame; nbody.automaticParticleCapture = autoSize; nbody.initialPos = initialPos; Debug.Log("new v=" + velocity); // must be after initialPos is updated nbody.ApplyScale(GravityEngine.Instance().GetLengthScale(), GravityEngine.Instance().GetVelocityScale()); EditorUtility.SetDirty(nbody); } }
/// <summary> /// Set the maneuver time in world units (e.g. in ORBITAL units in /// hours). /// </summary> /// <param name="time"></param> public void SetTimeScaled(float time) { worldTime = time / GravityScaler.GetGameSecondPerPhysicsSecond(); }
/// <summary> /// Return the dV in scaled units. /// </summary> /// <returns></returns> public float GetDvScaled() { return(dV / GravityScaler.GetVelocityScale()); }
public override void OnInspectorGUI() { GUI.changed = false; EllipseBase ellipseBase = (EllipseBase)target; // fields in class GameObject centerObject = null; EllipseBase.ParamBy paramBy = EllipseBase.ParamBy.AXIS_A; float ecc = 0; float a = 0; float p = 0; float omega_uc = 0; float omega_lc = 0; float inclination = 0; float phase = 0; if (!(target is BinaryPair)) { centerObject = (GameObject)EditorGUILayout.ObjectField( new GUIContent("CenterObject", centerTip), ellipseBase.centerObject, typeof(GameObject), true); } EditorGUILayout.Space(); EditorGUILayout.LabelField("Ellipse Parameters", EditorStyles.boldLabel); // If there is a SolarBody, it is the one place data can be changed. The EB holds the // orbit scaled per SolarSystem scale. SolarBody sbody = ellipseBase.GetComponent <SolarBody>(); if (sbody != null) { EditorGUILayout.LabelField("\tEllipse parameters controlled by SolarBody settings."); EditorGUILayout.LabelField(string.Format(" {0,-25}\t ({1,1})\t {2}", "Semi-Major Axis", "a", ellipseBase.a), EditorStyles.wordWrappedLabel); EditorGUILayout.LabelField(string.Format(" {0,-25}\t ({1,1})\t {2}", "Eccentricity", "e", ellipseBase.ecc), EditorStyles.wordWrappedLabel); EditorGUILayout.LabelField(string.Format(" {0,-25}\t ({1,1})\t {2}", "Incliniation", "i", ellipseBase.inclination), EditorStyles.wordWrappedLabel); EditorGUILayout.LabelField(string.Format(" {0,-25}\t ({1,1})\t {2}", "Arg. of pericenter", "\u03c9", ellipseBase.omega_lc), EditorStyles.wordWrappedLabel); EditorGUILayout.LabelField(string.Format(" {0,-25}\t ({1,1})\t {2}", "Longitude of node", "\u03a9", ellipseBase.omega_uc), EditorStyles.wordWrappedLabel); EditorGUILayout.LabelField(string.Format(" {0,-25}\t ({1,1})\t {2}", "Phase", "M", ellipseBase.phase), EditorStyles.wordWrappedLabel); return; } paramBy = (EllipseBase.ParamBy)EditorGUILayout.EnumPopup(new GUIContent("Parameter Choice", paramTip), ellipseBase.paramBy); ecc = EditorGUILayout.Slider(new GUIContent("Eccentricity", eTip), ellipseBase.ecc, 0f, 0.99f); axisUpdated = false; // backwards compatibility when loading scenes before unit scaling: if (ellipseBase.a_scaled < 0) { axisUpdated = true; } GravityScaler.Units units = GravityEngine.Instance().units; if (ellipseBase.paramBy == EllipseBase.ParamBy.AXIS_A) { float oldLabelWidth = EditorGUIUtility.labelWidth; EditorGUIUtility.labelWidth = oldLabelWidth + 30f; string prompt = string.Format("Semi-Major Axis (a) [{0}]", GravityScaler.LengthUnits(units)); a = EditorGUILayout.DelayedFloatField(new GUIContent(prompt, aTip), ellipseBase.a); if (a != ellipseBase.a) { axisUpdated = true; } EditorGUILayout.LabelField("Scaled a (Unity units): " + ellipseBase.a_scaled); EditorGUIUtility.labelWidth = oldLabelWidth; p = a * (1 - ecc); } else { string prompt = string.Format("Pericenter [{0}]", GravityScaler.LengthUnits(units)); p = EditorGUILayout.DelayedFloatField(new GUIContent(prompt, pTip), ellipseBase.p); if (p != ellipseBase.p) { axisUpdated = true; } EditorGUILayout.LabelField("Scaled p (Unity units) " + ellipseBase.p_scaled); a = p / (1 - ecc); } // implementation uses AngleAxis, so degrees are more natural omega_uc = EditorGUILayout.Slider(new GUIContent("\u03a9 (Longitude of AN)", omega_ucTip), ellipseBase.omega_uc, 0, 360f); omega_lc = EditorGUILayout.Slider(new GUIContent("\u03c9 (AN to Pericenter)", omega_lcTip), ellipseBase.omega_lc, 0, 360f); inclination = EditorGUILayout.Slider(new GUIContent("Inclination", inclinationTip), ellipseBase.inclination, 0f, 180f); // physics uses radians - but ask user for degrees to be consistent phase = EditorGUILayout.Slider(new GUIContent("Starting Phase", phaseTip), ellipseBase.phase, 0, 360f); // Checking the Event type lets us update after Undo and Redo commands. // A redo updates _lengthScale but does not run the setter // if (Event.current.type == EventType.ExecuteCommand && // Event.current.commandName == "UndoRedoPerformed") { // ellipseBase.ApplyScale(GravityEngine.Instance().GetLengthScale()); // } if (GUI.changed) { Undo.RecordObject(ellipseBase, "EllipseBase Change"); ellipseBase.a = a; ellipseBase.p = p; ellipseBase.ecc = ecc; ellipseBase.centerObject = centerObject; ellipseBase.omega_lc = omega_lc; ellipseBase.omega_uc = omega_uc; ellipseBase.inclination = inclination; ellipseBase.phase = phase; ellipseBase.paramBy = paramBy; EditorUtility.SetDirty(ellipseBase); } }
/// <summary> /// Set the deltaV for a scalar maneuver in world units (e.g. in ORBITAL /// units set velocity in km/hr) /// </summary> /// <param name="newDv"></param> public void SetDvScaled(float newDv) { dV = newDv * GravityScaler.GetVelocityScale(); }
public override void OnInspectorGUI() { GUI.changed = false; OrbitUniversal orbitU = (OrbitUniversal)target; bool displayAndExit = false; // check there is an NBody, if not likely a synthesized Orbit as part of a predictor if (orbitU.GetComponent <NBody>() == null) { EditorGUILayout.LabelField("Ellipse parameters determined from position/velocity."); EditorGUILayout.LabelField("(Orbit Predictor)."); displayAndExit = true; } // If there is a SolarBody, it is the one place data can be changed. The EB holds the // orbit scaled per SolarSystem scale. SolarBody sbody = orbitU.GetComponent <SolarBody>(); if (sbody != null) { EditorGUILayout.LabelField("Ellipse parameters controlled by SolarBody settings."); displayAndExit = true; } if (displayAndExit) { EditorGUILayout.LabelField(string.Format(" {0,-25} ({1,1})\t {2}", "Semi-Major Axis", "a", orbitU.GetMajorAxisInspector()), EditorStyles.wordWrappedLabel); EditorGUILayout.LabelField(string.Format(" {0,-25} ({1,1})\t {2}", "Eccentricity", "e", orbitU.eccentricity), EditorStyles.wordWrappedLabel); EditorGUILayout.LabelField(string.Format(" {0,-25} ({1,1})\t {2}", "Incliniation", "i", orbitU.inclination), EditorStyles.wordWrappedLabel); EditorGUILayout.LabelField(string.Format(" {0,-25} ({1,1})\t {2}", "Arg. of pericenter", "\u03c9", orbitU.omega_lc), EditorStyles.wordWrappedLabel); EditorGUILayout.LabelField(string.Format(" {0,-25} ({1,1})\t {2}", "Longitude of node", "\u03a9", orbitU.omega_uc), EditorStyles.wordWrappedLabel); EditorGUILayout.LabelField(string.Format(" {0,-25} ({1,1})\t {2}", "Phase", "M", orbitU.phase), EditorStyles.wordWrappedLabel); return; } // fields in class NBody centerNBody = null; OrbitUniversal.InputMode inputMode = orbitU.inputMode; double ecc = orbitU.eccentricity; double p_inspector = orbitU.p_inspector; double omega_uc = 0; double omega_lc = 0; double inclination = 0; double phase = 0; bool sizeUpdate = false; WarnAboutKeplerSeq(orbitU); centerNBody = (NBody)EditorGUILayout.ObjectField( new GUIContent("Center NBody", centerTip), orbitU.centerNbody, typeof(NBody), true); OrbitUniversal.EvolveMode evolveMode = orbitU.evolveMode; evolveMode = (OrbitUniversal.EvolveMode)EditorGUILayout.EnumPopup(new GUIContent("Evolve Mode", modeTip), evolveMode); EditorGUILayout.Space(); EditorGUILayout.LabelField("Shape Parameters", EditorStyles.boldLabel); inputMode = (OrbitUniversal.InputMode) EditorGUILayout.EnumPopup(new GUIContent("Parameter Choice", paramTip), orbitU.inputMode); sizeUpdate = false; GravityScaler.Units units = GravityEngine.Instance().units; string promptp = string.Format("Semi-parameter (p) [{0}]", GravityScaler.LengthUnits(units)); // The values for orbit size and shape can be entered in several ways. OrbitUniversal supports // these values as doubles (which is probably overkill in most case). Provide an explicit double mode // but also allow sliders (which reduce the value to a float). // The editor script changes p_inspector. (p in OU is scaled for GE internal units) switch (inputMode) { case OrbitUniversal.InputMode.ELLIPSE_MAJOR_AXIS_A: EditorGUILayout.LabelField("Ellipse with float/sliders using semi-major axis."); ecc = EditorGUILayout.Slider(new GUIContent("Eccentricity", eTip), (float)orbitU.eccentricity, 0f, 0.99f); GetMajorAxis(orbitU, ref p_inspector, ref sizeUpdate, units); break; case OrbitUniversal.InputMode.ELLIPSE_APOGEE_PERIGEE: EditorGUILayout.LabelField("Ellipse with float/sliders using agogee/perigee."); EditorGUILayout.LabelField("MUST use <Return> after updating values!"); double apogee_old = orbitU.GetApogeeInspector(); double apogee = EditorGUILayout.DelayedDoubleField(new GUIContent("Apogee", eTip), apogee_old); double perigee_old = orbitU.GetPerigeeInspector(); double perigee = EditorGUILayout.DelayedDoubleField(new GUIContent("Perigee", eTip), perigee_old); // enforce apogee > perigee if (apogee < perigee) { apogee = perigee; } if (!EditorApplication.isPlaying && (apogee != apogee_old) || (perigee != perigee_old)) { orbitU.SetSizeWithApogeePerigee(apogee, perigee); sizeUpdate = true; // Need to update ecc and p with new values p_inspector = orbitU.p_inspector; ecc = orbitU.eccentricity; } EditorGUILayout.LabelField(string.Format("Require Apogee > Perigee", ecc, p_inspector)); EditorGUILayout.LabelField(string.Format("Apogee/Perigee result in: eccentricty={0:0.00}, p={1:0.00}", ecc, p_inspector)); break; case OrbitUniversal.InputMode.ECC_PERIGEE: EditorGUILayout.LabelField("Orbit with double using eccentricity/perigee."); EditorGUILayout.LabelField("MUST use <Return> after updating values!"); double old_ecc = orbitU.eccentricity; ecc = EditorGUILayout.DelayedDoubleField(new GUIContent("Eccentricity", eTip), orbitU.eccentricity); double hperigee_old = orbitU.GetPerigeeInspector(); double hperigee = EditorGUILayout.DelayedDoubleField(new GUIContent("Perigee", eTip), hperigee_old); if (!EditorApplication.isPlaying && ((hperigee != hperigee_old) || (old_ecc != ecc))) { orbitU.SetSizeWithEccPerigee(ecc, hperigee); sizeUpdate = true; // Need to update ecc and p with new values p_inspector = orbitU.p_inspector; ecc = orbitU.eccentricity; } EditorGUILayout.LabelField(string.Format("Apogee/Perigee result in: eccentricty={0:0.00}, p={1:0.00}", ecc, p_inspector)); break; case OrbitUniversal.InputMode.DOUBLE: EditorGUILayout.LabelField("Specify values with double precision using semi-parameter"); EditorGUILayout.LabelField("MUST use <Return> after updating values!"); // no sliders (they do float) ecc = EditorGUILayout.DelayedDoubleField(new GUIContent("Eccentricity", eTip), orbitU.eccentricity); double old_p = orbitU.p_inspector; p_inspector = EditorGUILayout.DelayedDoubleField(new GUIContent(promptp, pTip), orbitU.p_inspector); if (old_p != p_inspector) { sizeUpdate = true; } break; case OrbitUniversal.InputMode.DOUBLE_ELLIPSE: EditorGUILayout.LabelField("Specify values with double precision using semi-parameter"); EditorGUILayout.LabelField("MUST use <Return> after updating values!"); // no sliders (they do float) ecc = EditorGUILayout.DelayedDoubleField(new GUIContent("Eccentricity", eTip), orbitU.eccentricity); GetMajorAxis(orbitU, ref p_inspector, ref sizeUpdate, units); break; default: Debug.LogWarning("Unknown input mode - internal error"); break; } if (!EditorApplication.isPlaying && (p_inspector != orbitU.p)) { sizeUpdate = true; } EditorGUILayout.LabelField("Scaled p (Unity units): " + orbitU.p); EditorGUILayout.Space(); EditorGUILayout.LabelField("Orientation Parameters", EditorStyles.boldLabel); if ((inputMode != OrbitUniversal.InputMode.DOUBLE) && (inputMode != OrbitUniversal.InputMode.DOUBLE_ELLIPSE)) { // implementation uses AngleAxis, so degrees are more natural omega_uc = EditorGUILayout.Slider(new GUIContent("\u03a9 (Longitude of AN)", omega_ucTip), (float)orbitU.omega_uc, 0, 360f); omega_lc = EditorGUILayout.Slider(new GUIContent("\u03c9 (AN to Pericenter)", omega_lcTip), (float)orbitU.omega_lc, 0, 360f); inclination = EditorGUILayout.Slider(new GUIContent("Inclination", inclinationTip), (float)orbitU.inclination, 0f, 180f); // physics uses radians - but ask user for degrees to be consistent phase = EditorGUILayout.Slider(new GUIContent("Starting Phase", phaseTip), (float)orbitU.phase, 0, 360f); } else { // DOUBLE, so no sliders omega_uc = EditorGUILayout.DoubleField(new GUIContent("\u03a9 (Longitude of AN)", omega_ucTip), orbitU.omega_uc); omega_lc = EditorGUILayout.DoubleField(new GUIContent("\u03c9 (AN to Pericenter)", omega_lcTip), orbitU.omega_lc); inclination = EditorGUILayout.DoubleField(new GUIContent("Inclination", inclinationTip), orbitU.inclination); phase = EditorGUILayout.DoubleField(new GUIContent("Starting Phase", phaseTip), orbitU.phase); } if (GUI.changed) { Undo.RecordObject(orbitU, "EllipseBase Change"); orbitU.p_inspector = p_inspector; orbitU.eccentricity = ecc; orbitU.centerNbody = centerNBody; orbitU.omega_lc = omega_lc; orbitU.omega_uc = omega_uc; orbitU.inclination = inclination; orbitU.phase = phase; orbitU.inputMode = inputMode; orbitU.evolveMode = evolveMode; EditorUtility.SetDirty(orbitU); } if (sizeUpdate) { orbitU.ApplyScale(GravityEngine.Instance().GetLengthScale()); } }