/*
* Copyright (C) 2022 NotEnoughUpdates contributors
*
* This file is part of NotEnoughUpdates.
*
* NotEnoughUpdates is free software: you can redistribute it
* and/or modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation, either
* version 3 of the License, or (at your option) any later version.
*
* NotEnoughUpdates is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with NotEnoughUpdates. If not, see .
*/
package io.github.moulberry.notenoughupdates.cosmetics;
import io.github.moulberry.notenoughupdates.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.client.renderer.vertex.VertexFormat;
import net.minecraft.client.renderer.vertex.VertexFormatElement;
import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.potion.Potion;
import net.minecraft.util.MathHelper;
import net.minecraft.util.ResourceLocation;
import net.minecraftforge.client.event.RenderPlayerEvent;
import org.lwjgl.BufferUtils;
import org.lwjgl.input.Keyboard;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL15;
import org.lwjgl.opengl.GL20;
import org.lwjgl.opengl.GL30;
import org.lwjgl.opengl.GL42;
import org.lwjgl.opengl.GL43;
import org.lwjgl.util.vector.Vector2f;
import org.lwjgl.util.vector.Vector3f;
import java.nio.ByteBuffer;
import java.nio.FloatBuffer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Random;
import java.util.TreeMap;
public class NEUCape {
private int currentFrame = 0;
private int displayFrame = 0;
private String capeName;
public ResourceLocation[] capeTextures = null;
private long lastFrameUpdate = 0;
private static final int ANIM_MODE_LOOP = 0;
private static final int ANIM_MODE_PINGPONG = 1;
private final int animMode = ANIM_MODE_LOOP;
private CapeNode[] nodes = null;
private final Random random = new Random();
private long eventMillis;
private float dvdPositionX = 100;
private float dvdPositionY = 100;
private float dvdVelocityX = -10;
private float dvdVelocityY = 10;
private float eventLength;
private float eventRandom;
private static final double vertOffset = 1.4;
private static final double shoulderLength = 0.24;
private static final 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 EntityPlayer currentPlayer;
private boolean keepCurrentPlayer = false;
private String shaderName = "cape";
public NEUCape(String capeName) {
setCapeTexture(capeName);
}
public void setCapeTexture(String capeName) {
if (this.capeName != null && this.capeName.equalsIgnoreCase(capeName)) return;
startTime = System.currentTimeMillis();
boolean defaultBehaviour = true;
if (NotEnoughUpdates.INSTANCE.config.hidden.disableBrokenCapes) {
if (capeName.equals("negative")) {
defaultBehaviour = false;
this.capeName = "fade";
this.shaderName = "fade_cape";
}
}
if (defaultBehaviour) {
this.capeName = capeName;
if (capeName.equalsIgnoreCase("fade")) {
shaderName = "fade_cape";
} else if (capeName.equalsIgnoreCase("space")) {
shaderName = "space_cape";
} else if (capeName.equalsIgnoreCase("mcworld")) {
shaderName = "mcworld_cape";
} else if (capeName.equalsIgnoreCase("lava") || capeName.equalsIgnoreCase("skyclient")) {
shaderName = "lava_cape";
} else if (capeName.equalsIgnoreCase("lightning")) {
shaderName = "lightning_cape";
} else if (capeName.equalsIgnoreCase("thebakery")) {
shaderName = "biscuit_cape";
} else if (capeName.equalsIgnoreCase("negative")) {
shaderName = "negative";
} else if (capeName.equalsIgnoreCase("void")) {
shaderName = "void";
} else if (capeName.equalsIgnoreCase("tunnel")) {
shaderName = "tunnel";
} else if (capeName.equalsIgnoreCase("planets")) {
shaderName = "planets";
} else if (capeName.equalsIgnoreCase("screensaver")) {
shaderName = "screensaver";
} else {
shaderName = "shiny_cape";
}
}
ResourceLocation staticCapeTex = new ResourceLocation("notenoughupdates:capes/" + capeName + ".png");
capeTextures = new ResourceLocation[1];
capeTextures[0] = staticCapeTex;
}
private void bindTexture() {
if (capeName.equalsIgnoreCase("negative")) {
CapeManager.getInstance().updateWorldFramebuffer = true;
if (CapeManager.getInstance().backgroundFramebuffer != null) {
CapeManager.getInstance().backgroundFramebuffer.bindFramebufferTexture();
}
} else if (capeTextures != null && capeTextures.length > 0) {
long currentTime = System.currentTimeMillis();
if (currentTime - lastFrameUpdate > 100) {
lastFrameUpdate = currentTime / 100 * 100;
currentFrame++;
if (animMode == ANIM_MODE_PINGPONG) {
if (capeTextures.length == 1) {
currentFrame = displayFrame = 0;
} else {
int frameCount = 2 * capeTextures.length - 2;
currentFrame %= frameCount;
displayFrame = currentFrame;
if (currentFrame >= capeTextures.length) {
displayFrame = frameCount - displayFrame;
}
}
} else if (animMode == ANIM_MODE_LOOP) {
currentFrame %= capeTextures.length;
displayFrame = currentFrame;
}
}
Minecraft.getMinecraft().getTextureManager().bindTexture(capeTextures[displayFrame]);
}
}
private CapeNode getNode(int x, int y) {
return nodes[x + y * HORZ_NODES];
}
public void createCapeNodes(EntityPlayer player) {
nodes = new CapeNode[HORZ_NODES * VERT_NODES];
float pX = (float) player.posX % 7789;
float pY = (float) player.posY;
float pZ = (float) player.posZ % 7789;
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);
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(pX, pY, pZ);//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;
}
nodes[j + i * HORZ_NODES] = node;
}
}
for (int y = 0; y < VERT_NODES; y++) {
for (int x = 0; x < HORZ_NODES; x++) {
CapeNode node = nodes[x + y * HORZ_NODES];
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 < HORZ_NODES
&& yNeighbor >= 0 && yNeighbor < VERT_NODES) {
CapeNode neighbor = nodes[xNeighbor + yNeighbor * HORZ_NODES];
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) {
String shaderId = "capes/" + shaderName + "/" + shaderName;
if (shaderName.equalsIgnoreCase("fade_cape") || shaderName.equalsIgnoreCase("planets")) {
shaderManager.loadData(shaderId, "millis", (int) (System.currentTimeMillis() - startTime));
} else if (shaderName.equalsIgnoreCase("space_cape")) {
shaderManager.loadData(shaderId, "millis", (int) (System.currentTimeMillis() - startTime));
shaderManager.loadData(shaderId, "eventMillis", (int) (System.currentTimeMillis() - eventMillis));
shaderManager.loadData(shaderId, "eventRand", eventRandom);
} else if (shaderName.equalsIgnoreCase("mcworld_cape")) {
shaderManager.loadData(shaderId, "millis", (int) (System.currentTimeMillis() - startTime));
} else if (shaderName.equalsIgnoreCase("lava_cape")) {
shaderManager.loadData(shaderId, "millis", (int) (System.currentTimeMillis() - startTime));
} else if (shaderName.equalsIgnoreCase("tunnel")) {
shaderManager.loadData(shaderId, "millis", (int) (System.currentTimeMillis() - startTime));
} else if (shaderName.equalsIgnoreCase("biscuit_cape") || shaderName.equalsIgnoreCase("shiny_cape")) {
shaderManager.loadData(shaderId, "millis", (int) (System.currentTimeMillis() - startTime));
shaderManager.loadData(shaderId, "eventMillis", (int) (System.currentTimeMillis() - eventMillis));
} else if (shaderName.equalsIgnoreCase("negative")) {
shaderManager.loadData(shaderId, "screensize", new Vector2f(
Minecraft.getMinecraft().displayWidth,
Minecraft.getMinecraft().displayHeight
));
} else if (shaderName.equalsIgnoreCase("void")) {
shaderManager.loadData(shaderId, "millis", (int) (System.currentTimeMillis() - startTime));
shaderManager.loadData(shaderId, "screensize", new Vector2f(
Minecraft.getMinecraft().displayWidth,
Minecraft.getMinecraft().displayHeight
));
} else if (shaderName.equalsIgnoreCase("screensaver")) {
shaderManager.loadData(shaderId, "something", (int) ((System.currentTimeMillis() / 4) % 256));
shaderManager.loadData(shaderId, "dvdPosition", new Vector2f(dvdPositionX, dvdPositionY));
}
}
long lastRender = 0;
public void onRenderPlayer(RenderPlayerEvent.Post e) {
EntityPlayer player = e.entityPlayer;
if (currentPlayer != null && keepCurrentPlayer && currentPlayer != player) return;
if (player.getActivePotionEffect(Potion.invisibility) != null) return;
if (player.isSpectator() || player.isInvisible()) return;
ensureCapeNodesCreated(player);
Entity viewer = Minecraft.getMinecraft().getRenderViewEntity();
double viewerX = (viewer.lastTickPosX + (viewer.posX - viewer.lastTickPosX) * e.partialRenderTick) % 7789;
double viewerY = viewer.lastTickPosY + (viewer.posY - viewer.lastTickPosY) * e.partialRenderTick;
double viewerZ = (viewer.lastTickPosZ + (viewer.posZ - viewer.lastTickPosZ) * e.partialRenderTick) % 7789;
GlStateManager.pushMatrix();
GlStateManager.enableBlend();
GlStateManager.tryBlendFuncSeparate(GL11.GL_SRC_ALPHA,
GL11.GL_ONE_MINUS_SRC_ALPHA, GL11.GL_ONE, GL11.GL_ZERO
);
bindTexture();
GlStateManager.enableTexture2D();
GlStateManager.enableDepth();
GlStateManager.disableCull();
GlStateManager.disableLighting();
GlStateManager.color(1, 1, 1, 1);
if (shaderName.equals("mcworld_cape")) {
GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_LINEAR);
GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_LINEAR);
} else {
GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_NEAREST);
GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_NEAREST);
}
GL11.glTranslatef(-(float) viewerX, -(float) viewerY, -(float) viewerZ);
ShaderManager shaderManager = ShaderManager.getInstance();
shaderManager.loadShader("capes/" + shaderName + "/" + shaderName);
loadShaderUniforms(shaderManager);
renderCape(player, e.partialRenderTick);
GL11.glTranslatef((float) viewerX, (float) viewerY, (float) viewerZ);
GL20.glUseProgram(0);
GlStateManager.enableCull();
GlStateManager.enableTexture2D();
GlStateManager.disableBlend();
GlStateManager.enableDepth();
GlStateManager.enableLighting();
GlStateManager.popMatrix();
lastRender = System.currentTimeMillis();
}
public void onTick(EntityPlayer player) {
if (player == null) return;
if (Minecraft.getMinecraft().isGamePaused()) return;
if (System.currentTimeMillis() - lastRender < 500) {
if (currentPlayer == null || !keepCurrentPlayer) {
keepCurrentPlayer = true;
currentPlayer = player;
} else if (currentPlayer != player) {
return;
}
ensureCapeNodesCreated(player);
for (int y = 0; y < VERT_NODES; y++) {
for (int x = 0; x < HORZ_NODES; x++) {
CapeNode node = nodes[x + y * HORZ_NODES];
node.lastPosition.x = node.position.x;
node.lastPosition.y = node.position.y;
node.lastPosition.z = node.position.z;
}
}
updateCape(player);
} else {
keepCurrentPlayer = false;
}
}
private static double interpolateRotation(float a, float b, float amount) {
double f;
for (f = b - a; f < -180.0F; f += 360.0F) {
}
while (f >= 180.0F) {
f -= 360.0F;
}
return a + amount * f;
}
private double getPlayerRenderAngle(EntityPlayer player, float partialRenderTick) {
double angle = interpolateRotation(player.prevRenderYawOffset, player.renderYawOffset, partialRenderTick);
if (player.isRiding() && player.ridingEntity instanceof EntityLivingBase && player.ridingEntity.shouldRiderSit()) {
EntityLivingBase entitylivingbase = (EntityLivingBase) player.ridingEntity;
double head = interpolateRotation(player.prevRotationYawHead, player.rotationYawHead, partialRenderTick);
angle = interpolateRotation(
entitylivingbase.prevRenderYawOffset,
entitylivingbase.renderYawOffset,
partialRenderTick
);
double wrapped = MathHelper.wrapAngleTo180_double(head - angle);
if (wrapped < -85.0F) {
wrapped = -85.0F;
}
if (wrapped >= 85.0F) {
wrapped = 85.0F;
}
angle = head - wrapped;
if (wrapped * wrapped > 2500.0F) {
angle += wrapped * 0.2F;
}
}
return Math.toRadians(angle);
}
private Vector3f updateFixedCapeNodes(EntityPlayer player) {
double pX = player.posX % 7789;//player.lastTickPosX + (player.posX - player.lastTickPosX) * partialRenderTick;
double pY = player.posY;//player.lastTickPosY + (player.posY - player.lastTickPosY) * partialRenderTick;
double pZ = player.posZ % 7789;//player.lastTickPosZ + (player.posZ - player.lastTickPosZ) * partialRenderTick;
double angle = getPlayerRenderAngle(player, 0);
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;
for (int i = 0; i < HORZ_NODES; i++) {
float mult = 1 - 2f * i / (HORZ_NODES - 1); //1 -> -1
float widthMult = 1.25f - (1.414f * i / (HORZ_NODES - 1) - 0.707f) * (1.414f * i / (HORZ_NODES - 1) - 0.707f);
CapeNode node = nodes[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) % 7789;
double pY = player.lastTickPosY + (player.posY - player.lastTickPosY) * partialRenderTick;
double pZ = (player.lastTickPosZ + (player.posZ - player.lastTickPosZ) * partialRenderTick) % 7789;
double angle = getPlayerRenderAngle(player, partialRenderTick);
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);
for (int i = 0; i < HORZ_NODES; i++) {
float mult = 1 - 2f * i / (HORZ_NODES - 1); //1 -> -1
float widthMult = 1.25f - (1.414f * i / (HORZ_NODES - 1) - 0.707f) * (1.414f * i / (HORZ_NODES - 1) - 0.707f);
CapeNode node = nodes[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;
}
}
private double deltaAngleAccum;
private double oldPlayerAngle;
private int crouchTicks = 0;
long startTime = 0;
public float deltaYComparedToLine(float x0, float y0, float x1, float y1) {
float m = (y1 - y0) / (x1 - x0);
float b = y0 - m * x0;
float lineAtX = dvdPositionX * m + b;
return dvdPositionY - lineAtX;
}
public float projectOntoLine(float x0, float y0, float x1, float y1, float x) {
float m = (y1 - y0) / (x1 - x0);
float b = y0 - m * x0;
return x * m + b;
}
public void resetNodes() {
nodes = null;
}
private void updateCape(EntityPlayer player) {
Vector3f capeTranslation = updateFixedCapeNodes(player);
if (shaderName.equals("space_cape")) {
long currentTime = System.currentTimeMillis();
if (currentTime - startTime > eventMillis - startTime + eventLength) {
eventMillis = currentTime;
eventLength = random.nextFloat() * 2000 + 4000;
eventRandom = random.nextFloat();
}
} else if (shaderName.equals("biscuit_cape") || shaderName.equals("shiny_cape")) {
long currentTime = System.currentTimeMillis();
if (currentTime - startTime > eventMillis - startTime + eventLength) {
eventMillis = currentTime;
eventLength = random.nextFloat() * 3000 + 3000;
}
} else if (shaderName.equals("screensaver")) {
dvdPositionX += dvdVelocityX;
dvdPositionY += dvdVelocityY;
float diskSizeX = 162 / 2F, diskSizeY = 78 / 2F;
// Left line
if (deltaYComparedToLine(0, 404, 47, 0) < 0) {
dvdVelocityX = 10;
dvdPositionX = projectOntoLine(404, 0, 0, 47, dvdPositionY);
}
// Bottom line
if (deltaYComparedToLine(0, 404 - diskSizeY, 292, 404 - diskSizeY) > 0) {
dvdVelocityY = -10;
dvdPositionY = 404 - diskSizeY;
}
// Top line
if (deltaYComparedToLine(47, 0, 246, 0) < 0) {
dvdVelocityY = 10;
dvdPositionY = 0;
}
// Right line
if (deltaYComparedToLine(246 - diskSizeX, 0, 293 - diskSizeX, 404) < 0) {
dvdVelocityX = -10;
dvdPositionX = projectOntoLine(0, 246 - diskSizeX, 404, 293 - diskSizeX, dvdPositionY);
}
}
double playerAngle = getPlayerRenderAngle(player, 0);
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);
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 < VERT_NODES; y++) {
for (int x = 0; x < HORZ_NODES; x++) {
CapeNode node = nodes[x + y * HORZ_NODES];
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 < VERT_NODES; y++) {
for (int x = 0; x < HORZ_NODES; x++) {
nodes[x + y * HORZ_NODES].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 < VERT_NODES; y++) {
for (int x = 0; x < HORZ_NODES; x++) {
CapeNode node = nodes[x + y * HORZ_NODES];
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 < VERT_NODES; y++) {
for (int x = 0; x < HORZ_NODES; x++) {
nodes[x + y * HORZ_NODES].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 < HORZ_NODES; x++) {
nodes[x + y * HORZ_NODES].applyForce(-dX * mult, 0, -dZ * mult);
}
}
} else {
crouchTicks = 0;
}
Vector3f avgPosition = avgFixedPosition();
for (int y = 0; y < VERT_NODES; y++) {
for (int x = 0; x < HORZ_NODES; x++) {
CapeNode node = nodes[x + y * HORZ_NODES];
Vector3f delta = Vector3f.sub(node.position, avgPosition, null);
if (delta.lengthSquared() > 5 * 5) {
Vector3f norm = delta.normalise(null);
node.position = Vector3f.add(avgPosition, norm, null);
}
}
}
oldPlayerAngle = playerAngle;
for (int y = 0; y < VERT_NODES; y++) {
for (int x = 0; x < HORZ_NODES; x++) {
nodes[x + y * HORZ_NODES].update();
}
}
int updates = 50;
for (int i = 0; i < updates; i++) {
for (int y = 0; y < VERT_NODES; y++) {
for (int x = 0; x < HORZ_NODES; x++) {
nodes[x + y * HORZ_NODES].resolveAll(2 + 1f * y / VERT_NODES, 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[x + y * HORZ_NODES].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[x + y * HORZ_NODES].readFromBuffer(buff);
}
}
}
private Vector3f avgRenderPosition() {
Vector3f accum = new Vector3f();
int num = 0;
for (int y = 0; y < VERT_NODES; y++) {
for (int x = 0; x < HORZ_NODES; x++) {
CapeNode node = nodes[x + y * HORZ_NODES];
Vector3f.add(accum, node.renderPosition, accum);
num++;
}
}
if (num != 0) {
accum.scale(1f / num);
}
return accum;
}
private Vector3f avgNormal() {
Vector3f accum = new Vector3f();
int num = 0;
for (int y = 0; y < VERT_NODES; y++) {
for (int x = 0; x < HORZ_NODES; x++) {
CapeNode node = nodes[x + y * HORZ_NODES];
Vector3f.add(accum, node.normal(), accum);
num++;
}
}
if (num != 0) {
accum.scale(1f / num);
}
return accum;
}
private Vector3f avgFixedRenderPosition() {
Vector3f accum = new Vector3f();
int numFixed = 0;
for (int y = 0; y < VERT_NODES; y++) {
for (int x = 0; x < HORZ_NODES; x++) {
CapeNode node = nodes[x + y * HORZ_NODES];
if (node.fixed) {
Vector3f.add(accum, node.renderPosition, accum);
numFixed++;
}
}
}
if (numFixed != 0) {
accum.scale(1f / numFixed);
}
return accum;
}
private Vector3f avgFixedPosition() {
Vector3f accum = new Vector3f();
int numFixed = 0;
for (int y = 0; y < VERT_NODES; y++) {
for (int x = 0; x < HORZ_NODES; x++) {
CapeNode node = nodes[x + y * HORZ_NODES];
if (node.fixed) {
Vector3f.add(accum, node.position, accum);
numFixed++;
}
}
}
if (numFixed != 0) {
accum.scale(1f / numFixed);
}
return accum;
}
private void renderBackAndDoFrontStencil() {
for (int y = 0; y < VERT_NODES; y++) {
for (int x = 0; x < HORZ_NODES; x++) {
nodes[x + y * HORZ_NODES].renderNode(CapeNode.DRAW_MASK_BACK | CapeNode.DRAW_MASK_SIDES);
}
}
if (!Minecraft.getMinecraft().getFramebuffer().isStencilEnabled())
Minecraft.getMinecraft().getFramebuffer().enableStencil();
GL11.glEnable(GL11.GL_STENCIL_TEST);
GL11.glStencilFunc(GL11.GL_ALWAYS, 1, 0xFF);
GL11.glStencilOp(GL11.GL_ZERO, GL11.GL_ZERO, GL11.GL_REPLACE);
GL11.glStencilMask(0xFF);
GL11.glClear(GL11.GL_STENCIL_BUFFER_BIT);
GlStateManager.enableDepth();
GL11.glColorMask(false, false, false, false);
for (int y = 0; y < VERT_NODES; y++) {
for (int x = 0; x < HORZ_NODES; x++) {
nodes[x + y * HORZ_NODES].renderNode(CapeNode.DRAW_MASK_FRONT);
}
}
GL11.glColorMask(true, true, true, true);
// Only pass stencil test if equal to 1
GL11.glStencilMask(0x00);
GL11.glStencilFunc(GL11.GL_EQUAL, 1, 0xFF);
}
private Vector3f getPoint(Vector3f point, Vector3f... vectors) {
Vector3f res = new Vector3f(point);
for (Vector3f vec : vectors) Vector3f.add(res, vec, res);
return res;
}
private static void renderVBO(WorldRenderer worldRenderer) {
if (worldRenderer != null && worldRenderer.getVertexCount() > 0) {
VertexFormat vertexformat = worldRenderer.getVertexFormat();
int stride = vertexformat.getNextOffset();
ByteBuffer bytebuffer = worldRenderer.getByteBuffer();
List list = vertexformat.getElements();
for (int index = 0; index < list.size(); index++) {
VertexFormatElement vertexformatelement = list.get(index);
vertexformatelement.getUsage().preDraw(vertexformat, index, stride, bytebuffer);
}
GL11.glDrawArrays(worldRenderer.getDrawMode(), 0, worldRenderer.getVertexCount());
for (int index = 0; index < list.size(); index++) {
VertexFormatElement vertexformatelement = list.get(index);
vertexformatelement.getUsage().postDraw(vertexformat, index, stride, bytebuffer);
}
}
}
private static WorldRenderer sphereVBO = null;
private void renderNodes() {
if (capeName.equalsIgnoreCase("planets")) {
renderBackAndDoFrontStencil();
Vector3f pointNorm = avgNormal();
Vector3f capeAvgPos = avgRenderPosition();
pointNorm.scale(0.5f / pointNorm.length());
pointNorm.scale(1 - pointNorm.y / 1.3f);
Vector3f point = Vector3f.sub(capeAvgPos, pointNorm, null);
if (sphereVBO == null || Keyboard.isKeyDown(Keyboard.KEY_K)) {
if (sphereVBO != null) sphereVBO.reset();
int arcSegments = 24;
int rotationSegments = 24;
double arcAngleDelta = Math.PI / (arcSegments - 1);
float xScale = 0.95f;
double diameterUnitArcLen = 0;
double arcAngle = 0;
for (int i = 0; i < arcSegments; i++) {
diameterUnitArcLen += Math.sin(arcAngle);
arcAngle += arcAngleDelta;
}
double arcLength = 2f / diameterUnitArcLen;
List> arcs = new ArrayList<>();
for (int angleI = 0; angleI < rotationSegments; angleI++) {
double angle = Math.PI * 2 * angleI / rotationSegments;
List arc = new ArrayList<>();
Vector3f arcPos = new Vector3f(0, 0, -1);
arc.add(arcPos);
arcAngle = 0;
for (int segmentI = 0; segmentI < arcSegments; segmentI++) {
double deltaZ = Math.sin(arcAngle) * arcLength;
double deltaY = Math.cos(arcAngle) * Math.cos(angle) * arcLength;
double deltaX = Math.cos(arcAngle) * Math.sin(angle) * arcLength * xScale;
arcPos = new Vector3f(arcPos);
arcPos.z += deltaZ;
arcPos.y += deltaY;
arcPos.x += deltaX;
arcPos.normalise();
arc.add(arcPos);
arcAngle += arcAngleDelta;
}
arcs.add(arc);
}
sphereVBO = new WorldRenderer(8 * 4 * rotationSegments * arcSegments);
sphereVBO.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX_NORMAL);
double maxXYRad = 0;
for (int angleI = 0; angleI < rotationSegments; angleI++) {
for (int segmentI = 0; segmentI <= arcSegments; segmentI++) {
List thisArc = arcs.get(angleI);
Vector3f point1 = thisArc.get(segmentI);
double rad = Math.sqrt(point1.x * point1.x + point1.y * point1.y);
maxXYRad = Math.max(maxXYRad, rad);
}
}
for (int angleI = 0; angleI < rotationSegments; angleI++) {
int nextAngleI = angleI + 1;
if (angleI == rotationSegments - 1) {
nextAngleI = 0;
}
float v = 0.5f * (angleI) / (rotationSegments);
float v2 = 0.5f * (angleI + 1) / (rotationSegments);
//if(v2 == 0) v2 = 0.5f;
List thisArc = arcs.get(angleI);
List nextArc = arcs.get(nextAngleI);
for (int segmentI = 1; segmentI <= arcSegments; segmentI++) {
Vector3f point1 = thisArc.get(segmentI);
Vector3f point2 = thisArc.get(segmentI - 1);
Vector3f point3 = nextArc.get(segmentI - 1);
Vector3f point4 = nextArc.get(segmentI);
double u1 = 0.5f * segmentI / arcSegments;
double u2 = 0.5f * (segmentI - 1) / arcSegments;
sphereVBO.pos(point4.x, point4.y, point4.z)
.tex(u1, v2).normal(-point4.x, -point4.y, -point4.z).endVertex();
sphereVBO.pos(point3.x, point3.y, point3.z)
.tex(u2, v2).normal(-point3.x, -point3.y, -point3.z).endVertex();
sphereVBO.pos(point2.x, point2.y, point2.z)
.tex(u2, v).normal(-point2.x, -point2.y, -point2.z).endVertex();
sphereVBO.pos(point1.x, point1.y, point1.z)
.tex(u1, v).normal(-point1.x, -point1.y, -point1.z).endVertex();
}
}
}
String shaderId = "capes/" + shaderName + "/" + shaderName;
double mercuryAngle = Math.PI * 2 * ((System.currentTimeMillis() - startTime) / 10000f % 1);
double mercuryX = Math.sin(mercuryAngle) * 0.3;
double mercuryZ = Math.cos(mercuryAngle) * 0.3;
double earthAngle = Math.PI * 2 * ((System.currentTimeMillis() - startTime) / 30000f % 1);
double earthSlant = Math.PI * 0.1;
double earthX = Math.sin(earthAngle) * Math.cos(earthSlant) * 0.6;
double earthY = Math.sin(earthAngle) * Math.sin(earthSlant) * 0.6;
double earthZ = Math.cos(earthAngle) * Math.cos(earthSlant) * 0.6;
float sunDist = Vector3f.sub(point, capeAvgPos, null).lengthSquared();
float mercuryDist = Vector3f.sub(new Vector3f(point.x + (float) mercuryX, point.y, point.z + (float) mercuryZ),
capeAvgPos, null
).lengthSquared();
float earthDist = Vector3f.sub(new Vector3f(
point.x + (float) earthX,
point.y + (float) earthY,
point.z + (float) earthZ
),
capeAvgPos, null
).lengthSquared();
double jupiterAngle = Math.PI * 2 * ((System.currentTimeMillis() - startTime) / 200000f % 1);
double jupiterSlant = Math.PI * -0.08;
double jupiterX = Math.sin(jupiterAngle) * Math.cos(jupiterSlant) * 1.5;
double jupiterY = Math.sin(jupiterAngle) * Math.sin(jupiterSlant) * 1.5;
double jupiterZ = Math.cos(jupiterAngle) * Math.cos(jupiterSlant) * 1.5;
float jupiterDist = Vector3f.sub(new Vector3f(
point.x + (float) jupiterX,
point.y + (float) jupiterY,
point.z + (float) jupiterZ
),
capeAvgPos, null
).lengthSquared();
double neptuneX = -Math.sin(earthAngle) * Math.cos(earthSlant);
double neptuneY = -Math.sin(earthAngle) * Math.sin(earthSlant);
double neptuneZ = -Math.cos(earthAngle) * Math.cos(earthSlant);
float neptuneDist = Vector3f.sub(new Vector3f(
point.x + (float) neptuneX,
point.y + (float) neptuneY,
point.z + (float) neptuneZ
),
capeAvgPos, null
).lengthSquared();
TreeMap orbitals = new TreeMap<>();
orbitals.put(sunDist, 0);
orbitals.put(earthDist, 1);
orbitals.put(mercuryDist, 2);
double delta = Minecraft.getMinecraft().getRenderViewEntity().getRotationYawHead() % 360;
while (delta < 0) delta += 360;
double jupDelta = (delta + Math.toDegrees(jupiterAngle)) % 360;
while (jupDelta < 0) jupDelta += 360;
if (jupDelta > 250 || jupDelta < 110) orbitals.put(jupiterDist, 3);
double nepDelta = (delta + Math.toDegrees(-earthAngle)) % 360;
while (nepDelta < 0) nepDelta += 360;
if (nepDelta > 250 || nepDelta < 110) orbitals.put(neptuneDist, 4);
GlStateManager.disableDepth();
GlStateManager.enableCull();
for (int planetId : orbitals.descendingMap().values()) {
GlStateManager.pushMatrix();
switch (planetId) {
case 0: {
GlStateManager.translate(point.x, point.y, point.z);
GlStateManager.scale(0.2f, 0.2f, 0.2f);
break;
}
case 1: {
Vector3f sunVec = new Vector3f((float) earthX, (float) earthY, (float) earthZ);
ShaderManager.getInstance().loadData(shaderId, "sunVec", sunVec);
GlStateManager.translate(point.x + earthX, point.y + earthY, point.z + earthZ);
GlStateManager.scale(0.1f, 0.1f, 0.1f);
break;
}
case 2: {
Vector3f sunVec = new Vector3f((float) mercuryX, 0, (float) mercuryZ);
ShaderManager.getInstance().loadData(shaderId, "sunVec", sunVec);
GlStateManager.translate(point.x + mercuryX, point.y, point.z + mercuryZ);
GlStateManager.scale(0.05f, 0.05f, 0.05f);
break;
}
case 3: {
Vector3f sunVec = new Vector3f((float) jupiterX, (float) jupiterY, (float) jupiterZ);
ShaderManager.getInstance().loadData(shaderId, "sunVec", sunVec);
GlStateManager.translate(point.x + jupiterX, point.y + jupiterY, point.z + jupiterZ);
GlStateManager.scale(0.3f, 0.3f, 0.3f);
break;
}
case 4: {
Vector3f sunVec = new Vector3f((float) neptuneX, (float) neptuneY, (float) neptuneZ);
ShaderManager.getInstance().loadData(shaderId, "sunVec", sunVec);
GlStateManager.translate(point.x + neptuneX, point.y + neptuneY, point.z + neptuneZ);
GlStateManager.scale(0.15f, 0.15f, 0.15f);
break;
}
}
ShaderManager.getInstance().loadData(shaderId, "planetType", planetId);
renderVBO(sphereVBO);
GlStateManager.popMatrix();
}
GlStateManager.disableCull();
GlStateManager.enableDepth();
GL11.glDisable(GL11.GL_STENCIL_TEST);
} else if (capeName.equalsIgnoreCase("parallax")) {
renderBackAndDoFrontStencil();
Vector3f pointNorm = avgNormal();
pointNorm.scale(-0.2f / pointNorm.length());
Vector3f negPointNorm = new Vector3f(pointNorm);
negPointNorm.scale(-1);
//pointNorm.scale(1 - pointNorm.y/1.3f);
Vector3f point = Vector3f.add(avgRenderPosition(), pointNorm, null);
Vector3f fixedPoint = Vector3f.add(avgFixedRenderPosition(), pointNorm, null);
Vector3f up = Vector3f.sub(fixedPoint, point, null);
float halfUp = up.length();
Vector3f down = new Vector3f(up);
down.scale(-1);
Vector3f left = Vector3f.cross(up, pointNorm, null);
left.scale(halfUp * 522f / 341f / left.length());
Vector3f right = new Vector3f(left);
right.scale(-1);
Vector3f point1 = getPoint(point, left);
Vector3f point2 = getPoint(point, left, down, down);
Vector3f point3 = getPoint(point, right, down, down);
Vector3f point4 = getPoint(point, right);
Vector3f point2Edge = getPoint(point2, negPointNorm, negPointNorm);
Vector3f point3Edge = getPoint(point3, negPointNorm, negPointNorm);
GlStateManager.disableDepth();
GlStateManager.disableCull();
GlStateManager.color(1, 1, 1, 1);
Tessellator tessellator = Tessellator.getInstance();
WorldRenderer worldrenderer = tessellator.getWorldRenderer();
worldrenderer.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX);
worldrenderer.pos(point1.x, point1.y, point1.z)
.tex(0, 943 / 1024f).endVertex();
worldrenderer.pos(point2.x, point2.y, point2.z)
.tex(280 / 1024f, 943 / 1024f).endVertex();
worldrenderer.pos(point3.x, point3.y, point3.z)
.tex(280 / 1024f, 421 / 1024f).endVertex();
worldrenderer.pos(point4.x, point4.y, point4.z)
.tex(0, 421 / 1024f).endVertex();
tessellator.draw();
worldrenderer.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX);
worldrenderer.pos(point2.x, point2.y, point2.z)
.tex(280 / 1024f, 943 / 1024f).endVertex();
worldrenderer.pos(point2Edge.x, point2Edge.y, point2Edge.z)
.tex(341 / 1024f, 943 / 1024f).endVertex();
worldrenderer.pos(point3Edge.x, point3Edge.y, point3Edge.z)
.tex(341 / 1024f, 421 / 1024f).endVertex();
worldrenderer.pos(point3.x, point3.y, point3.z)
.tex(280 / 1024f, 421 / 1024f).endVertex();
tessellator.draw();
GlStateManager.disableCull();
GlStateManager.enableDepth();
GL11.glDisable(GL11.GL_STENCIL_TEST);
} else if (capeName.equalsIgnoreCase("tunnel")) {
renderBackAndDoFrontStencil();
Vector3f pointNorm = avgNormal();
pointNorm.scale(0.7f / pointNorm.length());
pointNorm.scale(1 - pointNorm.y / 1.3f);
Vector3f point = Vector3f.sub(avgRenderPosition(), pointNorm, null);
List edgeNodes = new ArrayList<>();
List edgeCoords = new ArrayList<>();
//Left edge
for (int y = 0; y < VERT_NODES; y++) {
edgeNodes.add(nodes[y * HORZ_NODES]);
edgeCoords.add(new Vector2f(0, (float) y / (VERT_NODES - 1)));
}
edgeNodes.add(null);
edgeCoords.add(null);
//Bottom edge
int bottomIndex = VERT_NODES - 1;
int botSize = HORZ_NODES;
for (int x = 0; x < botSize; x++) {
edgeNodes.add(getNode(x, bottomIndex));
edgeCoords.add(new Vector2f((float) x / (botSize - 1), 1));
}
edgeNodes.add(null);
edgeCoords.add(null);
//Right edge
for (int y = VERT_NODES - 1; y >= 0; y--) {
edgeNodes.add(getNode(HORZ_NODES - 1, y));
edgeCoords.add(new Vector2f(1, (float) y / VERT_NODES));
}
edgeNodes.add(null);
edgeCoords.add(null);
//Top edge
int topSize = HORZ_NODES;
for (int x = topSize - 1; x >= 0; x--) {
edgeNodes.add(getNode(x, 0));
edgeCoords.add(new Vector2f((float) x / (topSize - 1), 0));
}
GlStateManager.disableDepth();
GlStateManager.enableCull();
CapeNode last = null;
for (int i = 0; i < edgeNodes.size(); i++) {
CapeNode node = edgeNodes.get(i);
if (last != null && node != null) {
Vector2f lastCoord = edgeCoords.get(i - 1);
Vector2f coord = edgeCoords.get(i);
Tessellator tessellator = Tessellator.getInstance();
WorldRenderer worldrenderer = tessellator.getWorldRenderer();
worldrenderer.begin(GL11.GL_TRIANGLES, DefaultVertexFormats.POSITION_TEX_NORMAL);
Vector3f lastNodeNorm = last.normal();
worldrenderer.pos(
last.renderPosition.x + lastNodeNorm.x * 0.05f,
last.renderPosition.y + lastNodeNorm.y * 0.05f,
last.renderPosition.z + lastNodeNorm.z * 0.05f
)
.tex(lastCoord.x * 300f / 1024f, lastCoord.y * 420f / 1024f)
.normal(-lastNodeNorm.x, -lastNodeNorm.y, -lastNodeNorm.z).endVertex();
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(coord.x * 300f / 1024f, coord.y * 420f / 1024f)
.normal(-nodeNorm.x, -nodeNorm.y, -nodeNorm.z).endVertex();
worldrenderer.pos(point.x, point.y, point.z)
.tex(150f / 1024f, 210f / 1024f)
.normal(-pointNorm.x, -pointNorm.y, -pointNorm.z).endVertex();
tessellator.draw();
}
last = node;
}
GlStateManager.disableCull();
GlStateManager.enableDepth();
GL11.glDisable(GL11.GL_STENCIL_TEST);
} else {
for (int y = 0; y < VERT_NODES; y++) {
for (int x = 0; x < HORZ_NODES; x++) {
nodes[x + y * HORZ_NODES].renderNode();
}
}
}
}
private void renderCape(EntityPlayer player, float partialRenderTick) {
ensureCapeNodesCreated(player);
Vector3f avgPositionFixedBefore = avgFixedRenderPosition();
updateFixedCapeNodesPartial(player, partialRenderTick);
Vector3f avgPositionFixed = avgFixedRenderPosition();
Vector3f delta = Vector3f.sub(avgPositionFixed, avgPositionFixedBefore, null);
if (delta.lengthSquared() > 9) {
updateFixedCapeNodes(player);
for (int y = 0; y < VERT_NODES; y++) {
for (int x = 0; x < HORZ_NODES; x++) {
CapeNode node = nodes[x + y * HORZ_NODES];
if (!node.fixed) {
Vector3f.add(node.renderPosition, delta, node.renderPosition);
node.position.set(node.renderPosition);
node.lastPosition.set(node.renderPosition);
} else {
node.lastPosition.set(node.position);
}
}
}
renderNodes();
return;
}
for (int y = 0; y < VERT_NODES; y++) {
for (int x = 0; x < HORZ_NODES; x++) {
CapeNode node = nodes[x + y * HORZ_NODES];
node.resetNormal();
if (node.fixed) continue;
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;
int length = node.oldRenderPosition.length;
int fps = Minecraft.getDebugFPS();
if (fps < 50) {
length = 2;
} else if (fps < 100) {
length = 2 + (int) ((fps - 50) / 50f * 3);
}
if (node.oldRenderPosition[length - 1] == null) {
Arrays.fill(node.oldRenderPosition, Vector3f.sub(newPosition, avgPositionFixed, null));
node.renderPosition = newPosition;
} else {
Vector3f accum = new Vector3f();
for (int i = 0; i < length; i++) {
Vector3f.add(accum, node.oldRenderPosition[i], accum);
Vector3f.add(accum, avgPositionFixed, accum);
}
accum.scale(1 / (float) (length));
float blendFactor = 0.5f + 0.3f * y / (float) (VERT_NODES - 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;
}
if (!Minecraft.getMinecraft().isGamePaused()) {
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);
}
}
}
}
}
renderNodes();
}
}