diff options
22 files changed, 1449 insertions, 202 deletions
diff --git a/cape.png b/cape.png Binary files differnew file mode 100644 index 00000000..a2a2d5f7 --- /dev/null +++ b/cape.png diff --git a/src/main/java/io/github/moulberry/notenoughupdates/NEUCape.java b/src/main/java/io/github/moulberry/notenoughupdates/NEUCape.java deleted file mode 100644 index 1a33a798..00000000 --- a/src/main/java/io/github/moulberry/notenoughupdates/NEUCape.java +++ /dev/null @@ -1,192 +0,0 @@ -package io.github.moulberry.notenoughupdates; - -import net.minecraft.client.Minecraft; -import net.minecraft.client.renderer.GlStateManager; -import net.minecraft.client.renderer.Tessellator; -import net.minecraft.client.renderer.WorldRenderer; -import net.minecraft.client.renderer.vertex.DefaultVertexFormats; -import net.minecraft.entity.Entity; -import net.minecraft.entity.player.EntityPlayer; -import net.minecraftforge.client.event.RenderPlayerEvent; -import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; -import org.lwjgl.input.Keyboard; -import org.lwjgl.opengl.GL11; -import org.lwjgl.util.vector.Vector3f; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; - -public class NEUCape { - - private List<List<Node>> nodes = null; - - int horzNodes = 20; - float targetDist = 1/30f; - - public void createNodes(EntityPlayer player) { - nodes = new ArrayList<>(); - for(int i=0; i<50; i++) { - List<Node> row = new ArrayList<>(); - for(int j=0; j<horzNodes; j++) { - row.add(new Node(-1, 2-i*targetDist, ((double)j)/(horzNodes-1))); - } - - nodes.add(row); - } - for(int y=0; y<nodes.size(); y++) { - for(int x=0; x<nodes.get(y).size(); x++) { - for(Direction dir : Direction.values()) { - for(int i=1; i<=2; i++) { - Offset offset = new Offset(dir, i); - - int xNeighbor = x+offset.getXOffset(); - int yNeighbor = y+offset.getYOffset(); - - if(xNeighbor >= 0 && xNeighbor < nodes.get(y).size() - && yNeighbor >= 0 && yNeighbor < nodes.size()) { - Node neighbor = nodes.get(yNeighbor).get(xNeighbor); - nodes.get(y).get(x).neighbors.put(offset, neighbor); - } - } - } - } - } - } - - public void ensureNodesCreated(EntityPlayer player) { - if(nodes == null) createNodes(player); - } - - public enum Direction { - LEFT(-1, 0), - UP(0, 1), - RIGHT(1, 0), - DOWN(0, -1), - UPLEFT(-1, 1), - UPRIGHT(1, 1), - DOWNLEFT(-1, -1), - DOWNRIGHT(1, -1); - - int xOff; - int yOff; - - Direction(int xOff, int yOff) { - this.xOff = xOff; - this.yOff = yOff; - } - } - - public static class Offset { - Direction direction; - int steps; - - public Offset(Direction direction, int steps) { - this.direction = direction; - this.steps = steps; - } - - public int getXOffset() { - return direction.xOff*steps; - } - - public int getYOffset() { - return direction.yOff*steps; - } - - public boolean equals(Object obj) { - if(obj instanceof Offset) { - Offset other = (Offset) obj; - return other.direction == direction && other.steps == steps; - } - return false; - } - - @Override - public int hashCode() { - return 13*direction.ordinal() + 7*steps; - } - } - - public static class Node { - private Vector3f position; - private Vector3f acceleration = new Vector3f(); - private HashMap<Offset, Node> neighbors = new HashMap<>(); - - public Node(double x, double y, double z) { - this.position = new Vector3f((float)x, (float)y, (float)z); - } - - public void updatePosition() { - - } - - public void renderNode() { - //System.out.println(neighbors.size()); - if(neighbors.containsKey(new Offset(Direction.DOWNRIGHT, 1))) { - //System.out.println("trying to render"); - Tessellator tessellator = Tessellator.getInstance(); - WorldRenderer worldrenderer = tessellator.getWorldRenderer(); - GlStateManager.color(1F, 1F, 1F, 1F); - worldrenderer.begin(GL11.GL_TRIANGLE_STRIP, DefaultVertexFormats.POSITION); - - Vector3f node2Pos = neighbors.get(new Offset(Direction.DOWN, 1)).position; - Vector3f node3Pos = neighbors.get(new Offset(Direction.RIGHT, 1)).position; - Vector3f node4Pos = neighbors.get(new Offset(Direction.DOWNRIGHT, 1)).position; - - worldrenderer.pos(position.x, position.y, position.z).endVertex(); - worldrenderer.pos(node2Pos.x, node2Pos.y, node2Pos.z).endVertex(); - worldrenderer.pos(node3Pos.x, node3Pos.y, node3Pos.z).endVertex(); - worldrenderer.pos(node4Pos.x, node4Pos.y, node4Pos.z).endVertex(); - - tessellator.draw(); - } - } - } - - @SubscribeEvent - public void onRenderPlayer(RenderPlayerEvent.Post e) { - EntityPlayer player = e.entityPlayer; - - ensureNodesCreated(player); - if(Keyboard.isKeyDown(Keyboard.KEY_R)) createNodes(player); - - Entity viewer = Minecraft.getMinecraft().getRenderViewEntity(); - double viewerX = viewer.lastTickPosX + (viewer.posX - viewer.lastTickPosX) * e.partialRenderTick; - double viewerY = viewer.lastTickPosY + (viewer.posY - viewer.lastTickPosY) * e.partialRenderTick; - double viewerZ = viewer.lastTickPosZ + (viewer.posZ - viewer.lastTickPosZ) * e.partialRenderTick; - - GlStateManager.pushMatrix(); - GlStateManager.enableBlend(); - GlStateManager.tryBlendFuncSeparate(GL11.GL_SRC_ALPHA, - GL11.GL_ONE_MINUS_SRC_ALPHA, GL11.GL_ONE, GL11.GL_ZERO); - GlStateManager.disableTexture2D(); - GlStateManager.disableCull(); - - //GL11.glTranslatef(-(float)viewerX, -(float)viewerY, -(float)viewerZ); - - updateCape(player); - renderCape(player); - - //GL11.glTranslatef((float)viewerX, (float)viewerY, (float)viewerZ); - - GL11.glEnable(GL11.GL_CULL_FACE); - GlStateManager.enableTexture2D(); - GlStateManager.disableBlend(); - GlStateManager.popMatrix(); - GlStateManager.color(1F, 1F, 1F, 1F); - } - - private void updateCape(EntityPlayer player) { - - } - - private void renderCape(EntityPlayer player) { - for(int y=0; y<nodes.size()-1; y++) { - for(int x=0; x<nodes.get(y).size()-1; x++) { - nodes.get(y).get(x).renderNode(); - } - } - } - -} diff --git a/src/main/java/io/github/moulberry/notenoughupdates/NEUCape2.java b/src/main/java/io/github/moulberry/notenoughupdates/NEUCape2.java index 4b834cbb..996809da 100644 --- a/src/main/java/io/github/moulberry/notenoughupdates/NEUCape2.java +++ b/src/main/java/io/github/moulberry/notenoughupdates/NEUCape2.java @@ -482,7 +482,6 @@ public class NEUCape2 { Tessellator tessellator = Tessellator.getInstance(); WorldRenderer worldrenderer = tessellator.getWorldRenderer(); - //Minecraft.getMinecraft().thePlayer.addChatMessage(new ChatComponentText(node1.normalY + " " + node2.normalY + " " + node3.normalY + " " + node4.normalY)); if(offset) { diff --git a/src/main/java/io/github/moulberry/notenoughupdates/NotEnoughUpdates.java b/src/main/java/io/github/moulberry/notenoughupdates/NotEnoughUpdates.java index 8d01e9a9..0724a132 100644 --- a/src/main/java/io/github/moulberry/notenoughupdates/NotEnoughUpdates.java +++ b/src/main/java/io/github/moulberry/notenoughupdates/NotEnoughUpdates.java @@ -8,7 +8,9 @@ import com.mojang.authlib.yggdrasil.YggdrasilAuthenticationService; import com.mojang.authlib.yggdrasil.YggdrasilUserAuthentication; import io.github.moulberry.notenoughupdates.auction.CustomAHGui; import io.github.moulberry.notenoughupdates.commands.SimpleCommand; +import io.github.moulberry.notenoughupdates.cosmetics.CapeManager; import io.github.moulberry.notenoughupdates.infopanes.CollectionLogInfoPane; +import io.github.moulberry.notenoughupdates.infopanes.CosmeticsInfoPane; import io.github.moulberry.notenoughupdates.util.Utils; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.GuiScreen; @@ -95,7 +97,6 @@ public class NotEnoughUpdates { private GuiScreen openGui = null; - ScheduledExecutorService guiDelaySES = Executors.newScheduledThreadPool(1); SimpleCommand collectionLogCommand = new SimpleCommand("neucl", new SimpleCommand.ProcessCommandRunnable() { public void processCommand(ICommandSender sender, String[] args) { if(!OpenGlHelper.isFramebufferEnabled()) { @@ -111,6 +112,15 @@ public class NotEnoughUpdates { } }); + SimpleCommand cosmeticsCommand = new SimpleCommand("neucosmetics", new SimpleCommand.ProcessCommandRunnable() { + public void processCommand(ICommandSender sender, String[] args) { + if(!(Minecraft.getMinecraft().currentScreen instanceof GuiContainer)) { + openGui = new GuiInventory(Minecraft.getMinecraft().thePlayer); + } + overlay.displayInformationPane(new CosmeticsInfoPane(overlay, manager)); + } + }); + SimpleCommand neuAhCommand = new SimpleCommand("neuah", new SimpleCommand.ProcessCommandRunnable() { public void processCommand(ICommandSender sender, String[] args) { if(!hasSkyblockScoreboard()) { @@ -136,11 +146,12 @@ public class NotEnoughUpdates { public void preinit(FMLPreInitializationEvent event) { INSTANCE = this; MinecraftForge.EVENT_BUS.register(this); - //MinecraftForge.EVENT_BUS.register(new NEUCape()); + MinecraftForge.EVENT_BUS.register(CapeManager.getInstance()); File f = new File(event.getModConfigurationDirectory(), "notenoughupdates"); f.mkdirs(); ClientCommandHandler.instance.registerCommand(collectionLogCommand); + ClientCommandHandler.instance.registerCommand(cosmeticsCommand); ClientCommandHandler.instance.registerCommand(neuAhCommand); neuio = new NEUIO(getAccessToken()); diff --git a/src/main/java/io/github/moulberry/notenoughupdates/auction/CustomAH.java b/src/main/java/io/github/moulberry/notenoughupdates/auction/CustomAH.java index aad82b9f..dd02c887 100644 --- a/src/main/java/io/github/moulberry/notenoughupdates/auction/CustomAH.java +++ b/src/main/java/io/github/moulberry/notenoughupdates/auction/CustomAH.java @@ -1028,8 +1028,6 @@ public class CustomAH extends Gui { return; } - System.out.println("Updating search:"+searchField.getText()); - lastUpdateSearch = System.currentTimeMillis(); shouldUpdateSearch = false; @@ -1345,7 +1343,6 @@ public class CustomAH extends Gui { } } - System.out.println(); Utils.playPressSound(); } else if(mouseY > guiTop+126 && mouseY < guiTop+126+16 && !leftFiller) { priceField.setFocused(true); diff --git a/src/main/java/io/github/moulberry/notenoughupdates/cosmetics/CapeManager.java b/src/main/java/io/github/moulberry/notenoughupdates/cosmetics/CapeManager.java new file mode 100644 index 00000000..9afbd17f --- /dev/null +++ b/src/main/java/io/github/moulberry/notenoughupdates/cosmetics/CapeManager.java @@ -0,0 +1,103 @@ +package io.github.moulberry.notenoughupdates.cosmetics; + +import net.minecraft.client.Minecraft; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraftforge.client.event.RenderPlayerEvent; +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; +import net.minecraftforge.fml.common.gameevent.TickEvent; +import org.apache.commons.lang3.tuple.MutablePair; +import org.apache.commons.lang3.tuple.Pair; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Set; + +public class CapeManager { + + public static final CapeManager INSTANCE = new CapeManager(); + + private HashMap<String, Pair<NEUCape, String>> capeMap = new HashMap<>(); + private String[] capes = new String[]{"testcape", "nullzee", "gravy", "fade", "contrib"}; + + public static CapeManager getInstance() { + return INSTANCE; + } + + public void setCape(String player, String capename) { + if(capename == null) { + capeMap.remove(player); + return; + } + if(capeMap.containsKey(player)) { + Pair<NEUCape, String> capePair = capeMap.get(player); + capePair.setValue(capename); + } else { + capeMap.put(player, new MutablePair<>(new NEUCape(capename), capename)); + } + } + + public String getCape(String player) { + if(capeMap.containsKey(player)) { + return capeMap.get(player).getRight(); + } + return null; + } + + public EntityPlayer getPlayerForName(String name) { + if(Minecraft.getMinecraft().theWorld != null) { + for(EntityPlayer player : Minecraft.getMinecraft().theWorld.playerEntities) { + if(player.getName().equals(name)) { + return player; + } + } + } + return null; + } + + @SubscribeEvent + public void onRenderPlayer(RenderPlayerEvent.Post e) { + if(capeMap.containsKey(e.entityPlayer.getName())) { + capeMap.get(e.entityPlayer.getName()).getLeft().onRenderPlayer(e); + } + } + + @SubscribeEvent + public void onTick(TickEvent.ClientTickEvent event) { + Set<String> toRemove = new HashSet<>(); + for(String playerName : capeMap.keySet()) { + EntityPlayer player = getPlayerForName(playerName); + if(player == null) { + toRemove.add(playerName); + } else { + String capeName = capeMap.get(playerName).getRight(); + if(capeName != null) { + capeMap.get(playerName).getLeft().setCapeTexture(capeName); + capeMap.get(playerName).getLeft().onTick(event, player); + } else { + toRemove.add(playerName); + } + } + } + for(String playerName : toRemove) { + capeMap.remove(playerName); + } + } + + public String[] getCapes() { + return capes; + } + + public boolean getPermissionForCape(String player, String capename) { + if(capename == null) { + return false; + } else if(player.equalsIgnoreCase("Moulberry")) { + return true; //Oh yeah gimme gimme + } else if(capename.equals("nullzee")) { + return player.equalsIgnoreCase("Nullzee"); + } else if(capename.equals("gravy")) { + return player.equalsIgnoreCase("ThatGravyBoat"); + } + return false; + } + +} diff --git a/src/main/java/io/github/moulberry/notenoughupdates/cosmetics/CapeNode.java b/src/main/java/io/github/moulberry/notenoughupdates/cosmetics/CapeNode.java new file mode 100644 index 00000000..1ec2dee3 --- /dev/null +++ b/src/main/java/io/github/moulberry/notenoughupdates/cosmetics/CapeNode.java @@ -0,0 +1,371 @@ +package io.github.moulberry.notenoughupdates.cosmetics; + +import net.minecraft.block.Block; +import net.minecraft.client.Minecraft; +import net.minecraft.client.renderer.GlStateManager; +import net.minecraft.client.renderer.Tessellator; +import net.minecraft.client.renderer.WorldRenderer; +import net.minecraft.client.renderer.vertex.DefaultVertexFormats; +import net.minecraft.util.BlockPos; +import net.minecraft.util.MathHelper; +import org.lwjgl.input.Keyboard; +import org.lwjgl.opengl.GL11; +import org.lwjgl.util.vector.Vector2f; +import org.lwjgl.util.vector.Vector3f; + +import java.nio.FloatBuffer; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + +public class CapeNode { + + private static final NEUCape.Direction[] cardinals = new NEUCape.Direction[]{NEUCape.Direction.UP, NEUCape.Direction.RIGHT, NEUCape.Direction.DOWN, NEUCape.Direction.LEFT}; + + public Vector3f position; + public Vector3f lastPosition = new Vector3f(); + public Vector3f renderPosition = new Vector3f(); + public final Vector3f[] oldRenderPosition = new Vector3f[5]; + public final Vector3f velocity = new Vector3f(); + public Vector3f normal = null; + public Vector3f sideNormal = null; + public boolean fixed = false; + + public HashMap<NEUCape.Offset, CapeNode> neighbors = new HashMap<>(); + + public float texU = 0; + public float texV = 0; + + public float horzDistMult = 2f; + public float vertDistMult = 0.5f; + + public float horzSideTexU = 0; + public float horzSideTexVTop = 0; + + public float vertSideTexU = 0; + public float vertSideTexVTop = 0; + + public static final float gravity = 0.1f; + public static final float resistance = 0.5f; + + public static final int FLOAT_NUM = 20; + + public CapeNode(float x, float y, float z) { + this.position = new Vector3f(x, y, z); + } + + private List<Vector2f> getConstaints() { + List<Vector2f> constaints = new ArrayList<>(); + for(NEUCape.Direction cardinal : cardinals) { + for(int i=1; i<=2; i++) { + NEUCape.Offset offset = new NEUCape.Offset(cardinal, i); + CapeNode other = neighbors.get(offset); + if(other != null) { + int iOffset = offset.getXOffset() + NEUCape.HORZ_NODES * offset.getYOffset(); + constaints.add(new Vector2f(iOffset, i*NEUCape.targetDist*(cardinal.yOff==0?horzDistMult:vertDistMult))); + } + } + + } + return constaints; + } + + public void loadIntoBuffer(FloatBuffer buffer) { + loadVec3IntoBuffer(buffer, position); + List<Vector2f> containts = getConstaints(); + buffer.put(containts.size()); + for(int i=0; i<8; i++) { + if(i < containts.size()) { + loadVec2IntoBuffer(buffer, containts.get(i)); + } else { + loadVec2IntoBuffer(buffer, new Vector2f()); + } + } + } + + public void readFromBuffer(FloatBuffer buffer) { + readVec3FromBuffer(buffer, position); + buffer.position(buffer.position()+17); + } + + private void readVec3FromBuffer(FloatBuffer buffer, Vector3f vec) { + vec.x = buffer.get(); + vec.y = buffer.get(); + vec.z = buffer.get(); + } + + private void loadVec2IntoBuffer(FloatBuffer buffer, Vector2f vec) { + buffer.put(vec.x); + buffer.put(vec.y); + } + + private void loadVec3IntoBuffer(FloatBuffer buffer, Vector3f vec) { + buffer.put(vec.x); + buffer.put(vec.y); + buffer.put(vec.z); + } + + public void update() { + if(!fixed) { + velocity.y -= gravity * (resistance)/(1-resistance); + + float actualResistance = resistance; + BlockPos pos = new BlockPos( + MathHelper.floor_double(position.x), + MathHelper.floor_double(position.y), + MathHelper.floor_double(position.z)); + Block block = Minecraft.getMinecraft().theWorld.getBlockState(pos).getBlock(); + if(block.getMaterial().isLiquid()) { + actualResistance = 0.8f; + } + + velocity.scale(1-actualResistance); + + Vector3f.add(position, velocity, position); + } + } + + public final CapeNode getNeighbor(NEUCape.Offset offset) { + return neighbors.get(offset); + } + + public final void setNeighbor(NEUCape.Offset offset, CapeNode node) { + neighbors.put(offset, node); + } + + public void applyForce(float dX, float dY, float dZ) { + velocity.x += dX; + velocity.y += dY; + velocity.z += dZ; + } + + public void move(float dX, float dY, float dZ) { + position.x += dX; + position.y += dY; + position.z += dZ; + lastPosition.x = position.x; + lastPosition.y = position.y; + lastPosition.z = position.z; + } + + public void resetNormal() { + normal = null; + sideNormal = null; + } + + public void resolveAll(float horzDistMult, boolean opt) { + resolveBend(horzDistMult, opt); + //resolveShear(); + resolveStruct(horzDistMult, opt); + } + + public void resolve(CapeNode other, float targetDist, float strength, boolean opt) { + if(other == null || Keyboard.isKeyDown(Keyboard.KEY_H)) return; + + double dX = position.x - other.position.x; + double dY = position.y - other.position.y; + double dZ = position.z - other.position.z; + + double distSq = dX*dX + dY*dY + dZ*dZ; + + double factor = (distSq - targetDist*targetDist)/(40*distSq)*strength; + + factor = Math.max(-0.5f, factor); + dX *= factor; + dY *= factor; + dZ *= factor; + + if(fixed || other.fixed) { + dX *= 2; + dY *= 2; + dZ *= 2; + } + + if(!fixed) { + position.x -= dX; + position.y -= dY; + position.z -= dZ; + } + + if(!other.fixed) { + other.position.x += dX; + other.position.y += dY; + other.position.z += dZ; + } + } + + public void resolveStruct(float horzDistMult, boolean opt) { + for(NEUCape.Direction cardinal : cardinals) { + NEUCape.Offset offset = new NEUCape.Offset(cardinal, 1); + CapeNode other = neighbors.get(offset); + if(other != null) { + resolve(other, NEUCape.targetDist*(cardinal.yOff==0?horzDistMult:1), 2f*7.5f, opt); + } + } + } + + public void resolveShear(float horzDistMult, boolean opt) { + for(NEUCape.Direction d : new NEUCape.Direction[]{NEUCape.Direction.DOWNLEFT, NEUCape.Direction.UPLEFT, NEUCape.Direction.DOWNRIGHT, NEUCape.Direction.DOWNLEFT}) { + NEUCape.Offset o = new NEUCape.Offset(d, 1); + CapeNode neighbor = getNeighbor(o); + if(neighbor != null) { + if(!Keyboard.isKeyDown(Keyboard.KEY_H))resolve(neighbor, 1f*NEUCape.targetDist*(d.yOff==0?horzDistMult:1f), 0.5f*7.5f, opt); + } + } + } + + public void resolveBend(float horzDistMult, boolean opt) { + for(NEUCape.Direction cardinal : cardinals) { + NEUCape.Offset offset = new NEUCape.Offset(cardinal, 2); + CapeNode other = neighbors.get(offset); + if(other != null) { + resolve(other, 2f*NEUCape.targetDist*(cardinal.yOff==0?horzDistMult:1), 1f*7.5f, opt); + } + } + } + + public Vector3f normal() { + if(normal != null) return normal; + + normal = new Vector3f(); + for(int i=0; i<cardinals.length; i++) { + NEUCape.Direction dir1 = cardinals[i]; + NEUCape.Direction dir2 = cardinals[(i+1)%cardinals.length]; + CapeNode node1 = getNeighbor(new NEUCape.Offset(dir1, 1)); + CapeNode node2 = getNeighbor(new NEUCape.Offset(dir2, 1)); + + if(node1 == null || node2 == null) continue; + + Vector3f toCapeNode1 = Vector3f.sub(node1.renderPosition, renderPosition, null); + Vector3f toCapeNode2 = Vector3f.sub(node2.renderPosition, renderPosition, null); + Vector3f cross = Vector3f.cross(toCapeNode1, toCapeNode2, null); + Vector3f.add(normal, cross.normalise(null), normal); + } + float l = normal.length(); + if(l != 0) { + normal.scale(1f/l); + } + return normal; + } + + public Vector3f sideNormal() { + if(sideNormal != null) return sideNormal; + + sideNormal = new Vector3f(); + NEUCape.Direction[] cardinals = new NEUCape.Direction[]{NEUCape.Direction.UP, NEUCape.Direction.RIGHT, NEUCape.Direction.DOWN, NEUCape.Direction.LEFT}; + for(NEUCape.Direction cardinal : cardinals) { + CapeNode nodeCardinal = getNeighbor(new NEUCape.Offset(cardinal, 1)); + if(nodeCardinal == null) { + NEUCape.Direction dirLeft = cardinal.rotateLeft90(); + NEUCape.Direction dirRight = cardinal.rotateRight90(); + CapeNode nodeLeft = getNeighbor(new NEUCape.Offset(dirLeft, 1)); + CapeNode nodeRight = getNeighbor(new NEUCape.Offset(dirRight, 1)); + + if(nodeRight != null) { + Vector3f toOther = Vector3f.sub(nodeRight.renderPosition, renderPosition, null); + Vector3f cross = Vector3f.cross(normal(), toOther, null); //Inverted + Vector3f.add(sideNormal, cross.normalise(null), sideNormal); + } + if(nodeLeft != null) { + Vector3f toOther = Vector3f.sub(nodeLeft.renderPosition, renderPosition, null); + Vector3f cross = Vector3f.cross(toOther, normal(), null); + Vector3f.add(sideNormal, cross.normalise(null), sideNormal); + } + } + } + float l = sideNormal.length(); + if(l != 0) { + sideNormal.scale(0.05f/l); + } + return sideNormal; + } + + public void renderNode() { + CapeNode nodeLeft = getNeighbor(new NEUCape.Offset(NEUCape.Direction.LEFT, 1)); + CapeNode nodeUp = getNeighbor(new NEUCape.Offset(NEUCape.Direction.UP, 1)); + CapeNode nodeDown = getNeighbor(new NEUCape.Offset(NEUCape.Direction.DOWN, 1)); + CapeNode nodeRight = getNeighbor(new NEUCape.Offset(NEUCape.Direction.RIGHT, 1)); + CapeNode nodeDownRight = getNeighbor(new NEUCape.Offset(NEUCape.Direction.DOWNRIGHT, 1)); + + Tessellator tessellator = Tessellator.getInstance(); + WorldRenderer worldrenderer = tessellator.getWorldRenderer(); + + if(nodeDown != null && nodeRight != null && nodeDownRight != null) { + //Back + worldrenderer.begin(GL11.GL_TRIANGLE_STRIP, DefaultVertexFormats.POSITION_TEX_NORMAL); + for(CapeNode node : new CapeNode[]{this, nodeDown, nodeRight, nodeDownRight}) { + Vector3f nodeNorm = node.normal(); + worldrenderer.pos(node.renderPosition.x, node.renderPosition.y, node.renderPosition.z) + .tex(1-node.texU, node.texV) + .normal(-nodeNorm.x, -nodeNorm.y, -nodeNorm.z).endVertex(); + } + tessellator.draw(); + + //Front (Offset by normal) + worldrenderer.begin(GL11.GL_TRIANGLE_STRIP, DefaultVertexFormats.POSITION_TEX_NORMAL); + for(CapeNode node : new CapeNode[]{this, nodeDown, nodeRight, nodeDownRight}) { + Vector3f nodeNorm = node.normal(); + worldrenderer.pos(node.renderPosition.x+nodeNorm.x*0.05f, node.renderPosition.y+nodeNorm.y*0.05f, node.renderPosition.z+nodeNorm.z*0.05f) + .tex(node.texU, node.texV) + .normal(nodeNorm.x, nodeNorm.y, nodeNorm.z).endVertex(); + } + tessellator.draw(); + + /*for(CapeNode node : new CapeNode[]{this, nodeDown, nodeRight, nodeDownRight}) { + Vector3f nodeNorm = node.normal(); + worldrenderer.begin(GL11.GL_LINES, DefaultVertexFormats.POSITION_TEX_NORMAL); + worldrenderer.pos(node.renderPosition.x+nodeNorm.x*0.05f, node.renderPosition.y+nodeNorm.y*0.05f, node.renderPosition.z+nodeNorm.z*0.05f) + .tex(node.texU, node.texV) + .normal(nodeNorm.x, nodeNorm.y, nodeNorm.z).endVertex(); + worldrenderer.pos(node.renderPosition.x+nodeNorm.x*0.25f, node.renderPosition.y+nodeNorm.y*0.25f, node.renderPosition.z+nodeNorm.z*0.25f) + .tex(node.texU, node.texV) + .normal(nodeNorm.x, nodeNorm.y, nodeNorm.z).endVertex(); + tessellator.draw(); + }*/ + } + + if(nodeLeft == null || nodeRight == null) { + //Render left/right edge + if(nodeDown != null) { + renderEdge(nodeDown, true); + } + } + if(nodeUp == null || nodeDown == null) { + //Render up/down edge + if(nodeRight != null) { + renderEdge(nodeRight, false); + } + } + } + + public void renderEdge(CapeNode other, boolean lr) { + float thisTexU = lr ? this.horzSideTexU : this.vertSideTexU; + float thisTexV = lr ? this.horzSideTexVTop : this.vertSideTexVTop; + float otherTexU = lr ? other.horzSideTexU : other.vertSideTexU; + float otherTexV = lr ? other.horzSideTexVTop : other.vertSideTexVTop; + + Tessellator tessellator = Tessellator.getInstance(); + WorldRenderer worldrenderer = tessellator.getWorldRenderer(); + + Vector3f thisNorm = normal(); + Vector3f otherNorm = other.normal(); + + Vector3f thisSideNorm = sideNormal(); + Vector3f otherSideNorm = other.sideNormal(); + + worldrenderer.begin(GL11.GL_TRIANGLE_STRIP, DefaultVertexFormats.POSITION_TEX_NORMAL); + worldrenderer.pos(this.renderPosition.x, this.renderPosition.y, this.renderPosition.z) + .tex(thisTexU, thisTexV+20/1024f) + .normal(thisSideNorm.x, thisSideNorm.y, thisSideNorm.z).endVertex(); + worldrenderer.pos(other.renderPosition.x, other.renderPosition.y, other.renderPosition.z) + .tex(otherTexU, otherTexV+20/1024f) + .normal(otherSideNorm.x, otherSideNorm.y, otherSideNorm.z).endVertex(); + worldrenderer.pos(this.renderPosition.x+thisNorm.x*0.05f, this.renderPosition.y+thisNorm.y*0.05f, this.renderPosition.z+thisNorm.z*0.05f) + .tex(thisTexU, thisTexV) + .normal(thisSideNorm.x, thisSideNorm.y, thisSideNorm.z).endVertex(); + worldrenderer.pos(other.renderPosition.x+otherNorm.x*0.05f, other.renderPosition.y+otherNorm.y*0.05f, other.renderPosition.z+otherNorm.z*0.05f) + .tex(otherTexU, otherTexV) + .normal(otherSideNorm.x, otherSideNorm.y, otherSideNorm.z).endVertex(); + tessellator.draw(); + } +}
\ No newline at end of file diff --git a/src/main/java/io/github/moulberry/notenoughupdates/cosmetics/NEUCape.java b/src/main/java/io/github/moulberry/notenoughupdates/cosmetics/NEUCape.java new file mode 100644 index 00000000..8ca3a538 --- /dev/null +++ b/src/main/java/io/github/moulberry/notenoughupdates/cosmetics/NEUCape.java @@ -0,0 +1,605 @@ +package io.github.moulberry.notenoughupdates.cosmetics; + +import io.github.moulberry.notenoughupdates.util.TexLoc; +import net.minecraft.block.Block; +import net.minecraft.client.Minecraft; +import net.minecraft.client.renderer.GlStateManager; +import net.minecraft.client.renderer.OpenGlHelper; +import net.minecraft.client.renderer.Tessellator; +import net.minecraft.client.renderer.WorldRenderer; +import net.minecraft.client.renderer.vertex.DefaultVertexFormats; +import net.minecraft.entity.Entity; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.util.BlockPos; +import net.minecraft.util.MathHelper; +import net.minecraft.util.ResourceLocation; +import net.minecraft.world.gen.NoiseGeneratorSimplex; +import net.minecraftforge.client.event.RenderPlayerEvent; +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; +import net.minecraftforge.fml.common.gameevent.TickEvent; +import org.lwjgl.BufferUtils; +import org.lwjgl.Sys; +import org.lwjgl.input.Keyboard; +import org.lwjgl.opengl.*; +import org.lwjgl.util.vector.Vector3f; + +import java.nio.FloatBuffer; +import java.nio.IntBuffer; +import java.security.Key; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + +public class NEUCape { + + public ResourceLocation capeTex = null; + + private List<List<CapeNode>> nodes = null; + + private static double vertOffset = 1.4; + private static double shoulderLength = 0.24; + private static double shoulderWidth = 0.13; + + public static final int HORZ_NODES = 6; + public static final int VERT_NODES = 22; + + public static float targetDist = 1/20f; + + private String shaderName = "cape"; + + public NEUCape(String capeName) { + setCapeTexture(capeName); + } + + public void setCapeTexture(String capeName) { + if(capeTex == null || !capeTex.getResourcePath().equals(capeName+".png")) { + if(capeName.equalsIgnoreCase("fade")) { + shaderName = "fade_cape"; + } else { + shaderName = "cape"; + } + capeTex = new ResourceLocation("notenoughupdates:"+capeName+".png"); + startTime = System.currentTimeMillis(); + } + } + + public void createCapeNodes(EntityPlayer player) { + nodes = new ArrayList<>(); + + float pX = (float)player.posX; + float pY = (float)player.posY; + float pZ = (float)player.posZ; + + float uMinTop = 48/1024f; + float uMaxTop = 246/1024f; + float uMinBottom = 0/1024f; + float uMaxBottom = 293/1024f; + + float vMaxSide = 404/1024f; + float vMaxCenter = 419/1024f; + + for(int i=0; i<VERT_NODES; i++) { + float uMin = uMinTop + (uMinBottom - uMinTop) * i/(float)(VERT_NODES-1); + float uMax = uMaxTop + (uMaxBottom - uMaxTop) * i/(float)(VERT_NODES-1); + + List<CapeNode> row = new ArrayList<>(); + for(int j=0; j<HORZ_NODES; j++) { + float vMin = 0f; + float centerMult = 1-Math.abs(j-(HORZ_NODES-1)/2f)/((HORZ_NODES-1)/2f);//0-(horzCapeNodes) -> 0-1-0 + float vMax = vMaxSide + (vMaxCenter - vMaxSide) * centerMult; + + CapeNode node = new CapeNode(0, 0, 0);//pX-1, pY+2-i*targetDist, pZ+(j-(horzCapeNodes-1)/2f)*targetDist*2 + node.texU = uMin + (uMax - uMin) * j/(float)(HORZ_NODES-1); + node.texV = vMin + (vMax - vMin) * i/(float)(VERT_NODES-1); + + node.horzDistMult = 2f+1f*i/(float)(VERT_NODES-1); + + if(j == 0 || j == HORZ_NODES-1) { + node.horzSideTexU = 406/1024f * i/(float)(VERT_NODES-1); + if(j == 0) { + node.horzSideTexVTop = 1 - 20/1024f; + } else { + node.horzSideTexVTop = 1 - 40/1024f; + } + } + if(i == 0) { + node.vertSideTexU = 198/1024f * j/(float)(HORZ_NODES-1); + node.vertSideTexVTop = 1 - 60/1024f; + } else if(i == VERT_NODES-1) { + node.vertSideTexU = 300/1024f * j/(float)(HORZ_NODES-1); + node.vertSideTexVTop = 1 - 80/1024f; + } + row.add(node); + } + + nodes.add(row); + } + for(int y=0; y<nodes.size(); y++) { + int xSize = nodes.get(y).size(); + for(int x=0; x<xSize; x++) { + CapeNode node = nodes.get(y).get(x); + + for(Direction dir : Direction.values()) { + for(int i=1; i<=2; i++) { + Offset offset = new Offset(dir, i); + + int xNeighbor = x+offset.getXOffset(); + int yNeighbor = y+offset.getYOffset(); + + if(xNeighbor >= 0 && xNeighbor < nodes.get(y).size() + && yNeighbor >= 0 && yNeighbor < nodes.size()) { + CapeNode neighbor = nodes.get(yNeighbor).get(xNeighbor); + node.setNeighbor(offset, neighbor); + } + } + } + } + } + } + + public void ensureCapeNodesCreated(EntityPlayer player) { + if(nodes == null) createCapeNodes(player); + } + + public enum Direction { + LEFT(-1, 0), + UP(0, 1), + RIGHT(1, 0), + DOWN(0, -1), + UPLEFT(-1, 1), + UPRIGHT(1, 1), + DOWNLEFT(-1, -1), + DOWNRIGHT(1, -1); + + int xOff; + int yOff; + + Direction(int xOff, int yOff) { + this.xOff = xOff; + this.yOff = yOff; + } + + public Direction rotateRight90() { + int wantXOff = -yOff; + int wantYOff = xOff; + for(Direction dir : values()) { + if(dir.xOff == wantXOff && dir.yOff == wantYOff) { + return dir; + } + } + return this; + } + + public Direction rotateLeft90() { + int wantXOff = yOff; + int wantYOff = -xOff; + for(Direction dir : values()) { + if(dir.xOff == wantXOff && dir.yOff == wantYOff) { + return dir; + } + } + return this; + } + } + + public static class Offset { + Direction direction; + int steps; + + public Offset(Direction direction, int steps) { + this.direction = direction; + this.steps = steps; + } + + public int getXOffset() { + return direction.xOff*steps; + } + + public int getYOffset() { + return direction.yOff*steps; + } + + public boolean equals(Object obj) { + if(obj instanceof Offset) { + Offset other = (Offset) obj; + return other.direction == direction && other.steps == steps; + } + return false; + } + + @Override + public int hashCode() { + return 13*direction.ordinal() + 7*steps; + } + } + + private void loadShaderUniforms(ShaderManager shaderManager) { + if(shaderName.equalsIgnoreCase("fade_cape")) { + shaderManager.loadData(shaderName, "millis", (int)(System.currentTimeMillis()-startTime)); + } + } + + long lastRender = 0; + public void onRenderPlayer(RenderPlayerEvent.Post e) { + EntityPlayer player = e.entityPlayer; + + ensureCapeNodesCreated(player); + + Entity viewer = Minecraft.getMinecraft().getRenderViewEntity(); + double viewerX = viewer.lastTickPosX + (viewer.posX - viewer.lastTickPosX) * e.partialRenderTick; + double viewerY = viewer.lastTickPosY + (viewer.posY - viewer.lastTickPosY) * e.partialRenderTick; + double viewerZ = viewer.lastTickPosZ + (viewer.posZ - viewer.lastTickPosZ) * e.partialRenderTick; + + GlStateManager.pushMatrix(); + GlStateManager.enableBlend(); + GlStateManager.tryBlendFuncSeparate(GL11.GL_SRC_ALPHA, + GL11.GL_ONE_MINUS_SRC_ALPHA, GL11.GL_ONE, GL11.GL_ZERO); + Minecraft.getMinecraft().getTextureManager().bindTexture(capeTex); + GlStateManager.enableTexture2D(); + GlStateManager.disableCull(); + + GL11.glTranslatef(-(float)viewerX, -(float)viewerY, -(float)viewerZ); + + ShaderManager shaderManager = ShaderManager.getInstance(); + shaderManager.loadShader(shaderName); + loadShaderUniforms(shaderManager); + + if(!Keyboard.isKeyDown(Keyboard.KEY_K)) renderCape(player, e.partialRenderTick); + + GL11.glTranslatef((float)viewerX, (float)viewerY, (float)viewerZ); + + GL20.glUseProgram(0); + + GL11.glEnable(GL11.GL_CULL_FACE); + GlStateManager.enableTexture2D(); + GlStateManager.disableBlend(); + GlStateManager.popMatrix(); + + lastRender = System.currentTimeMillis(); + } + + public void onTick(TickEvent.ClientTickEvent event, EntityPlayer player) { + if(player != null && System.currentTimeMillis() - lastRender < 100) { + ensureCapeNodesCreated(Minecraft.getMinecraft().thePlayer); + + for(int y=0; y<nodes.size(); y++) { + for(int x=0; x<nodes.get(y).size(); x++) { + CapeNode node = nodes.get(y).get(x); + node.lastPosition.x = node.position.x; + node.lastPosition.y = node.position.y; + node.lastPosition.z = node.position.z; + } + } + updateCape(player); + } + } + + private Vector3f updateFixedCapeNodes(EntityPlayer player) { + double pX = player.posX;//player.lastTickPosX + (player.posX - player.lastTickPosX) * partialRenderTick; + double pY = player.posY;//player.lastTickPosY + (player.posY - player.lastTickPosY) * partialRenderTick; + double pZ = player.posZ;//player.lastTickPosZ + (player.posZ - player.lastTickPosZ) * partialRenderTick; + double angle = Math.toRadians(player.renderYawOffset); + + double vertOffset2 = vertOffset + (player.isSneaking() ? -0.22f : 0) + (player.getCurrentArmor(2) != null ? 0.06f : 0); + double shoulderWidth2 = shoulderWidth + (player.getCurrentArmor(2) != null ? 0.08f : 0); + + float xOff = (float)(Math.cos(angle)*shoulderLength); + float zOff = (float)(Math.sin(angle)*shoulderLength); + + float totalDX = 0; + float totalDY = 0; + float totalDZ = 0; + int totalMovements = 0; + + int xSize = nodes.get(0).size(); + for(int i=0; i<xSize; i++) { + float mult = 1 - 2f*i/(xSize-1); //1 -> -1 + float widthMult = 1.25f-(1.414f*i/(xSize-1) - 0.707f)*(1.414f*i/(xSize-1) - 0.707f); + CapeNode node = nodes.get(0).get(i); + float x = (float)pX+(float)(xOff*mult-widthMult*Math.cos(angle+Math.PI/2)*shoulderWidth2); + float y = (float)pY+(float)(vertOffset2); + float z = (float)pZ+(float)(zOff*mult-widthMult*Math.sin(angle+Math.PI/2)*shoulderWidth2); + totalDX += x - node.position.x; + totalDY += y - node.position.y; + totalDZ += z - node.position.z; + totalMovements++; + node.position.x = x; + node.position.y = y; + node.position.z = z; + node.fixed = true; + } + + float avgDX = totalDX/totalMovements; + float avgDY = totalDY/totalMovements; + float avgDZ = totalDZ/totalMovements; + + return new Vector3f(avgDX, avgDY, avgDZ); + } + + private void updateFixedCapeNodesPartial(EntityPlayer player, float partialRenderTick) { + double pX = player.lastTickPosX + (player.posX - player.lastTickPosX) * partialRenderTick; + double pY = player.lastTickPosY + (player.posY - player.lastTickPosY) * partialRenderTick; + double pZ = player.lastTickPosZ + (player.posZ - player.lastTickPosZ) * partialRenderTick; + double angle = Math.toRadians(player.renderYawOffset); + + double vertOffset2 = vertOffset + (player.isSneaking() ? -0.22f : 0) + (player.getCurrentArmor(2) != null ? 0.06f : 0); + double shoulderWidth2 = shoulderWidth + (player.getCurrentArmor(2) != null ? 0.08f : 0); + + float xOff = (float)(Math.cos(angle)*shoulderLength); + float zOff = (float)(Math.sin(angle)*shoulderLength); + + int xSize = nodes.get(0).size(); + for(int i=0; i<xSize; i++) { + float mult = 1 - 2f*i/(xSize-1); //1 -> -1 + float widthMult = 1.25f-(1.414f*i/(xSize-1) - 0.707f)*(1.414f*i/(xSize-1) - 0.707f); + CapeNode node = nodes.get(0).get(i); + node.renderPosition.x = (float)pX+(float)(xOff*mult-widthMult*Math.cos(angle+Math.PI/2)*shoulderWidth2); + node.renderPosition.y = (float)pY+(float)(vertOffset2); + node.renderPosition.z = (float)pZ+(float)(zOff*mult-widthMult*Math.sin(angle+Math.PI/2)*shoulderWidth2); + node.fixed = true; + } + } + + TexLoc tl = new TexLoc(10, 75, Keyboard.KEY_M); + + private double deltaAngleAccum; + private double oldPlayerAngle; + private int crouchTicks = 0; + long startTime = 0; + long updateMillis = 0; + long renderMillis = 0; + private void updateCape(EntityPlayer player) { + Vector3f capeTranslation = updateFixedCapeNodes(player); + + if(System.currentTimeMillis() - lastRender > 100) { + for (int y = 0; y < nodes.size(); y++) { + for (int x = 0; x < nodes.get(y).size(); x++) { + Vector3f.add(nodes.get(y).get(x).position, capeTranslation, nodes.get(y).get(x).position); + nodes.get(y).get(x).lastPosition.set(nodes.get(y).get(x).position); + nodes.get(y).get(x).renderPosition.set(nodes.get(y).get(x).position); + } + } + } else { + double playerAngle = Math.toRadians(player.renderYawOffset); + double deltaAngle = playerAngle - oldPlayerAngle; + if(deltaAngle > Math.PI) { + deltaAngle = 2*Math.PI - deltaAngle; + } + if(deltaAngle < -Math.PI) { + deltaAngle = 2*Math.PI + deltaAngle; + } + deltaAngleAccum *= 0.5f; + deltaAngleAccum += deltaAngle; + + float dX = (float)Math.cos(playerAngle+Math.PI/2f); + float dZ = (float)Math.sin(playerAngle+Math.PI/2f); + + float factor = (float)(deltaAngleAccum*deltaAngleAccum); + + tl.handleKeyboardInput(); + + float capeTransLength = capeTranslation.length(); + + float capeTranslationFactor = 0f; + if(capeTransLength > 0.5f) { + capeTranslationFactor = (capeTransLength-0.5f)/capeTransLength; + } + Vector3f lookDir = new Vector3f(dX, 0, dZ); + Vector3f lookDirNorm = lookDir.normalise(null); + float dot = Vector3f.dot(capeTranslation, lookDirNorm); + if(dot < 0) { //Moving backwards + for(int y=0; y<nodes.size(); y++) { + for(int x=0; x<nodes.get(y).size(); x++) { + CapeNode node = nodes.get(y).get(x); + if(!node.fixed) { + node.position.x += lookDirNorm.x*dot; + node.position.y += lookDirNorm.y*dot; + node.position.z += lookDirNorm.z*dot; + } + } + } + //Apply small backwards force + factor = 0.05f; + } + + if(factor > 0) { + for(int y=0; y<nodes.size(); y++) { + for(int x=0; x<nodes.get(y).size(); x++) { + nodes.get(y).get(x).applyForce(-dX*factor, 0, -dZ*factor); + } + } + } + + if(capeTranslationFactor > 0f) { + float capeDX = capeTranslation.x*capeTranslationFactor; + float capeDY = capeTranslation.y*capeTranslationFactor; + float capeDZ = capeTranslation.z*capeTranslationFactor; + + for(int y=0; y<nodes.size(); y++) { + for(int x=0; x<nodes.get(y).size(); x++) { + CapeNode node = nodes.get(y).get(x); + if(!node.fixed) { + node.position.x += capeDX; + node.position.y += capeDY; + node.position.z += capeDZ; + } + } + } + } + + //Wind + float currTime = (System.currentTimeMillis()-startTime)/1000f; + float windRandom = Math.abs((float)(0.5f*Math.sin(0.22f*currTime)+Math.sin(0.44f*currTime)*Math.sin(0.47f*currTime))); + double windDir = playerAngle+Math.PI/4f*Math.sin(0.2f*currTime); + + float windDX = (float)Math.cos(windDir+Math.PI/2f); + float windDZ = (float)Math.sin(windDir+Math.PI/2f); + for(int y=0; y<nodes.size(); y++) { + for(int x=0; x<nodes.get(y).size(); x++) { + nodes.get(y).get(x).applyForce(-windDX*windRandom*0.01f, 0, -windDZ*windRandom*0.01f); + } + } + + if(player.isSneaking()) { + crouchTicks++; + float mult = 0.5f; + if(crouchTicks < 5) { + mult = 2f; + } + for(int y=0; y<8; y++) { + for(int x=0; x<nodes.get(y).size(); x++) { + nodes.get(y).get(x).applyForce(-dX*mult, 0, -dZ*mult); + } + } + } else { + crouchTicks = 0; + } + + oldPlayerAngle = playerAngle; + + for(int y=0; y<nodes.size(); y++) { + for(int x=0; x<nodes.get(y).size(); x++) { + nodes.get(y).get(x).update(); + } + } + int updates = player == Minecraft.getMinecraft().thePlayer ? 50 : 25; + for(int i=0; i<updates; i++) { + for(int y=0; y<nodes.size(); y++) { + for(int x=0; x<nodes.get(y).size(); x++) { + nodes.get(y).get(x).resolveAll(2+1f*y/nodes.size(), false); + } + } + } + } + } + + private int ssbo = -1; + private void generateSSBO() { + ssbo = GL15.glGenBuffers(); + loadSBBO(); + } + + private void loadSBBO() { + FloatBuffer buff = BufferUtils.createFloatBuffer(CapeNode.FLOAT_NUM*HORZ_NODES*VERT_NODES); + for(int y=0; y<VERT_NODES; y++) { + for(int x=0; x<HORZ_NODES; x++) { + nodes.get(y).get(x).loadIntoBuffer(buff); + } + } + buff.flip(); + + GL15.glBindBuffer(GL43.GL_SHADER_STORAGE_BUFFER, ssbo); + GL15.glBufferData(GL43.GL_SHADER_STORAGE_BUFFER, buff, GL15.GL_DYNAMIC_DRAW); + GL15.glBindBuffer(GL43.GL_SHADER_STORAGE_BUFFER, 0); + } + + private void resolveAllCompute() { + if(ssbo == -1) { + generateSSBO(); + } + loadSBBO(); + + int program = ShaderManager.getInstance().getShader("node"); + + int block_index = GL43.glGetProgramResourceIndex(program, GL43.GL_SHADER_STORAGE_BLOCK, "nodes_buffer"); + int ssbo_binding_point_index = 0; + GL43.glShaderStorageBlockBinding(program, block_index, ssbo_binding_point_index); + int binding_point_index = 0; + GL30.glBindBufferBase(GL43.GL_SHADER_STORAGE_BUFFER, binding_point_index, ssbo); + + GL20.glUseProgram(program); + + for(int i=0; i<30; i++) { + GL43.glDispatchCompute(VERT_NODES, 1, 1); + GL42.glMemoryBarrier(GL43.GL_SHADER_STORAGE_BARRIER_BIT); + } + + GL20.glUseProgram(0); + + FloatBuffer buff = BufferUtils.createFloatBuffer(CapeNode.FLOAT_NUM*HORZ_NODES*VERT_NODES); + + GL15.glBindBuffer(GL43.GL_SHADER_STORAGE_BUFFER, ssbo); + GL15.glGetBufferSubData(GL43.GL_SHADER_STORAGE_BUFFER, 0, buff); + GL15.glBindBuffer(GL43.GL_SHADER_STORAGE_BUFFER, 0); + + for(int y=0; y<VERT_NODES; y++) { + for(int x=0; x<HORZ_NODES; x++) { + nodes.get(y).get(x).readFromBuffer(buff); + } + } + } + + private Vector3f avgFixedRenderPosition() { + Vector3f accum = new Vector3f(); + int numFixed = 0; + for(int y=0; y<nodes.size(); y++) { + for(int x=0; x<nodes.get(y).size(); x++) { + CapeNode node = nodes.get(y).get(x); + if(node.fixed) { + Vector3f.add(accum, node.renderPosition, accum); + numFixed++; + } + } + } + if(numFixed != 0) { + accum.scale(1f/numFixed); + } + return accum; + } + + private void renderCape(EntityPlayer player, float partialRenderTick) { + ensureCapeNodesCreated(player); + if(System.currentTimeMillis() - lastRender > 100) { + updateCape(player); + } + updateFixedCapeNodesPartial(player, partialRenderTick); + + for(int y=0; y<nodes.size(); y++) { + for(int x=0; x<nodes.get(y).size(); x++) { + CapeNode node = nodes.get(y).get(x); + + node.resetNormal(); + + if(node.fixed) continue; + + Vector3f avgPositionFixed = avgFixedRenderPosition(); + + Vector3f newPosition = new Vector3f(); + newPosition.x = node.lastPosition.x + (node.position.x - node.lastPosition.x) * partialRenderTick; + newPosition.y = node.lastPosition.y + (node.position.y - node.lastPosition.y) * partialRenderTick; + newPosition.z = node.lastPosition.z + (node.position.z - node.lastPosition.z) * partialRenderTick; + + if(node.oldRenderPosition[node.oldRenderPosition.length-1] == null || Keyboard.isKeyDown(Keyboard.KEY_B)) { + node.renderPosition = newPosition; + } else { + Vector3f accum = new Vector3f(); + for(int i=0; i<node.oldRenderPosition.length; i++) { + Vector3f.add(accum, node.oldRenderPosition[i], accum); + Vector3f.add(accum, avgPositionFixed, accum); + } + accum.scale(1/(float)node.oldRenderPosition.length); + + float blendFactor = 0.5f+0.3f*y/(float)(nodes.size()-1); //0.5/0.5 -> 0.8/0.2 //0-1 + accum.scale(blendFactor); + newPosition.scale(1-blendFactor); + Vector3f.add(accum, newPosition, accum); + node.renderPosition = accum; + } + + for(int i=node.oldRenderPosition.length-1; i>=0; i--) { + if(i > 0) { + node.oldRenderPosition[i] = node.oldRenderPosition[i-1]; + } else { + node.oldRenderPosition[i] = Vector3f.sub(node.renderPosition, avgPositionFixed, null); + } + } + } + } + for(int y=0; y<nodes.size(); y++) { + for(int x=0; x<nodes.get(y).size(); x++) { + nodes.get(y).get(x).renderNode(); + } + } + } + +} diff --git a/src/main/java/io/github/moulberry/notenoughupdates/cosmetics/ShaderManager.java b/src/main/java/io/github/moulberry/notenoughupdates/cosmetics/ShaderManager.java new file mode 100644 index 00000000..7d74ae22 --- /dev/null +++ b/src/main/java/io/github/moulberry/notenoughupdates/cosmetics/ShaderManager.java @@ -0,0 +1,153 @@ +package io.github.moulberry.notenoughupdates.cosmetics; + +import net.minecraft.client.Minecraft; +import net.minecraft.util.ResourceLocation; +import org.lwjgl.opengl.GL20; +import org.lwjgl.opengl.GL43; +import org.lwjgl.util.vector.Vector3f; +import org.lwjgl.util.vector.Vector4f; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.util.HashMap; + +public class ShaderManager { + + private ResourceLocation shaderLocation = new ResourceLocation("notenoughupdates:shaders"); + private HashMap<String, Shader> shaderMap = new HashMap<>(); + + private static final ShaderManager INSTANCE = new ShaderManager(); + + public static ShaderManager getInstance() { + return INSTANCE; + } + + public class Shader { + public final int program; + + public Shader(int program) { + this.program = program; + } + } + + public int getShader(String name) { + if(!shaderMap.containsKey(name)) { + reloadShader(name); + } + return shaderMap.get(name).program; + } + + public int loadShader(String name) { + if(!shaderMap.containsKey(name)) { + reloadShader(name); + } + GL20.glUseProgram(shaderMap.get(name).program); + return shaderMap.get(name).program; + } + + public void loadData(String name, String var, Object value) { + int location = GL20.glGetUniformLocation(shaderMap.get(name).program, var); + + if(value instanceof Integer) { + GL20.glUniform1i(location, (Integer) value); + } else if(value instanceof Float) { + GL20.glUniform1f(location, (Float) value); + } else if(value instanceof Vector3f) { + Vector3f vec = (Vector3f) value; + GL20.glUniform3f(location, vec.x, vec.y, vec.z); + } else if(value instanceof Vector4f) { + Vector4f vec = (Vector4f) value; + GL20.glUniform4f(location, vec.x, vec.y, vec.z, vec.w); + } else { + throw new UnsupportedOperationException("Failed to load data into shader: Unsupported data type."); + } + } + + private void reloadShader(String name) { + int vertex = -1; + String sourceVert = getShaderSource(name, GL20.GL_VERTEX_SHADER); + if(!sourceVert.isEmpty()) { + vertex = GL20.glCreateShader(GL20.GL_VERTEX_SHADER); + GL20.glShaderSource(vertex, sourceVert); + GL20.glCompileShader(vertex); + + if (GL20.glGetShaderi(vertex, 35713) == 0) { + System.err.println(GL20.glGetShaderInfoLog(vertex, 100)); + } + } + + int fragment = -1; + String sourceFrag = getShaderSource(name, GL20.GL_FRAGMENT_SHADER); + if(!sourceFrag.isEmpty()) { + fragment = GL20.glCreateShader(GL20.GL_FRAGMENT_SHADER); + GL20.glShaderSource(fragment, sourceFrag); + GL20.glCompileShader(fragment); + + if (GL20.glGetShaderi(fragment, 35713) == 0) { + System.err.println(GL20.glGetShaderInfoLog(fragment, 100)); + } + } + + int compute = -1; + String sourceCompute = getShaderSource(name, GL43.GL_COMPUTE_SHADER); + if(!sourceCompute.isEmpty()) { + compute = GL20.glCreateShader(GL43.GL_COMPUTE_SHADER); + GL20.glShaderSource(compute, sourceCompute); + GL20.glCompileShader(compute); + + if (GL20.glGetShaderi(compute, 35713) == 0) { + System.err.println(GL20.glGetShaderInfoLog(compute, 100)); + } + } + + int program = GL20.glCreateProgram(); + if(vertex != -1) GL20.glAttachShader(program, vertex); + if(fragment != -1) GL20.glAttachShader(program, fragment); + if(compute != -1) GL20.glAttachShader(program, compute); + + GL20.glLinkProgram(program); + + if(vertex != -1) GL20.glDeleteShader(vertex); + if(fragment != -1) GL20.glDeleteShader(fragment); + if(compute != -1) GL20.glDeleteShader(compute); + + if (GL20.glGetProgrami(program, 35714) == 0) { + System.err.println(GL20.glGetProgramInfoLog(program, 100)); + } + GL20.glValidateProgram(program); + if (GL20.glGetProgrami(program, 35715) == 0) { + System.err.println(GL20.glGetProgramInfoLog(program, 100)); + } + + shaderMap.put(name, new Shader(program)); + } + + public String getShaderSource(String name, int type) { + String ext = ""; + if(type == GL20.GL_VERTEX_SHADER) { + ext = ".vert"; + } else if(type == GL20.GL_FRAGMENT_SHADER) { + ext = ".frag"; + } else if(type == GL43.GL_COMPUTE_SHADER) { + ext = ".compute"; + } else { + return ""; + } + ResourceLocation location = new ResourceLocation(shaderLocation.getResourceDomain(), + shaderLocation.getResourcePath()+"/"+name+ext); + try(InputStream is = Minecraft.getMinecraft().getResourceManager().getResource(location).getInputStream()) { + StringBuilder source = new StringBuilder(); + BufferedReader br = new BufferedReader(new InputStreamReader(is)); + + String line; + while((line = br.readLine()) != null) { + source.append(line).append("\n"); + } + return source.toString(); + } catch(IOException e) { + } + return ""; + } +} diff --git a/src/main/java/io/github/moulberry/notenoughupdates/infopanes/CollectionLogInfoPane.java b/src/main/java/io/github/moulberry/notenoughupdates/infopanes/CollectionLogInfoPane.java index 8d53a152..38edc0f9 100644 --- a/src/main/java/io/github/moulberry/notenoughupdates/infopanes/CollectionLogInfoPane.java +++ b/src/main/java/io/github/moulberry/notenoughupdates/infopanes/CollectionLogInfoPane.java @@ -392,10 +392,12 @@ public class CollectionLogInfoPane extends ScrollableInfoPane { String[] items = getItemList(); iterateItemSlots(new ItemSlotConsumer() { public void consume(int x, int y, int id) { - String internalname = items[id]; + if(id < items.length) { + String internalname = items[id]; - ItemStack stack = manager.jsonToStack(manager.getItemInformation().get(internalname)); - Utils.drawItemStack(stack, x, y); + ItemStack stack = manager.jsonToStack(manager.getItemInformation().get(internalname)); + Utils.drawItemStack(stack, x, y); + } } }, left, right, top, bottom); } diff --git a/src/main/java/io/github/moulberry/notenoughupdates/infopanes/CosmeticsInfoPane.java b/src/main/java/io/github/moulberry/notenoughupdates/infopanes/CosmeticsInfoPane.java new file mode 100644 index 00000000..e972e2c8 --- /dev/null +++ b/src/main/java/io/github/moulberry/notenoughupdates/infopanes/CosmeticsInfoPane.java @@ -0,0 +1,87 @@ +package io.github.moulberry.notenoughupdates.infopanes; + +import io.github.moulberry.notenoughupdates.cosmetics.CapeManager; +import io.github.moulberry.notenoughupdates.NEUManager; +import io.github.moulberry.notenoughupdates.NEUOverlay; +import io.github.moulberry.notenoughupdates.util.Utils; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.FontRenderer; +import net.minecraft.client.gui.ScaledResolution; +import net.minecraft.client.renderer.GlStateManager; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.util.ResourceLocation; + +import java.awt.*; +import java.util.HashMap; + +public class CosmeticsInfoPane extends InfoPane { + + public CosmeticsInfoPane(NEUOverlay overlay, NEUManager manager) { + super(overlay, manager); + } + + private HashMap<String, ResourceLocation> capeTextures = new HashMap<>(); + + private String selectedCape = null; + + public void render(int width, int height, Color bg, Color fg, ScaledResolution scaledresolution, int mouseX, + int mouseY) { + super.renderDefaultBackground(width, height, bg); + + FontRenderer fr = Minecraft.getMinecraft().fontRendererObj; + int currentY = overlay.getBoxPadding()+10; + fr.drawString("NEU Capes", overlay.getBoxPadding()+10, currentY, Color.WHITE.getRGB(), true); currentY += 10; + + selectedCape = null; + for(String cape : CapeManager.getInstance().getCapes()) { + if(CapeManager.getInstance().getPermissionForCape(Minecraft.getMinecraft().thePlayer.getName(), cape)) { + currentY += renderCapeSelector(cape, currentY, mouseX, mouseY); + currentY += 5; + } + } + } + + public int renderCapeSelector(String capename, int y, int mouseX, int mouseY) { + if(mouseX > overlay.getBoxPadding()+5 && mouseX < overlay.getBoxPadding()+75) { + if(mouseY > y && mouseY < y+100) { + selectedCape = capename; + } + } + boolean selected = capename.equals(CapeManager.getInstance().getCape(Minecraft.getMinecraft().thePlayer.getName())); + + if(selected) { + drawRect(overlay.getBoxPadding()+5, y, overlay.getBoxPadding()+75, y+100, Color.YELLOW.getRGB()); + drawGradientRect(overlay.getBoxPadding()+10, y+5, overlay.getBoxPadding()+70, y+95, Color.GRAY.darker().getRGB(), Color.GRAY.getRGB()); + } else { + drawGradientRect(overlay.getBoxPadding()+5, y, overlay.getBoxPadding()+75, y+100, Color.GRAY.darker().getRGB(), Color.GRAY.getRGB()); + } + + GlStateManager.color(1, 1, 1, 1); + + ResourceLocation capeTex = capeTextures.computeIfAbsent(capename, k -> new ResourceLocation("notenoughupdates:"+capename+".png")); + + Minecraft.getMinecraft().getTextureManager().bindTexture(capeTex); + Utils.drawTexturedRect(overlay.getBoxPadding()+10, y+10, 60, 80, 0, 300/1024f, 0, 425/1024f); + return 100; + } + + public void mouseInput(int width, int height, int mouseX, int mouseY, boolean mouseDown) { + if(mouseDown && selectedCape != null) { + if(selectedCape.equals(CapeManager.getInstance().getCape(Minecraft.getMinecraft().thePlayer.getName()))) { + for(EntityPlayer player : Minecraft.getMinecraft().theWorld.playerEntities) { + CapeManager.getInstance().setCape(player.getName(), null); + } + } else { + for(EntityPlayer player : Minecraft.getMinecraft().theWorld.playerEntities) { + CapeManager.getInstance().setCape(player.getName(), selectedCape); + } + } + } + } + + @Override + public boolean keyboardInput() { + return false; + } + +} diff --git a/src/main/java/io/github/moulberry/notenoughupdates/util/HypixelApi.java b/src/main/java/io/github/moulberry/notenoughupdates/util/HypixelApi.java index 84d970d5..c271d63a 100644 --- a/src/main/java/io/github/moulberry/notenoughupdates/util/HypixelApi.java +++ b/src/main/java/io/github/moulberry/notenoughupdates/util/HypixelApi.java @@ -9,6 +9,7 @@ import java.io.InputStream; import java.io.InputStreamReader; import java.net.URL; import java.net.URLConnection; +import java.nio.charset.StandardCharsets; import java.util.HashMap; import java.util.Map; import java.util.concurrent.ExecutorService; @@ -45,7 +46,7 @@ public class HypixelApi { return null; } - try(BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream()))) { + try(BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream(), StandardCharsets.UTF_8))) { StringBuilder builder = new StringBuilder(); int codePoint; while((codePoint = reader.read()) != -1) { diff --git a/src/main/resources/assets/notenoughupdates/contrib.png b/src/main/resources/assets/notenoughupdates/contrib.png Binary files differnew file mode 100644 index 00000000..51699e6e --- /dev/null +++ b/src/main/resources/assets/notenoughupdates/contrib.png diff --git a/src/main/resources/assets/notenoughupdates/fade.png b/src/main/resources/assets/notenoughupdates/fade.png Binary files differnew file mode 100644 index 00000000..d898ec4d --- /dev/null +++ b/src/main/resources/assets/notenoughupdates/fade.png diff --git a/src/main/resources/assets/notenoughupdates/gravy.png b/src/main/resources/assets/notenoughupdates/gravy.png Binary files differnew file mode 100644 index 00000000..e43ba7d2 --- /dev/null +++ b/src/main/resources/assets/notenoughupdates/gravy.png diff --git a/src/main/resources/assets/notenoughupdates/nullzee.png b/src/main/resources/assets/notenoughupdates/nullzee.png Binary files differnew file mode 100644 index 00000000..5939034b --- /dev/null +++ b/src/main/resources/assets/notenoughupdates/nullzee.png diff --git a/src/main/resources/assets/notenoughupdates/shaders/cape.frag b/src/main/resources/assets/notenoughupdates/shaders/cape.frag new file mode 100644 index 00000000..cbe00c70 --- /dev/null +++ b/src/main/resources/assets/notenoughupdates/shaders/cape.frag @@ -0,0 +1,16 @@ +#version 120 + +varying vec4 passColour; +varying vec3 passNormal; +uniform sampler2D textureIn; + +void main() { + vec4 texture = texture2D(textureIn, gl_TexCoord[0].st); + gl_FragColor = texture * passColour; + + vec3 fakeSunNormal = normalize(vec3(0.2f,1f,-0.2f)); + vec3 normNormal = normalize(passNormal); + float shading = max(0.6f, dot(fakeSunNormal, normNormal)); + + gl_FragColor = vec4(gl_FragColor.rgb*shading, gl_FragColor.a);//gl_FragColor.rgb*shading +}
\ No newline at end of file diff --git a/src/main/resources/assets/notenoughupdates/shaders/cape.vert b/src/main/resources/assets/notenoughupdates/shaders/cape.vert new file mode 100644 index 00000000..2b5c48f8 --- /dev/null +++ b/src/main/resources/assets/notenoughupdates/shaders/cape.vert @@ -0,0 +1,12 @@ +#version 120 + +varying vec4 passColour; +varying vec3 passNormal; + +void main() { + gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex; + gl_TexCoord[0] = gl_MultiTexCoord0; + + passColour = gl_Color; + passNormal = normalize(gl_Normal); +}
\ No newline at end of file diff --git a/src/main/resources/assets/notenoughupdates/shaders/fade_cape.frag b/src/main/resources/assets/notenoughupdates/shaders/fade_cape.frag new file mode 100644 index 00000000..76738c31 --- /dev/null +++ b/src/main/resources/assets/notenoughupdates/shaders/fade_cape.frag @@ -0,0 +1,31 @@ +#version 120 + +varying vec4 passColour; +varying vec3 passNormal; +uniform sampler2D textureIn; + +uniform int millis; + +//Algorithm by hughsk +vec3 hsv2rgb(vec3 c) { + vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0); + vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www); + return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y); +} + +void main() { + vec4 texture = texture2D(textureIn, gl_TexCoord[0].st); + + float hue = mod(millis/10000f+gl_TexCoord[0].t, 1f); + float sat = 0.5f; + float val = 0.5f; + vec3 fade = hsv2rgb(vec3(hue, sat, val)); + + gl_FragColor = vec4(texture.rgb*texture.a + fade*(1-texture.a), 1f) * passColour; + + vec3 fakeSunNormal = normalize(vec3(0.2f,1f,-0.2f)); + vec3 normNormal = normalize(passNormal); + float shading = max(0.6f, dot(fakeSunNormal, normNormal)); + + gl_FragColor = vec4(gl_FragColor.rgb*shading, gl_FragColor.a); +}
\ No newline at end of file diff --git a/src/main/resources/assets/notenoughupdates/shaders/fade_cape.vert b/src/main/resources/assets/notenoughupdates/shaders/fade_cape.vert new file mode 100644 index 00000000..2b5c48f8 --- /dev/null +++ b/src/main/resources/assets/notenoughupdates/shaders/fade_cape.vert @@ -0,0 +1,12 @@ +#version 120 + +varying vec4 passColour; +varying vec3 passNormal; + +void main() { + gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex; + gl_TexCoord[0] = gl_MultiTexCoord0; + + passColour = gl_Color; + passNormal = normalize(gl_Normal); +}
\ No newline at end of file diff --git a/src/main/resources/assets/notenoughupdates/shaders/node.compute b/src/main/resources/assets/notenoughupdates/shaders/node.compute new file mode 100644 index 00000000..df60dfdc --- /dev/null +++ b/src/main/resources/assets/notenoughupdates/shaders/node.compute @@ -0,0 +1,39 @@ +#version 430 compatibility +#extension GL_ARB_compute_shader : enable +#extension GL_ARB_shader_storage_buffer_object : enable + +#define HORZ_NODES 6 +#define VERT_NODES 22 + +#define INDEX gl_GlobalInvocationID.x + +#define TARGET_DIST 0.05f + +layout(local_size_x = HORZ_NODES, local_size_y = 1, local_size_z = 1) in; + +struct Node { + vec3 position; + float numInfluences; + vec2 influences[8]; +}; + +layout(std430, binding=0) buffer nodes_buffer { + Node nodes[HORZ_NODES*VERT_NODES]; +}; + + +void resolve(vec2 influence, float strength) { + vec3 dist = nodes[INDEX+int(influence.x)].position - nodes[INDEX].position; + float l = length(dist); + float factor = strength*(l - influence.y)/(2f*l); + + if(INDEX >= HORZ_NODES) nodes[INDEX].position = nodes[INDEX].position + dist * factor; +} + +void main() { + float kPrime = 1.0 - pow(1-0.9f, 1.0/30f); //K = 0.9f + kPrime = 0.5f; + + int influences = int(nodes[INDEX].numInfluences); + for(int i=0; i<influences; i++) resolve(nodes[INDEX].influences[i], kPrime); +}
\ No newline at end of file diff --git a/src/main/resources/assets/notenoughupdates/testcape.png b/src/main/resources/assets/notenoughupdates/testcape.png Binary files differnew file mode 100644 index 00000000..4b8ba499 --- /dev/null +++ b/src/main/resources/assets/notenoughupdates/testcape.png |