Нарисовать молнию

Версия Minecraft
1.7.10
1,007
36
206
Хочу сделать нечто похожее на шоковый жезл из таумкрафта, но молнию хочу нарисовать свою. Как её нарисовать?
 
Решение
Ух сцук вот щас ты меня очень сильно озадачил. В тауме наверно текстуркой фигачат хотя не уверен, но посмотрев на рисунок молнии
%25D0%25BC%25D0%25BE%25D0%25BB%25D0%25BD%25D0%25B8%25D1%258F.jpg
Я заметил что молния в принципе в любом случае имеет какое то направления и все что отличает ее от обычной линии это случайные искривления в рандомных местах и рандомной силы + иногда появляются какие то случайные ответвления.
Продолжая в направлении упрощения задачи я прикинул что генерировать молнию можно довольно просто. Например у нас есть точка A и B между которыми нам нужна молния. Окей во-первых на этапе генерации нам...
1,111
47
420
Ух сцук вот щас ты меня очень сильно озадачил. В тауме наверно текстуркой фигачат хотя не уверен, но посмотрев на рисунок молнии
%25D0%25BC%25D0%25BE%25D0%25BB%25D0%25BD%25D0%25B8%25D1%258F.jpg
Я заметил что молния в принципе в любом случае имеет какое то направления и все что отличает ее от обычной линии это случайные искривления в рандомных местах и рандомной силы + иногда появляются какие то случайные ответвления.
Продолжая в направлении упрощения задачи я прикинул что генерировать молнию можно довольно просто. Например у нас есть точка A и B между которыми нам нужна молния. Окей во-первых на этапе генерации нам совершенно плевать на координаты этих точек, достаточно только знать расстояние между ними. Пусть оно будет равно S. Тогда примерно прикидываем прямую линию в голове и делим ее на S / const частей. Получаем примерно вот такие вершины (0; 0; 0); (const;0;0); (2const;0;0)..(S;0;0). Дальше задача совсем простая нам нужно не трогая первую и последнюю вершины передвинуть все остальные на случайные небольшие расстояния. Затем используя тригонометрию найти угол между точками A и B и повернуть этот массив после чего отрисовать.
Вот код генерации молнии:
Java:
public static class Vertex {
        public final float x;
        public final float y;
        public final float z;

        public Vertex(float x, float y, float z) {
            this.x = x;
            this.y = y;
            this.z = z;
        }
    }

    /**
     * @param length длинна молнии
     * @param partLength длинна одного кусочка
     * @param random ваш личный рандом. Мало ли нужно
     * @param power сила искревления
     * @return вершины молнии
     */
    public static Vertex[] generateLightning(float length, float partLength, Random random, int power) {
        final Vertex[] vertices = new Vertex[(int) ((length + partLength / 2) / partLength)];
        for (int i = 0; i < vertices.length; i++) {
            vertices[i] = i == vertices.length - 1 ? new Vertex(length, 0, 0) : new Vertex(partLength * i, 0, 0);
        }
        for (int i = 1; i < vertices.length - 1; i++) {
            Vertex oldOne = vertices[i];
            Vertex newOne = new Vertex(oldOne.x - (random.nextFloat() - 0.5f) * power, oldOne.y - (random.nextFloat() - 0.5f) * power, oldOne.z - (random.nextFloat() - 0.5f) * power);
            vertices[i] = newOne;
        }
        return vertices;
    }
С поворотом думаю сам разбрешься)0
P.S. хз может есть уже какие нибудь графики а я тут велосипедю
 
1,057
50
234
Java:
public class FXLightningBolt extends EntityFX {

   private int type = 0;
   private float width = 0.03F;
   private FXLightningBoltCommon main;


   public FXLightningBolt(World world, WRVector3 jammervec, WRVector3 targetvec, long seed) {
      super(world, 0.0D, 0.0D, 0.0D, 0.0D, 0.0D, 0.0D);
      this.main = new FXLightningBoltCommon(world, jammervec, targetvec, seed);
      this.setupFromMain();
   }

   public FXLightningBolt(World world, Entity detonator, Entity target, long seed) {
      super(world, 0.0D, 0.0D, 0.0D, 0.0D, 0.0D, 0.0D);
      this.main = new FXLightningBoltCommon(world, detonator, target, seed);
      this.setupFromMain();
   }

   public FXLightningBolt(World world, Entity detonator, Entity target, long seed, int speed) {
      super(world, 0.0D, 0.0D, 0.0D, 0.0D, 0.0D, 0.0D);
      this.main = new FXLightningBoltCommon(world, detonator, target, seed, speed);
      this.setupFromMain();
   }

   public FXLightningBolt(World world, TileEntity detonator, Entity target, long seed) {
      super(world, 0.0D, 0.0D, 0.0D, 0.0D, 0.0D, 0.0D);
      this.main = new FXLightningBoltCommon(world, detonator, target, seed);
      this.setupFromMain();
   }

   public FXLightningBolt(World world, double x1, double y1, double z1, double x, double y, double z, long seed, int duration, float multi) {
      super(world, 0.0D, 0.0D, 0.0D, 0.0D, 0.0D, 0.0D);
      this.main = new FXLightningBoltCommon(world, x1, y1, z1, x, y, z, seed, duration, multi);
      this.setupFromMain();
   }

   public FXLightningBolt(World world, double x1, double y1, double z1, double x, double y, double z, long seed, int duration, float multi, int speed) {
      super(world, 0.0D, 0.0D, 0.0D, 0.0D, 0.0D, 0.0D);
      this.main = new FXLightningBoltCommon(world, x1, y1, z1, x, y, z, seed, duration, multi, speed);
      this.setupFromMain();
   }

   public FXLightningBolt(World world, double x1, double y1, double z1, double x, double y, double z, long seed, int duration) {
      super(world, 0.0D, 0.0D, 0.0D, 0.0D, 0.0D, 0.0D);
      this.main = new FXLightningBoltCommon(world, x1, y1, z1, x, y, z, seed, duration, 1.0F);
      this.setupFromMain();
   }

   public FXLightningBolt(World world, TileEntity detonator, double x, double y, double z, long seed) {
      super(world, 0.0D, 0.0D, 0.0D, 0.0D, 0.0D, 0.0D);
      this.main = new FXLightningBoltCommon(world, detonator, x, y, z, seed);
      this.setupFromMain();
   }

   private void setupFromMain() {
      this.particleAge = this.main.particleMaxAge;
      this.setPosition((double)this.main.start.x, (double)this.main.start.y, (double)this.main.start.z);
      this.setVelocity(0.0D, 0.0D, 0.0D);
   }

   public void defaultFractal() {
      this.main.defaultFractal();
   }

   public void fractal(int splits, float amount, float splitchance, float splitlength, float splitangle) {
      this.main.fractal(splits, amount, splitchance, splitlength, splitangle);
   }

   public void finalizeBolt() {
      this.main.finalizeBolt();
      FMLClientHandler.instance().getClient().effectRenderer.addEffect(this);
   }

   public void setType(int type) {
      this.type = type;
      this.main.type = type;
   }

   public void setMultiplier(float m) {
      this.main.multiplier = m;
   }

   public void setWidth(float m) {
      this.width = m;
   }

   public void onUpdate() {
      this.main.onUpdate();
      if(this.main.particleAge >= this.main.particleMaxAge) {
         this.setDead();
      }

   }

   private static WRVector3 getRelativeViewVector(WRVector3 pos) {
      EntityClientPlayerMP renderentity = FMLClientHandler.instance().getClient().thePlayer;
      return new WRVector3((double)((float)renderentity.posX - pos.x), (double)((float)renderentity.posY - pos.y), (double)((float)renderentity.posZ - pos.z));
   }

   private void renderBolt(Tessellator tessellator, float partialframe, float cosyaw, float cospitch, float sinyaw, float cossinpitch, int pass, float mainalpha) {
      WRVector3 playervec = new WRVector3((double)(sinyaw * -cospitch), (double)(-cossinpitch / cosyaw), (double)(cosyaw * cospitch));
      float boltage = this.main.particleAge >= 0?(float)this.main.particleAge / (float)this.main.particleMaxAge:0.0F;
      if(pass == 0) {
         mainalpha = (1.0F - boltage) * 0.4F;
      } else {
         mainalpha = 1.0F - boltage * 0.5F;
      }

      int renderlength = (int)(((float)this.main.particleAge + partialframe + (float)((int)(this.main.length * 3.0F))) / (float)((int)(this.main.length * 3.0F)) * (float)this.main.numsegments0);
      Iterator iterator = this.main.segments.iterator();

      while(iterator.hasNext()) {
         FXLightningBoltCommon.Segment rendersegment = (FXLightningBoltCommon.Segment)iterator.next();
         if(rendersegment.segmentno <= renderlength) {
            float width = this.width * (getRelativeViewVector(rendersegment.startpoint.point).length() / 5.0F + 1.0F) * (1.0F + rendersegment.light) * 0.5F;
            WRVector3 diff1 = WRVector3.crossProduct(playervec, rendersegment.prevdiff).scale(width / rendersegment.sinprev);
            WRVector3 diff2 = WRVector3.crossProduct(playervec, rendersegment.nextdiff).scale(width / rendersegment.sinnext);
            WRVector3 startvec = rendersegment.startpoint.point;
            WRVector3 endvec = rendersegment.endpoint.point;
            float rx1 = (float)((double)startvec.x - interpPosX);
            float ry1 = (float)((double)startvec.y - interpPosY);
            float rz1 = (float)((double)startvec.z - interpPosZ);
            float rx2 = (float)((double)endvec.x - interpPosX);
            float ry2 = (float)((double)endvec.y - interpPosY);
            float rz2 = (float)((double)endvec.z - interpPosZ);
            tessellator.setColorRGBA_F(this.particleRed, this.particleGreen, this.particleBlue, mainalpha * rendersegment.light);
            tessellator.addVertexWithUV((double)(rx2 - diff2.x), (double)(ry2 - diff2.y), (double)(rz2 - diff2.z), 0.5D, 0.0D);
            tessellator.addVertexWithUV((double)(rx1 - diff1.x), (double)(ry1 - diff1.y), (double)(rz1 - diff1.z), 0.5D, 0.0D);
            tessellator.addVertexWithUV((double)(rx1 + diff1.x), (double)(ry1 + diff1.y), (double)(rz1 + diff1.z), 0.5D, 1.0D);
            tessellator.addVertexWithUV((double)(rx2 + diff2.x), (double)(ry2 + diff2.y), (double)(rz2 + diff2.z), 0.5D, 1.0D);
            WRVector3 roundend;
            float rx3;
            float ry3;
            float rz3;
            if(rendersegment.next == null) {
               roundend = rendersegment.endpoint.point.copy().add(rendersegment.diff.copy().normalize().scale(width));
               rx3 = (float)((double)roundend.x - interpPosX);
               ry3 = (float)((double)roundend.y - interpPosY);
               rz3 = (float)((double)roundend.z - interpPosZ);
               tessellator.addVertexWithUV((double)(rx3 - diff2.x), (double)(ry3 - diff2.y), (double)(rz3 - diff2.z), 0.0D, 0.0D);
               tessellator.addVertexWithUV((double)(rx2 - diff2.x), (double)(ry2 - diff2.y), (double)(rz2 - diff2.z), 0.5D, 0.0D);
               tessellator.addVertexWithUV((double)(rx2 + diff2.x), (double)(ry2 + diff2.y), (double)(rz2 + diff2.z), 0.5D, 1.0D);
               tessellator.addVertexWithUV((double)(rx3 + diff2.x), (double)(ry3 + diff2.y), (double)(rz3 + diff2.z), 0.0D, 1.0D);
            }

            if(rendersegment.prev == null) {
               roundend = rendersegment.startpoint.point.copy().sub(rendersegment.diff.copy().normalize().scale(width));
               rx3 = (float)((double)roundend.x - interpPosX);
               ry3 = (float)((double)roundend.y - interpPosY);
               rz3 = (float)((double)roundend.z - interpPosZ);
               tessellator.addVertexWithUV((double)(rx1 - diff1.x), (double)(ry1 - diff1.y), (double)(rz1 - diff1.z), 0.5D, 0.0D);
               tessellator.addVertexWithUV((double)(rx3 - diff1.x), (double)(ry3 - diff1.y), (double)(rz3 - diff1.z), 0.0D, 0.0D);
               tessellator.addVertexWithUV((double)(rx3 + diff1.x), (double)(ry3 + diff1.y), (double)(rz3 + diff1.z), 0.0D, 1.0D);
               tessellator.addVertexWithUV((double)(rx1 + diff1.x), (double)(ry1 + diff1.y), (double)(rz1 + diff1.z), 0.5D, 1.0D);
            }
         }
      }

   }

   public void renderParticle(Tessellator tessellator, float partialframe, float cosyaw, float cospitch, float sinyaw, float sinsinpitch, float cossinpitch) {
      EntityClientPlayerMP renderentity = FMLClientHandler.instance().getClient().thePlayer;
      byte visibleDistance = 100;
      if(!FMLClientHandler.instance().getClient().gameSettings.fancyGraphics) {
         visibleDistance = 50;
      }

      if(renderentity.getDistance(this.posX, this.posY, this.posZ) <= (double)visibleDistance) {
         tessellator.draw();
         GL11.glPushMatrix();
         GL11.glDepthMask(false);
         GL11.glEnable(3042);
         this.particleRed = this.particleGreen = this.particleBlue = 1.0F;
         float ma = 1.0F;
         switch(this.type) {
         case 0:
            this.particleRed = 0.6F;
            this.particleGreen = 0.3F;
            this.particleBlue = 0.6F;
            GL11.glBlendFunc(770, 1);
            break;
         case 1:
            this.particleRed = 0.6F;
            this.particleGreen = 0.6F;
            this.particleBlue = 0.1F;
            GL11.glBlendFunc(770, 1);
            break;
         case 2:
            this.particleRed = 0.1F;
            this.particleGreen = 0.1F;
            this.particleBlue = 0.6F;
            GL11.glBlendFunc(770, 1);
            break;
         case 3:
            this.particleRed = 0.1F;
            this.particleGreen = 1.0F;
            this.particleBlue = 0.1F;
            GL11.glBlendFunc(770, 1);
            break;
         case 4:
            this.particleRed = 0.6F;
            this.particleGreen = 0.1F;
            this.particleBlue = 0.1F;
            GL11.glBlendFunc(770, 1);
            break;
         case 5:
            this.particleRed = 0.6F;
            this.particleGreen = 0.2F;
            this.particleBlue = 0.6F;
            GL11.glBlendFunc(770, 771);
            break;
         case 6:
            this.particleRed = 0.75F;
            this.particleGreen = 1.0F;
            this.particleBlue = 1.0F;
            ma = 0.2F;
            GL11.glBlendFunc(770, 771);
         }

         UtilsFX.bindTexture("textures/misc/p_large.png");
         tessellator.startDrawingQuads();
         tessellator.setBrightness(15728880);
         this.renderBolt(tessellator, partialframe, cosyaw, cospitch, sinyaw, cossinpitch, 0, ma);
         tessellator.draw();
         switch(this.type) {
         case 0:
            this.particleRed = 1.0F;
            this.particleGreen = 0.6F;
            this.particleBlue = 1.0F;
            break;
         case 1:
            this.particleRed = 1.0F;
            this.particleGreen = 1.0F;
            this.particleBlue = 0.1F;
            break;
         case 2:
            this.particleRed = 0.1F;
            this.particleGreen = 0.1F;
            this.particleBlue = 1.0F;
            break;
         case 3:
            this.particleRed = 0.1F;
            this.particleGreen = 0.6F;
            this.particleBlue = 0.1F;
            break;
         case 4:
            this.particleRed = 1.0F;
            this.particleGreen = 0.1F;
            this.particleBlue = 0.1F;
            break;
         case 5:
            this.particleRed = 0.0F;
            this.particleGreen = 0.0F;
            this.particleBlue = 0.0F;
            GL11.glBlendFunc(770, 771);
            break;
         case 6:
            this.particleRed = 0.75F;
            this.particleGreen = 1.0F;
            this.particleBlue = 1.0F;
            ma = 0.2F;
            GL11.glBlendFunc(770, 771);
         }

         UtilsFX.bindTexture("textures/misc/p_small.png");
         tessellator.startDrawingQuads();
         tessellator.setBrightness(15728880);
         this.renderBolt(tessellator, partialframe, cosyaw, cospitch, sinyaw, cossinpitch, 1, ma);
         tessellator.draw();
         GL11.glDisable(3042);
         GL11.glDepthMask(true);
         GL11.glPopMatrix();
         Minecraft.getMinecraft().renderEngine.bindTexture(UtilsFX.getParticleTexture());
         tessellator.startDrawingQuads();
      }
   }

   public int getRenderPass() {
      return 2;
   }
}

Java:
public class FXLightningBoltCommon {

   ArrayList segments;
   WRVector3 start;
   WRVector3 end;
   HashMap splitparents;
   public float multiplier;
   public float length;
   public int numsegments0;
   public int increment;
   public int type;
   public boolean nonLethal;
   private int numsplits;
   private boolean finalized;
   private Random rand;
   public long seed;
   public int particleAge;
   public int particleMaxAge;
   private World world;
   public static final float speed = 3.0F;
   public static final int fadetime = 20;


   public FXLightningBoltCommon(World world, WRVector3 jammervec, WRVector3 targetvec, long seed) {
      this.type = 0;
      this.nonLethal = false;
      this.segments = new ArrayList();
      this.splitparents = new HashMap();
      this.world = world;
      this.start = jammervec;
      this.end = targetvec;
      this.seed = seed;
      this.rand = new Random(seed);
      this.numsegments0 = 1;
      this.increment = 1;
      this.length = this.end.copy().sub(this.start).length();
      this.particleMaxAge = 3 + this.rand.nextInt(3) - 1;
      this.multiplier = 1.0F;
      this.particleAge = -((int)(this.length * 3.0F));
      this.segments.add(new FXLightningBoltCommon.Segment(this.start, this.end));
   }

   public FXLightningBoltCommon(World world, Entity detonator, Entity target, long seed) {
      this(world, new WRVector3(detonator), new WRVector3(target), seed);
   }

   public FXLightningBoltCommon(World world, Entity detonator, Entity target, long seed, int speed) {
      this(world, new WRVector3(detonator), new WRVector3(target.posX, target.posY + (double)target.getEyeHeight() - 0.699999988079071D, target.posZ), seed);
      this.increment = speed;
      this.multiplier = 0.4F;
   }

   public FXLightningBoltCommon(World world, TileEntity detonator, Entity target, long seed) {
      this(world, new WRVector3(detonator), new WRVector3(target), seed);
   }

   public FXLightningBoltCommon(World world, TileEntity detonator, double x, double y, double z, long seed) {
      this(world, new WRVector3(detonator), new WRVector3(x, y, z), seed);
   }

   public FXLightningBoltCommon(World world, double x1, double y1, double z1, double x, double y, double z, long seed, int duration, float multi) {
      this(world, new WRVector3(x1, y1, z1), new WRVector3(x, y, z), seed);
      this.particleMaxAge = duration + this.rand.nextInt(duration) - duration / 2;
      this.multiplier = multi;
   }

   public FXLightningBoltCommon(World world, double x1, double y1, double z1, double x, double y, double z, long seed, int duration, float multi, int speed) {
      this(world, new WRVector3(x1, y1, z1), new WRVector3(x, y, z), seed);
      this.particleMaxAge = duration + this.rand.nextInt(duration) - duration / 2;
      this.multiplier = multi;
      this.increment = speed;
   }

   public void setMultiplier(float m) {
      this.multiplier = m;
   }

   public void fractal(int splits, float amount, float splitchance, float splitlength, float splitangle) {
      if(!this.finalized) {
         ArrayList oldsegments = this.segments;
         this.segments = new ArrayList();
         FXLightningBoltCommon.Segment prev = null;
         Iterator iterator = oldsegments.iterator();

         while(iterator.hasNext()) {
            FXLightningBoltCommon.Segment segment = (FXLightningBoltCommon.Segment)iterator.next();
            prev = segment.prev;
            WRVector3 subsegment = segment.diff.copy().scale(1.0F / (float)splits);
            FXLightningBoltCommon.BoltPoint[] newpoints = new FXLightningBoltCommon.BoltPoint[splits + 1];
            WRVector3 startpoint = segment.startpoint.point;
            newpoints[0] = segment.startpoint;
            newpoints[splits] = segment.endpoint;

            int i;
            WRVector3 splitrot;
            for(i = 1; i < splits; ++i) {
               WRVector3 next = WRVector3.getPerpendicular(segment.diff).rotate(this.rand.nextFloat() * 360.0F, segment.diff);
               next.scale((this.rand.nextFloat() - 0.5F) * amount);
               splitrot = startpoint.copy().add(subsegment.copy().scale((float)i));
               newpoints[i] = new FXLightningBoltCommon.BoltPoint(splitrot, next);
            }

            for(i = 0; i < splits; ++i) {
               FXLightningBoltCommon.Segment var18 = new FXLightningBoltCommon.Segment(newpoints[i], newpoints[i + 1], segment.light, segment.segmentno * splits + i, segment.splitno);
               var18.prev = prev;
               if(prev != null) {
                  prev.next = var18;
               }

               if(i != 0 && this.rand.nextFloat() < splitchance) {
                  splitrot = WRVector3.xCrossProduct(var18.diff).rotate(this.rand.nextFloat() * 360.0F, var18.diff);
                  WRVector3 diff = var18.diff.copy().rotate((this.rand.nextFloat() * 0.66F + 0.33F) * splitangle, splitrot).scale(splitlength);
                  ++this.numsplits;
                  this.splitparents.put(Integer.valueOf(this.numsplits), Integer.valueOf(var18.splitno));
                  FXLightningBoltCommon.Segment split = new FXLightningBoltCommon.Segment(newpoints[i], new FXLightningBoltCommon.BoltPoint(newpoints[i + 1].basepoint, newpoints[i + 1].offsetvec.copy().add(diff)), segment.light / 2.0F, var18.segmentno, this.numsplits);
                  split.prev = prev;
                  this.segments.add(split);
               }

               prev = var18;
               this.segments.add(var18);
            }

            if(segment.next != null) {
               segment.next.prev = prev;
            }
         }

         this.numsegments0 *= splits;
      }
   }

   public void defaultFractal() {
      this.fractal(2, this.length * this.multiplier / 8.0F, 0.7F, 0.1F, 45.0F);
      this.fractal(2, this.length * this.multiplier / 12.0F, 0.5F, 0.1F, 50.0F);
      this.fractal(2, this.length * this.multiplier / 17.0F, 0.5F, 0.1F, 55.0F);
      this.fractal(2, this.length * this.multiplier / 23.0F, 0.5F, 0.1F, 60.0F);
      this.fractal(2, this.length * this.multiplier / 30.0F, 0.0F, 0.0F, 0.0F);
      this.fractal(2, this.length * this.multiplier / 34.0F, 0.0F, 0.0F, 0.0F);
      this.fractal(2, this.length * this.multiplier / 40.0F, 0.0F, 0.0F, 0.0F);
   }

   private void calculateCollisionAndDiffs() {
      HashMap lastactivesegment = new HashMap();
      Collections.sort(this.segments, new FXLightningBoltCommon.SegmentSorter());
      int lastsplitcalc = 0;
      int lastactiveseg = 0;

      FXLightningBoltCommon.Segment iterator;
      for(Iterator segment = this.segments.iterator(); segment.hasNext(); lastactiveseg = iterator.segmentno) {
         iterator = (FXLightningBoltCommon.Segment)segment.next();
         if(iterator.splitno > lastsplitcalc) {
            lastactivesegment.put(Integer.valueOf(lastsplitcalc), Integer.valueOf(lastactiveseg));
            lastsplitcalc = iterator.splitno;
            lastactiveseg = ((Integer)lastactivesegment.get(this.splitparents.get(Integer.valueOf(iterator.splitno)))).intValue();
         }
      }

      lastactivesegment.put(Integer.valueOf(lastsplitcalc), Integer.valueOf(lastactiveseg));
      lastsplitcalc = 0;
      lastactiveseg = ((Integer)lastactivesegment.get(Integer.valueOf(0))).intValue();

      FXLightningBoltCommon.Segment segment1;
      for(Iterator iterator1 = this.segments.iterator(); iterator1.hasNext(); segment1.calcEndDiffs()) {
         segment1 = (FXLightningBoltCommon.Segment)iterator1.next();
         if(lastsplitcalc != segment1.splitno) {
            lastsplitcalc = segment1.splitno;
            lastactiveseg = ((Integer)lastactivesegment.get(Integer.valueOf(segment1.splitno))).intValue();
         }

         if(segment1.segmentno > lastactiveseg) {
            iterator1.remove();
         }
      }

   }

   public void finalizeBolt() {
      if(!this.finalized) {
         this.finalized = true;
         this.calculateCollisionAndDiffs();
         Collections.sort(this.segments, new FXLightningBoltCommon.SegmentLightSorter());
      }
   }

   public void onUpdate() {
      this.particleAge += this.increment;
      if(this.particleAge > this.particleMaxAge) {
         this.particleAge = this.particleMaxAge;
      }

   }

   public class SegmentSorter implements Comparator {

      final FXLightningBoltCommon this$0 = FXLightningBoltCommon.this;


      public int compare(FXLightningBoltCommon.Segment o1, FXLightningBoltCommon.Segment o2) {
         int comp = Integer.valueOf(o1.splitno).compareTo(Integer.valueOf(o2.splitno));
         return comp == 0?Integer.valueOf(o1.segmentno).compareTo(Integer.valueOf(o2.segmentno)):comp;
      }

      public int compare(Object obj, Object obj1) {
         return this.compare((FXLightningBoltCommon.Segment)obj, (FXLightningBoltCommon.Segment)obj1);
      }

   }

   public class SegmentLightSorter implements Comparator {

      final FXLightningBoltCommon this$0 = FXLightningBoltCommon.this;


      public int compare(FXLightningBoltCommon.Segment o1, FXLightningBoltCommon.Segment o2) {
         return Float.compare(o2.light, o1.light);
      }

      public int compare(Object obj, Object obj1) {
         return this.compare((FXLightningBoltCommon.Segment)obj, (FXLightningBoltCommon.Segment)obj1);
      }

   }

   public class BoltPoint {

      WRVector3 point;
      WRVector3 basepoint;
      WRVector3 offsetvec;
      final FXLightningBoltCommon this$0 = FXLightningBoltCommon.this;


      public BoltPoint(WRVector3 basepoint, WRVector3 offsetvec) {
         this.point = basepoint.copy().add(offsetvec);
         this.basepoint = basepoint;
         this.offsetvec = offsetvec;
      }
   }

   public class Segment {

      public FXLightningBoltCommon.BoltPoint startpoint;
      public FXLightningBoltCommon.BoltPoint endpoint;
      public WRVector3 diff;
      public FXLightningBoltCommon.Segment prev;
      public FXLightningBoltCommon.Segment next;
      public WRVector3 nextdiff;
      public WRVector3 prevdiff;
      public float sinprev;
      public float sinnext;
      public float light;
      public int segmentno;
      public int splitno;
      final FXLightningBoltCommon this$0;


      public void calcDiff() {
         this.diff = this.endpoint.point.copy().sub(this.startpoint.point);
      }

      public void calcEndDiffs() {
         WRVector3 nextdiffnorm;
         WRVector3 thisdiffnorm;
         if(this.prev != null) {
            nextdiffnorm = this.prev.diff.copy().normalize();
            thisdiffnorm = this.diff.copy().normalize();
            this.prevdiff = thisdiffnorm.add(nextdiffnorm).normalize();
            this.sinprev = (float)Math.sin((double)(WRVector3.anglePreNorm(thisdiffnorm, nextdiffnorm.scale(-1.0F)) / 2.0F));
         } else {
            this.prevdiff = this.diff.copy().normalize();
            this.sinprev = 1.0F;
         }

         if(this.next != null) {
            nextdiffnorm = this.next.diff.copy().normalize();
            thisdiffnorm = this.diff.copy().normalize();
            this.nextdiff = thisdiffnorm.add(nextdiffnorm).normalize();
            this.sinnext = (float)Math.sin((double)(WRVector3.anglePreNorm(thisdiffnorm, nextdiffnorm.scale(-1.0F)) / 2.0F));
         } else {
            this.nextdiff = this.diff.copy().normalize();
            this.sinnext = 1.0F;
         }

      }

      public String toString() {
         return this.startpoint.point.toString() + " " + this.endpoint.point.toString();
      }

      public Segment(FXLightningBoltCommon.BoltPoint start, FXLightningBoltCommon.BoltPoint end, float light, int segmentnumber, int splitnumber) {
         this.this$0 = FXLightningBoltCommon.this;
         this.startpoint = start;
         this.endpoint = end;
         this.light = light;
         this.segmentno = segmentnumber;
         this.splitno = splitnumber;
         this.calcDiff();
      }

      public Segment(WRVector3 start, WRVector3 end) {
         this(FXLightningBoltCommon.this.new BoltPoint(start, new WRVector3(0.0D, 0.0D, 0.0D)), FXLightningBoltCommon.this.new BoltPoint(end, new WRVector3(0.0D, 0.0D, 0.0D)), 1.0F, 0, 0);
      }
   }
}

Это из таума
 
1,038
57
229
7,099
324
1,510
1,038
57
229
7,099
324
1,510
Тогда это вообще другой подход. У JustAGod молния на ходу генерится
 
Сверху