Special Aircraft Service

Please login or register.

Login with username, password and session length
Advanced search  
Pages: [1]   Go Down

Author Topic: More work on plane wreckage  (Read 624 times)

0 Members and 1 Guest are viewing this topic.

WxTech

  • Modder
  • member
  • Offline Offline
  • Posts: 6040
More work on plane wreckage
« on: May 07, 2024, 11:22:14 AM »

Wreckage.class handles the numerous bits into which a plane is discombobulated when exploded, in the air or on the ground. For instance, a B-26 becomes a cloud of about 100 parts, each of which is handled separately. The size of the mesh making each individual chunk is used to determine a volume, mass and density via some simple algorithms, which in turn has a retardation value calculated for the degree of frictional slowing in air. The result of this treatment is what I would characterize as a general inversion of reality, where smaller is less dense. And so the big pieces fall more quickly while the ever smaller bits take longer to reach the ground.

On the contrary, for a plane it's more generally the case that bigger parts are less dense. For instance a good part of a wing or fuselage is mostly air. It looks supremely disconcerting to see half a wing screaming to earth as fast as the lawn-darting plane it was just cut from. ;) In reality a low-density, tumbling part would slow down much more so than an aerodynamically oriented fuselage or some small, denser chunk. Furthermore, if the remaining plane is in a flat spin or otherwise tumbling on the way down, it will reach a fairly slow terminal velocity via the more sophisticated calculations of drag working upon it, while those tumbling wing sections are treated more simply, being hardly slowed in comparison and thus reaching the ground much sooner.

This has led me to re-work the treatment so that the density calculation is opposite to that formerly applied. And the decelerating parameter has been tweaked as well. Now the big chunks tumble down more slowly and the little bits fall more quickly.

As I've written about previously, I've also reworked the velocity dispersion applied to the exploded bits, so that they
- no longer fall over a ridiculously wide arc oriented perpendicular to the flight path,
- no longer fall over a quite small range in distance along the flight path, and
- do not fan outward over an increasingly curving series of arcs the longer they are in motion.
Now they fall much more reasonably along the flight path, with much less lateral dispersion and with much more longitudinal dispersion. Recall the wreckage from the shuttle Columbia break-up over Texas, and how the debris field was much more elongated along the flight path. That was entirely due to density and aerodynamic differences among the pieces, with wind acting over differing durations of fall to exert lateral dispersion.


Before you ask, it would be awful trouble to even attempt to try to determine the actual part of the plane each piece is in order to determine a more accurate density/deceleron value. A simplistic approach like this, which is just a tweaking of the stock scheme, is as far as I'm willing to go.  :-|  But I think the result hews much more closely to real behaviour than hitherto.
Logged
Great minds discuss ideas. Average minds discuss events. Small minds discuss people. - Hyman Rickover (but probably predating his use.)

WxTech

  • Modder
  • member
  • Offline Offline
  • Posts: 6040
Re: More work on plane wreckage
« Reply #1 on: May 07, 2024, 05:40:21 PM »

Here we have a B-25 that just exploded. The left panel is quite soon after the event, and the right panel a bit later still. Note the general differentiation of the wreckage by size, where the trend is for the larger pieces to have lesser density and hence are retarded more strongly by drag. Over time this dispersion increases as the bigger stuff continues to slow more than the small stuff. And the big pieces, in their greater slowing, fall to earth a bit more slowly, taking longer to reach the surface.

Note the big chunk of fuselage, trailing farthest behind the debris. The two sizeable parts that are on fire and leaving the far denser smoke trails are also lagging behind the pack of smaller stuff. The more rapid deceleration is revealed by the increasingly sharper downward curving of the smoke.

[click for larger]
Logged
Great minds discuss ideas. Average minds discuss events. Small minds discuss people. - Hyman Rickover (but probably predating his use.)

WxTech

  • Modder
  • member
  • Offline Offline
  • Posts: 6040
Re: More work on plane wreckage
« Reply #2 on: May 12, 2024, 11:30:34 AM »

One element of the handling of wreckage bits which is particularly ugly is the way they are made to diverge horizontally outward on ever more strongly curving paths as the speed slows. If a plane blows up in the air, and more so if not particularly high in altitude, the pieces will bounce along the ground for a time. For the bits that are more horizontally displaced from the average ground track, they can curve so much as they slow that their direction of travel can end up doing a full 180 degree arc! The overall pattern of the motions is very geometrically uniform, being what I call a Fleur-de-lis.

For wreckage falling from a considerable height this arcing is in effect, right from the start. The outlier pieces can end up being horizontally separated from the mean ground track by hundreds of meters. The result is a pattern of fall which is on an arc convex in the direction of travel, not much separated in distance along the direction of travel, and HUGELY elongated on its arcuate line perpendicular to the line of travel. That is, the surface distribution in the fall pattern should be elongated in the direction of travel, but instead is elongated perpendicular to the direction of travel. Most unnatural!

What's the cause of this silly curving? I include my full, current Wreckage.class below. The primary culprit is

    private static float dv[] = {
        -80.0F, -60.0F, -40.0F, -20.0F, 20.0F, 40.0F, 60.0F, 80.0F, 0.0F
    };

Which I've scaled down considerably to

    private static float dv[] = {
        -20.0F, -15.0F, -10.0F, -5.0F, 5.0F, 10.0F, 15.0F, 20.0F, 0.0F
    };

Acting in concert, in tick(), is the following. I've halved the magnitude of variable f1

            float f = Time.tickLenFs();
            float f2 = (float)v.length();
//            float f1 = 10F / (f2 + 0.1F);  //original; imparts more curvature at slower speed??
            float f1 = 5F / (f2 + 0.1F);  //half the curvature rate?
            if(f1 > 1.0F)
                f1 = 1.0F;  //10m/s and slower (now 5m/s and slower)
            f1 *= Wreckage.dv[lr] * f;  //[dv] range was -80 to 80, now -20 to 20

The result is a VERY reduced tendency of curvature of motion in the horizontal plane; much more natural.

But this change by itself results in the wreckage bits remaining much more closely spaced for some time. This scheme seems to be used primarily to get the bits flying apart at a reasonable rate from the outset. To get the pieces initially flying apart I've implemented in the three relevant methods in Aircraft.class the application of an envelope of random velocity. For destruction in the air I apply an additional random velocity of up to 7m/s on each axis individually, and for destruction on the ground this envelope of dispersion is up to 30m/s (to get more 'spread' as the bits bounce across the ground).

For destruction on the ground I wanted to stop the silly looking instances of a cloud of pieces arcing upward and flying for a considerable distance before hitting the ground. For a planes crashing with a fairly high vertical velocity, the effect is of the ground having something of a rubber-like property, spring-launching the scores of parts on a long, arcing path. And so as part of the added random velocity component scheme just mentioned, for ground crashes the Z (vertical) component of velocity is ultimately limited to the range of 0.5-1.5m/s (formerly a single value of 5m/s was added) as set in setSpeed() by first making it zero in the method in Aircraft.class calling setSpeed. This has the pieces remaining more sensibly close to the ground


Code: [Select]
package com.maddox.il2.objects;

import com.maddox.JGP.*;
import com.maddox.il2.ai.RangeRandom;
import com.maddox.il2.ai.World;
import com.maddox.il2.engine.*;
import com.maddox.il2.game.Main;  //NEW, for consideration of weather type
import com.maddox.il2.objects.air.Aircraft;
import com.maddox.il2.objects.effects.Explosions;
import com.maddox.il2.objects.ships.BigshipGeneric;
import com.maddox.il2.objects.ships.ShipGeneric;
import com.maddox.rts.*;
import com.maddox.sas1946.il2.util.TrueRandom;  //NEW

// Referenced classes of package com.maddox.il2.objects:
//            Wreck

public class Wreckage extends ActorMesh
{
    static class ClipFilter
        implements ActorFilter
    {

        public boolean isUse(Actor actor, double d)
        {
            return (actor instanceof BigshipGeneric) || (actor instanceof ShipGeneric);
        }

        ClipFilter()
        {
        }
    }

    class Interpolater extends Interpolate
    {

        private double deceleron(double d, float f)  //d=velocity component (x, y or z), f=acceleration factor
        {
            if(d > 0.0D)
                d -= (double)f * d * d;
            else
                d += (double)f * d * d;
            return d;
        }

        public boolean tick()
        {
            float f = Time.tickLenFs();
            float f2 = (float)v.length();
//            float f1 = 10F / (f2 + 0.1F);  //original; imparts more curvature at slower speed??
            float f1 = 5F / (f2 + 0.1F);  //half the curvature rate?
            if(f1 > 1.0F)
                f1 = 1.0F;  //10m/s and slower (now 5m/s and slower)
            f1 *= Wreckage.dv[lr] * f;  //[dv] range was -80 to 80, now -20 to 20
            pos.getAbs(Wreckage.p, Wreckage.o);
            Wreckage.o.increment(W.z * f, W.y * f, -W.x * f);
            Wreckage.oh.set(f1, 0.0F, 0.0F);
            Wreckage.oh.transform(v);
            Wreckage.o.setYaw(Wreckage.o.getYaw() - f1);
            float f4 = A * f;  //A originally = 0.02 / M; now A = M / 1,300,000^0.635
            v.x = deceleron(v.x, f4);
            v.y = deceleron(v.y, f4);
            v.z = deceleron(v.z, f4);
            v.z -= World.g() * f;  //original
            Wreckage.p.scaleAdd(f, v, Wreckage.p);
            double d = World.land().HQ(Wreckage.p.x, Wreckage.p.y);
            if(Wreckage.p.z <= d)
            {
                World.land().N(Wreckage.p.x, Wreckage.p.y, Wreckage.Nf);
                Wreckage.N.set(Wreckage.Nf);
                float f3;
                if((f3 = (float)v.dot(Wreckage.N)) < 0.0F)
                {
                    if(f3 < -40F)
                        f3 = -40F;
                    Wreckage.N.scale(2.0F * f3);
                    v.sub(Wreckage.N);
                    v.scale(0.5D);
                    if(World.land().isWater(Wreckage.p.x, Wreckage.p.y))
                    {
                        MsgDestroy.Post(Time.current(), actor);
                        Wreckage.WreckageONLYDrop_Water(Wreckage.p);
                        return false;
                    }

                    if(!World.land().isWater(Wreckage.p.x, Wreckage.p.y))  //NEW, for land effects (post v1.6 effects mod)
                    {
EffClouds effclouds = Main.cur().clouds;
if(effclouds.type() < 5 || World.cur().camouflage == World.CAMOUFLAGE_WINTER)  //i.e., if NOT precipitation OR if winter, create dust/snow impact effect
Wreckage.WreckageONLYDrop_Land(Wreckage.p);
                    }

                    if(!bBoundToBeDestroyed && Math.abs(v.z) < 1.0D)
                    {
//                        MsgDestroy.Post(Time.current() + 0x13880L, actor);  //original; 0x13880L = 80,000, or 80 seconds; 10 sec = 0x2710L
                        MsgDestroy.Post(Time.current() + 10000L, actor);  //wreckage hangs about for 10 seconds after stopping
                        bBoundToBeDestroyed = true;
                        return false;
                    }
                }
                Wreckage.p.z = d;
            }
            pos.setAbs(Wreckage.p, Wreckage.o);
            return true;
        }

        Interpolater()
        {
        }
    }


    public void setSpeed(Vector3d vector3d)  //called by Aircraft.class and numerous plane classes
    {
        v.set(vector3d);
//        v.z += 5D;  //original
v.z += TrueRandom.nextDouble(0.5D, 1.5D);
        if(v.z > 50D)
            v.z = 50D;
    }

    private void construct(float f)  //f = chunk mass
    {
        collide(true);
        drawing(true);
        getDimensions(sz);
        M = f;  //seems to be always zero...
        float f1 = sz.x * sz.y + sz.x * sz.z + sz.z * sz.y;  //f1 = 1/2 surface area
        if(f1 < 0.01F)
            f1 = 0.01F;  //min sfc half-area (~6cm cube)

        if(M < 0.001F)  //important, because M is generally or always zero!
        {
            float f2 = sz.x * sz.y * sz.z;  //f2 = volume
//            if(f2 < 0.01F)  //original
//                f2 = 0.01F;  //min volume (0.215m cube)
            if(f2 < 0.001F)  //lowered so as to not introduce a density turnover--keeps volume decreasing for the smallest pieces
                f2 = 0.001F;  //min volume (0.1m cube)
            float f4 = f1 * 2.0F * 0.02F;  //1/2 surface area
//            M = 500F * Math.min(f2, f4);  //min(vol, area), or min(8, 12*0.04) = 0.48; f4 generally smaller; typical range of M is roughly 2kg to 1400kg
M = 500F * f4;
        }

//NEW; to increase by a random amount the mass of ALL pieces up to 1000kg (slight decrease for heavier); this introduces more even dispersion
float maxM = (float) Math.pow(M, 0.6) * 16F;  //upper mass limit for randomizing, based on mass
M = TrueRandom.nextFloat(M, maxM);

        float f3 = sz.x;
        float f5 = sz.y;
        float f6 = sz.z;
        int i = 0;
        int j = 1;
        int k = 2;
        if(f3 > f5)
        {
            float f7 = f3;
            f3 = f5;
            f5 = f7;
            int l = i;
            i = j;
            j = l;
        }
        if(f5 > f6)
        {
            float f8 = f5;
            f5 = f6;
            f6 = f8;
            int i1 = j;
            j = k;
            k = i1;
            if(f3 > f5)
            {
                float f9 = f3;
                f3 = f5;
                f5 = f9;
                int j1 = i;
                i = j;
                j = j1;
            }
        }
        if(f5 * 8F < f6)
        {
            switch(i)
            {
            case 0: // '\0'
                W.set(1.0F, 0.0F, 0.0F);
                break;

            case 1: // '\001'
                W.set(0.0F, 1.0F, 0.0F);
                break;

            case 2: // '\002'
                W.set(0.0F, 0.0F, 1.0F);
                break;
            }
            wn = k;
        } else
        if(f3 * 3F < f5)
        {
            switch(k)
            {
            case 0: // '\0'
                W.set(1.0F, 0.0F, 0.0F);
                break;

            case 1: // '\001'
                W.set(0.0F, 1.0F, 0.0F);
                break;

            case 2: // '\002'
                W.set(0.0F, 0.0F, 1.0F);
                break;
            }
            wn = j;
        } else
        {
            W.set(sz);
            if(W.x < 1E-010F)
                W.x += 1E-010F;
            W.normalize();
            wn = k;
        }
        wn = 60F + 80F / (wn + 0.2F);
        W.scale(wn);
        lr = World.Rnd().nextInt(0, 8);
//        A = 0.02F / M;  //original
A = (float) Math.pow(M / 1300000F, 0.635F);  //4th experiment, in which bigger/higher mass = lower density = more rapidly slowed
        interpPut(new Interpolater(), null, Time.current(), null);
        MsgDestroy.Post(Time.current() + 0x1d4c0L, this);  //1d4c0 = 120,000, or 120 sec
    }

    float DEG2RAD(float f)
    {
        return f * 0.01745329F;
    }

    float RAD2DEG(float f)
    {
        return f * 57.29578F;
    }

    public Object getSwitchListener(Message message)
    {
        return this;
    }

    public Wreckage(ActorHMesh actorhmesh, int i)
    {
        super(actorhmesh, i);
        v = new Vector3d();
        W = new Vector3f();
        bBoundToBeDestroyed = false;
        construct(actorhmesh.getChunkMass());
        actorhmesh.hierMesh().setCurChunk(i);
        if(actorhmesh instanceof Aircraft)
        {
            setOwner(actorhmesh, false, false, false);
            netOwner = ((Aircraft)actorhmesh).netUser();
        }
    }

    private static void WreckageONLYDrop_Water(Point3d point3d)
    {
        if(!Config.isUSE_RENDER())
            return;
        long l = Time.current();
        if(l == timeLastWreckageDrop_Water)
        {
            double d = posLastWreckageDrop_Water.x - point3d.x;
            double d1 = posLastWreckageDrop_Water.y - point3d.y;
            if(d * d + d1 * d1 < 100D)
                return;
        }
        pClipZ1.set(p);
        pClipZ2.set(p);
        pClipZ1.z -= 2D;
        pClipZ2.z += 42D;
        Actor actor = Engine.collideEnv().getLine(pClipZ2, pClipZ1, false, clipFilter, pClipRes);
        if(Actor.isValid(actor))
        {
            return;
        } else
        {
            timeLastWreckageDrop_Water = l;
            posLastWreckageDrop_Water.set(point3d);
            Explosions.WreckageDrop_Water(point3d);
            return;
        }
    }


//NEW method, for post v1.6 effects mod; points to new method in Explosions.class
    private static void WreckageONLYDrop_Land(Point3d point3d)
    {
        if(!Config.isUSE_RENDER())
            return;
        long l = Time.current();
        if(l == timeLastWreckageDrop_Land)
        {
            double d = posLastWreckageDrop_Land.x - point3d.x;
            double d1 = posLastWreckageDrop_Land.y - point3d.y;
            if(d * d + d1 * d1 < 100D)  //original; if d and d1 = 7.07, result is 100; 10 would be 200, 15 would be 450, 3.5 would be 25
                return;
        }
        pClipZ1.set(p);
        pClipZ2.set(p);
        pClipZ1.z -= 2D;
        pClipZ2.z += 42D;
        Actor actor = Engine.collideEnv().getLine(pClipZ2, pClipZ1, false, clipFilter, pClipRes);
        if(Actor.isValid(actor))
        {
            return;
        } else
        {
            timeLastWreckageDrop_Land = l;
            posLastWreckageDrop_Land.set(point3d);
            Explosions.WreckageDrop_Land(point3d);
            return;
        }
    }

    public static Wreck makeWreck(ActorHMesh actorhmesh, int i)
    {
        HierMesh hiermesh = actorhmesh.hierMesh();
        hiermesh.setCurChunk(i);
        int ai[] = hiermesh.getSubTrees(hiermesh.chunkName());
        if(ai == null || ai.length < 1)
            return null;
        double d = 0.0D;
        Point3d point3d = new Point3d(0.0D, 0.0D, 0.0D);
        Point3f point3f = new Point3f();
        Point3f point3f1 = new Point3f();
        Point3d point3d1 = new Point3d();
        for(int j = 0; j < ai.length; j++)
        {
            hiermesh.setCurChunk(ai[j]);
            if(hiermesh.isChunkVisible())
            {
                hiermesh.getChunkCurVisBoundBox(point3f, point3f1);
                point3d1.set(point3f);
                point3d1.add(point3f1.x, point3f1.y, point3f1.z);
                point3d1.scale(0.5D);
                hiermesh.getChunkLocObj(LO);
                LO.transform(point3d1);
                point3f1.sub(point3f);
                double d1 = point3f1.x * point3f1.y * point3f1.z;
                double d2 = 500D;
                double d3 = d1 * d2;
                d += d3;
                point3d1.scale(d3);
                point3d.add(point3d1);
            }
        }

        if(d > 0.0001D)
        {
            point3d.scale(1.0D / d);
        } else
        {
            hiermesh.setCurChunk(i);
            hiermesh.getChunkLocObj(LO);
            point3d.set(LO.getPoint());
        }
        hiermesh.setCurChunk(i);
        hiermesh.getChunkLocObj(LO);
        LO.getPoint().set(point3d);
        HierMesh hiermesh1 = new HierMesh(hiermesh, i, LO);
        Loc loc = new Loc();
        actorhmesh.pos.getAbs(loc);
        LO.add(loc);
        return new Wreck(hiermesh1, LO);
    }

    public static String SMOKE = "EFFECTS/Smokes/SmokeBlack_Wreckage.eff";  //invoked in Aircraft.class, methods cut(String s) and cut_Subtrees(String s); 50% chance of occurring; lasts 3sec
    public static String SMOKE_EXPLODE = "EFFECTS/Smokes/SmokeBlack_Wreckage_Explode.eff";  ;//invoked in Aircraft.class, method explode(); lasts 1 sec longer than Wreckage_Burn.eff
    public static String FIRE = "EFFECTS/Explodes/Wreckage_Burn.eff";  ;//invoked in Aircraft.class, method explode()
    public static String FIRE_EXPLODE_CRASH = "EFFECTS/Explodes/Wreckage_Burn_Crash.eff";  ;//NEW: invoked in Aircraft.class, method explode()
    public static String SMOKE_EXPLODE_CRASH = "EFFECTS/Explodes/Wreckage_Smoke_Crash.eff";  ;//NEW; invoked in Aircraft.class, method explode()
    private float M;
    private float A;
    private int lr;
    private Vector3d v;
    private Vector3f W;
    private static Vector3d N = new Vector3d();
    private static Vector3f Nf = new Vector3f();
    private static Vector3f sz = new Vector3f();
    private static Point3d p = new Point3d();
    private static Orient o = new Orient();
    private static Orient oh = new Orient();
    private static float wn;
//    private static float dv[] = {  //original; imparts too much curvature in motion?
//        -80.0F, -60.0F, -40.0F, -20.0F, 20.0F, 40.0F, 60.0F, 80.0F, 0.0F
//    };
    private static float dv[] = {
        -20.0F, -15.0F, -10.0F, -5.0F, 5.0F, 10.0F, 15.0F, 20.0F, 0.0F
    };
    private boolean bBoundToBeDestroyed;
    public NetObj netOwner;
    private static long timeLastWreckageDrop_Water = 0L;
    private static Point3d posLastWreckageDrop_Water = new Point3d();
    private static Point3d pClipZ1 = new Point3d();
    private static Point3d pClipZ2 = new Point3d();
    private static Point3d pClipRes = new Point3d();
    static ClipFilter clipFilter = new ClipFilter();
    private static Loc LO = new Loc();
    private static long timeLastWreckageDrop_Land = 0L;  //NEW
    private static Point3d posLastWreckageDrop_Land = new Point3d();  //NEW

}
Logged
Great minds discuss ideas. Average minds discuss events. Small minds discuss people. - Hyman Rickover (but probably predating his use.)

WxTech

  • Modder
  • member
  • Offline Offline
  • Posts: 6040
Re: More work on plane wreckage
« Reply #3 on: May 27, 2024, 02:37:40 AM »

After further work in harmonizing the treatment of the removed (cut) mesh chunks and the randomly generated bits so as to obtain a uniform behaviour. An example of the inconsistent behaviour now corrected is seen in my reply #1 above. Note the group of wreckage pieces both lagging farther behind and falling faster than the main group. Now all pieces, which are generated by two discrete methods, and with a third method possibly contributing, have the same teeatment applied to them.

In this 3-panel view taken from the Kamikaze02 track, we see the end of the last Kate, shortly before the track finishes. The main takeaways here are:

- The larger pieces are treated as having less density and hence suffer higher drag. This causes them to lag farther behind and hence fall at a shorter distance from the dissociative event. It is these larger pieces which are more likely to carry with them the fire/smoke of damaged fuel tanks and engines, and which are the larger/denser and long-lived effects. Note here in the lowest panel how the smoke trails arc down more steeply toward the surface due to their more rapid deceleration, falling near or at the rear of the pattern of fall.
- The pieces have a range of splash and water ring effect size based on piece size (dust clouds for ground hits are similarly scaled). Note the larger splashes where the larger, more rapidly slowed parts fall toward the rear of the pattern.
- The code has had the silly and continued outward curving of the fall pattern HUGELY reduced, thus keeping the pattern of fall more tightly constrained to the mean ground track.

Run the Kamikaze track with your current effects and note the significant differences.


Also seen here are other new effects since release v1.6 of my effects mod:

- A small 'oil patch' on the water when a plane leaves a fire and smoke column (only a random, smallish fraction of crashes do this.) Here a plane had earlier crashed very close to the carrier's hull, its crash location now at the stern.
- An exploding plane (and other objects) will generate one to several burning/smoking ballistic projectiles. Furthermore, if they strike the surface before 'burning out' (I apply a random duration for their existence) they will create a small splash or dust cloud. Here we see one such ballistic projectile directed almost straight down, having just hit the water in the lower two panels (which are two views at the same instant in time.)


Logged
Great minds discuss ideas. Average minds discuss events. Small minds discuss people. - Hyman Rickover (but probably predating his use.)

vonOben

  • Modder
  • member
  • Offline Offline
  • Posts: 932
  • Wer den Tod fürchtet, hat das Leben verloren.
    • vonOben's Flight Sim Mods
Re: More work on plane wreckage
« Reply #4 on: May 27, 2024, 10:11:41 PM »

Looks good, nice tweaking!  :)
Logged
vonOben's Flight Sim Mods  http://vonoben.free.fr/ Twenty Years online January 3, 2022!
Pages: [1]   Go Up
 

Page created in 0.035 seconds with 24 queries.