private BiVector3 CalculateAerodynamicForces(Vector3 velocity, Vector3 angularVelocity, Vector3 wind, float airDensity, Vector3 centerOfMass)
    {
        BiVector3 forceAndTorque = new BiVector3();

        foreach (var surface in aerodynamicSurfaces)
        {
            Vector3 relativePosition = surface.transform.position - centerOfMass;
            forceAndTorque += surface.CalculateForces(-velocity + wind
                                                      - Vector3.Cross(angularVelocity,
                                                                      relativePosition),
                                                      airDensity, relativePosition);
        }
        return(forceAndTorque);
    }
    private void FixedUpdate()
    {
        BiVector3 forceAndTorqueThisFrame =
            CalculateAerodynamicForces(rb.velocity, rb.angularVelocity, Vector3.zero, 1.2f, rb.worldCenterOfMass);

        Vector3 velocityPrediction        = PredictVelocity(forceAndTorqueThisFrame.p);
        Vector3 angularVelocityPrediction = PredictAngularVelocity(forceAndTorqueThisFrame.q);

        BiVector3 forceAndTorquePrediction =
            CalculateAerodynamicForces(velocityPrediction, angularVelocityPrediction, Vector3.zero, 1.2f, rb.worldCenterOfMass);

        currentForceAndTorque = (forceAndTorqueThisFrame + forceAndTorquePrediction) * 0.5f;
        rb.AddForce(currentForceAndTorque.p);
        rb.AddTorque(currentForceAndTorque.q);

        rb.AddForce(transform.forward * thrust * thrustPercent);
    }
    public BiVector3 CalculateForces(Vector3 worldAirVelocity, float airDensity, Vector3 relativePosition)
    {
        BiVector3 forceAndTorque = new BiVector3();

        if (!gameObject.activeInHierarchy || config == null)
        {
            return(forceAndTorque);
        }

        if (!initialized)
        {
            Initialize();
        }

        Vector3 airVelocity = transform.InverseTransformDirection(worldAirVelocity);

        airVelocity = new Vector3(airVelocity.x, airVelocity.y);

        Vector3 dragDirection = transform.TransformDirection(airVelocity.normalized);
        Vector3 liftDirection = Vector3.Cross(dragDirection, transform.forward);

        float dynamicPressure = 0.5f * airDensity * airVelocity.sqrMagnitude;
        float angleOfAttack   = Mathf.Atan2(airVelocity.y, -airVelocity.x);


        IsAtStall = !(angleOfAttack <stallAngleHigh && angleOfAttack> stallAngleLow);
        Vector3 aerodynamicCoefficients = CalculateCoefficients(angleOfAttack);

        CurrentLift   = liftDirection * aerodynamicCoefficients.x * dynamicPressure * area;
        CurrentDrag   = dragDirection * aerodynamicCoefficients.y * dynamicPressure * area;
        CurrentTorque = -transform.forward * aerodynamicCoefficients.z * dynamicPressure * area * config.chord;

        forceAndTorque.p += CurrentDrag + CurrentLift;
        forceAndTorque.q += Vector3.Cross(relativePosition, forceAndTorque.p);
        forceAndTorque.q += CurrentTorque;

        return(forceAndTorque);
    }
Example #4
0
    public BiVector3 CalculateForces(Vector3 worldAirVelocity, float airDensity, Vector3 relativePosition)
    {
        BiVector3 forceAndTorque = new BiVector3();

        if (!gameObject.activeInHierarchy || config == null)
        {
            return(forceAndTorque);
        }

        // Accounting for aspect ratio effect on lift coefficient.
        float correctedLiftSlope = config.liftSlope * config.aspectRatio /
                                   (config.aspectRatio + 2 * (config.aspectRatio + 4) / (config.aspectRatio + 2));

        // Calculating flap deflection influence on zero lift angle of attack
        // and angles at which stall happens.
        float theta            = Mathf.Acos(2 * config.flapFraction - 1);
        float flapEffectivness = 1 - (theta - Mathf.Sin(theta)) / Mathf.PI;
        float deltaLift        = correctedLiftSlope * flapEffectivness * FlapEffectivnessCorrection(flapAngle) * flapAngle;

        float zeroLiftAoaBase = config.zeroLiftAoA * Mathf.Deg2Rad;
        float zeroLiftAoA     = zeroLiftAoaBase - deltaLift / correctedLiftSlope;

        float stallAngleHighBase = config.stallAngleHigh * Mathf.Deg2Rad;
        float stallAngleLowBase  = config.stallAngleLow * Mathf.Deg2Rad;

        float clMaxHigh = correctedLiftSlope * (stallAngleHighBase - zeroLiftAoaBase) + deltaLift * LiftCoefficientMaxFraction(config.flapFraction);
        float clMaxLow  = correctedLiftSlope * (stallAngleLowBase - zeroLiftAoaBase) + deltaLift * LiftCoefficientMaxFraction(config.flapFraction);

        float stallAngleHigh = zeroLiftAoA + clMaxHigh / correctedLiftSlope;
        float stallAngleLow  = zeroLiftAoA + clMaxLow / correctedLiftSlope;

        // Calculating air velocity relative to the surface's coordinate system.
        // Z component of the velocity is discarded.
        Vector3 airVelocity = transform.InverseTransformDirection(worldAirVelocity);

        airVelocity = new Vector3(airVelocity.x, airVelocity.y);
        Vector3 dragDirection = transform.TransformDirection(airVelocity.normalized);
        Vector3 liftDirection = Vector3.Cross(dragDirection, transform.forward);

        float area            = config.chord * config.span;
        float dynamicPressure = 0.5f * airDensity * airVelocity.sqrMagnitude;
        float angleOfAttack   = Mathf.Atan2(airVelocity.y, -airVelocity.x);

        Vector3 aerodynamicCoefficients = CalculateCoefficients(angleOfAttack,
                                                                correctedLiftSlope,
                                                                zeroLiftAoA,
                                                                stallAngleHigh,
                                                                stallAngleLow);

        Vector3 lift   = liftDirection * aerodynamicCoefficients.x * dynamicPressure * area;
        Vector3 drag   = dragDirection * aerodynamicCoefficients.y * dynamicPressure * area;
        Vector3 torque = -transform.forward * aerodynamicCoefficients.z * dynamicPressure * area * config.chord;

        forceAndTorque.p += lift + drag;
        forceAndTorque.q += Vector3.Cross(relativePosition, forceAndTorque.p);
        forceAndTorque.q += torque;

#if UNITY_EDITOR
        // For gizmos drawing.
        IsAtStall     = !(angleOfAttack <stallAngleHigh && angleOfAttack> stallAngleLow);
        CurrentLift   = lift;
        CurrentDrag   = drag;
        CurrentTorque = torque;
#endif

        return(forceAndTorque);
    }