3

I am looking for a library or algorithm that would allow me to add simple 'flight sim' capabilities to rigid bodies in a 2D world.

I've made a game as an art project, where virtual fish swim in a tank of water. I started off using Box2D/liquidfun for particle-based fluid simulation, and it behaved realistically when rigid bodies were dragged through the fluid. However, the computational load was intense, even though it is very well optimized.

Instead, I want to compute the lift and drag forces for each face so that the bodies behave as if they are moving through water, with no particles needed. It would be like the flight models of some flight simulators. I believe X-Plane (https://www.x-plane.com/) works in this way. Some of the answers to other questions on this site suggest modeling the lift and drag of your entire 'airplane' as constants- but they are too simple, because i need to show the physical consequences of the body's geometry, and how it affects movement.

I was not able to find a module that offered this, but I had a go at writing my own. It breaks a polygon down into faces and applies the lift and drag equations to them. ("BoneUserData" is a struct that stores information for a single rigid body. Box2D / C++):

bool chooseDMostVertex(float angle, b2Vec2 p1, b2Vec2 p2) {
    // given 2 vertices, identify which one lines most in a given direction. return true if it is p1 and false if it is p2.

    // unrotate the set of vertices by the direction, so that the distance to measure is along the Y axis.
    // get the centroid, this is your rotation center
    b2Vec2 thisFaceCentroid = b2Vec2((p1.x + p2.x)/2,(p1.y+p2.y)/2);

    // perform the rotation
    b2Vec2 p1r = rotatePoint(thisFaceCentroid.x, thisFaceCentroid.y, angle, p1);
    b2Vec2 p2r = rotatePoint(thisFaceCentroid.x, thisFaceCentroid.y, angle, p2);

    if (p1r.y > p2r.y) {
        return true;
    }
    else {
        return false;
    }
}

// provides FEA-based lift and drag calculations
void flightModel(BoneUserData * bone) {

    uint nVertices = bone->shape.GetVertexCount();

    for (uint j = 0; j < nVertices; ++j)
    {
        // get the face vertices from the b2 shape
        b2Vec2 p1 = bone->shape.GetVertex(j);
        b2Vec2 p2 = b2Vec2(0.0f, 0.0f);

        // use the pair n and n-1 to make a face. if n is zero, make a face from the first to the last vertices.
        if (j == 0) {
            p2 = bone->shape.GetVertex(nVertices-1);
        }
        else {
            p2 = bone->shape.GetVertex(j-1);
        }
        
        // get the position of the face center point, taking into account the body's present rotation.
        b2Vec2 worldCenter = bone->p_body->GetWorldCenter();
        b2Vec2 p1r = rotatePoint(0.0f, 0.0f, bone->p_body->GetAngle(), p1 );
        p1r += worldCenter;
        b2Vec2 p2r = rotatePoint(0.0f, 0.0f, bone->p_body->GetAngle(), p2 );
        p2r += worldCenter;
        b2Vec2 faceCenter = b2Vec2( (p1r.x + p2r.x)/2, (p1r.y + p2r.y)/2 ) ;
        float faceAngle = atan2(p1r.y - p2r.y, p1r.x - p2r.x);

        // calculate the angle of incidence into the oncoming 'wind'
        b2Vec2 linearVelocity = bone->p_body->GetLinearVelocity();// must get the linear velocity of the face center, not just of the body itself.

        // you can calculate the face center velocity, by taking the radius and multiplying it by the angular velocity.
        b2Vec2 localFaceCenter = b2Vec2( (p1.x + p2.x)/2, (p1.y + p2.y)/2 ) ;
        float magLinearVelocityOfRotatingFace = (bone->p_body->GetAngularVelocity() * magnitude( localFaceCenter));  // https://courses.lumenlearning.com/boundless-physics/chapter/velocity-acceleration-and-force/
        b2Vec2 faceAngularVelocity = b2Vec2( cos(faceAngle) * magLinearVelocityOfRotatingFace, sin(faceAngle) * magLinearVelocityOfRotatingFace);
        b2Vec2 totalVelocity = b2Vec2(linearVelocity.x + faceAngularVelocity.x, linearVelocity.y + faceAngularVelocity.y);
        float magnitudeVelocity = magnitude(totalVelocity);

        b2Vec2 distanceBetweenPoints = b2Vec2(p2r.x - p1r.x, p2r.y - p1r.y);
        float magnitudeArea = magnitude(distanceBetweenPoints);
        float angleOfForwardDirection = atan2(linearVelocity.x, linearVelocity.y) - 0.5 * pi;

        // calculate the force of drag
        float dragCoefficient = 0.002;
        float dragForce = magnitudeVelocity * magnitudeArea * dragCoefficient * -1; // the -1 in this statement is what makes it an opposing force.
        b2Vec2 dragVector = b2Vec2( cos(angleOfForwardDirection) * dragForce , sin(angleOfForwardDirection) * dragForce *-1);

        // calculate the force of lift
        float liftCoeff  = 0.5;
        float atmosphericDensity = 1;
        float liftForce = liftCoeff * ((atmosphericDensity * (magnitudeVelocity*magnitudeVelocity))/2) * magnitudeArea * -1;

        // the lift angle is normal to the face but its direction is determined by how the plane meets the incoming wind.
        float liftAngle = faceAngle + 0.5*pi;
        if (chooseDMostVertex(angleOfForwardDirection, p1r, p2r)) {
            liftForce = liftForce * -1;
        }
        b2Vec2 fluidDynamicForce = b2Vec2(cos(liftAngle ) * liftForce, sin(liftAngle ) * liftForce );
    
        // apply the force to the body directly in the center of the face.
        bone->p_body->ApplyForce(dragVector, faceCenter, true);

        if (    fluidDynamicForce.x < 1000 && fluidDynamicForce.y < 1000
            &&  fluidDynamicForce.x > -1000 && fluidDynamicForce.y > -1000 ) {
            bone->p_body->ApplyForce(fluidDynamicForce, faceCenter, true);
        }
    }
}

However, it suffers from physical bugs. I understand that the way I am modelling drag is incorrect, that it should depend on the area presented to the wind, and not the area overall. But there are other problems such as near-stationary objects randomly jumping, rotating symmetrical objects do not slow down, and some rotating objects will speed up exponentially. I don't understand how these issues are being introduced by my algorithm. If I could get rid of them, I would be happy enough to keep using this model.

stanged
  • 31
  • 3
  • "Please help me by recommending a library, module, script or algorithm that I can use in place of my model, even if it is in a white paper or another language ..." Sorry, but phrased like that your question is off-topic. Please read [ask] and [help/on-topic]. – Yunnosch Nov 23 '20 at 07:04
  • 1
    "modeling the lift and drag of your entire 'airplane' as constants- but they are too simple, because i need to show the physical consequences of the body's geometry, and how it affects movement." Why does a body-specific constant not show the effect of the bodys geometry? A set of body-specific constants could also dynamically take the effect of different movement speeds and directions into account. – Yunnosch Nov 23 '20 at 07:20
  • "it suffers from physical bugs" What are the symptoms of the bugs? – Yunnosch Nov 23 '20 at 07:21
  • "But there are other problems" How about fixing the problems you have understood first? Reduce the unknowns/effects in your problem. – Yunnosch Nov 23 '20 at 07:22
  • Did you experiment with simple geometries, so simple that you can predict the needed values on pen-and-paper and compare the values your program creates/uses? – Yunnosch Nov 23 '20 at 07:24
  • You are correct on drag. It is the "Planform" area presented to the relative wind used in the drag computation. However, that isn't hard to obtain if you know the orientation of your polygon,and know direction the wind is meeting the polygon, then you can calculate the planform area for each face. I also saw a page not to long ago that listed the aerodynamic characteristics for a large number of shapes. They may have what you need already measured (Coeff of lift and drag) – David C. Rankin Nov 23 '20 at 07:47
  • @Yunnosch, I removed the last paragraph. Sorry. Even a body specific constant would lose the nuance that is important to me: A long thin shape would slip through the water the same whether it was pointed edge on or face on. I did a test using a rectangle. It was dragged into the fluid at a 45 degree angle. It should have experienced lots of drag and moved into the fluid toward the leading edge. That did happen, as well as wobbling when dragged face-on, like a dropped sheet of paper. – stanged Nov 23 '20 at 08:07
  • The other problems are just the ones mentioned: - symmetrical objects do not stop spinning - some spinning objects accelerate To solve this, I would add a counter-rotation drag based on the polygon area * rotation speed. Also, I would balance the amount of lift to the amount of drag, so that energy cannot be added into the sim. But really, I asked because I hoped there was already a tool for this, instead of wanting to perfect my own. – stanged Nov 23 '20 at 08:07
  • @DavidC.Rankin Thank you. I will try to calculate this and add it to the model. It would be very useful to have more reference materials. – stanged Nov 23 '20 at 08:09
  • "slip through the water the same whether it was pointed edge on or face on" not if you used a multi-constant based mechanism to take the direction into account. That is what I meant by "A set of body-specific constants could also dynamically take the effect of different movement speeds and directions into account.". – Yunnosch Nov 23 '20 at 08:10

0 Answers0