From 7184286be72cf3050f4ec9317f3abd7a6708cfaa Mon Sep 17 00:00:00 2001 From: Nicolas BARBOTIN Date: Tue, 30 Jan 2018 02:11:33 +0100 Subject: [PATCH] * WIP laser pointer --- src/main/java/net/montoyo/wd/WebDisplays.java | 6 +- .../net/montoyo/wd/client/ClientProxy.java | 45 ++++-- .../wd/client/gui/GuiScreenConfig.java | 3 + .../wd/client/renderers/IItemRenderer.java | 13 ++ .../renderers/LaserPointerRenderer.java | 132 ++++++++++++++++++ .../wd/client/renderers/MinePadRenderer.java | 5 +- .../assets/webdisplays/lang/en_us.lang | 1 + .../resources/assets/webdisplays/sounds.json | 9 +- .../webdisplays/sounds/screencfg_open.ogg | Bin 0 -> 5820 bytes 9 files changed, 194 insertions(+), 20 deletions(-) create mode 100644 src/main/java/net/montoyo/wd/client/renderers/IItemRenderer.java create mode 100644 src/main/java/net/montoyo/wd/client/renderers/LaserPointerRenderer.java create mode 100644 src/main/resources/assets/webdisplays/sounds/screencfg_open.ogg diff --git a/src/main/java/net/montoyo/wd/WebDisplays.java b/src/main/java/net/montoyo/wd/WebDisplays.java index bbcf48a..0d2a244 100644 --- a/src/main/java/net/montoyo/wd/WebDisplays.java +++ b/src/main/java/net/montoyo/wd/WebDisplays.java @@ -65,6 +65,7 @@ public class WebDisplays { public SoundEvent soundTyping; public SoundEvent soundUpgradeAdd; public SoundEvent soundUpgradeDel; + public SoundEvent soundScreenCfg; //Config public static final double PAD_RATIO = 59.0 / 30.0; @@ -150,7 +151,10 @@ public class WebDisplays { soundUpgradeDel = new SoundEvent(new ResourceLocation("webdisplays", "upgradeDel")); soundUpgradeDel.setRegistryName(soundUpgradeDel.getSoundName()); - ev.getRegistry().registerAll(soundTyping, soundUpgradeAdd, soundUpgradeDel); + soundScreenCfg = new SoundEvent(new ResourceLocation("webdisplays", "screencfgOpen")); + soundScreenCfg.setRegistryName(soundScreenCfg.getSoundName()); + + ev.getRegistry().registerAll(soundTyping, soundUpgradeAdd, soundUpgradeDel, soundScreenCfg); } @SubscribeEvent diff --git a/src/main/java/net/montoyo/wd/client/ClientProxy.java b/src/main/java/net/montoyo/wd/client/ClientProxy.java index d16fbf5..63aab08 100644 --- a/src/main/java/net/montoyo/wd/client/ClientProxy.java +++ b/src/main/java/net/montoyo/wd/client/ClientProxy.java @@ -20,11 +20,10 @@ import net.minecraft.nbt.NBTTagCompound; import net.minecraft.util.EnumHand; import net.minecraft.util.EnumHandSide; import net.minecraft.util.NonNullList; +import net.minecraft.util.math.RayTraceResult; +import net.minecraft.util.math.Vec3d; import net.minecraft.world.World; -import net.minecraftforge.client.event.ModelBakeEvent; -import net.minecraftforge.client.event.ModelRegistryEvent; -import net.minecraftforge.client.event.RenderSpecificHandEvent; -import net.minecraftforge.client.event.TextureStitchEvent; +import net.minecraftforge.client.event.*; import net.minecraftforge.client.model.ModelLoader; import net.minecraftforge.common.MinecraftForge; import net.minecraftforge.fml.client.registry.ClientRegistry; @@ -40,15 +39,13 @@ import net.montoyo.wd.client.gui.GuiScreenConfig; import net.montoyo.wd.client.gui.GuiSetURL2; import net.montoyo.wd.client.gui.WDScreen; import net.montoyo.wd.client.gui.loading.GuiLoader; -import net.montoyo.wd.client.renderers.IModelBaker; -import net.montoyo.wd.client.renderers.MinePadRenderer; -import net.montoyo.wd.client.renderers.ScreenBaker; -import net.montoyo.wd.client.renderers.ScreenRenderer; +import net.montoyo.wd.client.renderers.*; import net.montoyo.wd.core.DefaultUpgrade; import net.montoyo.wd.data.GuiData; import net.montoyo.wd.entity.TileEntityScreen; import net.montoyo.wd.net.SMessagePadCtrl; import net.montoyo.wd.utilities.*; +import org.lwjgl.input.Mouse; import scala.tools.nsc.doc.model.Def; import java.util.ArrayList; @@ -76,6 +73,7 @@ public class ClientProxy extends SharedProxy implements IResourceManagerReloadLi private ArrayList modelBakers = new ArrayList<>(); private net.montoyo.mcef.api.API mcef; private MinePadRenderer minePadRenderer; + private LaserPointerRenderer laserPointerRenderer; //Tracking private ArrayList screenTracking = new ArrayList<>(); @@ -102,6 +100,7 @@ public class ClientProxy extends SharedProxy implements IResourceManagerReloadLi ClientRegistry.bindTileEntitySpecialRenderer(TileEntityScreen.class, new ScreenRenderer()); mcef = MCEFApi.getAPI(); minePadRenderer = new MinePadRenderer(); + laserPointerRenderer = new LaserPointerRenderer(); } @Override @@ -321,13 +320,31 @@ public class ClientProxy extends SharedProxy implements IResourceManagerReloadLi @SubscribeEvent public void onRenderPlayerHand(RenderSpecificHandEvent ev) { - if(ev.getItemStack().getItem() == WebDisplays.INSTANCE.itemMinePad) { - EnumHandSide handSide = mc.player.getPrimaryHand(); - if(ev.getHand() == EnumHand.OFF_HAND) - handSide = handSide.opposite(); + Item item = ev.getItemStack().getItem(); + IItemRenderer renderer; - minePadRenderer.render(ev.getItemStack(), (handSide == EnumHandSide.RIGHT) ? 1.0f : -1.0f, ev.getSwingProgress(), ev.getEquipProgress()); - ev.setCanceled(true); + if(item == WebDisplays.INSTANCE.itemMinePad) + renderer = minePadRenderer; + else if(item == WebDisplays.INSTANCE.itemLaserPointer) + renderer = laserPointerRenderer; + else + return; + + EnumHandSide handSide = mc.player.getPrimaryHand(); + if(ev.getHand() == EnumHand.OFF_HAND) + handSide = handSide.opposite(); + + renderer.render(ev.getItemStack(), (handSide == EnumHandSide.RIGHT) ? 1.0f : -1.0f, ev.getSwingProgress(), ev.getEquipProgress()); + ev.setCanceled(true); + } + + @SubscribeEvent + public void onMouseButton(MouseEvent ev) { + if(mc.player != null && mc.player.getHeldItem(EnumHand.MAIN_HAND).getItem() == WebDisplays.INSTANCE.itemLaserPointer) { + if(ev.getButton() == 1) { + //Right button + laserPointerRenderer.isOn = ev.isButtonstate(); + } } } diff --git a/src/main/java/net/montoyo/wd/client/gui/GuiScreenConfig.java b/src/main/java/net/montoyo/wd/client/gui/GuiScreenConfig.java index 1da6e5c..7d3b8d8 100644 --- a/src/main/java/net/montoyo/wd/client/gui/GuiScreenConfig.java +++ b/src/main/java/net/montoyo/wd/client/gui/GuiScreenConfig.java @@ -4,6 +4,7 @@ package net.montoyo.wd.client.gui; +import net.minecraft.client.audio.PositionedSoundRecord; import net.minecraft.util.ResourceLocation; import net.montoyo.wd.WebDisplays; import net.montoyo.wd.client.gui.controls.*; @@ -142,6 +143,8 @@ public class GuiScreenConfig extends WDScreen { updateRights(friendRights, friendRights, friendBoxes, true); updateRights(otherRights, otherRights, otherBoxes, true); updateMyRights(); + + mc.getSoundHandler().playSound(PositionedSoundRecord.getRecord(WebDisplays.INSTANCE.soundScreenCfg, 1.0f, 1.0f)); } private void addFriend(String name) { diff --git a/src/main/java/net/montoyo/wd/client/renderers/IItemRenderer.java b/src/main/java/net/montoyo/wd/client/renderers/IItemRenderer.java new file mode 100644 index 0000000..e298c20 --- /dev/null +++ b/src/main/java/net/montoyo/wd/client/renderers/IItemRenderer.java @@ -0,0 +1,13 @@ +/* + * Copyright (C) 2018 BARBOTIN Nicolas + */ + +package net.montoyo.wd.client.renderers; + +import net.minecraft.item.ItemStack; + +public interface IItemRenderer { + + void render(ItemStack is, float handSideSign, float swingProgress, float equipProgress); + +} diff --git a/src/main/java/net/montoyo/wd/client/renderers/LaserPointerRenderer.java b/src/main/java/net/montoyo/wd/client/renderers/LaserPointerRenderer.java new file mode 100644 index 0000000..fa17db5 --- /dev/null +++ b/src/main/java/net/montoyo/wd/client/renderers/LaserPointerRenderer.java @@ -0,0 +1,132 @@ +/* + * Copyright (C) 2018 BARBOTIN Nicolas + */ + +package net.montoyo.wd.client.renderers; + +import net.minecraft.client.Minecraft; +import net.minecraft.client.renderer.*; +import net.minecraft.client.renderer.vertex.DefaultVertexFormats; +import net.minecraft.item.ItemStack; +import net.minecraft.util.math.MathHelper; +import net.minecraft.util.math.RayTraceResult; +import net.minecraft.util.math.Vec3d; +import net.minecraftforge.fml.relauncher.Side; +import net.minecraftforge.fml.relauncher.SideOnly; +import org.lwjgl.BufferUtils; + +import java.nio.FloatBuffer; + +import static org.lwjgl.opengl.GL11.*; + +@SideOnly(Side.CLIENT) +public final class LaserPointerRenderer implements IItemRenderer { + + private static final float PI = (float) Math.PI; + private final Tessellator t = Tessellator.getInstance(); + private final BufferBuilder bb = t.getBuffer(); + private final Minecraft mc = Minecraft.getMinecraft(); + private final FloatBuffer matrix1 = BufferUtils.createFloatBuffer(16); + private final FloatBuffer renderBuffer = BufferUtils.createFloatBuffer(8); + + public boolean isOn = false; + + public LaserPointerRenderer() { + for(int i = 0; i < 8; i++) + renderBuffer.put(0.0f); + + renderBuffer.position(0); + } + + @Override + public final void render(ItemStack is, float handSideSign, float swingProgress, float equipProgress) { + //This whole method is a fucking hack + float sqrtSwingProg = (float) Math.sqrt((double) swingProgress); + float sinSqrtSwingProg1 = MathHelper.sin(sqrtSwingProg * PI); + + GlStateManager.disableCull(); + GlStateManager.disableTexture2D(); + GlStateManager.enableRescaleNormal(); + + //Laser pointer + glPushMatrix(); + glTranslatef(handSideSign * -0.4f * sinSqrtSwingProg1, 0.2f * MathHelper.sin(sqrtSwingProg * PI * 2.0f), -0.2f * MathHelper.sin(swingProgress * PI)); + glTranslatef(handSideSign * 0.56f, -0.52f - equipProgress * 0.6f, -0.72f); + glRotatef(handSideSign * (45.0f - MathHelper.sin(swingProgress * swingProgress * PI) * 20.0f), 0.0f, 1.0f, 0.0f); + glRotatef(handSideSign * sinSqrtSwingProg1 * -20.0f, 0.0f, 0.0f, 1.0f); + glRotatef(sinSqrtSwingProg1 * -80.0f, 1.0f, 0.0f, 0.0f); + glRotatef(handSideSign * -30.0f, 0.0f, 1.0f, 0.0f); + glTranslatef(0.0f, 0.2f, 0.0f); + glRotatef(10.0f, 1.0f, 0.0f, 0.0f); + glScalef(1.0f / 16.0f, 1.0f / 16.0f, 1.0f / 16.0f); + + glColor4f(0.5f, 0.5f, 0.5f, 1.0f); + bb.begin(GL_QUADS, DefaultVertexFormats.POSITION_NORMAL); + bb.pos(0.0, 0.0, 0.0).normal(0.0f, 1.0f, 0.0f).endVertex(); + bb.pos(1.0, 0.0, 0.0).normal(0.0f, 1.0f, 0.0f).endVertex(); + bb.pos(1.0, 0.0, 4.0).normal(0.0f, 1.0f, 0.0f).endVertex(); + bb.pos(0.0, 0.0, 4.0).normal(0.0f, 1.0f, 0.0f).endVertex(); + + bb.pos(0.0, 0.0, 0.0).normal(-1.0f, 0.0f, 0.0f).endVertex(); + bb.pos(0.0, -1.0, 0.0).normal(-1.0f, 0.0f, 0.0f).endVertex(); + bb.pos(0.0, -1.0, 4.0).normal(-1.0f, 0.0f, 0.0f).endVertex(); + bb.pos(0.0, 0.0, 4.0).normal(-1.0f, 0.0f, 0.0f).endVertex(); + + bb.pos(1.0, 0.0, 0.0).normal(1.0f, 0.0f, 0.0f).endVertex(); + bb.pos(1.0, -1.0, 0.0).normal(1.0f, 0.0f, 0.0f).endVertex(); + bb.pos(1.0, -1.0, 4.0).normal(1.0f, 0.0f, 0.0f).endVertex(); + bb.pos(1.0, 0.0, 4.0).normal(1.0f, 0.0f, 0.0f).endVertex(); + + bb.pos(0.0, -1.0, 4.0).normal(0.0f, 0.0f, 1.0f).endVertex(); + bb.pos(1.0, -1.0, 4.0).normal(0.0f, 0.0f, 1.0f).endVertex(); + bb.pos(1.0, 0.0, 4.0).normal(0.0f, 0.0f, 1.0f).endVertex(); + bb.pos(0.0, 0.0, 4.0).normal(0.0f, 0.0f, 1.0f).endVertex(); + t.draw(); + + if(isOn) { + glTranslatef(0.5f, -0.5f, 0.0f); + matrix1.position(0); + glGetFloat(GL_MODELVIEW_MATRIX, matrix1); //Hax to get that damn position + } + + glPopMatrix(); + + if(isOn) { + RenderHelper.disableStandardItemLighting(); + GlStateManager.setActiveTexture(OpenGlHelper.lightmapTexUnit); + GlStateManager.disableTexture2D(); + GlStateManager.setActiveTexture(OpenGlHelper.defaultTexUnit); + + //Actual laser + glPushMatrix(); + glLoadIdentity(); + GlStateManager.enableBlend(); + GlStateManager.tryBlendFuncSeparate(GlStateManager.SourceFactor.SRC_ALPHA, GlStateManager.DestFactor.ONE_MINUS_SRC_ALPHA, GlStateManager.SourceFactor.SRC_ALPHA, GlStateManager.DestFactor.DST_ALPHA); + glColor4f(1.0f, 0.0f, 0.0f, 0.5f); + GlStateManager.glLineWidth(3.0f); + + matrix1.position(12); + renderBuffer.put(matrix1.get()); + renderBuffer.put(matrix1.get()); + renderBuffer.put(matrix1.get() - 0.02f); //I know this is stupid, but it's the only thing that worked... + renderBuffer.put(matrix1.get()); + renderBuffer.position(0); + glVertexPointer(4, 0, renderBuffer); + glEnableClientState(GL_VERTEX_ARRAY); + glDrawArrays(GL_LINES, 0, 2); + glDisableClientState(GL_VERTEX_ARRAY); + glPopMatrix(); + } + + GlStateManager.enableTexture2D(); //Fix for shitty minecraft fire + } + + private void raycast(float ptt, double dist) { + Vec3d start = mc.player.getPositionEyes(ptt); + Vec3d lookVec = mc.player.getLook(ptt); + Vec3d end = start.addVector(lookVec.x * dist, lookVec.y * dist, lookVec.z * dist); + + RayTraceResult rtr = mc.world.rayTraceBlocks(start, end, true, true, false); + } + +} diff --git a/src/main/java/net/montoyo/wd/client/renderers/MinePadRenderer.java b/src/main/java/net/montoyo/wd/client/renderers/MinePadRenderer.java index d60a2aa..aed8f6a 100644 --- a/src/main/java/net/montoyo/wd/client/renderers/MinePadRenderer.java +++ b/src/main/java/net/montoyo/wd/client/renderers/MinePadRenderer.java @@ -21,7 +21,7 @@ import static org.lwjgl.opengl.GL11.*; import static org.lwjgl.opengl.GL12.GL_RESCALE_NORMAL; @SideOnly(Side.CLIENT) -public final class MinePadRenderer { +public final class MinePadRenderer implements IItemRenderer { private static final float PI = (float) Math.PI; private final Minecraft mc = Minecraft.getMinecraft(); @@ -34,7 +34,7 @@ public final class MinePadRenderer { private float sinSwingProg1; private float sinSwingProg2; - private static void drawAxis() { + public static void drawAxis() { glDisable(GL_TEXTURE_2D); glBegin(GL_LINES); glColor4f(1.f, 0.f, 0.f, 1.f); glVertex3d(0.0, 0.0, 0.0); @@ -47,6 +47,7 @@ public final class MinePadRenderer { glEnable(GL_TEXTURE_2D); } + @Override public final void render(ItemStack is, float handSideSign, float swingProgress, float equipProgress) { //Pre-compute values float sqrtSwingProg = (float) Math.sqrt((double) swingProgress); diff --git a/src/main/resources/assets/webdisplays/lang/en_us.lang b/src/main/resources/assets/webdisplays/lang/en_us.lang index 22469e6..bf35ef6 100644 --- a/src/main/resources/assets/webdisplays/lang/en_us.lang +++ b/src/main/resources/assets/webdisplays/lang/en_us.lang @@ -15,6 +15,7 @@ item.webdisplays.upgrade.name=Screen Upgrade item.webdisplays.upgrade.lasermouse.name=Laser Sensor item.webdisplays.upgrade.redinput.name=Redstone Input Port item.webdisplays.upgrade.redoutput.name=Redstone Output Port +item.webdisplays.laserpointer.name=Laser Pointer webdisplays.message.tooSmall=Too small! Minimum size is 2x2. webdisplays.message.invalid=Structure is invalid; look at %s. webdisplays.message.turnOn=You need to turn the screen on first! diff --git a/src/main/resources/assets/webdisplays/sounds.json b/src/main/resources/assets/webdisplays/sounds.json index 7558e4e..f1f2b2b 100644 --- a/src/main/resources/assets/webdisplays/sounds.json +++ b/src/main/resources/assets/webdisplays/sounds.json @@ -1,7 +1,6 @@ { "keyboardType": { "category": "block", - "subtitle": "block.webdisplays.keyboard", "sounds": [ "webdisplays:keyboard1", "webdisplays:keyboard2", @@ -15,16 +14,20 @@ }, "upgradeAdd": { "category": "block", - "subtitle": "block.webdisplays.upgradeAdd", "sounds": [ "webdisplays:upgrade_add" ] }, "upgradeDel": { "category": "block", - "subtitle": "block.webdisplays.upgradeDel", "sounds": [ "webdisplays:upgrade_del" ] + }, + "screencfgOpen": { + "category": "master", + "sounds": [ + "webdisplays:screencfg_open" + ] } } diff --git a/src/main/resources/assets/webdisplays/sounds/screencfg_open.ogg b/src/main/resources/assets/webdisplays/sounds/screencfg_open.ogg new file mode 100644 index 0000000000000000000000000000000000000000..51a6d1f98dd5abe5472497ea166764d2942c3627 GIT binary patch literal 5820 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 literal 0 HcmV?d00001