Package vazkii.botania.client.core.handler

Source Code of vazkii.botania.client.core.handler.LightningHandler$LightningBolt$SegmentLightSorter

/**
* This class was created by <Vazkii/ChickenBones>. It's distributed as
* part of the Botania Mod. Get the Source Code in github:
* https://github.com/Vazkii/Botania
*
* Botania is Open Source and distributed under a
* Creative Commons Attribution-NonCommercial-ShareAlike 3.0 License
* (http://creativecommons.org/licenses/by-nc-sa/3.0/deed.en_GB)
*
* File Created @ [Feb 3, 2014, 9:05:38 PM (GMT)]
*/
package vazkii.botania.client.core.handler;

import java.awt.Color;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Random;
import java.util.concurrent.ConcurrentLinkedQueue;

import net.minecraft.block.Block;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.ActiveRenderInfo;
import net.minecraft.client.renderer.Tessellator;
import net.minecraft.client.renderer.texture.TextureManager;
import net.minecraft.entity.Entity;
import net.minecraft.profiler.Profiler;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.AxisAlignedBB;
import net.minecraft.util.ChunkCoordinates;
import net.minecraft.util.MovingObjectPosition;
import net.minecraft.util.ResourceLocation;
import net.minecraft.world.World;
import net.minecraftforge.client.event.RenderWorldLastEvent;

import org.lwjgl.opengl.GL11;

import vazkii.botania.client.core.handler.LightningHandler.LightningBolt.Segment;
import vazkii.botania.client.fx.ParticleRenderDispatcher;
import vazkii.botania.client.lib.LibResources;
import vazkii.botania.common.core.helper.Vector3;
import cpw.mods.fml.common.eventhandler.SubscribeEvent;

public class LightningHandler {

  private static final ResourceLocation outsideResource = new ResourceLocation(LibResources.MISC_WISP_LARGE);
  private static final ResourceLocation insideResource = new ResourceLocation(LibResources.MISC_WISP_SMALL);

  static double interpPosX;
  static double interpPosY;
  static double interpPosZ;

  private static Vector3 getRelativeViewVector(Vector3 pos) {
    Entity renderEntity = Minecraft.getMinecraft().renderViewEntity;
    return new Vector3((float) renderEntity.posX - pos.x, (float) renderEntity.posY + renderEntity.getEyeHeight() - pos.y, (float) renderEntity.posZ - pos.z);
  }

  @SubscribeEvent
  public void onRenderWorldLast(RenderWorldLastEvent event) {
    Profiler profiler = Minecraft.getMinecraft().mcProfiler;
    profiler.startSection("botania-particles");
    ParticleRenderDispatcher.dispatch();
    profiler.startSection("lightning");

    float frame = event.partialTicks;
    Entity entity = Minecraft.getMinecraft().thePlayer;
    TextureManager render = Minecraft.getMinecraft().renderEngine;

    interpPosX = entity.lastTickPosX + (entity.posX - entity.lastTickPosX) * frame;
    interpPosY = entity.lastTickPosY + (entity.posY - entity.lastTickPosY) * frame;
    interpPosZ = entity.lastTickPosZ + (entity.posZ - entity.lastTickPosZ) * frame;

    GL11.glTranslated(-interpPosX, -interpPosY, -interpPosZ);

    Tessellator tessellator = Tessellator.instance;

    GL11.glDepthMask(false);
    GL11.glEnable(GL11.GL_BLEND);
    GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);

    ParticleRenderDispatcher.lightningCount = 0;

    render.bindTexture(outsideResource);
    tessellator.startDrawingQuads();
    tessellator.setBrightness(0xF000F0);
    for(LightningBolt bolt : LightningBolt.boltlist)
      renderBolt(bolt, tessellator, frame, ActiveRenderInfo.rotationX, ActiveRenderInfo.rotationXZ, ActiveRenderInfo.rotationZ, ActiveRenderInfo.rotationXY, 0, false);
    tessellator.draw();

    render.bindTexture(insideResource);
    tessellator.startDrawingQuads();
    tessellator.setBrightness(0xF000F0);
    for(LightningBolt bolt : LightningBolt.boltlist)
      renderBolt(bolt, tessellator, frame, ActiveRenderInfo.rotationX, ActiveRenderInfo.rotationXZ, ActiveRenderInfo.rotationZ, ActiveRenderInfo.rotationXY, 1, true);
    tessellator.draw();

    GL11.glDisable(GL11.GL_BLEND);
    GL11.glDepthMask(true);

    GL11.glTranslated(interpPosX, interpPosY, interpPosZ);
    profiler.endSection();
    profiler.endSection();

  }

  public static void spawnLightningBolt(World world, Vector3 vectorStart, Vector3 vectorEnd, float ticksPerMeter, long seed, int colorOuter, int colorInner) {
    LightningBolt bolt = new LightningBolt(world, vectorStart, vectorEnd, ticksPerMeter, seed, colorOuter, colorInner);
    bolt.defaultFractal();
    bolt.finalizeBolt();
    LightningBolt.boltlist.add(bolt);
  }

  private void renderBolt(LightningBolt bolt, Tessellator tessellator, float partialframe, float cosyaw, float cospitch, float sinyaw, float cossinpitch, int pass, boolean inner) {
    ParticleRenderDispatcher.lightningCount++;
    float boltage = bolt.particleAge < 0 ? 0 : (float) bolt.particleAge / (float) bolt.particleMaxAge;
    float mainalpha = 1;
    if(pass == 0)
      mainalpha = (1 - boltage) * 0.4F;
    else mainalpha = 1 - boltage * 0.5F;

    int expandTime = (int) (bolt.length * bolt.speed);

    int renderstart = (int) ((expandTime / 2 - bolt.particleMaxAge + bolt.particleAge) / (float) (expandTime / 2) * bolt.numsegments0);
    int renderend = (int) ((bolt.particleAge + expandTime) / (float) expandTime * bolt.numsegments0);

    for(Segment rendersegment : bolt.segments) {
      if(rendersegment.segmentno < renderstart || rendersegment.segmentno > renderend)
        continue;

      Vector3 playervec = getRelativeViewVector(rendersegment.startpoint.point).multiply(-1);

      double width = 0.025F * (playervec.mag() / 5 + 1) * (1 + rendersegment.light) * 0.5F;

      Vector3 diff1 = playervec.copy().crossProduct(rendersegment.prevdiff).normalize().multiply(width / rendersegment.sinprev);
      Vector3 diff2 = playervec.copy().crossProduct(rendersegment.nextdiff).normalize().multiply(width / rendersegment.sinnext);

      Vector3 startvec = rendersegment.startpoint.point;
      Vector3 endvec = rendersegment.endpoint.point;

      int color = inner ? bolt.colorInner : bolt.colorOuter;
      tessellator.setColorRGBA_I(color, (int) (mainalpha * rendersegment.light * new Color(color).getAlpha()));

      tessellator.addVertexWithUV(endvec.x - diff2.x, endvec.y - diff2.y, endvec.z - diff2.z, 0.5, 0);
      tessellator.addVertexWithUV(startvec.x - diff1.x, startvec.y - diff1.y, startvec.z - diff1.z, 0.5, 0);
      tessellator.addVertexWithUV(startvec.x + diff1.x, startvec.y + diff1.y, startvec.z + diff1.z, 0.5, 1);
      tessellator.addVertexWithUV(endvec.x + diff2.x, endvec.y + diff2.y, endvec.z + diff2.z, 0.5, 1);

      if(rendersegment.next == null) {
        Vector3 roundend = rendersegment.endpoint.point.copy().add(rendersegment.diff.copy().normalize().multiply(width));

        tessellator.addVertexWithUV(roundend.x - diff2.x, roundend.y - diff2.y, roundend.z - diff2.z, 0, 0);
        tessellator.addVertexWithUV(endvec.x - diff2.x, endvec.y - diff2.y, endvec.z - diff2.z, 0.5, 0);
        tessellator.addVertexWithUV(endvec.x + diff2.x, endvec.y + diff2.y, endvec.z + diff2.z, 0.5, 1);
        tessellator.addVertexWithUV(roundend.x + diff2.x, roundend.y + diff2.y, roundend.z + diff2.z, 0, 1);
      }

      if(rendersegment.prev == null) {
        Vector3 roundend = rendersegment.startpoint.point.copy().subtract(rendersegment.diff.copy().normalize().multiply(width));

        tessellator.addVertexWithUV(startvec.x - diff1.x, startvec.y - diff1.y, startvec.z - diff1.z, 0.5, 0);
        tessellator.addVertexWithUV(roundend.x - diff1.x, roundend.y - diff1.y, roundend.z - diff1.z, 0, 0);
        tessellator.addVertexWithUV(roundend.x + diff1.x, roundend.y + diff1.y, roundend.z + diff1.z, 0, 1);
        tessellator.addVertexWithUV(startvec.x + diff1.x, startvec.y + diff1.y, startvec.z + diff1.z, 0.5, 1);
      }
    }
  }

  public static class LightningBolt {

    public ArrayList<Segment> segments = new ArrayList<Segment>();
    public Vector3 start;
    public Vector3 end;
    ChunkCoordinates target;
    HashMap<Integer, Integer> splitparents = new HashMap<Integer, Integer>();

    public double length;
    public int numsegments0;
    private int numsplits;
    private boolean finalized;
    private Random rand;
    public long seed;

    public int particleAge;
    public int particleMaxAge;
    public boolean isDead;
    private AxisAlignedBB boundingBox;

    private World world;
    private Entity source;

    public static ConcurrentLinkedQueue<LightningBolt> boltlist = new ConcurrentLinkedQueue<LightningBolt>();

    public float speed = 1.5F;
    public static final int fadetime = 20;

    public static int playerdamage = 1;
    public static int entitydamage = 1;

    public int colorOuter;
    public int colorInner;

    public class BoltPoint {

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

      public Vector3 point;
      Vector3 basepoint;
      Vector3 offsetvec;
    }

    public class SegmentSorter implements Comparator<Segment> {

      @Override
      public int compare(Segment o1, Segment o2) {
        int comp = Integer.valueOf(o1.splitno).compareTo(o2.splitno);
        if(comp == 0)
          return Integer.valueOf(o1.segmentno).compareTo(o2.segmentno);
        else return comp;
      }
    }

    public class SegmentLightSorter implements Comparator<Segment> {
      @Override
      public int compare(Segment o1, Segment o2) {
        return Float.compare(o2.light, o1.light);
      }
    }

    public class Segment {
      public Segment(BoltPoint start, BoltPoint end, float light, int segmentnumber, int splitnumber) {
        startpoint = start;
        endpoint = end;
        this.light = light;
        segmentno = segmentnumber;
        splitno = splitnumber;

        calcDiff();
      }

      public Segment(Vector3 start, Vector3 end) {
        this(new BoltPoint(start, new Vector3(0, 0, 0)), new BoltPoint(end, new Vector3(0, 0, 0)), 1, 0, 0);
      }

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

      public void calcEndDiffs() {
        if(prev != null) {
          Vector3 prevdiffnorm = prev.diff.copy().normalize();
          Vector3 thisdiffnorm = diff.copy().normalize();

          prevdiff = thisdiffnorm.copy().add(prevdiffnorm).normalize();
          sinprev = (float) Math.sin(thisdiffnorm.angle(prevdiffnorm.multiply(-1)) / 2);
        } else {
          prevdiff = diff.copy().normalize();
          sinprev = 1;
        }

        if(next != null) {
          Vector3 nextdiffnorm = next.diff.copy().normalize();
          Vector3 thisdiffnorm = diff.copy().normalize();

          nextdiff = thisdiffnorm.add(nextdiffnorm).normalize();
          sinnext = (float) Math.sin(thisdiffnorm.angle(nextdiffnorm.multiply(-1)) / 2);
        } else {
          nextdiff = diff.copy().normalize();
          sinnext = 1;
        }
      }

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

      public BoltPoint startpoint;
      public BoltPoint endpoint;

      public Vector3 diff;

      public Segment prev;
      public Segment next;

      public Vector3 nextdiff;
      public Vector3 prevdiff;

      public float sinprev;
      public float sinnext;
      public float light;

      public int segmentno;
      public int splitno;
    }

    public LightningBolt(World world, Vector3 sourcevec, Vector3 targetvec, float ticksPerMeter, long seed, int colorOuter, int colorInner) {
      this.world = world;
      this.seed = seed;
      rand = new Random(seed);

      start = sourcevec;
      end = targetvec;

      speed = ticksPerMeter;

      this.colorOuter = colorOuter;
      this.colorInner = colorInner;

      numsegments0 = 1;

      length = end.copy().subtract(start).mag();
      particleMaxAge = fadetime + rand.nextInt(fadetime) - fadetime / 2;
      particleAge = -(int) (length * speed);

      boundingBox = AxisAlignedBB.getBoundingBox(0, 0, 0, 0, 0, 0);
      boundingBox.setBB(AxisAlignedBB.getBoundingBox(Math.min(start.x, end.x), Math.min(start.y, end.y), Math.min(start.z, end.z), Math.max(start.x, end.x), Math.max(start.y, end.y), Math.max(start.z, end.z)).expand(length / 2, length / 2, length / 2));

      segments.add(new Segment(start, end));
    }

    public static Vector3 getFocalPoint(TileEntity tile) {
      return Vector3.fromTileEntityCenter(tile);
    }

    public LightningBolt(World world, Vector3 sourcevec, TileEntity target, float ticksPerMeter, long seed, int colorOuter, int colorInner) {
      this(world, sourcevec, getFocalPoint(target), ticksPerMeter, seed, colorOuter, colorInner);
      this.target = new ChunkCoordinates(target.xCoord, target.yCoord, target.zCoord);
    }

    public void setWrapper(Entity entity) {
      source = entity;
    }

    public void fractal(int splits, double amount, double splitchance, double splitlength, double splitangle) {
      if(finalized)
        return;

      ArrayList<Segment> oldsegments = segments;
      segments = new ArrayList<Segment>();

      Segment prev = null;

      for(Segment segment : oldsegments) {
        prev = segment.prev;

        Vector3 subsegment = segment.diff.copy().multiply(1F / splits);

        BoltPoint[] newpoints = new BoltPoint[splits + 1];

        Vector3 startpoint = segment.startpoint.point;
        newpoints[0] = segment.startpoint;
        newpoints[splits] = segment.endpoint;

        for(int i = 1; i < splits; i++) {
          Vector3 randoff = segment.diff.copy().perpendicular().normalize().rotate(rand.nextFloat() * 360, segment.diff);
          randoff.multiply((rand.nextFloat() - 0.5F) * amount * 2);

          Vector3 basepoint = startpoint.copy().add(subsegment.copy().multiply(i));

          newpoints[i] = new BoltPoint(basepoint, randoff);
        }

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

          if(i != 0 && rand.nextFloat() < splitchance) {
            Vector3 splitrot = next.diff.copy().xCrossProduct().rotate(rand.nextFloat() * 360, next.diff);
            Vector3 diff = next.diff.copy().rotate((rand.nextFloat() * 0.66F + 0.33F) * splitangle, splitrot).multiply(splitlength);

            numsplits++;
            splitparents.put(numsplits, next.splitno);

            Segment split = new Segment(newpoints[i], new BoltPoint(newpoints[i + 1].basepoint, newpoints[i + 1].offsetvec.copy().add(diff)), segment.light / 2F, next.segmentno, numsplits);
            split.prev = prev;

            segments.add(split);
          }

          prev = next;
          segments.add(next);
        }

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

      numsegments0 *= splits;
    }

    public void defaultFractal() {
      fractal(2, length / 1.5, 0.7F, 0.7F, 45);
      fractal(2, length / 4, 0.5F, 0.8F, 50);
      fractal(2, length / 15, 0.5F, 0.9F, 55);
      fractal(2, length / 30, 0.5F, 1.0F, 60);
      fractal(2, length / 60, 0, 0, 0);
      fractal(2, length / 100, 0, 0, 0);
      fractal(2, length / 400, 0, 0, 0);
    }

    private float rayTraceResistance(Vector3 start, Vector3 end, float prevresistance) {
      MovingObjectPosition mop = world.rayTraceBlocks(start.toVec3D(), end.toVec3D());

      if(mop == null)
        return prevresistance;

      if(mop.typeOfHit == MovingObjectPosition.MovingObjectType.BLOCK) {
        Block block = world.getBlock(mop.blockX, mop.blockY, mop.blockZ);

        if(world.isAirBlock(mop.blockX, mop.blockY, mop.blockZ))
          return prevresistance;

        return prevresistance + block.getExplosionResistance(source) + 0.3F;
      } else return prevresistance;
    }

    private void calculateCollisionAndDiffs() {
      HashMap<Integer, Integer> lastactivesegment = new HashMap<Integer, Integer>();

      Collections.sort(segments, new SegmentSorter());

      int lastsplitcalc = 0;
      int lastactiveseg = 0;// unterminated
      float splitresistance = 0;

      for(Segment segment : segments) {
        if(segment.splitno > lastsplitcalc) {
          lastactivesegment.put(lastsplitcalc, lastactiveseg);
          lastsplitcalc = segment.splitno;
          lastactiveseg = lastactivesegment.get(splitparents.get(segment.splitno));
          splitresistance = lastactiveseg < segment.segmentno ? 50 : 0;
        }

        if(splitresistance >= 40 * segment.light)
          continue;

        splitresistance = rayTraceResistance(segment.startpoint.point, segment.endpoint.point, splitresistance);
        lastactiveseg = segment.segmentno;
      }
      lastactivesegment.put(lastsplitcalc, lastactiveseg);

      lastsplitcalc = 0;
      lastactiveseg = lastactivesegment.get(0);
      for(Iterator<Segment> iterator = segments.iterator(); iterator.hasNext();) {
        Segment segment = iterator.next();
        if(lastsplitcalc != segment.splitno) {
          lastsplitcalc = segment.splitno;
          lastactiveseg = lastactivesegment.get(segment.splitno);
        }

        if(segment.segmentno > lastactiveseg)
          iterator.remove();
        segment.calcEndDiffs();
      }
    }

    public void finalizeBolt() {
      if(finalized)
        return;
      finalized = true;

      calculateCollisionAndDiffs();

      Collections.sort(segments, new SegmentLightSorter());

      boltlist.add(this);
    }

    public void onUpdate() {
      particleAge++;

      if (particleAge >= particleMaxAge)
        isDead = true;
    }

    // Called in ClientTickHandler
    public static void update() {
      for(Iterator<LightningBolt> iterator = boltlist.iterator(); iterator.hasNext();) {
        LightningBolt bolt = iterator.next();

        bolt.onUpdate();
        if(bolt.isDead)
          iterator.remove();
      }
    }
  }
}
TOP

Related Classes of vazkii.botania.client.core.handler.LightningHandler$LightningBolt$SegmentLightSorter

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.