/** * Copyright (c) Lambda Innovation, 2013-2016 * This file is part of the AcademyCraft mod. * https://github.com/LambdaInnovation/AcademyCraft * Licensed under GPLv3, see project root for more information. */ package cn.academy.vanilla.electromaster.client.effect; import cn.lambdalib.util.client.RenderUtils; import cn.lambdalib.util.client.shader.ShaderSimple; import cn.lambdalib.util.generic.RandUtils; import cn.lambdalib.util.generic.VecUtils; import net.minecraft.util.MathHelper; import net.minecraft.util.ResourceLocation; import net.minecraft.util.Vec3; import org.lwjgl.opengl.GL11; import org.lwjgl.opengl.GL20; import org.lwjgl.util.vector.Matrix4f; import org.lwjgl.util.vector.Vector3f; import java.util.ArrayList; import java.util.List; import java.util.Random; import static cn.lambdalib.util.generic.VecUtils.*; /** * Used the concept of L-system and recursion to generate a lightning pattern. * @author WeAthFolD */ public class ArcFactory { static final ResourceLocation TEXTURE = new ResourceLocation("academy:textures/effects/arc/line_segment.png"); static Random rand = new Random(); static Matrix4f matrix = new Matrix4f(); public double width = 0.1; //public ICurve curve; public double lengthShrink = 0.7; public double alphaShrink = 0.9; public int passes = 6; public double maxOffset = 1.5; public double branchFactor = 0.4; public double widthShrink = 0.7; public Vec3 normal = vec(0, 0, 1); //States only used when generating List< List<Segment> > listAll = new ArrayList(); List< List<Segment> > bufferAll = new ArrayList(); /** * Handle a single list for 1 pass. * @param list * @param buffer */ private void handleSingle(List<Segment> list, List<Segment> buffer, double offset) { buffer.clear(); for(Segment s : list) { Point ave = average(s.start, s.end); float theta = (float) (rand.nextFloat() * Math.PI * 2); //Rand dir across YZ plane double sin = MathHelper.sin(theta), cos = MathHelper.cos(theta); double off = rand.nextFloat() * offset; ave.pt.yCoord += off * sin; ave.pt.zCoord += off * cos; Segment s1 = s, s2 = new Segment(ave, s.end, s.alpha); s1.end = ave; buffer.add(s1); buffer.add(s2); // Branching with probability if(rand.nextDouble() < branchFactor) { matrix.setIdentity(); Vector3f v3f; Vec3 dir = multiply(subtract(ave.pt, s.start.pt), lengthShrink); dir = randomRotate(10, dir); //matrix.rotate(GenericUtils.randIntv(-50, 50) / 180 * (float)Math.PI, asV3f(random())); //dir = applyMatrix(matrix, dir); double w2 = ave.width * widthShrink; Point p1 = new Point(ave.pt, w2), p2 = new Point(add(ave.pt, dir), w2); List<Segment> toAdd = new ArrayList(); toAdd.add(new Segment(p1, p2, s.alpha * alphaShrink)); bufferAll.add(toAdd); listAll.add(new ArrayList()); } } } /** * Generate count arcs with random picked length in [lengthFrom, lengthTo). */ public Arc[] generateList(int count, double lengthFrom, double lengthTo) { Arc[] arr = new Arc[count]; for(int i = 0; i < count; ++i) arr[i] = generate(RandUtils.ranged(lengthFrom, lengthTo)); return arr; } public Arc generate(double length) { listAll.clear(); bufferAll.clear(); Vec3 v0 = vec(0, 0, 0), v1 = vec(length, 0, 0); List<Segment> init = new ArrayList(); init.add(new Segment( new Point(v0, width), new Point(v1, width), 1.0)); listAll.add(init); bufferAll.add(new ArrayList()); boolean flip = false; double offset = maxOffset; int realPasses = passes; for(int i = 0; i < realPasses; ++i) { if(flip) { for(int j = 0; j < listAll.size(); ++j) { handleSingle(bufferAll.get(j), listAll.get(j), offset); } } else { for(int j = 0; j < listAll.size(); ++j) { handleSingle(listAll.get(j), bufferAll.get(j), offset); } } flip = !flip; offset /= 2; } return new Arc(flip ? bufferAll : listAll, normal, length); } static private Vec3 randomRotate(float range, Vec3 dir) { float a = (float) (RandUtils.rangef(-range, range) / 180 * Math.PI); Vec3 ret = VecUtils.copy(dir); ret.rotateAroundX(RandUtils.rangef(-a, a)); ret.rotateAroundY(RandUtils.rangef(-a, a)); ret.rotateAroundZ(RandUtils.rangef(-a, a)); return ret; } class Point { Vec3 pt; double width; public Point(Vec3 _pt, double _w) { pt = _pt; width = _w; } } private Point average(Point pa, Point pb) { Vec3 v = VecUtils.lerp(pa.pt, pb.pt, 0.5); return new Point(v, (pa.width + pb.width) / 2); } class Segment { Point start, end; double alpha; public Segment(Point s, Point e, double a) { start = s; end = e; alpha = a; } } public static class Arc { int listId; private final List< List<Segment> > segmentList; private final Vec3 normal; public final double length; public Arc(List< List<Segment> > list, Vec3 _normal, double len) { segmentList = new ArrayList(list); normal = _normal; buildList(); length = len; } public void draw() { if(RenderUtils.isInShadowPass()) return; ShaderSimple.instance().useProgram(); doPreWork(); GL11.glCallList(listId); doPostWork(); GL20.glUseProgram(0); } public void draw(double length) { if(RenderUtils.isInShadowPass()) return; ShaderSimple.instance().useProgram(); draw(length, true); GL20.glUseProgram(0); } private void draw(double length, boolean really) { if(really) doPreWork(); GL11.glDisable(GL11.GL_CULL_FACE); GL11.glBegin(GL11.GL_QUADS); for(List<Segment> l : segmentList) { handleSegment(l, normal, length); } GL11.glEnd(); GL11.glEnable(GL11.GL_CULL_FACE); if(really) doPostWork(); } private void buildList() { listId = GL11.glGenLists(1); GL11.glNewList(listId, GL11.GL_COMPILE); draw(23333333, false); GL11.glEndList(); } private void handleSegment(List<Segment> list, Vec3 normal, double len) { Vec3 lastDir = null; for(Segment s : list) { if(s.start.pt.xCoord > len) break; Vec3 dir = randomRotate(15, crossProduct(subtract(s.end.pt, s.start.pt), normal)).normalize(); if(lastDir == null) lastDir = dir; Vec3 p1 = add(s.start.pt, multiply(lastDir, s.start.width)), p2 = add(s.start.pt, multiply(lastDir, -s.start.width)), p3 = add(s.end.pt, multiply(dir, s.end.width)), p4 = add(s.end.pt, multiply(dir, -s.end.width)); GL11.glColor4d(1, 1, 1, s.alpha); addVert(p1, 0, 0); addVert(p2, 0, 1); addVert(p4, 1, 1); addVert(p3, 1, 0); lastDir = dir; } } private void doPreWork() { RenderUtils.loadTexture(TEXTURE); GL11.glEnable(GL11.GL_BLEND); GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA); GL11.glDisable(GL11.GL_LIGHTING); GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA); GL11.glLineWidth(0.4f); } private void doPostWork() {} private void addVert(Vec3 vec, double u, double v) { GL11.glTexCoord2d(u, v); GL11.glVertex3d(vec.xCoord, vec.yCoord, vec.zCoord); } } }