private static void ApplyMoment(SectionForceInput input) { Vector3 moment = input.worldAxis * input.force; input.section.Moment += moment; if (!input.body) { return; } input.body.AddTorqueAtPosition(moment, input.worldAxisZero, input.sectionWorldPosition, ForceMode.Force); }
private static void ApplyDrag(SectionForceInput input) { Vector3 drag = input.sectionRelativeDirection * -input.force; input.section.Drag += drag; if (!input.body) { return; } input.body.AddForceAtPosition(drag, input.sectionWorldPosition); }
// Todo: We might be able to skip a normalize and a scale operation if we use RelativeVelocity instead of RelativeDirection+Magnitude breakup private static void ApplyLift(SectionForceInput input) { Vector3 liftDirection = Vector3.Cross(input.sectionRelativeDirection, input.worldAxis); float liftMagnitude = input.force * Mathf.Pow(input.efficiency, 2f); Vector3 lift = liftDirection * liftMagnitude; input.section.Lift += lift; if (!input.body) { return; } input.body.AddForceAtPosition(lift, input.sectionWorldPosition); }
private void EvaluateForces() { Transform wingTransform = transform; Vector3 wingPosition = wingTransform.position; Vector3 windVelocity = _wind.GetWindVelocity(wingPosition); float airDensity = _wind.GetAirDensity(wingPosition); Vector3 velocity = (wingPosition - _lastPosition) / Time.deltaTime; _lastPosition = wingPosition; RelativeVelocity = velocity - windVelocity; AirSpeed = RelativeVelocity.magnitude; const float groundEffectHeight = 32f; const float groundEffectPow = 2f; float groundEffectMultiplier = 1f; RaycastHit hit; if (Physics.Raycast(_body.transform.position, Vector3.down, out hit, groundEffectHeight, _layerMask)) { groundEffectMultiplier += Mathf.Pow(Mathf.InverseLerp(groundEffectHeight, 0f, hit.distance), groundEffectPow); } UpdateStats(wingTransform); // Calculate forces per section of the wing for (int i = 0; i < _numSections; i++) { var section = _sectionStates[i]; Vector3 sectionWorldPosition = wingTransform.TransformPoint(section.LocalPosition); Vector3 sectionWorldVelocity = (sectionWorldPosition - section.LastSectionWorldPosition) / Time.fixedDeltaTime; section.LastSectionWorldPosition = sectionWorldPosition; section.RelativeVelocity = sectionWorldVelocity - windVelocity; section.AirSpeed = section.RelativeVelocity.magnitude; float dynamicPressure = 0.5f * airDensity * section.AirSpeed * section.AirSpeed * groundEffectMultiplier; section.Lift = Vector3.zero; section.Drag = Vector3.zero; section.Moment = Vector3.zero; //Vector3 localRelativeVelocity = wingTransform.InverseTransformDirection(section.RelativeVelocity); // Calculate the effects around each axis for (int j = 0; j < _components.Length; j++) { AirfoilAxisResponse coefficient = _components[j]; AirfoilAxis airfoilAxis = _axes[(int)coefficient.AxisType]; Vector3 worldAxis = wingTransform.TransformDirection(airfoilAxis.Axis); Vector3 worldAxisBase = wingTransform.TransformDirection(airfoilAxis.AxisZero); // Todo: Collapse this all into 2D calculations around the coefficient axis Vector3 projectedVelocity = section.RelativeVelocity - Vector3.Project(section.RelativeVelocity, worldAxis); section.AngleOfAttack = MathUtils.AngleAroundAxis(worldAxisBase, projectedVelocity, worldAxis); Vector3 sectionRelativeDirection = projectedVelocity.normalized; float forceCoefficient = coefficient.Coefficients.Evaluate(section.AngleOfAttack + section.Offset) * coefficient.Multiplier; float forceMagnitude = dynamicPressure * section.SurfaceArea * forceCoefficient; var input = new SectionForceInput() { body = _body, force = forceMagnitude, efficiency = _efficiency, section = section, sectionWorldPosition = sectionWorldPosition, sectionRelativeDirection = sectionRelativeDirection, worldAxis = worldAxis, worldAxisZero = worldAxisBase }; //ForceApplicators[(int) coefficient.Type](input); switch (coefficient.Type) { case ForceType.Lift: ApplyLift(input); break; case ForceType.Drag: ApplyDrag(input); break; case ForceType.Moment: ApplyMoment(input); break; } } } }