private void AoAOffsetFromControl() { AoAdesired = 0; if ((object)vessel != null && vessel.staticPressure > 0) { if (pitchaxis) { AoAdesired += PitchLocation * vessel.ctrlState.pitch; } if (yawaxis) { AoAdesired += YawLocation * vessel.ctrlState.yaw; } if (rollaxis) { AoAdesired += RollLocation * vessel.ctrlState.roll; } AoAdesired *= AoAsign * maxdeflect; AoAdesired = FARMathUtil.Clamp(AoAdesired, -Math.Abs(maxdeflect), Math.Abs(maxdeflect)); AoAdesired += AoAfromflap; } ChangeDeflection(timeConstant); DeflectionAnimation(); }
public override double CalculateAoA(Vector3d velocity) { // Use the vector computed by DeflectionAnimation Vector3d perp = part_transform.TransformDirection(deflectedNormal); double PerpVelocity = Vector3d.Dot(perp, velocity.normalized); return(Math.Asin(FARMathUtil.Clamp(PerpVelocity, -1, 1))); }
// Had to add this one since the parent class don't use AoAoffset and adding it would break GetWingInFrontOf public double CalculateAoA(Vector3d velocity, double AoAoffset) { double radAoAoffset = AoAoffset * FARMathUtil.deg2rad * ctrlSurfFrac; Vector3 perp = part_transform.TransformDirection(new Vector3d(0, Math.Sin(radAoAoffset), Math.Cos(radAoAoffset))); double PerpVelocity = Vector3d.Dot(perp, velocity.normalized); return(Math.Asin(FARMathUtil.Clamp(PerpVelocity, -1, 1))); }
public void SetControlStateEditor(Vector3 CoM, Vector3 velocityVec, float pitch, float yaw, float roll, int flap, bool brake) { if (HighLogic.LoadedSceneIsEditor) { Transform partTransform = part.partTransform; Transform rootTransform = EditorLogic.RootPart.partTransform; Vector3 CoMoffset = (partTransform.position - CoM); PitchLocation = Vector3.Dot(partTransform.forward, rootTransform.forward) * Math.Sign(Vector3.Dot(CoMoffset, rootTransform.up)); YawLocation = -Vector3.Dot(partTransform.forward, rootTransform.right) * Math.Sign(Vector3.Dot(CoMoffset, rootTransform.up)); RollLocation = Vector3.Dot(partTransform.forward, rootTransform.forward) * Math.Sign(Vector3.Dot(CoMoffset, -rootTransform.right)); AoAsign = Math.Sign(Vector3.Dot(partTransform.up, rootTransform.up)); AoAdesiredControl = 0; if (pitchaxis != 0.0) { AoAdesiredControl += PitchLocation * pitch * pitchaxis * 0.01; } if (yawaxis != 0.0) { AoAdesiredControl += YawLocation * yaw * yawaxis * 0.01; } if (rollaxis != 0.0) { AoAdesiredControl += RollLocation * roll * rollaxis * 0.01; } AoAdesiredControl *= maxdeflect; if (pitchaxisDueToAoA != 0.0) { Vector3 tmpVec = rootTransform.up * Vector3.Dot(rootTransform.up, velocityVec) + rootTransform.forward * Vector3.Dot(rootTransform.forward, velocityVec); //velocity vector projected onto a plane that divides the airplane into left and right halves double AoA = base.CalculateAoA(tmpVec.normalized); //using base.CalculateAoA gets the deflection using WingAeroModel's code, which does not account for deflection; this gives us the AoA that the surface _would_ be at if it hadn't deflected at all. AoA = FARMathUtil.rad2deg * AoA; if (double.IsNaN(AoA)) { AoA = 0; } AoAdesiredControl += AoA * pitchaxisDueToAoA * 0.01; } AoAdesiredControl *= AoAsign; AoAdesiredControl = FARMathUtil.Clamp(AoAdesiredControl, -Math.Abs(maxdeflect), Math.Abs(maxdeflect)); AoAcurrentControl = AoAdesiredControl; AoAcurrentFlap = 0; if (isFlap == true) { int flapDeflectionLevel = flap; flapLocation = (int)Math.Sign(Vector3.Dot(rootTransform.forward, partTransform.forward)); //figure out which way is up AoAcurrentFlap += maxdeflectFlap * flapLocation * flapDeflectionLevel * 0.3333333333333; } else if (isSpoiler == true) { flapLocation = -(int)Math.Sign(Vector3.Dot(rootTransform.forward, partTransform.forward)); //figure out which way is up AoAcurrentFlap += brake ? maxdeflectFlap * flapLocation : 0; } AoAdesiredFlap = AoAcurrentFlap; AoAoffset = AoAcurrentFlap + AoAcurrentControl; DeflectionAnimation(); } }
private double WingInterference(Vector3 rayDirection, List <Part> PartList, float dist) { double interferencevalue = 1; Ray ray = new Ray(); ray.origin = parentWingModule.WingCentroid(); ray.direction = rayDirection; RaycastHit hit = new RaycastHit(); bool gotSomething = false; hit.distance = 0; RaycastHit[] hits = Physics.RaycastAll(ray, dist, FARAeroUtil.RaycastMask); for (int i = 0; i < hits.Length; i++) { RaycastHit h = hits[i]; if (h.collider != null) { for (int j = 0; j < PartList.Count; j++) { Part p = PartList[j]; if (p == parentWingPart) { continue; } FARWingAerodynamicModel w = p.GetComponent <FARWingAerodynamicModel>(); if ((object)w != null) { Collider[] colliders = w.PartColliders; for (int k = 0; k < colliders.Length; k++) { if (h.collider == colliders[k] && h.distance > 0) { double tmp = h.distance / dist; tmp = FARMathUtil.Clamp(tmp, 0, 1); interferencevalue = Math.Min(tmp, interferencevalue); gotSomething = true; break; } } } if (gotSomething) { break; } } } } return(interferencevalue); }
public void SetControlStateEditor(Vector3 CoM, Vector3 velocityVec, float pitch, float yaw, float roll, int flap, bool brake) { if (HighLogic.LoadedSceneIsEditor) { Vector3 CoMoffset = (part.transform.position - CoM).normalized; PitchLocation = Vector3.Dot(part.transform.forward, EditorLogic.RootPart.transform.forward) * Mathf.Sign(Vector3.Dot(CoMoffset, EditorLogic.RootPart.transform.up)); YawLocation = -Vector3.Dot(part.transform.forward, EditorLogic.RootPart.transform.right) * Mathf.Sign(Vector3.Dot(CoMoffset, EditorLogic.RootPart.transform.up)); RollLocation = Vector3.Dot(part.transform.forward, EditorLogic.RootPart.transform.forward) * Mathf.Sign(Vector3.Dot(CoMoffset, -EditorLogic.RootPart.transform.right)); AoAsign = Math.Sign(Vector3.Dot(part.transform.up, EditorLogic.RootPart.transform.up)); AoAdesiredControl = 0; if (pitchaxis != 0.0) { AoAdesiredControl += PitchLocation * pitch * pitchaxis * 0.01; } if (yawaxis != 0.0) { AoAdesiredControl += YawLocation * yaw * yawaxis * 0.01; } if (rollaxis != 0.0) { AoAdesiredControl += RollLocation * roll * rollaxis * 0.01; } AoAdesiredControl *= maxdeflect; if (pitchaxisDueToAoA != 0.0) { Vector3 tmpVec = EditorLogic.RootPart.transform.up * Vector3.Dot(EditorLogic.RootPart.transform.up, velocityVec) + EditorLogic.RootPart.transform.forward * Vector3.Dot(EditorLogic.RootPart.transform.forward, velocityVec); //velocity vector projected onto a plane that divides the airplane into left and right halves double AoA = Vector3.Dot(tmpVec.normalized, EditorLogic.RootPart.transform.forward); AoA = FARMathUtil.rad2deg * Math.Asin(AoA); if (double.IsNaN(AoA)) { AoA = 0; } AoAdesiredControl += PitchLocation * AoA * pitchaxisDueToAoA * 0.01; } AoAdesiredControl *= AoAsign; AoAdesiredControl = FARMathUtil.Clamp(AoAdesiredControl, -Math.Abs(maxdeflect), Math.Abs(maxdeflect)); AoAcurrentFlap = 0; if (isFlap == true) { int flapDeflectionLevel = flap; flapLocation = (int)Math.Sign(Vector3.Dot(EditorLogic.RootPart.transform.forward, part.transform.forward)); //figure out which way is up AoAcurrentFlap += maxdeflectFlap * flapLocation * flapDeflectionLevel * 0.3333333333333; } else if (isSpoiler == true) { flapLocation = -(int)Math.Sign(Vector3.Dot(EditorLogic.RootPart.transform.forward, part.transform.forward)); //figure out which way is up AoAcurrentFlap += brake ? maxdeflectFlap * flapLocation : 0; } AoAdesiredFlap = AoAcurrentFlap; AoAoffset = AoAdesiredFlap + AoAdesiredControl; DeflectionAnimation(); } }
private void AoAOffsetFromSpoilerDeflection() { if (brake) { AoAdesiredFlap = maxdeflectFlap * spoilerLocation; } else { AoAdesiredFlap = 0; } AoAdesiredFlap = FARMathUtil.Clamp(AoAdesiredFlap, -Math.Abs(maxdeflectFlap), Math.Abs(maxdeflectFlap)); }
//DaMichel: Factored the time evolution for deflection AoA into this function. This one results into an exponential asympotic //"decay" towards the desired value. Good for stick inputs, i suppose, and the original method. private static double BlendDeflectionExp(double current, double desired, double timeConstant, bool forceSetToDesired) { double error = desired - current; if (!forceSetToDesired && Math.Abs(error) >= 0.1) // DaMichel: i changed the threshold since i noticed a "bump" at max deflection { double recip_timeconstant = 1 / timeConstant; double tmp1 = error * recip_timeconstant; current += FARMathUtil.Clamp((double)TimeWarp.fixedDeltaTime * tmp1, -Math.Abs(0.6 * error), Math.Abs(0.6 * error)); } else { current = desired; } return(current); }
//DaMichel: Similarly, this is used for constant rate movment towards the desired value. I presume it is more realistic for //for slow moving flaps and spoilers. It looks better anyways. private static double BlendDeflectionLinear(double current, double desired, double timeConstant, bool forceSetToDesired) { double error = desired - current; if (!forceSetToDesired && Math.Abs(error) >= 0.1) { double recip_timeconstant = 1 / timeConstant; double tmp1 = Math.Sign(error) * recip_timeconstant; current += FARMathUtil.Clamp((double)TimeWarp.deltaTime * tmp1, -Math.Abs(0.6 * error), Math.Abs(0.6 * error)); } else { current = desired; } return(current); }
private void ChangeDeflection(double timeconstant) { if (AoAoffset != AoAdesired) { double error = AoAdesired - AoAoffset; if (!justStarted && Math.Abs(error) >= 0.5) { double recip_timeconstant = 1 / timeconstant; double tmp1 = error * recip_timeconstant; //float tmp2 = (error + TimeWarp.deltaTime * tmp1) * recip_timeconstant; AoAoffset += FARMathUtil.Clamp((double)TimeWarp.deltaTime * tmp1, -Math.Abs(0.6 * error), Math.Abs(0.6 * error)); } else { AoAoffset = AoAdesired; } } }
private void AoAOffsetFromControl() { AoAdesiredControl = 0; if ((object)vessel != null && vessel.atmDensity > 0) { if (!pitchaxis.NearlyEqual(0)) { AoAdesiredControl += PitchLocation * vessel.ctrlState.pitch * pitchaxis * 0.01; } if (!yawaxis.NearlyEqual(0)) { AoAdesiredControl += YawLocation * vessel.ctrlState.yaw * yawaxis * 0.01; } if (!rollaxis.NearlyEqual(0)) { AoAdesiredControl += RollLocation * vessel.ctrlState.roll * rollaxis * 0.01; } if (!brakeRudder.NearlyEqual(0)) { AoAdesiredControl += BrakeRudderLocation * Math.Max(0.0, BrakeRudderSide * vessel.ctrlState.yaw) * brakeRudder * 0.01; } AoAdesiredControl *= maxdeflect; if (pitchaxisDueToAoA != 0.0) { Vector3d vel = this.GetVelocity(); double velMag = vel.magnitude; if (velMag > 5) { //Vector3 tmpVec = vessel.ReferenceTransform.up * Vector3.Dot(vessel.ReferenceTransform.up, vel) + vessel.ReferenceTransform.forward * Vector3.Dot(vessel.ReferenceTransform.forward, vel); //velocity vector projected onto a plane that divides the airplane into left and right halves //double AoA = Vector3.Dot(tmpVec.normalized, vessel.ReferenceTransform.forward); double AoA = base.CalculateAoA(vel); //using base.CalculateAoA gets the deflection using WingAeroModel's code, which does not account for deflection; this gives us the AoA that the surface _would_ be at if it hadn't deflected at all. AoA = FARMathUtil.rad2deg * AoA; if (double.IsNaN(AoA)) { AoA = 0; } AoAdesiredControl += AoA * pitchaxisDueToAoA * 0.01; } } AoAdesiredControl *= AoAsign; AoAdesiredControl = FARMathUtil.Clamp(AoAdesiredControl, -Math.Abs(maxdeflect), Math.Abs(maxdeflect)); } }
public void SetControlStateEditor(Vector3 CoM, float pitch, float yaw, float roll, int flap, bool brake) { if (HighLogic.LoadedSceneIsEditor) { Vector3 CoMoffset = (part.transform.position - CoM).normalized; PitchLocation = Vector3.Dot(part.transform.forward, EditorLogic.startPod.transform.forward) * Mathf.Sign(Vector3.Dot(CoMoffset, EditorLogic.startPod.transform.up)); YawLocation = -Vector3.Dot(part.transform.forward, EditorLogic.startPod.transform.right) * Mathf.Sign(Vector3.Dot(CoMoffset, EditorLogic.startPod.transform.up)); RollLocation = Vector3.Dot(part.transform.forward, EditorLogic.startPod.transform.forward) * Mathf.Sign(Vector3.Dot(CoMoffset, -EditorLogic.startPod.transform.right)); AoAcurrentControl = 0; if (pitchaxis == true) { AoAcurrentControl += PitchLocation * pitch; } if (yawaxis == true) { AoAcurrentControl += YawLocation * yaw; } if (rollaxis == true) { AoAcurrentControl += RollLocation * roll; } AoAcurrentControl = AoAdesiredControl = FARMathUtil.Clamp(AoAcurrentControl, -1, 1) * maxdeflect; AoAcurrentFlap = 0; if (isFlap == true) { int flapDeflectionLevel = flap; flapLocation = (int)Math.Sign(Vector3.Dot(EditorLogic.startPod.transform.forward, part.transform.forward)); //figure out which way is up AoAcurrentFlap += maxdeflectFlap * flapLocation * flapDeflectionLevel * 0.3333333333333; } else if (isSpoiler == true) { flapLocation = -(int)Math.Sign(Vector3.Dot(EditorLogic.startPod.transform.forward, part.transform.forward)); //figure out which way is up AoAcurrentFlap += brake ? maxdeflectFlap * flapLocation : 0; } AoAdesiredFlap = AoAcurrentFlap; AoAoffset = AoAdesiredFlap + AoAdesiredControl; DeflectionAnimation(); } }
private void AoAOffsetFromControl() { AoAdesiredControl = 0; if ((object)vessel != null && vessel.staticPressure > 0) { if (pitchaxis) { AoAdesiredControl += PitchLocation * vessel.ctrlState.pitch; } if (yawaxis) { AoAdesiredControl += YawLocation * vessel.ctrlState.yaw; } if (rollaxis) { AoAdesiredControl += RollLocation * vessel.ctrlState.roll; } AoAdesiredControl *= AoAsign * maxdeflect; AoAdesiredControl = FARMathUtil.Clamp(AoAdesiredControl, -Math.Abs(maxdeflect), Math.Abs(maxdeflect)); } }
private void AoAOffsetFromControl() { AoAdesiredControl = 0; if ((object)vessel != null && vessel.staticPressure > 0) { if (pitchaxis != 0.0) { AoAdesiredControl += PitchLocation * vessel.ctrlState.pitch * pitchaxis * 0.01; } if (yawaxis != 0.0) { AoAdesiredControl += YawLocation * vessel.ctrlState.yaw * yawaxis * 0.01; } if (rollaxis != 0.0) { AoAdesiredControl += RollLocation * vessel.ctrlState.roll * rollaxis * 0.01; } AoAdesiredControl *= maxdeflect; if (pitchaxisDueToAoA != 0.0) { Vector3d vel = this.GetVelocity(); //Vector3 tmpVec = vessel.ReferenceTransform.up * Vector3.Dot(vessel.ReferenceTransform.up, vel) + vessel.ReferenceTransform.forward * Vector3.Dot(vessel.ReferenceTransform.forward, vel); //velocity vector projected onto a plane that divides the airplane into left and right halves //double AoA = Vector3.Dot(tmpVec.normalized, vessel.ReferenceTransform.forward); double AoA = base.CalculateAoA(vel.normalized); AoA = FARMathUtil.rad2deg * Math.Asin(AoA); if (double.IsNaN(AoA)) { AoA = 0; } AoAdesiredControl += AoA * pitchaxisDueToAoA * 0.01; } AoAdesiredControl *= AoAsign; AoAdesiredControl = FARMathUtil.Clamp(AoAdesiredControl, -Math.Abs(maxdeflect), Math.Abs(maxdeflect)); } }
//DaMichel: Similarly, this is used for constant rate movment towards the desired value. I presume it is more realistic for //for slow moving flaps and spoilers. It looks better anyways. //ferram4: The time constant specifies the time it would take for a first-order system to reach its steady-state value, //assuming that it was proportional to only the initial error, not the error as a function of time private static double BlendDeflectionLinear(double current, double desired, double maximumDeflection, double timeConstant, bool forceSetToDesired) { double error = desired - current; if (!forceSetToDesired && Math.Abs(error) >= 0.1) { double degreesPerSecond = Math.Max(Math.Abs(maximumDeflection), Math.Abs(current)) / timeConstant; double tmp = current + (double)TimeWarp.fixedDeltaTime * degreesPerSecond * Math.Sign(desired - current); if (error > 0) { current = FARMathUtil.Clamp(tmp, current, desired); } else { current = FARMathUtil.Clamp(tmp, desired, current); } } else { return(desired); } return(current); }
public Vector3d RunDragCalculation(Vector3d velocity, double MachNumber, double rho) { if (isShielded) { Cl = Cd = Cm = 0; return(Vector3d.zero); } double v_scalar = velocity.magnitude; if (v_scalar > 0.1) //Don't Bother if it's not moving or in space { CoDshift = Vector3d.zero; Cd = 0; Vector3d velocity_normalized = velocity / v_scalar; //float Parallel = Vector3.Dot(upVector, velocity_normalized); Vector3d upVector = part_transform.TransformDirection(localUpVector); perp = Vector3d.Cross(upVector, velocity).normalized; liftDir = Vector3d.Cross(velocity, perp).normalized; Vector3d local_velocity = part_transform.InverseTransformDirection(velocity_normalized); DragModel(local_velocity, MachNumber); //if(gear && start != StartState.Editor) // if(gear.gearState != ModuleLandingGear.GearStates.RETRACTED) // Cd += 0.1f; /* if(anim) * if (anim.Progress > 0.5f) * Cd *= 1.5f;*/ double qS = 0.5 * rho * v_scalar * v_scalar * S; //dynamic pressure, q Vector3d D = velocity_normalized * (-qS * Cd); //drag Vector3d L = liftDir * (qS * Cl); Vector3d force = (L + D) * 0.001; double force_scalar = force.magnitude; currentDrag = (float)force_scalar; Vector3d moment = perp * (qS * Cm * 0.001); Rigidbody rb = part.Rigidbody; if (start != StartState.Editor && rb) { if (rb.angularVelocity.sqrMagnitude != 0) { Vector3d rot = Vector3d.Exclude(velocity_normalized, rb.angularVelocity); //This prevents aerodynamic damping a spinning object if its spin axis is aligned with the velocity axis rot *= (-0.00001 * qS); // This seems redundant due to the moment addition below? /* * if(!float.IsNaN(rot.sqrMagnitude)) * part.Rigidbody.AddTorque(rot); */ moment += rot; } //moment = (moment + lastMoment) / 2; //lastMoment = moment; // CoDshift += CenterOfDrag; } //Must handle aero-structural failure before transforming CoD pos and adding pitching moment to forces or else parts with blunt body drag fall apart too easily if (Math.Abs(Vector3d.Dot(force, upVector)) > YmaxForce || Vector3d.Exclude(upVector, force).magnitude > XZmaxForce) { if (part.parent && !vessel.packed) { part.SendEvent("AerodynamicFailureStatus"); FlightLogger.eventLog.Add("[" + FARMathUtil.FormatTime(vessel.missionTime) + "] Joint between " + part.partInfo.title + " and " + part.parent.partInfo.title + " failed due to aerodynamic stresses."); part.decouple(25); } } globalCoDShift = Vector3d.Cross(force, moment) / (force_scalar * force_scalar); if (double.IsNaN(force_scalar) || double.IsNaN(moment.sqrMagnitude) || double.IsNaN(globalCoDShift.sqrMagnitude)) { Debug.LogWarning("FAR Error: Aerodynamic force = " + force.magnitude + " Aerodynamic moment = " + moment.magnitude + " CoD Local = " + CoDshift.magnitude + " CoD Global = " + globalCoDShift.magnitude + " " + part.partInfo.title); force = moment = CoDshift = globalCoDShift = Vector3.zero; return(force); } //part.Rigidbody.AddTorque(moment); return(force); } else { return(Vector3d.zero); } }
public void SetControlStateEditor(Vector3 CoM, Vector3 velocityVec, float pitch, float yaw, float roll, int flap, bool brake) { if (HighLogic.LoadedSceneIsEditor) { Transform partTransform = part.partTransform; Transform rootTransform = EditorLogic.RootPart.partTransform; // cache transform vectors Vector3 partPosition = partTransform.position; Vector3 CoMoffset = partPosition - CoM; Vector3 partForward = partTransform.forward; Vector3 forward = rootTransform.forward; Vector3 up = rootTransform.up; Vector3 right = rootTransform.right; PitchLocation = Vector3.Dot(partForward, forward) * Math.Sign(Vector3.Dot(CoMoffset, up)); YawLocation = -Vector3.Dot(partForward, right) * Math.Sign(Vector3.Dot(CoMoffset, up)); RollLocation = Vector3.Dot(partForward, forward) * Math.Sign(Vector3.Dot(CoMoffset, -right)); BrakeRudderLocation = Vector3.Dot(partForward, forward); BrakeRudderSide = Mathf.Sign(Vector3.Dot(CoMoffset, right)); AoAsign = Math.Sign(Vector3.Dot(partTransform.up, up)); AoAdesiredControl = 0; if (!pitchaxis.NearlyEqual(0)) { AoAdesiredControl += PitchLocation * pitch * pitchaxis * 0.01; } if (!yawaxis.NearlyEqual(0)) { AoAdesiredControl += YawLocation * yaw * yawaxis * 0.01; } if (!rollaxis.NearlyEqual(0)) { AoAdesiredControl += RollLocation * roll * rollaxis * 0.01; } if (!brakeRudder.NearlyEqual(0)) { AoAdesiredControl += BrakeRudderLocation * Math.Max(0.0, BrakeRudderSide * yawaxis) * brakeRudder * 0.01; } AoAdesiredControl *= maxdeflect; if (!pitchaxisDueToAoA.NearlyEqual(0)) { Vector3 tmpVec = up * Vector3.Dot(up, velocityVec) + forward * Vector3.Dot(forward, velocityVec); //velocity vector projected onto a plane that divides the airplane into left and right halves double AoA = base.CalculateAoA(tmpVec); //using base.CalculateAoA gets the deflection using WingAeroModel's code, which does not account for deflection; this gives us the AoA that the surface _would_ be at if it hadn't deflected at all. AoA = FARMathUtil.rad2deg * AoA; if (double.IsNaN(AoA)) { AoA = 0; } AoAdesiredControl += AoA * pitchaxisDueToAoA * 0.01; } AoAdesiredControl *= AoAsign; AoAdesiredControl = FARMathUtil.Clamp(AoAdesiredControl, -Math.Abs(maxdeflect), Math.Abs(maxdeflect)); AoAcurrentControl = AoAdesiredControl; AoAcurrentFlap = 0; if (part.symMethod == SymmetryMethod.Mirror || part.symmetryCounterparts.Count < 1) { if (HighLogic.LoadedSceneIsFlight) { flapLocation = Math.Sign(Vector3.Dot(vessel.ReferenceTransform.forward, partForward)); //figure out which way is up } else { flapLocation = Math.Sign(Vector3.Dot(EditorLogic.RootPart.partTransform.forward, partForward)); //figure out which way is up } spoilerLocation = -flapLocation; } else if (part.parent != null) { flapLocation = Math.Sign(Vector3.Dot(partPosition - part.parent.partTransform.position, partForward)); spoilerLocation = flapLocation; } else { flapLocation = 1; spoilerLocation = flapLocation; } if (isFlap) { AoAcurrentFlap += maxdeflectFlap * flapLocation * flap * 0.3333333333333; } else if (isSpoiler) { AoAcurrentFlap += brake ? maxdeflectFlap * spoilerLocation : 0; } AoAdesiredFlap = AoAcurrentFlap; AoAoffset = AoAcurrentFlap + AoAcurrentControl; DeflectionAnimation(); } }
private void AoAOffsetFromFlapDeflection() { AoAdesiredFlap = maxdeflectFlap * flapLocation * flapDeflectionLevel * 0.33333333333; AoAdesiredFlap = FARMathUtil.Clamp(AoAdesiredFlap, -Math.Abs(maxdeflectFlap), Math.Abs(maxdeflectFlap)); }
public Vector3d RunDragCalculation(Vector3d velocity, double MachNumber, double rho, double failureForceScaling) { if (isShielded) { Cl = Cd = Cm = 0; return(Vector3d.zero); } double v_scalar = velocity.magnitude; if (v_scalar > 0.1) //Don't Bother if it's not moving or in space { CoDshift = Vector3d.zero; Cd = 0; Vector3d velocity_normalized = velocity / v_scalar; Vector3d upVector = part_transform.TransformDirection(localUpVector); perp = Vector3d.Cross(upVector, velocity).normalized; liftDir = Vector3d.Cross(velocity, perp).normalized; Vector3d local_velocity = part_transform.InverseTransformDirection(velocity_normalized); DragModel(local_velocity, MachNumber, rho); double qS = 0.5 * rho * v_scalar * v_scalar * S; //dynamic pressure, q Vector3d D = velocity_normalized * (-qS * Cd); //drag Vector3d L = liftDir * (qS * Cl); Vector3d force = (L + D) * 0.001; double force_scalar = force.magnitude; currentDrag = (float)force_scalar; Vector3d moment = perp * (qS * Cm * 0.001); Rigidbody rb = part.Rigidbody; if (HighLogic.LoadedSceneIsFlight && (object)rb != null) { if (rb.angularVelocity.sqrMagnitude != 0) { Vector3d rot = Vector3d.Exclude(velocity_normalized, rb.angularVelocity); //This prevents aerodynamic damping a spinning object if its spin axis is aligned with the velocity axis rot *= (-0.00001 * qS); moment += rot; } } //Must handle aero-structural failure before transforming CoD pos and adding pitching moment to forces or else parts with blunt body drag fall apart too easily if (Math.Abs(Vector3d.Dot(force, upVector)) > YmaxForce * failureForceScaling || Vector3d.Exclude(upVector, force).magnitude > XZmaxForce * failureForceScaling) { if (part.parent && !vessel.packed) { part.SendEvent("AerodynamicFailureStatus"); FlightLogger.eventLog.Add("[" + FARMathUtil.FormatTime(vessel.missionTime) + "] Joint between " + part.partInfo.title + " and " + part.parent.partInfo.title + " failed due to aerodynamic stresses."); part.decouple(25); if (FARDebugValues.aeroFailureExplosions) { FXMonger.Explode(part, GetCoDWithoutMomentShift(), 5); } } } globalCoDShift = Vector3d.Cross(force, moment) / (force_scalar * force_scalar); if (double.IsNaN(force_scalar) || double.IsNaN(moment.sqrMagnitude) || double.IsNaN(globalCoDShift.sqrMagnitude)) { Debug.LogWarning("FAR Error: Aerodynamic force = " + force.magnitude + " Aerodynamic moment = " + moment.magnitude + " CoD Local = " + CoDshift.magnitude + " CoD Global = " + globalCoDShift.magnitude + " " + part.partInfo.title); force = moment = CoDshift = globalCoDShift = Vector3.zero; return(force); } double numericalControlFactor = (rb.mass * v_scalar * 0.67) / (force_scalar * TimeWarp.fixedDeltaTime); force *= Math.Min(numericalControlFactor, 1); //part.Rigidbody.AddTorque(moment); return(force); } else { return(Vector3d.zero); } }
/// <summary> /// Accounts for increments in lift due to camber changes from upstream wings, and returns changes for this wing part; returns true if there are wings in front of it /// </summary> /// <param name="thisWingAoA">AoA of this wing in rad</param> /// <param name="thisWingMachNumber">Mach Number of this wing in rad</param> /// <param name="ACWeight">Weighting value for applying ACshift</param> /// <param name="ACShift">Value used to shift the wing AC due to interactive effects</param> /// <param name="ClIncrementFromRear">Increase in Cl due to this</param> /// <returns></returns> public void CalculateEffectsOfUpstreamWing(double thisWingAoA, double thisWingMachNumber, Vector3d parallelInPlaneLocal, ref double ACweight, ref double ACshift, ref double ClIncrementFromRear) { double thisWingMAC, thisWingb_2; thisWingMAC = parentWingModule.GetMAC(); thisWingb_2 = parentWingModule.Getb_2(); effectiveUpstreamMAC = 0; effectiveUpstreamb_2 = 0; effectiveUpstreamArea = 0; effectiveUpstreamLiftSlope = 0; effectiveUpstreamStall = 0; effectiveUpstreamCosSweepAngle = 0; effectiveUpstreamAoAMax = 0; effectiveUpstreamAoA = 0; effectiveUpstreamCd0 = 0; effectiveUpstreamInfluence = 0; double wingForwardDir = parallelInPlaneLocal.y; double wingRightwardDir = parallelInPlaneLocal.x * srfAttachFlipped; if (wingForwardDir > 0) { wingForwardDir *= wingForwardDir; UpdateUpstreamValuesFromWingModules(nearbyWingModulesForwardList, nearbyWingModulesForwardInfluence, wingForwardDir, thisWingAoA); } else { wingForwardDir *= wingForwardDir; UpdateUpstreamValuesFromWingModules(nearbyWingModulesBackwardList, nearbyWingModulesBackwardInfluence, wingForwardDir, thisWingAoA); } if (wingRightwardDir > 0) { wingRightwardDir *= wingRightwardDir; UpdateUpstreamValuesFromWingModules(nearbyWingModulesRightwardList, nearbyWingModulesRightwardInfluence, wingRightwardDir, thisWingAoA); } else { wingRightwardDir *= wingRightwardDir; UpdateUpstreamValuesFromWingModules(nearbyWingModulesLeftwardList, nearbyWingModulesLeftwardInfluence, wingRightwardDir, thisWingAoA); } double MachCoeff = FARMathUtil.Clamp(1 - thisWingMachNumber * thisWingMachNumber, 0, 1); if (MachCoeff != 0) { double flapRatio = FARMathUtil.Clamp(thisWingMAC / (thisWingMAC + effectiveUpstreamMAC), 0, 1); float flt_flapRatio = (float)flapRatio; double flapFactor = wingCamberFactor.Evaluate(flt_flapRatio); //Flap Effectiveness Factor double dCm_dCl = wingCamberMoment.Evaluate(flt_flapRatio); //Change in moment due to change in lift from flap //This accounts for the wing possibly having a longer span than the flap double WingFraction = FARMathUtil.Clamp(thisWingb_2 / effectiveUpstreamb_2, 0, 1); //This accounts for the flap possibly having a longer span than the wing it's attached to double FlapFraction = FARMathUtil.Clamp(effectiveUpstreamb_2 / thisWingb_2, 0, 1); double ClIncrement = flapFactor * effectiveUpstreamLiftSlope * effectiveUpstreamAoA; //Lift created by the flap interaction ClIncrement *= (parentWingModule.S * FlapFraction + effectiveUpstreamArea * WingFraction) / parentWingModule.S; //Increase the Cl so that even though we're working with the flap's area, it accounts for the added lift across the entire object ACweight = ClIncrement * MachCoeff; // Total flap Cl for the purpose of applying ACshift, including the bit subtracted below ClIncrement -= FlapFraction * effectiveUpstreamLiftSlope * effectiveUpstreamAoA; //Removing additional angle so that lift of the flap is calculated as lift at wing angle + lift due to flap interaction rather than being greater ACshift = (dCm_dCl + 0.75 * (1 - flapRatio)) * (thisWingMAC + effectiveUpstreamMAC); //Change in Cm with change in Cl ClIncrementFromRear = ClIncrement * MachCoeff; } }
private void UpdateAttachGroupDrag(List <AttachNode> attachNodeGroup, AttachGroupType attachGroupType, Transform[] ThisPartModelTransforms, Transform[] AllVesselModelTransforms) { //This is standard single node; the blunt area is to be determined by everything near it if (attachGroupType == AttachGroupType.INDEPENDENT_NODE) { //Get area represented by current node Vector2 bounds = FARGeoUtil.NodeBoundaries(part, attachNodeGroup, Vector3.zero, 0.05f, ThisPartModelTransforms); double area = (bounds.x + bounds.y); area *= 0.5; area *= area; area *= Math.PI; //Calculate area based on circular cross-section //Get area covered by other parts Transform[] OtherTransforms = FARGeoUtil.ChooseNearbyModelTransforms(part, attachNodeGroup, bounds, ThisPartModelTransforms, AllVesselModelTransforms); Vector2 otherBounds = FARGeoUtil.NodeBoundaries(part, attachNodeGroup, Vector3.zero, 0.05f, OtherTransforms); double opposedArea = (otherBounds.x + otherBounds.y); opposedArea *= 0.5f; opposedArea *= opposedArea; opposedArea *= Math.PI; //Calculate area based on circular cross-section //Debug.Log(part.partInfo.title + " Area: " + area + " Opposed area: " + opposedArea + " OtherTransforms: " + OtherTransforms.Length); area = FARMathUtil.Clamp(area - opposedArea, 0, double.PositiveInfinity); //Update attachNodeDragDict with new data attachNodeData newAttachNodeData = new attachNodeData(); newAttachNodeData.areaValue = area / FARMathUtil.Clamp(S, 0.01, double.PositiveInfinity); Vector3 orientation = attachNodeGroup[0].position; if (Vector3d.Dot(orientation, localUpVector) > 1) { newAttachNodeData.pitchesAwayFromUpVec = true; } else { newAttachNodeData.pitchesAwayFromUpVec = false; } if (attachNodeDragDict.ContainsKey(orientation)) { attachNodeData tmp = attachNodeDragDict[orientation]; tmp.areaValue += newAttachNodeData.areaValue; attachNodeDragDict[orientation] = tmp; } else { attachNodeDragDict.Add(orientation, newAttachNodeData); } return; } //This is a group of nodes that can be used for clustered tanks/engines; the area calculated must be divided over them else if (attachGroupType == AttachGroupType.PARALLEL_NODES) { //Get area represented by current nodes Vector2 bounds = FARGeoUtil.NodeBoundaries(part, attachNodeGroup, Vector3.zero, 0.05f, ThisPartModelTransforms); double area = (bounds.x + bounds.y); area *= 0.5; area *= area; area *= Math.PI; //Calculate area based on circular cross-section //Get area covered by other parts Transform[] OtherTransforms = FARGeoUtil.ChooseNearbyModelTransforms(part, attachNodeGroup, bounds, ThisPartModelTransforms, AllVesselModelTransforms); Vector2 otherBounds = FARGeoUtil.NodeBoundaries(part, attachNodeGroup, Vector3.zero, 0.05f, OtherTransforms); double opposedArea = (otherBounds.x + otherBounds.y); opposedArea *= 0.5; opposedArea *= opposedArea; opposedArea *= Mathf.PI; //Calculate area based on circular cross-section //Debug.Log(part.partInfo.title + " Area: " + area + " Opposed area: " + opposedArea + " OtherTransforms: " + OtherTransforms.Length); area = FARMathUtil.Clamp(area - opposedArea, 0, double.PositiveInfinity); //Update attachNodeDragDict with new data attachNodeData newAttachNodeData = new attachNodeData(); newAttachNodeData.areaValue = area / FARMathUtil.Clamp(S, 0.01, double.PositiveInfinity); Vector3 orientation = Vector3.zero; foreach (AttachNode attach in attachNodeGroup) { orientation += attach.position; } orientation /= attachNodeGroup.Count; if (Vector3d.Dot(orientation, localUpVector) > 1) { newAttachNodeData.pitchesAwayFromUpVec = true; } else { newAttachNodeData.pitchesAwayFromUpVec = false; } if (attachNodeDragDict.ContainsKey(orientation)) { attachNodeData tmp = attachNodeDragDict[orientation]; tmp.areaValue += newAttachNodeData.areaValue; attachNodeDragDict[orientation] = tmp; } else { attachNodeDragDict.Add(orientation, newAttachNodeData); } return; } //This represents a vertical stack of nodes, for different payload heights, multiple payload fairings, etc. One node being used means that they are all used else if (attachGroupType == AttachGroupType.VERTICAL_NODES) { double area = 0; AttachNode usedNode; Vector2 bounds = Vector2.zero; Vector3 orientation = Vector3.zero; foreach (AttachNode attach in attachNodeGroup) { //Get area represented by current node bounds = FARGeoUtil.NodeBoundaries(part, attach, Vector3.zero, 0.05f, ThisPartModelTransforms); double tmpArea = (bounds.x + bounds.y); tmpArea *= 0.5; tmpArea *= tmpArea; tmpArea *= Math.PI; //Calculate area based on circular cross-section if (tmpArea > area) { area = tmpArea; usedNode = attach; } orientation += attach.position; } //Get area covered by other parts Transform[] OtherTransforms = FARGeoUtil.ChooseNearbyModelTransforms(part, attachNodeGroup, bounds, ThisPartModelTransforms, AllVesselModelTransforms); Vector2 otherBounds = FARGeoUtil.NodeBoundaries(part, attachNodeGroup, Vector3.zero, 0.05f, OtherTransforms); double opposedArea = (otherBounds.x + otherBounds.y); opposedArea *= 0.5; opposedArea *= opposedArea; opposedArea *= Math.PI; //Calculate area based on circular cross-section area = FARMathUtil.Clamp(area - opposedArea, 0, double.PositiveInfinity); //Update attachNodeDragDict with new data attachNodeData newAttachNodeData = new attachNodeData(); newAttachNodeData.areaValue = area / FARMathUtil.Clamp(S, 0.01, double.PositiveInfinity); orientation /= attachNodeGroup.Count; if (Vector3d.Dot(orientation, localUpVector) > 1) { newAttachNodeData.pitchesAwayFromUpVec = true; } else { newAttachNodeData.pitchesAwayFromUpVec = false; } if (attachNodeDragDict.ContainsKey(orientation)) { attachNodeData tmp = attachNodeDragDict[orientation]; tmp.areaValue += newAttachNodeData.areaValue; attachNodeDragDict[orientation] = tmp; } else { attachNodeDragDict.Add(orientation, newAttachNodeData); } return; } }
private void UpdateNonAttachBluntEnds(Dictionary <string, List <AttachNode> > attachNodeGroups, Dictionary <string, AttachGroupType> attachNodeType, Matrix4x4 partUpMatrix, Transform[] ModelTransforms, Transform[] VesselModelTransforms) { bool upperEndHandled = false; bool lowerEndHandled = false; Vector2 verticalPartMeshBounds = FARGeoUtil.PartLengthBounds(part, Vector3.zero, partUpMatrix, ModelTransforms); foreach (KeyValuePair <string, List <AttachNode> > pair in attachNodeGroups) { Vector3 avgPos = Vector3.zero; Vector3 avgOrient = Vector3.zero; foreach (AttachNode node in pair.Value) { avgPos += node.position; avgOrient += node.orientation; } avgPos /= pair.Value.Count; avgOrient.Normalize(); if (Mathf.Abs(avgPos.y - verticalPartMeshBounds.x) <= 0.3f) { upperEndHandled = true; } if (Mathf.Abs(avgPos.y - verticalPartMeshBounds.y) <= 0.3f) { lowerEndHandled = true; } if (upperEndHandled && lowerEndHandled) { break; } } if (!upperEndHandled) { Vector3 position = Vector3.up * verticalPartMeshBounds.x; Vector2 bounds = FARGeoUtil.NodeBoundaries(part, position, position, Vector3.zero, 0.05f, ModelTransforms); double area = (bounds.x + bounds.y); area *= 0.5; area *= area; area *= Math.PI; //Calculate area based on circular cross-section area = FARMathUtil.Clamp(area, 0, double.PositiveInfinity); //Update attachNodeDragDict with new data attachNodeData newAttachNodeData = new attachNodeData(); newAttachNodeData.areaValue = area / FARMathUtil.Clamp(S, 0.01, double.PositiveInfinity); Vector3 orientation = position; if (Vector3d.Dot(orientation, localUpVector) > 1) { newAttachNodeData.pitchesAwayFromUpVec = true; } else { newAttachNodeData.pitchesAwayFromUpVec = false; } if (attachNodeDragDict.ContainsKey(orientation)) { attachNodeData tmp = attachNodeDragDict[orientation]; tmp.areaValue += newAttachNodeData.areaValue; attachNodeDragDict[orientation] = tmp; } else { attachNodeDragDict.Add(orientation, newAttachNodeData); } } if (!lowerEndHandled) { Vector3 position = Vector3.up * verticalPartMeshBounds.y; Vector2 bounds = FARGeoUtil.NodeBoundaries(part, position, position, Vector3.zero, 0.05f, ModelTransforms); double area = (bounds.x + bounds.y); area *= 0.5; area *= area; area *= Math.PI; //Calculate area based on circular cross-section area = FARMathUtil.Clamp(area, 0, double.PositiveInfinity); //Update attachNodeDragDict with new data attachNodeData newAttachNodeData = new attachNodeData(); newAttachNodeData.areaValue = area / FARMathUtil.Clamp(S, 0.01, double.PositiveInfinity); Vector3 orientation = position; if (Vector3d.Dot(orientation, localUpVector) > 1) { newAttachNodeData.pitchesAwayFromUpVec = true; } else { newAttachNodeData.pitchesAwayFromUpVec = false; } if (attachNodeDragDict.ContainsKey(orientation)) { attachNodeData tmp = attachNodeDragDict[orientation]; tmp.areaValue += newAttachNodeData.areaValue; attachNodeDragDict[orientation] = tmp; } else { attachNodeDragDict.Add(orientation, newAttachNodeData); } } }
private void AttachNodeCdAdjust() { if (part.Modules.Contains("FARPayloadFairingModule")) //This doesn't apply blunt drag drag to fairing parts if one of their "exempt" attach nodes is used, indicating attached fairings { return; } if (VesselPartList == null) { UpdateShipPartsList(); } if (attachNodeDragList == null) { attachNodeDragList = new List <attachNodeData>(); } attachNodeDragList.Clear(); Transform transform = part.partTransform; if (transform == null) { transform = part.transform; } if (transform == null) { Debug.LogError("Part " + part.partInfo.title + " has null transform; drag interactions cannot be applied."); return; } SPlusAttachArea = S; Vector3d partUpVector = transform.TransformDirection(localUpVector); Bounds[] rendererBounds = part.GetRendererBounds(); //print("Updating drag for " + part.partInfo.title); foreach (AttachNode Attach in part.attachNodes) { if (Attach.nodeType == AttachNode.NodeType.Stack) { if (Attach.id.ToLowerInvariant() == "strut") { continue; } Vector3d relPos = Attach.position + Attach.offset; if (part.Modules.Contains("FARCargoBayModule")) { FARCargoBayModule bay = (FARCargoBayModule)part.Modules["FARCargoBayModule"]; Vector3d maxBounds = bay.maxBounds; Vector3d minBounds = bay.minBounds; if (relPos.x < maxBounds.x && relPos.y < maxBounds.y && relPos.z < maxBounds.z && relPos.x > minBounds.x && relPos.y > minBounds.y && relPos.z > minBounds.z) { continue; } } if (Attach.attachedPart != null) { if (AttachedPartIsNotClipping(Attach.attachedPart, rendererBounds)) { continue; } } Vector3d origToNode = transform.localToWorldMatrix.MultiplyVector(relPos); double attachSize = FARMathUtil.Clamp(Attach.size, 0.5, double.PositiveInfinity); if (UnattachedPartRightAgainstNode(origToNode, attachSize, relPos, Attach.attachedPart)) { continue; } attachNodeData newAttachNodeData = new attachNodeData(); double exposedAttachArea = attachSize * FARAeroUtil.attachNodeRadiusFactor; newAttachNodeData.recipDiameter = 1 / (2 * exposedAttachArea); exposedAttachArea *= exposedAttachArea; exposedAttachArea *= Math.PI * FARAeroUtil.areaFactor; SPlusAttachArea += exposedAttachArea; exposedAttachArea /= FARMathUtil.Clamp(S, 0.01, double.PositiveInfinity); newAttachNodeData.areaValue = exposedAttachArea; if (Vector3d.Dot(origToNode, partUpVector) > 1) { newAttachNodeData.pitchesAwayFromUpVec = true; } else { newAttachNodeData.pitchesAwayFromUpVec = false; } newAttachNodeData.location = transform.worldToLocalMatrix.MultiplyVector(origToNode); attachNodeDragList.Add(newAttachNodeData); } } }
private void AttachNodeCdAdjust() { //BaseCd = 0; // if (!part.Modules.Contains("FARPayloadFairingModule")) // { if (part.Modules.Contains("FARPayloadFairingModule")) //This doesn't apply blunt drag drag to fairing parts if one of their "exempt" attach nodes is used, indicating attached fairings { return; } if (VesselPartList == null) { UpdateShipPartsList(); } if (attachNodeDragDict == null) { attachNodeDragDict = new Dictionary <Vector3d, attachNodeData>(); } attachNodeDragDict.Clear(); Transform transform = part.partTransform; Vector3d partUpVector = transform.TransformDirection(localUpVector); //print("Updating drag for " + part.partInfo.title); foreach (AttachNode Attach in part.attachNodes) { if (Attach.nodeType == AttachNode.NodeType.Stack) { if (Attach.attachedPart != null) { continue; } if (Attach.id.ToLowerInvariant() == "strut") { continue; } /* string attachId = Attach.id.ToLowerInvariant(); * bool leaveAttachLoop = false; * foreach (string s in FARMiscData.exemptAttachNodes) * if (attachId.Contains(s)) * { * leaveAttachLoop = true; * break; * } * if (leaveAttachLoop) * continue;*/ Ray ray = new Ray(); Vector3d relPos = Attach.position + Attach.offset; if (part.Modules.Contains("FARCargoBayModule")) { FARCargoBayModule bay = (FARCargoBayModule)part.Modules["FARCargoBayModule"]; Vector3d maxBounds = bay.maxBounds; Vector3d minBounds = bay.minBounds; if (relPos.x < maxBounds.x && relPos.y < maxBounds.y && relPos.z < maxBounds.z && relPos.x > minBounds.x && relPos.y > minBounds.y && relPos.z > minBounds.z) { return; } } Vector3d origToNode = transform.localToWorldMatrix.MultiplyVector(relPos); double mag = (origToNode).magnitude; //print(part.partInfo.title + " Part Loc: " + part.transform.position + " Attach Loc: " + (origToNode + part.transform.position) + " Dist: " + mag); ray.direction = origToNode; ray.origin = transform.position; double attachSize = FARMathUtil.Clamp(Attach.size, 0.5, double.PositiveInfinity); bool gotIt = false; RaycastHit[] hits = Physics.RaycastAll(ray, (float)(mag + attachSize), FARAeroUtil.RaycastMask); foreach (RaycastHit h in hits) { if (h.collider == part.collider) { continue; } if (h.distance < (mag + attachSize) && h.distance > (mag - attachSize)) { foreach (Part p in VesselPartList) { if (p.collider == h.collider) { gotIt = true; break; } } } if (gotIt) { break; } } if (!gotIt) { // float exposedAttachArea = (Mathf.PI * Mathf.Pow(attachSize * FARAeroUtil.attachNodeRadiusFactor, 2) / Mathf.Clamp(S, 0.01f, Mathf.Infinity)); double exposedAttachArea = attachSize * FARAeroUtil.attachNodeRadiusFactor; exposedAttachArea *= exposedAttachArea; exposedAttachArea *= Math.PI * FARAeroUtil.areaFactor; exposedAttachArea /= FARMathUtil.Clamp(S, 0.01, double.PositiveInfinity); attachNodeData newAttachNodeData = new attachNodeData(); newAttachNodeData.areaValue = exposedAttachArea; if (Vector3d.Dot(origToNode, partUpVector) > 1) { newAttachNodeData.pitchesAwayFromUpVec = true; } else { newAttachNodeData.pitchesAwayFromUpVec = false; } if (attachNodeDragDict.ContainsKey(transform.worldToLocalMatrix.MultiplyVector(origToNode))) { attachNodeData tmp = attachNodeDragDict[transform.worldToLocalMatrix.MultiplyVector(origToNode)]; tmp.areaValue += newAttachNodeData.areaValue; attachNodeDragDict[transform.worldToLocalMatrix.MultiplyVector(origToNode)] = tmp; } else { attachNodeDragDict.Add(part.transform.worldToLocalMatrix.MultiplyVector(origToNode), newAttachNodeData); } } } } // } //print(part.partInfo.title + " Num unused Attach Nodes: " + attachNodeDragDict.Count); }
private void AttachNodeCdAdjust() { if (part.Modules.Contains("FARPayloadFairingModule")) //This doesn't apply blunt drag drag to fairing parts if one of their "exempt" attach nodes is used, indicating attached fairings { return; } if (VesselPartList == null) { UpdateShipPartsList(); } if (attachNodeDragList == null) { attachNodeDragList = new List <attachNodeData>(); } attachNodeDragList.Clear(); Transform transform = part.transform; if (transform == null) { transform = part.transform; } if (transform == null) { Debug.LogError("Part " + part.partInfo.title + " has null transform; drag interactions cannot be applied."); return; } SPlusAttachArea = S; Vector3d partUpVector = transform.TransformDirection(localUpVector); //print("Updating drag for " + part.partInfo.title); foreach (AttachNode attach in part.attachNodes) { if (attach.nodeType == AttachNode.NodeType.Stack) { if (attach.id.ToLowerInvariant() == "strut") { continue; } Vector3d relPos = attach.position;// +Attach.offset; if (part.Modules.Contains("FARCargoBayModule")) { FARCargoBayModule bay = (FARCargoBayModule)part.Modules["FARCargoBayModule"]; if (bay.bayBounds.Contains(relPos)) { continue; } } Vector3d origToNode = transform.localToWorldMatrix.MultiplyVector(relPos); double attachSize = FARMathUtil.Clamp(attach.size, 0.5, double.PositiveInfinity); if (attach.attachedPart != null) { Vector3 location = attach.attachedPart.transform.position; FARBasicDragModel d = attach.attachedPart.GetComponent <FARBasicDragModel>(); if (d != null) { location += attach.attachedPart.transform.localToWorldMatrix.MultiplyVector(d.CenterOfDrag); } //Debug.Log(Attach.attachedPart.partInfo.title + " " + location + " " + Attach.attachedPart.transform.position + " " + (origToNode + part.transform.position)); if (AttachedPartCoDIsFurtherThanAttachLocation(location, origToNode)) { continue; } if (AttachedPartIsNotClipping(location, PartBounds)) { if (d != null) { location = this.part.transform.localToWorldMatrix.MultiplyVector(this.CenterOfDrag); location += this.part.transform.position; if (d.AttachedPartIsNotClipping(location, d.PartBounds)) { continue; } } else { continue; } } //this is a bit of a hack to make intakes function slightly better, since very thin ones seem to have issues. ModuleResourceIntake intake = attach.attachedPart.GetComponent <ModuleResourceIntake>(); if (intake != null) { Transform intakeTrans = attach.attachedPart.FindModelTransform(intake.intakeTransformName); if ((object)intakeTrans != null) { Vector3 intakeForwardVec = (intakeTrans.forward); //Vector3 attachOrientation = this.part.transform.localToWorldMatrix.MultiplyVector(attach.orientation); int intakeOrientSign = Math.Sign(Vector3.Dot((Vector3)origToNode, intakeForwardVec)); //int attachLocSign = Math.Sign(Vector3.Dot(attachOrientation, (Vector3)origToNode)); //int intakeOrientationSign = Math.Sign(Vector3.Dot(intakeForwardVec, (Vector3)origToNode)); if (intakeOrientSign > 0) { continue; } } } } if (UnattachedPartRightAgainstNode(origToNode, attachSize, attach.attachedPart)) { continue; } attachNodeData newAttachNodeData = new attachNodeData(); double exposedAttachArea = attachSize * FARAeroUtil.attachNodeRadiusFactor; newAttachNodeData.recipDiameter = 1 / (2 * exposedAttachArea); exposedAttachArea *= exposedAttachArea; exposedAttachArea *= Math.PI * FARAeroUtil.areaFactor; SPlusAttachArea += exposedAttachArea; exposedAttachArea /= FARMathUtil.Clamp(S, 0.01, double.PositiveInfinity); newAttachNodeData.areaValue = exposedAttachArea; if (Vector3d.Dot(origToNode, partUpVector) > 0) { newAttachNodeData.pitchesAwayFromUpVec = true; } else { newAttachNodeData.pitchesAwayFromUpVec = false; } newAttachNodeData.location = relPos; //Debug.Log(part.partInfo.title + " found open node"); attachNodeDragList.Add(newAttachNodeData); } } }
private void AttachNodeCdAdjust() { if (part.Modules.Contains("FARPayloadFairingModule")) //This doesn't apply blunt drag drag to fairing parts if one of their "exempt" attach nodes is used, indicating attached fairings { return; } if (VesselPartList == null) { UpdateShipPartsList(); } if (attachNodeDragList == null) { attachNodeDragList = new List <attachNodeData>(); } attachNodeDragList.Clear(); Transform transform = part.partTransform; if (transform == null) { transform = part.transform; } if (transform == null) { Debug.LogError("Part " + part.partInfo.title + " has null transform; drag interactions cannot be applied."); return; } SPlusAttachArea = S; Vector3d partUpVector = transform.TransformDirection(localUpVector); //print("Updating drag for " + part.partInfo.title); foreach (AttachNode Attach in part.attachNodes) { if (Attach.nodeType == AttachNode.NodeType.Stack) { if (Attach.attachedPart != null) { continue; } if (Attach.id.ToLowerInvariant() == "strut") { continue; } Ray ray = new Ray(); Vector3d relPos = Attach.position + Attach.offset; if (part.Modules.Contains("FARCargoBayModule")) { FARCargoBayModule bay = (FARCargoBayModule)part.Modules["FARCargoBayModule"]; Vector3d maxBounds = bay.maxBounds; Vector3d minBounds = bay.minBounds; if (relPos.x < maxBounds.x && relPos.y < maxBounds.y && relPos.z < maxBounds.z && relPos.x > minBounds.x && relPos.y > minBounds.y && relPos.z > minBounds.z) { return; } } Vector3d origToNode = transform.localToWorldMatrix.MultiplyVector(relPos); double mag = (origToNode).magnitude; //print(part.partInfo.title + " Part Loc: " + part.transform.position + " Attach Loc: " + (origToNode + part.transform.position) + " Dist: " + mag); ray.direction = origToNode; ray.origin = transform.position; double attachSize = FARMathUtil.Clamp(Attach.size, 0.5, double.PositiveInfinity); bool gotIt = false; RaycastHit[] hits = Physics.RaycastAll(ray, (float)(mag + attachSize), FARAeroUtil.RaycastMask); foreach (RaycastHit h in hits) { if (h.collider == part.collider) { continue; } if (h.distance < (mag + attachSize) && h.distance > (mag - attachSize)) { foreach (Part p in VesselPartList) { if (p.collider == h.collider) { gotIt = true; break; } } } if (gotIt) { break; } } if (!gotIt) { double exposedAttachArea = attachSize * FARAeroUtil.attachNodeRadiusFactor; exposedAttachArea *= exposedAttachArea; exposedAttachArea *= Math.PI * FARAeroUtil.areaFactor; SPlusAttachArea += exposedAttachArea; exposedAttachArea /= FARMathUtil.Clamp(S, 0.01, double.PositiveInfinity); attachNodeData newAttachNodeData = new attachNodeData(); newAttachNodeData.areaValue = exposedAttachArea; if (Vector3d.Dot(origToNode, partUpVector) > 1) { newAttachNodeData.pitchesAwayFromUpVec = true; } else { newAttachNodeData.pitchesAwayFromUpVec = false; } newAttachNodeData.location = transform.worldToLocalMatrix.MultiplyVector(origToNode); attachNodeDragList.Add(newAttachNodeData); } } } }