Shader with dissolve and blue pulse on block previews.

Floor mode.
Block preview sorting based on player distance.
This commit is contained in:
Christian Knaapen
2019-02-06 00:09:11 +01:00
parent ba82449632
commit dfc2b11e94
12 changed files with 555 additions and 27 deletions

View File

@@ -57,5 +57,8 @@ public class BuildConfig {
public static class Visuals {
@Comment({"Show a block preview if you have a block in hand on build mode Normal"})
public boolean alwaysShowBlockPreview = false;
@Comment({"Use fancy shaders while placing blocks"})
public boolean useShaders = true;
}
}

View File

@@ -3,34 +3,136 @@ package nl.requios.effortlessbuilding.buildmode;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.util.EnumFacing;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.RayTraceResult;
import net.minecraft.util.math.Vec3d;
import nl.requios.effortlessbuilding.helper.ReachHelper;
import java.util.ArrayList;
import java.util.List;
import java.util.*;
public class Floor implements IBuildMode {
Dictionary<UUID, Integer> rightClickNrTable = new Hashtable<>();
Dictionary<UUID, BlockPos> firstPosTable = new Hashtable<>();
Dictionary<UUID, EnumFacing> sideHitTable = new Hashtable<>();
Dictionary<UUID, Vec3d> hitVecTable = new Hashtable<>();
@Override
public void initialize(EntityPlayer player) {
rightClickNrTable.put(player.getUniqueID(), 0);
firstPosTable.put(player.getUniqueID(), BlockPos.ORIGIN);
sideHitTable.put(player.getUniqueID(), EnumFacing.UP);
hitVecTable.put(player.getUniqueID(), Vec3d.ZERO);
}
@Override
public List<BlockPos> onRightClick(EntityPlayer player, BlockPos blockPos, EnumFacing sideHit, Vec3d hitVec) {
return new ArrayList<>();
List<BlockPos> list = new ArrayList<>();
int rightClickNr = rightClickNrTable.get(player.getUniqueID());
rightClickNr++;
rightClickNrTable.put(player.getUniqueID(), rightClickNr);
if (rightClickNr == 1) {
//If clicking in air, reset and try again
if (blockPos == null) {
rightClickNrTable.put(player.getUniqueID(), 0);
return list;
}
//First click, remember starting position
firstPosTable.put(player.getUniqueID(), blockPos);
sideHitTable.put(player.getUniqueID(), sideHit);
hitVecTable.put(player.getUniqueID(), hitVec);
//Keep list empty, dont place any blocks yet
} else {
//Second click, place wall
list = findCoordinates(player, blockPos);
rightClickNrTable.put(player.getUniqueID(), 0);
}
return list;
}
@Override
public List<BlockPos> findCoordinates(EntityPlayer player, BlockPos blockPos) {
return new ArrayList<>();
List<BlockPos> list = new ArrayList<>();
int rightClickNr = rightClickNrTable.get(player.getUniqueID());
BlockPos firstPos = firstPosTable.get(player.getUniqueID());
if (rightClickNr == 0) {
if (blockPos != null)
list.add(blockPos);
} else {
Vec3d look = player.getLookVec();
Vec3d start = new Vec3d(player.posX, player.posY + player.getEyeHeight(), player.posZ);
//try on z axis
double y = firstPos.getY();
//then x and z are
double x = (y - start.y) / look.y * look.x + start.x;
double z = (y - start.y) / look.y * look.z + start.z;
Vec3d yBound = new Vec3d(x, y, z);
//distance to player
double yDistSquared = yBound.subtract(start).lengthSquared();
int reach = ReachHelper.getMaxReach(player); //4 times as much as normal placement reach
//check if its not behind the player and its not too close and not too far
boolean yValid = yBound.subtract(start).dotProduct(look) > 0 &&
yDistSquared > 4 && yDistSquared < reach * reach;
//select the one that is closest to the player and is valid
Vec3d selected = null;
if (yValid) selected = yBound;
if (selected == null) return list;
//check if it doesnt go through blocks
RayTraceResult rayTraceResult = player.world.rayTraceBlocks(start, selected, false, false, false);
if (rayTraceResult != null && rayTraceResult.typeOfHit != RayTraceResult.Type.BLOCK) {
//return empty list
return list;
}
BlockPos secondPos = new BlockPos(selected);
//Add whole wall
//Limit amount of blocks you can place per row
int limit = ReachHelper.getMaxBlocksPlacedAtOnce(player);
int x1 = firstPos.getX(), x2 = secondPos.getX();
int y1 = firstPos.getY(), y2 = secondPos.getY();
int z1 = firstPos.getZ(), z2 = secondPos.getZ();
for (int l = x1; x1 < x2 ? l <= x2 : l >= x2; l += x1 < x2 ? 1 : -1) {
for (int n = z1; z1 < z2 ? n <= z2 : n >= z2; n += z1 < z2 ? 1 : -1) {
//check if whole row fits within limit
if (Math.abs(y1 - y2) < limit - list.size()) {
for (int m = y1; y1 < y2 ? m <= y2 : m >= y2; m += y1 < y2 ? 1 : -1) {
list.add(new BlockPos(l, m, n));
}
}
}
}
}
return list;
}
@Override
public EnumFacing getSideHit(EntityPlayer player) {
return null;
return sideHitTable.get(player.getUniqueID());
}
@Override
public Vec3d getHitVec(EntityPlayer player) {
return null;
return hitVecTable.get(player.getUniqueID());
}
}

View File

@@ -5,7 +5,6 @@ import net.minecraft.util.EnumFacing;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.RayTraceResult;
import net.minecraft.util.math.Vec3d;
import nl.requios.effortlessbuilding.EffortlessBuilding;
import nl.requios.effortlessbuilding.helper.ReachHelper;
import java.util.*;

View File

@@ -12,7 +12,7 @@ import nl.requios.effortlessbuilding.EffortlessBuilding;
@SideOnly(Side.CLIENT)
public class RandomizerBagGuiContainer extends GuiContainer {
private static final ResourceLocation guiTextures =
new ResourceLocation(EffortlessBuilding.MODID + ":textures/gui/container/randomizerbag.png");
new ResourceLocation(EffortlessBuilding.MODID, "textures/gui/container/randomizerbag.png");
private final InventoryPlayer inventoryPlayer;
private final IItemHandler inventoryBag;

View File

@@ -1,17 +1,21 @@
package nl.requios.effortlessbuilding.helper;
import net.minecraft.block.Block;
import net.minecraft.block.BlockDirt;
import net.minecraft.block.state.IBlockState;
import net.minecraft.client.Minecraft;
import net.minecraft.client.entity.EntityPlayerSP;
import net.minecraft.client.gui.Gui;
import net.minecraft.client.renderer.*;
import net.minecraft.client.renderer.texture.TextureMap;
import net.minecraft.client.renderer.vertex.DefaultVertexFormats;
import net.minecraft.entity.Entity;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.init.Blocks;
import net.minecraft.item.ItemBlock;
import net.minecraft.item.ItemStack;
import net.minecraft.util.EnumFacing;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.SoundCategory;
import net.minecraft.util.SoundEvent;
import net.minecraft.util.math.AxisAlignedBB;
@@ -35,13 +39,18 @@ import nl.requios.effortlessbuilding.buildmodifier.ModifierSettingsManager;
import nl.requios.effortlessbuilding.buildmodifier.RadialMirror;
import nl.requios.effortlessbuilding.item.ItemRandomizerBag;
import nl.requios.effortlessbuilding.proxy.ClientProxy;
import org.lwjgl.opengl.ARBMultitexture;
import org.lwjgl.opengl.ARBShaderObjects;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL14;
import org.lwjgl.util.Color;
import javax.annotation.Nullable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.function.Consumer;
@Mod.EventBusSubscriber(Side.CLIENT)
public class RenderHelper implements IWorldEventListener {
@@ -56,6 +65,8 @@ public class RenderHelper implements IWorldEventListener {
private static List<BlockPos> previousCoordinates;
private static final int secondaryTextureUnit = 7;
private static void begin(float partialTicks) {
EntityPlayer player = Minecraft.getMinecraft().player;
double playerX = player.prevPosX + (player.posX - player.prevPosX) * partialTicks;
@@ -89,14 +100,17 @@ public class RenderHelper implements IWorldEventListener {
GL11.glPushAttrib(GL11.GL_ENABLE_BIT);
GL11.glEnable(GL11.GL_CULL_FACE);
GL11.glEnable(GL11.GL_TEXTURE_2D);
Minecraft.getMinecraft().renderEngine.bindTexture(new ResourceLocation(EffortlessBuilding.MODID, "textures/shader_color.png"));
Minecraft.getMinecraft().renderEngine.bindTexture(new ResourceLocation(EffortlessBuilding.MODID, "textures/shader_mask.png"));
Minecraft.getMinecraft().renderEngine.bindTexture(TextureMap.LOCATION_BLOCKS_TEXTURE);
GlStateManager.enableBlend();
GlStateManager.blendFunc(GL11.GL_CONSTANT_ALPHA, GL11.GL_ONE_MINUS_CONSTANT_ALPHA);
GlStateManager.blendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
GL14.glBlendColor(1F, 1F, 1F, 0.8f);
}
private static void endBlockPreviews() {
GlStateManager.blendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
ShaderHelper.releaseShader();
GlStateManager.disableBlend();
GL11.glPopAttrib();
}
@@ -241,6 +255,9 @@ public class RenderHelper implements IWorldEventListener {
if (sideHit != null) {
BlockRendererDispatcher dispatcher = Minecraft.getMinecraft().getBlockRendererDispatcher();
float percentile = ClientProxy.ticksInGame / 100f % 1f;
//TODO test
percentile = 1f;
//get coordinates
List<BlockPos> startCoordinates = BuildModes.findCoordinates(player, startPos);
@@ -253,6 +270,13 @@ public class RenderHelper implements IWorldEventListener {
List<BlockPos> newCoordinates = BuildModifiers.findCoordinates(player, startCoordinates);
Collections.sort(newCoordinates, (lhs, rhs) -> {
// -1 - less than, 1 - greater than, 0 - equal
double lhsDistanceToPlayer = new Vec3d(lhs).subtract(player.getPositionEyes(1f)).lengthSquared();
double rhsDistanceToPlayer = new Vec3d(rhs).subtract(player.getPositionEyes(1f)).lengthSquared();
return (int) Math.signum(lhsDistanceToPlayer - rhsDistanceToPlayer);
});
//check if they are different from previous
if (!BuildModifiers.compareCoordinates(previousCoordinates, newCoordinates)) {
previousCoordinates = newCoordinates;
@@ -275,6 +299,8 @@ public class RenderHelper implements IWorldEventListener {
if (!itemstack.isEmpty() && SurvivalHelper.canPlayerEdit(player, player.world, blockPos, itemstack) &&
SurvivalHelper.mayPlace(player.world, Block.getBlockFromItem(itemstack.getItem()), blockState, blockPos, true, EnumFacing.UP, player) &&
SurvivalHelper.canReplace(player.world, player, blockPos)) {
ShaderHelper.useShader(ShaderHelper.psiBar, generateCallback(percentile, new Vec3d(blockPos), i == 0 || i == newCoordinates.size() - 1));
renderBlockPreview(dispatcher, blockPos, blockState);
}
}
@@ -395,6 +421,40 @@ public class RenderHelper implements IWorldEventListener {
tessellator.draw();
}
private static Consumer<Integer> generateCallback(final float percentile, final Vec3d blockpos, final boolean highlight) {
Minecraft mc = Minecraft.getMinecraft();
return (Integer shader) -> {
int percentileUniform = ARBShaderObjects.glGetUniformLocationARB(shader, "percentile");
int highlightUniform = ARBShaderObjects.glGetUniformLocationARB(shader, "highlight");
int blockposUniform = ARBShaderObjects.glGetUniformLocationARB(shader, "blockpos");
int imageUniform = ARBShaderObjects.glGetUniformLocationARB(shader, "image");
int maskUniform = ARBShaderObjects.glGetUniformLocationARB(shader, "mask");
//image
OpenGlHelper.setActiveTexture(ARBMultitexture.GL_TEXTURE0_ARB);
GL11.glBindTexture(GL11.GL_TEXTURE_2D, mc.renderEngine.getTexture(TextureMap.LOCATION_BLOCKS_TEXTURE/*new ResourceLocation(EffortlessBuilding.MODID, "textures/shader_color.png")*/).getGlTextureId());
ARBShaderObjects.glUniform1iARB(imageUniform, 0);
OpenGlHelper.setActiveTexture(ARBMultitexture.GL_TEXTURE0_ARB + secondaryTextureUnit);
GlStateManager.enableTexture2D();
GL11.glGetInteger(GL11.GL_TEXTURE_BINDING_2D);
//mask
GL11.glBindTexture(GL11.GL_TEXTURE_2D,
mc.renderEngine.getTexture(new ResourceLocation(EffortlessBuilding.MODID, "textures/shader_mask.png")).getGlTextureId());
ARBShaderObjects.glUniform1iARB(maskUniform, secondaryTextureUnit);
//blockpos
ARBShaderObjects.glUniform3fARB(blockposUniform, (float) blockpos.x, (float) blockpos.y, (float) blockpos.z);
//percentile
ARBShaderObjects.glUniform1fARB(percentileUniform, percentile);
//highlight
ARBShaderObjects.glUniform1iARB(highlightUniform, highlight ? 1 : 0);
};
}
//IWORLDEVENTLISTENER IMPLEMENTATION
@Override
public void notifyBlockUpdate(World worldIn, BlockPos pos, IBlockState oldState, IBlockState newState, int flags) {

View File

@@ -0,0 +1,195 @@
/**
* This class was created by <Vazkii>. It's distributed as
* part of the Botania Mod. Get the Source Code in github:
* https://github.com/Vazkii/Botania
*
* Modified by Requios
*
* Botania is Open Source and distributed under the
* Botania License: http://botaniamod.net/license.php
*
* File Created @ [Apr 9, 2014, 11:20:26 PM (GMT)]
*/
package nl.requios.effortlessbuilding.helper;
import net.minecraft.client.renderer.OpenGlHelper;
import nl.requios.effortlessbuilding.BuildConfig;
import nl.requios.effortlessbuilding.EffortlessBuilding;
import nl.requios.effortlessbuilding.proxy.ClientProxy;
import org.apache.logging.log4j.Level;
import org.lwjgl.opengl.ARBFragmentShader;
import org.lwjgl.opengl.ARBShaderObjects;
import org.lwjgl.opengl.ARBVertexShader;
import org.lwjgl.opengl.GL11;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.util.function.Consumer;
public final class ShaderHelper {
private static final int VERT_ST = ARBVertexShader.GL_VERTEX_SHADER_ARB;
private static final int FRAG_ST = ARBFragmentShader.GL_FRAGMENT_SHADER_ARB;
private static final int VERT = 1;
private static final int FRAG = 2;
private static final String VERT_EXTENSION = ".vert";
private static final String FRAG_EXTENSION = ".frag";
public static int rawColor;
public static int psiBar;
public static void init() {
if(!doUseShaders())
return;
rawColor = createProgram("/assets/effortlessbuilding/shaders/raw_color", FRAG);
psiBar = createProgram("/assets/effortlessbuilding/shaders/dissolve", FRAG);
}
public static void useShader(int shader, Consumer<Integer> callback) {
if(!doUseShaders())
return;
ARBShaderObjects.glUseProgramObjectARB(shader);
if(shader != 0) {
int time = ARBShaderObjects.glGetUniformLocationARB(shader, "time");
ARBShaderObjects.glUniform1iARB(time, ClientProxy.ticksInGame);
if(callback != null)
callback.accept(shader);
}
}
public static void useShader(int shader) {
useShader(shader, null);
}
public static void releaseShader() {
useShader(0);
}
public static boolean doUseShaders() {
return BuildConfig.visuals.useShaders && OpenGlHelper.shadersSupported;
}
private static int createProgram(String s, int sides) {
boolean vert = (sides & VERT) != 0;
boolean frag = (sides & FRAG) != 0;
return createProgram(vert ? s + VERT_EXTENSION : null, frag ? s + FRAG_EXTENSION : null);
}
// Most of the code taken from the LWJGL wiki
// http://lwjgl.org/wiki/index.php?title=GLSL_Shaders_with_LWJGL
private static int createProgram(String vert, String frag) {
int vertId = 0, fragId = 0, program;
if(vert != null)
vertId = createShader(vert, VERT_ST);
if(frag != null)
fragId = createShader(frag, FRAG_ST);
program = ARBShaderObjects.glCreateProgramObjectARB();
if(program == 0)
return 0;
if(vert != null)
ARBShaderObjects.glAttachObjectARB(program, vertId);
if(frag != null)
ARBShaderObjects.glAttachObjectARB(program, fragId);
ARBShaderObjects.glLinkProgramARB(program);
if(ARBShaderObjects.glGetObjectParameteriARB(program, ARBShaderObjects.GL_OBJECT_LINK_STATUS_ARB) == GL11.GL_FALSE) {
EffortlessBuilding.logger.log(Level.ERROR, getLogInfo(program));
return 0;
}
ARBShaderObjects.glValidateProgramARB(program);
if (ARBShaderObjects.glGetObjectParameteriARB(program, ARBShaderObjects.GL_OBJECT_VALIDATE_STATUS_ARB) == GL11.GL_FALSE) {
EffortlessBuilding.logger.log(Level.ERROR, getLogInfo(program));
return 0;
}
return program;
}
private static int createShader(String filename, int shaderType){
int shader = 0;
try {
shader = ARBShaderObjects.glCreateShaderObjectARB(shaderType);
if(shader == 0)
return 0;
ARBShaderObjects.glShaderSourceARB(shader, readFileAsString(filename));
ARBShaderObjects.glCompileShaderARB(shader);
if (ARBShaderObjects.glGetObjectParameteriARB(shader, ARBShaderObjects.GL_OBJECT_COMPILE_STATUS_ARB) == GL11.GL_FALSE)
throw new RuntimeException("Error creating shader: " + getLogInfo(shader));
return shader;
}
catch(Exception e) {
ARBShaderObjects.glDeleteObjectARB(shader);
e.printStackTrace();
return -1;
}
}
private static String getLogInfo(int obj) {
return ARBShaderObjects.glGetInfoLogARB(obj, ARBShaderObjects.glGetObjectParameteriARB(obj, ARBShaderObjects.GL_OBJECT_INFO_LOG_LENGTH_ARB));
}
private static String readFileAsString(String filename) throws Exception {
StringBuilder source = new StringBuilder();
InputStream in = ShaderHelper.class.getResourceAsStream(filename);
Exception exception = null;
BufferedReader reader;
if(in == null)
return "";
try {
reader = new BufferedReader(new InputStreamReader(in, StandardCharsets.UTF_8));
Exception innerExc= null;
try {
String line;
while((line = reader.readLine()) != null)
source.append(line).append('\n');
} catch(Exception exc) {
exception = exc;
} finally {
try {
reader.close();
} catch(Exception exc) {
innerExc = exc;
}
}
if(innerExc != null)
throw innerExc;
} catch(Exception exc) {
exception = exc;
} finally {
try {
in.close();
} catch(Exception exc) {
if(exception == null)
exception = exc;
else exc.printStackTrace();
}
if(exception != null)
throw exception;
}
return source.toString();
}
}

View File

@@ -6,6 +6,7 @@ import net.minecraft.block.state.IBlockState;
import net.minecraft.client.Minecraft;
import net.minecraft.client.audio.PositionedSoundRecord;
import net.minecraft.client.entity.EntityPlayerSP;
import net.minecraft.client.gui.GuiScreen;
import net.minecraft.client.gui.ScaledResolution;
import net.minecraft.client.renderer.block.model.ModelResourceLocation;
import net.minecraft.client.renderer.texture.TextureMap;
@@ -51,6 +52,7 @@ import nl.requios.effortlessbuilding.gui.buildmode.RadialMenu;
import nl.requios.effortlessbuilding.gui.buildmodifier.ModifierSettingsGui;
import nl.requios.effortlessbuilding.helper.ReachHelper;
import nl.requios.effortlessbuilding.helper.RenderHelper;
import nl.requios.effortlessbuilding.helper.ShaderHelper;
import nl.requios.effortlessbuilding.item.ItemRandomizerBag;
import nl.requios.effortlessbuilding.network.BlockBrokenMessage;
import nl.requios.effortlessbuilding.network.BlockPlacedMessage;
@@ -70,10 +72,13 @@ public class ClientProxy implements IProxy {
public static RayTraceResult currentLookAt;
private static int breakCooldown = 0;
public static int ticksInGame = 0;
private static final HashMap<BuildModes.BuildModeEnum, RadialMenu.SpriteIconPositioning> buildModeIcons = new HashMap<>();
@Override
public void preInit(FMLPreInitializationEvent event) {
ShaderHelper.init();
}
@Override
@@ -142,6 +147,10 @@ public class ClientProxy implements IProxy {
}
}
/**
* From Chisels and Bits by AlgorithmX2
* https://github.com/AlgorithmX2/Chisels-and-Bits/blob/1.12/src/main/java/mod/chiselsandbits/core/ClientSide.java
*/
private static void loadIcon(final TextureMap map, final BuildModes.BuildModeEnum mode) {
final RadialMenu.SpriteIconPositioning sip = new RadialMenu.SpriteIconPositioning();
@@ -284,6 +293,11 @@ public class ClientProxy implements IProxy {
//QuickReplace toggle
if (keyBindings[1].isPressed()) {
//TODO testing
EffortlessBuilding.log(player, "ShaderHelper init");
ShaderHelper.init();
ModifierSettingsManager.ModifierSettings modifierSettings = ModifierSettingsManager.getModifierSettings(player);
modifierSettings.setQuickReplace(!modifierSettings.doQuickReplace());
EffortlessBuilding.log(player, "Set "+ TextFormatting.GOLD + "Quick Replace " + TextFormatting.RESET + (
@@ -304,29 +318,35 @@ public class ClientProxy implements IProxy {
@SubscribeEvent
public static void onClientTick(TickEvent.ClientTickEvent event) {
if (event.phase != TickEvent.Phase.START) return;
if (event.phase == TickEvent.Phase.START) {
RayTraceResult objectMouseOver = Minecraft.getMinecraft().objectMouseOver;
//Checking for null is necessary! Even in vanilla when looking down ladders it is occasionally null (instead of Type MISS)
if (objectMouseOver == null) return;
RayTraceResult objectMouseOver = Minecraft.getMinecraft().objectMouseOver;
//Checking for null is necessary! Even in vanilla when looking down ladders it is occasionally null (instead of Type MISS)
if (objectMouseOver == null) return;
if (currentLookAt == null) {
currentLookAt = objectMouseOver;
previousLookAt = objectMouseOver;
return;
}
if (objectMouseOver.typeOfHit == RayTraceResult.Type.BLOCK) {
if (currentLookAt.typeOfHit != RayTraceResult.Type.BLOCK) {
if (currentLookAt == null) {
currentLookAt = objectMouseOver;
previousLookAt = objectMouseOver;
} else {
if (currentLookAt.getBlockPos() != objectMouseOver.getBlockPos()){
previousLookAt = currentLookAt;
return;
}
if (objectMouseOver.typeOfHit == RayTraceResult.Type.BLOCK) {
if (currentLookAt.typeOfHit != RayTraceResult.Type.BLOCK) {
currentLookAt = objectMouseOver;
previousLookAt = objectMouseOver;
} else {
if (currentLookAt.getBlockPos() != objectMouseOver.getBlockPos()) {
previousLookAt = currentLookAt;
currentLookAt = objectMouseOver;
}
}
}
} else if (event.phase == TickEvent.Phase.END){
GuiScreen gui = Minecraft.getMinecraft().currentScreen;
if(gui == null || !gui.doesGuiPauseGame()) {
ticksInGame++;
}
}
}
@SubscribeEvent