Assisted Placement, Part IV

- added 👻-blocks as a placement preview
This commit is contained in:
Zelophed 2021-02-16 00:48:13 +01:00
parent 34de9a0319
commit b9d1a586c1
18 changed files with 668 additions and 76 deletions

View file

@ -1,10 +1,5 @@
package com.simibubi.create;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import com.simibubi.create.content.contraptions.base.KineticTileEntityRenderer;
import com.simibubi.create.content.contraptions.components.structureMovement.ContraptionRenderer;
import com.simibubi.create.content.contraptions.relays.encased.CasingConnectivity;
@ -17,8 +12,8 @@ import com.simibubi.create.foundation.block.render.SpriteShifter;
import com.simibubi.create.foundation.item.CustomItemModels;
import com.simibubi.create.foundation.item.CustomRenderedItems;
import com.simibubi.create.foundation.utility.SuperByteBufferCache;
import com.simibubi.create.foundation.utility.ghost.GhostBlocks;
import com.simibubi.create.foundation.utility.outliner.Outliner;
import net.minecraft.block.Block;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.BlockModelShapes;
@ -36,6 +31,11 @@ import net.minecraftforge.client.model.ModelLoader;
import net.minecraftforge.eventbus.api.IEventBus;
import net.minecraftforge.fml.event.lifecycle.FMLClientSetupEvent;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
public class CreateClient {
public static ClientSchematicLoader schematicSender;
@ -43,6 +43,7 @@ public class CreateClient {
public static SchematicAndQuillHandler schematicAndQuillHandler;
public static SuperByteBufferCache bufferCache;
public static final Outliner outliner = new Outliner();
public static GhostBlocks ghostBlocks;
private static CustomBlockModels customBlockModels;
private static CustomItemModels customItemModels;
@ -67,6 +68,8 @@ public class CreateClient {
bufferCache.registerCompartment(KineticTileEntityRenderer.KINETIC_TILE);
bufferCache.registerCompartment(ContraptionRenderer.CONTRAPTION, 20);
ghostBlocks = new GhostBlocks();
AllKeys.register();
AllContainerTypes.registerScreenFactories();
//AllTileEntities.registerRenderers();

View file

@ -5,7 +5,6 @@ import com.simibubi.create.AllShapes;
import com.simibubi.create.foundation.block.ProperDirectionalBlock;
import com.simibubi.create.foundation.utility.DyeHelper;
import com.simibubi.create.foundation.utility.Iterate;
import com.simibubi.create.foundation.utility.VecHelper;
import com.simibubi.create.foundation.utility.placement.IPlacementHelper;
import com.simibubi.create.foundation.utility.placement.PlacementHelpers;
import com.simibubi.create.foundation.utility.placement.PlacementOffset;
@ -216,7 +215,8 @@ public class SailBlock extends ProperDirectionalBlock {
@Override
public void renderAt(BlockPos pos, BlockState state, BlockRayTraceResult ray, PlacementOffset offset) {
IPlacementHelper.renderArrow(VecHelper.getCenterOf(pos), VecHelper.getCenterOf(offset.getPos()), state.get(FACING));
//IPlacementHelper.renderArrow(VecHelper.getCenterOf(pos), VecHelper.getCenterOf(offset.getPos()), state.get(FACING));
displayGhost(offset);
}
}
}

View file

@ -279,7 +279,6 @@ public class GantryShaftBlock extends DirectionalKineticBlock {
return PlacementOffset.success(offset.getPos(), offset.getTransform()
.andThen(s -> s.with(POWERED, state.get(POWERED))));
}
}
}

View file

@ -7,7 +7,6 @@ import com.simibubi.create.content.contraptions.base.HorizontalAxisKineticBlock;
import com.simibubi.create.content.contraptions.relays.elementary.CogWheelBlock;
import com.simibubi.create.content.contraptions.relays.elementary.CogwheelBlockItem;
import com.simibubi.create.foundation.block.ITE;
import com.simibubi.create.foundation.utility.VecHelper;
import com.simibubi.create.foundation.utility.placement.IPlacementHelper;
import com.simibubi.create.foundation.utility.placement.PlacementHelpers;
import com.simibubi.create.foundation.utility.placement.PlacementOffset;
@ -20,7 +19,6 @@ import net.minecraft.item.BlockItemUseContext;
import net.minecraft.item.ItemStack;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.ActionResultType;
import net.minecraft.util.Direction;
import net.minecraft.util.Direction.Axis;
import net.minecraft.util.Hand;
import net.minecraft.util.math.BlockPos;
@ -111,9 +109,11 @@ public class SpeedControllerBlock extends HorizontalAxisKineticBlock implements
@Override
public void renderAt(BlockPos pos, BlockState state, BlockRayTraceResult ray, PlacementOffset offset) {
IPlacementHelper.renderArrow(VecHelper.getCenterOf(pos), VecHelper.getCenterOf(offset.getPos()),
Direction.getFacingFromAxis(Direction.AxisDirection.POSITIVE,
state.get(HORIZONTAL_AXIS) == Axis.X ? Axis.Z : Axis.X));
//IPlacementHelper.renderArrow(VecHelper.getCenterOf(pos), VecHelper.getCenterOf(offset.getPos()),
// Direction.getFacingFromAxis(Direction.AxisDirection.POSITIVE,
// state.get(HORIZONTAL_AXIS) == Axis.X ? Axis.Z : Axis.X));
displayGhost(offset);
}
}

View file

@ -7,7 +7,6 @@ import com.simibubi.create.content.contraptions.base.HorizontalKineticBlock;
import com.simibubi.create.content.contraptions.base.IRotate;
import com.simibubi.create.foundation.advancement.AllTriggers;
import com.simibubi.create.foundation.utility.Iterate;
import com.simibubi.create.foundation.utility.VecHelper;
import com.simibubi.create.foundation.utility.placement.IPlacementHelper;
import com.simibubi.create.foundation.utility.placement.PlacementHelpers;
import com.simibubi.create.foundation.utility.placement.PlacementOffset;
@ -150,9 +149,11 @@ public class CogwheelBlockItem extends BlockItem {
@Override
public void renderAt(BlockPos pos, BlockState state, BlockRayTraceResult ray, PlacementOffset offset) {
IPlacementHelper.renderArrow(VecHelper.getCenterOf(pos), VecHelper.getCenterOf(offset.getPos()),
Direction.getFacingFromAxis(Direction.AxisDirection.POSITIVE, state.get(AXIS)),
((CogWheelBlock) state.getBlock()).isLarge ? 1.5D : 0.75D);
//IPlacementHelper.renderArrow(VecHelper.getCenterOf(pos), VecHelper.getCenterOf(offset.getPos()),
// Direction.getFacingFromAxis(Direction.AxisDirection.POSITIVE, state.get(AXIS)),
// ((CogWheelBlock) state.getBlock()).isLarge ? 1.5D : 0.75D);
displayGhost(offset);
}
}
@ -227,8 +228,10 @@ public class CogwheelBlockItem extends BlockItem {
@Override
public void renderAt(BlockPos pos, BlockState state, BlockRayTraceResult ray, PlacementOffset offset) {
IPlacementHelper.renderArrow(VecHelper.getCenterOf(pos), VecHelper.getCenterOf(offset.getPos()),
Direction.getFacingFromAxis(Direction.AxisDirection.POSITIVE, state.get(AXIS)), 1D);
//IPlacementHelper.renderArrow(VecHelper.getCenterOf(pos), VecHelper.getCenterOf(offset.getPos()),
// Direction.getFacingFromAxis(Direction.AxisDirection.POSITIVE, state.get(AXIS)), 1D);
displayGhost(offset);
}
protected boolean hitOnShaft(BlockState state, BlockRayTraceResult ray) {
@ -320,10 +323,12 @@ public class CogwheelBlockItem extends BlockItem {
@Override
public void renderAt(BlockPos pos, BlockState state, BlockRayTraceResult ray, PlacementOffset offset) {
IPlacementHelper.renderArrow(VecHelper.getCenterOf(pos), VecHelper.getCenterOf(offset.getPos()),
Direction.getFacingFromAxis(Direction.AxisDirection.POSITIVE, offset.getTransform()
.apply(AllBlocks.LARGE_COGWHEEL.getDefaultState())
.get(AXIS)));
//IPlacementHelper.renderArrow(VecHelper.getCenterOf(pos), VecHelper.getCenterOf(offset.getPos()),
// Direction.getFacingFromAxis(Direction.AxisDirection.POSITIVE, offset.getTransform()
// .apply(AllBlocks.LARGE_COGWHEEL.getDefaultState())
// .get(AXIS)));
displayGhost(offset);
}
}
}

View file

@ -104,6 +104,7 @@ public class ClientEvents {
ArmInteractionPointHandler.tick();
PlacementHelpers.tick();
CreateClient.outliner.tickOutlines();
CreateClient.ghostBlocks.tickGhosts();
}
@SubscribeEvent
@ -122,6 +123,8 @@ public class ClientEvents {
CouplingRenderer.renderAll(ms, buffer);
CreateClient.schematicHandler.render(ms, buffer);
CreateClient.ghostBlocks.renderAll(ms, buffer);
CreateClient.outliner.renderOutlines(ms, buffer);
// CollisionDebugger.render(ms, buffer);
buffer.draw();

View file

@ -24,19 +24,7 @@ public class HighlightCommand {
public static ArgumentBuilder<CommandSource, ?> register() {
return Commands.literal("highlight")
.requires(cs -> cs.hasPermissionLevel(0))
.requires(AllCommands.sourceIsPlayer)
.then(Commands.argument("pos", BlockPosArgument.blockPos())
.requires(AllCommands.sourceIsPlayer)
.executes(ctx -> {
BlockPos pos = BlockPosArgument.getLoadedBlockPos(ctx, "pos");
AllPackets.channel.send(
PacketDistributor.PLAYER.with(() -> (ServerPlayerEntity) ctx.getSource().getEntity()),
new HighlightPacket(pos)
);
return Command.SINGLE_SUCCESS;
})
.then(Commands.argument("players", EntityArgument.players())
.executes(ctx -> {
Collection<ServerPlayerEntity> players = EntityArgument.getPlayers(ctx, "players");
@ -52,7 +40,19 @@ public class HighlightCommand {
return players.size();
})
)
//.requires(AllCommands.sourceIsPlayer)
.executes(ctx -> {
BlockPos pos = BlockPosArgument.getLoadedBlockPos(ctx, "pos");
AllPackets.channel.send(
PacketDistributor.PLAYER.with(() -> (ServerPlayerEntity) ctx.getSource().getEntity()),
new HighlightPacket(pos)
);
return Command.SINGLE_SUCCESS;
})
)
//.requires(AllCommands.sourceIsPlayer)
.executes(ctx -> {
ServerPlayerEntity player = ctx.getSource().asPlayer();
return highlightAssemblyExceptionFor(player, ctx.getSource());

View file

@ -1,9 +1,9 @@
package com.simibubi.create.foundation.command;
import com.mojang.brigadier.Command;
import com.mojang.brigadier.arguments.BoolArgumentType;
import com.mojang.brigadier.builder.ArgumentBuilder;
import com.simibubi.create.foundation.networking.AllPackets;
import net.minecraft.command.CommandSource;
import net.minecraft.command.Commands;
import net.minecraft.entity.player.ServerPlayerEntity;
@ -30,7 +30,8 @@ public class ToggleDebugCommand {
ctx.getSource().sendFeedback(new StringTextComponent((value ? "enabled" : "disabled") + " rainbow debug"), true);
return 1;
}));
return Command.SINGLE_SUCCESS;
})
);
}
}

View file

@ -15,6 +15,7 @@ public class CClient extends ConfigBase {
public ConfigInt overlayOffsetX = i(20, Integer.MIN_VALUE, Integer.MAX_VALUE, "overlayOffsetX", "Offset the overlay from goggle- and hover- information by this many pixels on the X axis; Use /create overlay");
public ConfigInt overlayOffsetY = i(0, Integer.MIN_VALUE, Integer.MAX_VALUE, "overlayOffsetY", "Offset the overlay from goggle- and hover- information by this many pixels on the Y axis; Use /create overlay");
public ConfigBool smoothPlacementIndicator = b(false, "smoothPlacementIndicator", "Use an alternative indicator when showing where the assisted placement ends up relative to your crosshair");
@Override
public String getName() {

View file

@ -1,9 +1,11 @@
package com.simibubi.create.foundation.utility;
import java.util.Random;
import javax.annotation.Nullable;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.ActiveRenderInfo;
import net.minecraft.client.renderer.Quaternion;
import net.minecraft.client.renderer.Vector3f;
import net.minecraft.entity.Entity;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.nbt.DoubleNBT;
import net.minecraft.nbt.ListNBT;
import net.minecraft.util.Direction;
@ -13,6 +15,9 @@ import net.minecraft.util.math.MathHelper;
import net.minecraft.util.math.Vec3d;
import net.minecraft.util.math.Vec3i;
import javax.annotation.Nullable;
import java.util.Random;
public class VecHelper {
public static final Vec3d CENTER_OF_ORIGIN = new Vec3d(.5, .5, .5);
@ -145,4 +150,52 @@ public class VecHelper {
return origin.add(lineDirection.scale(t));
}
//https://forums.minecraftforge.net/topic/88562-116solved-3d-to-2d-conversion/?do=findComment&comment=413573 slightly modified
public static Vec3d projectToPlayerView(Vec3d target, float partialTicks) {
/* The (centered) location on the screen of the given 3d point in the world.
* Result is (dist right of center screen, dist up from center screen, if < 0, then in front of view plane) */
ActiveRenderInfo ari = Minecraft.getInstance().gameRenderer.getActiveRenderInfo();
Vec3d camera_pos = ari.getProjectedView();
Quaternion camera_rotation_conj = ari.getRotation().copy();
camera_rotation_conj.conjugate();
Vector3f result3f = new Vector3f((float) (camera_pos.x - target.x),
(float) (camera_pos.y - target.y),
(float) (camera_pos.z - target.z));
result3f.func_214905_a(camera_rotation_conj);
// ----- compensate for view bobbing (if active) -----
// the following code adapted from GameRenderer::applyBobbing (to invert it)
Minecraft mc = Minecraft.getInstance();
if (mc.gameSettings.viewBobbing) {
Entity renderViewEntity = mc.getRenderViewEntity();
if (renderViewEntity instanceof PlayerEntity) {
PlayerEntity playerentity = (PlayerEntity) renderViewEntity;
float distwalked_modified = playerentity.distanceWalkedModified;
float f = distwalked_modified - playerentity.prevDistanceWalkedModified;
float f1 = -(distwalked_modified + f * partialTicks);
float f2 = MathHelper.lerp(partialTicks, playerentity.prevCameraYaw, playerentity.cameraYaw);
Quaternion q2 = new Quaternion(Vector3f.POSITIVE_X, Math.abs(MathHelper.cos(f1 * (float) Math.PI - 0.2F) * f2) * 5.0F, true);
q2.conjugate();
result3f.func_214905_a(q2);
Quaternion q1 = new Quaternion(Vector3f.POSITIVE_Z, MathHelper.sin(f1 * (float) Math.PI) * f2 * 3.0F, true);
q1.conjugate();
result3f.func_214905_a(q1);
Vector3f bob_translation = new Vector3f((MathHelper.sin(f1 * (float) Math.PI) * f2 * 0.5F), (-Math.abs(MathHelper.cos(f1 * (float) Math.PI) * f2)), 0.0f);
bob_translation.setY(-bob_translation.getY()); // this is weird but hey, if it works
result3f.add(bob_translation);
}
}
// ----- adjust for fov -----
float fov = (float) mc.gameRenderer.getFOVModifier(ari, partialTicks, true);
float half_height = (float) mc.getWindow().getScaledHeight() / 2;
float scale_factor = half_height / (result3f.getZ() * (float) Math.tan(Math.toRadians(fov / 2)));
return new Vec3d(-result3f.getX() * scale_factor, result3f.getY() * scale_factor, result3f.getZ());
}
}

View file

@ -0,0 +1,50 @@
package com.simibubi.create.foundation.utility.ghost;
import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.util.math.BlockPos;
import java.util.function.Supplier;
public class GhostBlockParams {
protected final BlockState state;
protected BlockPos pos;
protected Supplier<Float> alphaSupplier;
private GhostBlockParams(BlockState state) {
this.state = state;
this.pos = BlockPos.ZERO;
this.alphaSupplier = () -> 1f;
}
public static GhostBlockParams of(BlockState state) {
return new GhostBlockParams(state);
}
public static GhostBlockParams of(Block block) {
return of(block.getDefaultState());
}
public GhostBlockParams at(BlockPos pos) {
this.pos = pos;
return this;
}
public GhostBlockParams at(int x, int y, int z) {
return this.at(new BlockPos(x, y, z));
}
public GhostBlockParams alpha(Supplier<Float> alphaSupplier) {
this.alphaSupplier = alphaSupplier;
return this;
}
public GhostBlockParams alpha(float alpha) {
return this.alpha(() -> alpha);
}
public GhostBlockParams breathingAlpha() {
return this.alpha(() -> (float) GhostBlocks.getBreathingAlpha());
}
}

View file

@ -0,0 +1,172 @@
package com.simibubi.create.foundation.utility.ghost;
import com.mojang.blaze3d.matrix.MatrixStack;
import com.mojang.blaze3d.vertex.IVertexBuilder;
import com.simibubi.create.foundation.renderState.SuperRenderTypeBuffer;
import com.simibubi.create.foundation.utility.VirtualEmptyModelData;
import net.minecraft.block.BlockState;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.*;
import net.minecraft.client.renderer.model.BakedQuad;
import net.minecraft.client.renderer.model.IBakedModel;
import net.minecraft.client.renderer.texture.OverlayTexture;
import net.minecraft.client.renderer.vertex.DefaultVertexFormats;
import net.minecraft.util.Direction;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.MathHelper;
import net.minecraft.util.math.Vec3i;
import org.lwjgl.system.MemoryStack;
import javax.annotation.Nullable;
import java.nio.Buffer;
import java.nio.ByteBuffer;
import java.nio.IntBuffer;
import java.util.List;
import java.util.Random;
public abstract class GhostBlockRenderer {
private static final GhostBlockRenderer transparent = new TransparentGhostBlockRenderer();
public static GhostBlockRenderer transparent() {
return transparent;
}
private static final GhostBlockRenderer standard = new DefaultGhostBlockRenderer();
public static GhostBlockRenderer standard() {
return standard;
}
public abstract void render(MatrixStack ms, SuperRenderTypeBuffer buffer, GhostBlockParams params);
private static class DefaultGhostBlockRenderer extends GhostBlockRenderer {
public void render(MatrixStack ms, SuperRenderTypeBuffer buffer, GhostBlockParams params) {
ms.push();
BlockRendererDispatcher dispatcher = Minecraft.getInstance().getBlockRendererDispatcher();
IBakedModel model = dispatcher.getModelForState(params.state);
RenderType layer = RenderTypeLookup.getEntityBlockLayer(params.state);
IVertexBuilder vb = buffer.getEarlyBuffer(layer);
BlockPos pos = params.pos;
ms.translate(pos.getX(), pos.getY(), pos.getZ());
dispatcher.getBlockModelRenderer().renderModel(ms.peek(), vb, params.state, model, 1f, 1f, 1f, 0xF000F0, OverlayTexture.DEFAULT_UV, VirtualEmptyModelData.INSTANCE);
ms.pop();
}
}
private static class TransparentGhostBlockRenderer extends GhostBlockRenderer {
public void render(MatrixStack ms, SuperRenderTypeBuffer buffer, GhostBlockParams params) {
//prepare
ms.push();
//RenderSystem.pushMatrix();
BlockRendererDispatcher dispatcher = Minecraft.getInstance().getBlockRendererDispatcher();
IBakedModel model = dispatcher.getModelForState(params.state);
//RenderType layer = RenderTypeLookup.getEntityBlockLayer(params.state);
RenderType layer = RenderType.getTranslucent();
IVertexBuilder vb = buffer.getEarlyBuffer(layer);
BlockPos pos = params.pos;
ms.translate(pos.getX(), pos.getY(), pos.getZ());
//dispatcher.getBlockModelRenderer().renderModel(ms.peek(), vb, params.state, model, 1f, 1f, 1f, 0xF000F0, OverlayTexture.DEFAULT_UV, VirtualEmptyModelData.INSTANCE);
renderModel(params, ms.peek(), vb, params.state, model, 1f, 1f, 1f, 0xF000F0, OverlayTexture.DEFAULT_UV, VirtualEmptyModelData.INSTANCE);
//buffer.draw();
//clean
//RenderSystem.popMatrix();
ms.pop();
}
//BlockModelRenderer
public void renderModel(GhostBlockParams params, MatrixStack.Entry entry, IVertexBuilder vb, @Nullable BlockState state, IBakedModel model, float p_228804_5_, float p_228804_6_, float p_228804_7_, int p_228804_8_, int p_228804_9_, net.minecraftforge.client.model.data.IModelData modelData) {
Random random = new Random();
for (Direction direction : Direction.values()) {
random.setSeed(42L);
renderQuad(params, entry, vb, p_228804_5_, p_228804_6_, p_228804_7_, model.getQuads(state, direction, random, modelData), p_228804_8_, p_228804_9_);
}
random.setSeed(42L);
renderQuad(params, entry, vb, p_228804_5_, p_228804_6_, p_228804_7_, model.getQuads(state, (Direction) null, random, modelData), p_228804_8_, p_228804_9_);
}
//BlockModelRenderer
private static void renderQuad(GhostBlockParams params, MatrixStack.Entry p_228803_0_, IVertexBuilder p_228803_1_, float p_228803_2_, float p_228803_3_, float p_228803_4_, List<BakedQuad> p_228803_5_, int p_228803_6_, int p_228803_7_) {
Float alpha = params.alphaSupplier.get();
for (BakedQuad bakedquad : p_228803_5_) {
float f;
float f1;
float f2;
if (bakedquad.hasTintIndex()) {
f = MathHelper.clamp(p_228803_2_, 0.0F, 1.0F);
f1 = MathHelper.clamp(p_228803_3_, 0.0F, 1.0F);
f2 = MathHelper.clamp(p_228803_4_, 0.0F, 1.0F);
} else {
f = 1.0F;
f1 = 1.0F;
f2 = 1.0F;
}
quad(alpha, p_228803_1_, p_228803_0_, bakedquad, new float[]{1f, 1f, 1f, 1f}, f, f1, f2, new int[]{p_228803_6_, p_228803_6_, p_228803_6_, p_228803_6_}, p_228803_7_);
}
}
//IVertexBuilder
static void quad(float alpha, IVertexBuilder vb, MatrixStack.Entry p_227890_1_, BakedQuad p_227890_2_, float[] p_227890_3_, float p_227890_4_, float p_227890_5_, float p_227890_6_, int[] p_227890_7_, int p_227890_8_) {
int[] aint = p_227890_2_.getVertexData();
Vec3i vec3i = p_227890_2_.getFace().getDirectionVec();
Vector3f vector3f = new Vector3f((float) vec3i.getX(), (float) vec3i.getY(), (float) vec3i.getZ());
Matrix4f matrix4f = p_227890_1_.getModel();
vector3f.transform(p_227890_1_.getNormal());
int i = 8;
int j = aint.length / 8;
try (MemoryStack memorystack = MemoryStack.stackPush()) {
ByteBuffer bytebuffer = memorystack.malloc(DefaultVertexFormats.BLOCK.getSize());
IntBuffer intbuffer = bytebuffer.asIntBuffer();
for (int k = 0; k < j; ++k) {
((Buffer) intbuffer).clear();
intbuffer.put(aint, k * 8, 8);
float f = bytebuffer.getFloat(0);
float f1 = bytebuffer.getFloat(4);
float f2 = bytebuffer.getFloat(8);
float r;
float g;
float b;
r = p_227890_3_[k] * p_227890_4_;
g = p_227890_3_[k] * p_227890_5_;
b = p_227890_3_[k] * p_227890_6_;
int l = vb.applyBakedLighting(p_227890_7_[k], bytebuffer);
float f9 = bytebuffer.getFloat(16);
float f10 = bytebuffer.getFloat(20);
Vector4f vector4f = new Vector4f(f, f1, f2, 1.0F);
vector4f.transform(matrix4f);
vb.applyBakedNormals(vector3f, bytebuffer, p_227890_1_.getNormal());
vb.vertex(vector4f.getX(), vector4f.getY(), vector4f.getZ(), r, g, b, alpha, f9, f10, p_227890_8_, l, vector3f.getX(), vector3f.getY(), vector3f.getZ());
}
}
}
}
}

View file

@ -0,0 +1,82 @@
package com.simibubi.create.foundation.utility.ghost;
import com.mojang.blaze3d.matrix.MatrixStack;
import com.simibubi.create.foundation.renderState.SuperRenderTypeBuffer;
import net.minecraft.block.BlockState;
import java.util.HashMap;
import java.util.Map;
public class GhostBlocks {
public static double getBreathingAlpha() {
double period = 2500;
double timer = System.currentTimeMillis() % period;
double offset = Math.cos((2d/period) * Math.PI * timer);
return 0.75d - 0.2d * offset;
}
final Map<Object, Entry> ghosts;
public GhostBlockParams showGhostState(Object slot, BlockState state) {
return showGhostState(slot, state, 1);
}
public GhostBlockParams showGhostState(Object slot, BlockState state, int ttl) {
Entry e = refresh(slot, GhostBlockRenderer.transparent(), GhostBlockParams.of(state), ttl);
return e.params;
}
public GhostBlockParams showGhost(Object slot, GhostBlockRenderer ghost, GhostBlockParams params, int ttl) {
Entry e = refresh(slot, ghost, params, ttl);
return e.params;
}
private Entry refresh(Object slot, GhostBlockRenderer ghost, GhostBlockParams params, int ttl) {
if (!ghosts.containsKey(slot))
ghosts.put(slot, new Entry(ghost, params, ttl));
Entry e = ghosts.get(slot);
e.ticksToLive = ttl;
e.params = params;
e.ghost = ghost;
return e;
}
public GhostBlocks() {
ghosts = new HashMap<>();
}
public void tickGhosts() {
ghosts.forEach((slot, entry) -> entry.ticksToLive--);
ghosts.entrySet().removeIf(e -> !e.getValue().isAlive());
}
public void renderAll(MatrixStack ms, SuperRenderTypeBuffer buffer) {
ghosts.forEach((slot, entry) -> {
GhostBlockRenderer ghost = entry.ghost;
ghost.render(ms, buffer, entry.params);
});
}
static class Entry {
private GhostBlockRenderer ghost;
private GhostBlockParams params;
private int ticksToLive;
public Entry(GhostBlockRenderer ghost, GhostBlockParams params) {
this(ghost, params, 1);
}
public Entry(GhostBlockRenderer ghost, GhostBlockParams params, int ttl) {
this.ghost = ghost;
this.params = params;
this.ticksToLive = ttl;
}
public boolean isAlive() {
return ticksToLive >= 0;
}
}
}

View file

@ -7,6 +7,7 @@ import com.simibubi.create.foundation.utility.VecHelper;
import mcp.MethodsReturnNonnullByDefault;
import net.minecraft.block.BlockState;
import net.minecraft.block.Blocks;
import net.minecraft.item.BlockItem;
import net.minecraft.item.ItemStack;
import net.minecraft.util.Direction;
import net.minecraft.util.math.BlockPos;
@ -46,9 +47,21 @@ public interface IPlacementHelper {
*/
PlacementOffset getOffset(World world, BlockState state, BlockPos pos, BlockRayTraceResult ray);
//overrides the default ghost state of the helper with the actual state of the held block item, this is used in PlacementHelpers and can be ignored in most cases
default PlacementOffset getOffset(World world, BlockState state, BlockPos pos, BlockRayTraceResult ray, ItemStack heldItem) {
PlacementOffset offset = getOffset(world, state, pos, ray);
if (heldItem.getItem() instanceof BlockItem) {
BlockItem blockItem = (BlockItem) heldItem.getItem();
offset = offset.withGhostState(blockItem.getBlock().getDefaultState());
}
return offset;
}
//only gets called when placementOffset is successful
default void renderAt(BlockPos pos, BlockState state, BlockRayTraceResult ray, PlacementOffset offset) {
IPlacementHelper.renderArrow(VecHelper.getCenterOf(pos), VecHelper.getCenterOf(offset.getPos()), ray.getFace());
//IPlacementHelper.renderArrow(VecHelper.getCenterOf(pos), VecHelper.getCenterOf(offset.getPos()), ray.getFace());
displayGhost(offset);
}
static void renderArrow(Vec3d center, Vec3d target, Direction arrowPlane) {
@ -67,6 +80,15 @@ public interface IPlacementHelper {
CreateClient.outliner.showLine("placementArrowB" + center + target, start.add(offset), endB.add(offset)).lineWidth(1/16f);
}
default void displayGhost(PlacementOffset offset) {
if (!offset.hasGhostState())
return;
CreateClient.ghostBlocks.showGhostState(this, offset.getTransform().apply(offset.getGhostState()))
.at(offset.getBlockPos())
.breathingAlpha();
}
static List<Direction> orderedByDistanceOnlyAxis(BlockPos pos, Vec3d hit, Direction.Axis axis) {
return orderedByDistance(pos, hit, dir -> dir.getAxis() == axis);
}

View file

@ -1,22 +1,43 @@
package com.simibubi.create.foundation.utility.placement;
import com.mojang.blaze3d.systems.RenderSystem;
import com.simibubi.create.foundation.config.AllConfigs;
import com.simibubi.create.foundation.gui.widgets.InterpolatedChasingAngle;
import com.simibubi.create.foundation.gui.widgets.InterpolatedChasingValue;
import com.simibubi.create.foundation.utility.AngleHelper;
import com.simibubi.create.foundation.utility.VecHelper;
import net.minecraft.block.BlockState;
import net.minecraft.client.MainWindow;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.BufferBuilder;
import net.minecraft.client.renderer.Tessellator;
import net.minecraft.client.renderer.vertex.DefaultVertexFormats;
import net.minecraft.client.world.ClientWorld;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.item.ItemStack;
import net.minecraft.util.Hand;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.BlockRayTraceResult;
import net.minecraft.util.math.Vec3d;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import net.minecraftforge.client.event.RenderGameOverlayEvent;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.common.Mod;
import org.lwjgl.opengl.GL11;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
@Mod.EventBusSubscriber
public class PlacementHelpers {
private static final List<IPlacementHelper> helpers = new ArrayList<>();
private static int animationTick = 0;
private static final InterpolatedChasingValue angle = new InterpolatedChasingAngle().withSpeed(0.15f);
private static BlockPos target = null;
private static BlockPos lastTarget = null;
public static int register(IPlacementHelper helper) {
helpers.add(helper);
@ -32,6 +53,23 @@ public class PlacementHelpers {
@OnlyIn(Dist.CLIENT)
public static void tick() {
setTarget(null);
checkHelpers();
if (target == null) {
if (animationTick > 0)
animationTick = Math.max(animationTick - 2, 0);
return;
}
if (animationTick < 10)
animationTick++;
}
@OnlyIn(Dist.CLIENT)
private static void checkHelpers() {
Minecraft mc = Minecraft.getInstance();
ClientWorld world = mc.world;
@ -46,29 +84,154 @@ public class PlacementHelpers {
if (mc.player == null)
return;
List<IPlacementHelper> filteredForHeldItem = helpers.stream().filter(helper -> Arrays.stream(Hand.values()).anyMatch(hand -> helper.getItemPredicate().test(mc.player.getHeldItem(hand)))).collect(Collectors.toList());
if (filteredForHeldItem.isEmpty())
return;
if (mc.player.isSneaking())//for now, disable all helpers when sneaking TODO add helpers that respect sneaking but still show position
return;
BlockPos pos = ray.getPos();
BlockState state = world.getBlockState(pos);
for (Hand hand : Hand.values()) {
List<IPlacementHelper> filteredForState = filteredForHeldItem.stream().filter(helper -> helper.getStatePredicate().test(state)).collect(Collectors.toList());
ItemStack heldItem = mc.player.getHeldItem(hand);
List<IPlacementHelper> filteredForHeldItem = helpers.stream().filter(helper -> helper.matchesItem(heldItem)).collect(Collectors.toList());
if (filteredForHeldItem.isEmpty())
continue;
if (filteredForState.isEmpty())
return;
BlockPos pos = ray.getPos();
BlockState state = world.getBlockState(pos);
for (IPlacementHelper h : filteredForState) {
PlacementOffset offset = h.getOffset(world, state, pos, ray);
List<IPlacementHelper> filteredForState = filteredForHeldItem.stream().filter(helper -> helper.matchesState(state)).collect(Collectors.toList());
if (filteredForState.isEmpty())
continue;
boolean atLeastOneMatch = false;
for (IPlacementHelper h : filteredForState) {
PlacementOffset offset = h.getOffset(world, state, pos, ray, heldItem);
if (offset.isSuccessful()) {
h.renderAt(pos, state, ray, offset);
setTarget(offset.getBlockPos());
atLeastOneMatch = true;
break;
}
if (offset.isSuccessful()) {
h.renderAt(pos, state, ray, offset);
break;
}
//at least one helper activated, no need to check the offhand if we are still in the mainhand
if (atLeastOneMatch)
return;
}
}
static void setTarget(BlockPos target) {
PlacementHelpers.target = target;
if (target == null)
return;
if (lastTarget == null) {
lastTarget = target;
return;
}
if (!lastTarget.equals(target))
lastTarget = target;
}
@SubscribeEvent
@OnlyIn(Dist.CLIENT)
public static void onRender(RenderGameOverlayEvent.Pre event) {
if (event.getType() != RenderGameOverlayEvent.ElementType.CROSSHAIRS)
return;
Minecraft mc = Minecraft.getInstance();
PlayerEntity player = mc.player;
if (player != null && animationTick > 0) {
MainWindow res = event.getWindow();
//MatrixStack matrix = event.getMatrix();
//String text = "( )";
//matrix.push();
//matrix.translate(res.getScaledWidth() / 2F, res.getScaledHeight() / 2f - 4, 0);
float screenY = res.getScaledHeight() / 2f;
float screenX = res.getScaledWidth() / 2f;
//float y = screenY - 3.5f;
//float x = screenX;
//x -= mc.fontRenderer.getStringWidth(text)/2f - 0.25f;
float progress = Math.min(animationTick / 10f/* + event.getPartialTicks()*/, 1f);
//int opacity = ((int) (255 * (progress * progress))) << 24;
//mc.fontRenderer.drawString(text, x, y, 0xFFFFFF | opacity);
boolean flag = AllConfigs.CLIENT.smoothPlacementIndicator.get();
if (flag)
drawDirectionIndicator(event.getPartialTicks(), screenX, screenY, progress);
else {
//TODO find something more in style
}
//matrix.pop();
}
}
@OnlyIn(Dist.CLIENT)
private static void drawDirectionIndicator(float partialTicks, float centerX, float centerY, float progress) {
float r = .8f;
float g = .8f;
float b = .8f;
float a = progress * progress;
Vec3d projTarget = VecHelper.projectToPlayerView(VecHelper.getCenterOf(lastTarget), partialTicks);
Vec3d target = new Vec3d(projTarget.x, projTarget.y, 0);
Vec3d norm = target.normalize();
Vec3d ref = new Vec3d(0, 1, 0);
float targetAngle = AngleHelper.deg(Math.acos(norm.dotProduct(ref)));
if (norm.x > 0) {
targetAngle = 360 - targetAngle;
}
if (animationTick < 10)
angle.set(targetAngle);
angle.target(targetAngle);
angle.tick();
float length = 10;
//TOD O if the target is off screen, use length to show a meaningful distance
RenderSystem.pushMatrix();
RenderSystem.disableTexture();
RenderSystem.enableBlend();
RenderSystem.disableAlphaTest();
RenderSystem.defaultBlendFunc();
RenderSystem.shadeModel(GL11.GL_SMOOTH);
RenderSystem.translated(centerX, centerY, 0);
RenderSystem.rotatef(angle.get(0.1f), 0, 0, -1);
//RenderSystem.scaled(3, 3, 3);
Tessellator tessellator = Tessellator.getInstance();
BufferBuilder bufferbuilder = tessellator.getBuffer();
bufferbuilder.begin(GL11.GL_POLYGON, DefaultVertexFormats.POSITION_COLOR);
bufferbuilder.vertex(0, - (10 + length), 0).color(r, g, b, a).endVertex();
bufferbuilder.vertex(-9, -3, 0).color(r, g, b, 0f).endVertex();
bufferbuilder.vertex(-6, -6, 0).color(r, g, b, 0f).endVertex();
bufferbuilder.vertex(-3, -8, 0).color(r, g, b, 0f).endVertex();
bufferbuilder.vertex(0, -8.5, 0).color(r, g, b, 0f).endVertex();
bufferbuilder.vertex(3, -8, 0).color(r, g, b, 0f).endVertex();
bufferbuilder.vertex(6, -6, 0).color(r, g, b, 0f).endVertex();
bufferbuilder.vertex(9, -3, 0).color(r, g, b, 0f).endVertex();
tessellator.draw();
RenderSystem.shadeModel(GL11.GL_FLAT);
RenderSystem.disableBlend();
RenderSystem.enableAlphaTest();
RenderSystem.enableTexture();
RenderSystem.popMatrix();
}
}

View file

@ -27,25 +27,46 @@ import java.util.function.Function;
public class PlacementOffset {
private final boolean success;
private final Vec3i pos;
private final Function<BlockState, BlockState> stateTransform;
private Vec3i pos;
private Function<BlockState, BlockState> stateTransform;
private BlockState ghostState;
private PlacementOffset(boolean success, Vec3i pos, Function<BlockState, BlockState> transform) {
private PlacementOffset(boolean success) {
this.success = success;
this.pos = pos;
this.stateTransform = transform == null ? Function.identity() : transform;
this.pos = BlockPos.ZERO;
this.stateTransform = Function.identity();
this.ghostState = null;
}
public static PlacementOffset fail() {
return new PlacementOffset(false, Vec3i.NULL_VECTOR, null);
return new PlacementOffset(false);
}
public static PlacementOffset success() {
return new PlacementOffset(true);
}
public static PlacementOffset success(Vec3i pos) {
return new PlacementOffset(true, pos, null);
return success().at(pos);
}
public static PlacementOffset success(Vec3i pos, Function<BlockState, BlockState> transform) {
return new PlacementOffset(true, pos, transform);
return success().at(pos).withTransform(transform);
}
public PlacementOffset at(Vec3i pos) {
this.pos = pos;
return this;
}
public PlacementOffset withTransform(Function<BlockState, BlockState> stateTransform) {
this.stateTransform = stateTransform;
return this;
}
public PlacementOffset withGhostState(BlockState ghostState) {
this.ghostState = ghostState;
return this;
}
public boolean isSuccessful() {
@ -56,10 +77,25 @@ public class PlacementOffset {
return pos;
}
public BlockPos getBlockPos() {
if (pos instanceof BlockPos)
return (BlockPos) pos;
return new BlockPos(pos);
}
public Function<BlockState, BlockState> getTransform() {
return stateTransform;
}
public boolean hasGhostState() {
return ghostState != null;
}
public BlockState getGhostState() {
return ghostState;
}
public boolean isReplaceable(World world) {
if (!success)
return false;
@ -69,16 +105,15 @@ public class PlacementOffset {
public ActionResultType placeInWorld(World world, BlockItem blockItem, PlayerEntity player, Hand hand, BlockRayTraceResult ray) {
ItemUseContext context = new ItemUseContext(player, hand, ray);
if (!isReplaceable(world))
return ActionResultType.PASS;
ItemUseContext context = new ItemUseContext(player, hand, ray);
BlockPos newPos = new BlockPos(pos);
if (!world.isBlockModifiable(player, newPos))
return ActionResultType.PASS;
if (!isReplaceable(world))
return ActionResultType.PASS;
BlockState state = stateTransform.apply(blockItem.getBlock().getDefaultState());
if (state.has(BlockStateProperties.WATERLOGGED)) {
IFluidState fluidState = world.getFluidState(newPos);

View file

@ -1,6 +1,5 @@
package com.simibubi.create.foundation.utility.placement.util;
import com.simibubi.create.foundation.utility.VecHelper;
import com.simibubi.create.foundation.utility.placement.IPlacementHelper;
import com.simibubi.create.foundation.utility.placement.PlacementOffset;
import mcp.MethodsReturnNonnullByDefault;
@ -9,7 +8,6 @@ import net.minecraft.state.IProperty;
import net.minecraft.util.Direction;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.BlockRayTraceResult;
import net.minecraft.util.math.Vec3d;
import net.minecraft.world.World;
import java.util.List;
@ -71,7 +69,9 @@ public abstract class PoleHelper<T extends Comparable<T>> implements IPlacementH
@Override
public void renderAt(BlockPos pos, BlockState state, BlockRayTraceResult ray, PlacementOffset offset) {
Vec3d centerOffset = new Vec3d(ray.getFace().getDirectionVec()).scale(.3);
IPlacementHelper.renderArrow(VecHelper.getCenterOf(pos).add(centerOffset), VecHelper.getCenterOf(offset.getPos()).add(centerOffset), ray.getFace(), 0.75D);
//Vec3d centerOffset = new Vec3d(ray.getFace().getDirectionVec()).scale(.3);
//IPlacementHelper.renderArrow(VecHelper.getCenterOf(pos).add(centerOffset), VecHelper.getCenterOf(offset.getPos()).add(centerOffset), ray.getFace(), 0.75D);
displayGhost(offset);
}
}

View file

@ -23,4 +23,7 @@ public net.minecraft.tileentity.BeaconTileEntity field_174909_f # beamSegments
# Server Tick List (For stopping placed fluids from spilling)
public net.minecraft.world.server.ServerTickList field_205374_d # pendingTickListEntriesHashSet
public net.minecraft.world.server.ServerTickList field_205375_e # pendingTickListEntriesTreeSet
public net.minecraft.world.server.ServerTickList field_205375_e # pendingTickListEntriesTreeSet
# GameRenderer
public net.minecraft.client.renderer.GameRenderer func_215311_a(Lnet/minecraft/client/renderer/ActiveRenderInfo;FZ)D #getFOVModifier