Better Schematics

- Visual rework to Schematic items and their interface
- Written Schematics can now be re-used in Schematic Tables
- Schematic and Quill selection is now smoother and more precise
- Schematics no longer re-render their preview every time they are re-positioned
- Schematic tools now move, rotate or flip the schematic more smoothly
- Schematic tools now render the new cuboid outlines rather than plain GL lines
- Fixed Schematics not rendering TileEntities in their preview
- Fixed inconsistent shifting when rotating Schematics with an "odd by even" size
- Fixed typo in Mechanical Press tooltip
This commit is contained in:
simibubi 2020-04-27 14:04:06 +02:00
parent c62d356f9b
commit a97418e784
41 changed files with 1302 additions and 850 deletions

View file

@ -9,6 +9,8 @@ import net.minecraftforge.fml.common.Mod.EventBusSubscriber;
public enum AllSpecialTextures { public enum AllSpecialTextures {
BLANK("blank.png"), BLANK("blank.png"),
CHECKERED("checkerboard.png"),
HIGHLIGHT_CHECKERED("highlighted_checkerboard.png"),
SELECTION("selection.png"), SELECTION("selection.png"),
; ;

View file

@ -67,8 +67,6 @@ public class ClientEvents {
@SubscribeEvent @SubscribeEvent
public static void onRenderWorld(RenderWorldLastEvent event) { public static void onRenderWorld(RenderWorldLastEvent event) {
CreateClient.schematicHandler.render(); CreateClient.schematicHandler.render();
CreateClient.schematicAndQuillHandler.render();
CreateClient.schematicHologram.render();
KineticDebugger.renderSourceOutline(); KineticDebugger.renderSourceOutline();
ChassisRangeDisplay.renderOutlines(event.getPartialTicks()); ChassisRangeDisplay.renderOutlines(event.getPartialTicks());
TerrainZapperRenderHandler.render(); TerrainZapperRenderHandler.render();

View file

@ -16,7 +16,6 @@ import com.simibubi.create.modules.contraptions.components.contraptions.Contrapt
import com.simibubi.create.modules.schematics.ClientSchematicLoader; import com.simibubi.create.modules.schematics.ClientSchematicLoader;
import com.simibubi.create.modules.schematics.client.SchematicAndQuillHandler; import com.simibubi.create.modules.schematics.client.SchematicAndQuillHandler;
import com.simibubi.create.modules.schematics.client.SchematicHandler; import com.simibubi.create.modules.schematics.client.SchematicHandler;
import com.simibubi.create.modules.schematics.client.SchematicHologram;
import net.minecraft.block.Block; import net.minecraft.block.Block;
import net.minecraft.client.Minecraft; import net.minecraft.client.Minecraft;
@ -40,10 +39,8 @@ public class CreateClient {
public static ClientSchematicLoader schematicSender; public static ClientSchematicLoader schematicSender;
public static SchematicHandler schematicHandler; public static SchematicHandler schematicHandler;
public static SchematicHologram schematicHologram;
public static SchematicAndQuillHandler schematicAndQuillHandler; public static SchematicAndQuillHandler schematicAndQuillHandler;
public static SuperByteBufferCache bufferCache; public static SuperByteBufferCache bufferCache;
public static int renderTicks;
public static void addListeners(IEventBus modEventBus) { public static void addListeners(IEventBus modEventBus) {
DistExecutor.runWhenOn(Dist.CLIENT, () -> () -> { DistExecutor.runWhenOn(Dist.CLIENT, () -> () -> {
@ -58,7 +55,6 @@ public class CreateClient {
public static void clientInit(FMLClientSetupEvent event) { public static void clientInit(FMLClientSetupEvent event) {
schematicSender = new ClientSchematicLoader(); schematicSender = new ClientSchematicLoader();
schematicHandler = new SchematicHandler(); schematicHandler = new SchematicHandler();
schematicHologram = new SchematicHologram();
schematicAndQuillHandler = new SchematicAndQuillHandler(); schematicAndQuillHandler = new SchematicAndQuillHandler();
bufferCache = new SuperByteBufferCache(); bufferCache = new SuperByteBufferCache();
@ -81,7 +77,6 @@ public class CreateClient {
schematicSender.tick(); schematicSender.tick();
schematicAndQuillHandler.tick(); schematicAndQuillHandler.tick();
schematicHandler.tick(); schematicHandler.tick();
schematicHologram.tick();
ChassisRangeDisplay.clientTick(); ChassisRangeDisplay.clientTick();
} }

View file

@ -0,0 +1,12 @@
package com.simibubi.create.foundation.gui.widgets;
import com.simibubi.create.foundation.utility.AngleHelper;
public class InterpolatedChasingAngle extends InterpolatedChasingValue {
@Override
protected float getCurrentDiff() {
return AngleHelper.getShortestAngleDiff(value, getTarget());
}
}

View file

@ -7,11 +7,15 @@ public class InterpolatedChasingValue extends InterpolatedValue {
float eps = 1 / 4096f; float eps = 1 / 4096f;
public void tick() { public void tick() {
float diff = target - value; float diff = getCurrentDiff();
if (Math.abs(diff) < eps) if (Math.abs(diff) < eps)
return; return;
set(value + (diff) * speed); set(value + (diff) * speed);
} }
protected float getCurrentDiff() {
return getTarget() - value;
}
public InterpolatedChasingValue withSpeed(float speed) { public InterpolatedChasingValue withSpeed(float speed) {
this.speed = speed; this.speed = speed;
@ -22,5 +26,15 @@ public class InterpolatedChasingValue extends InterpolatedValue {
this.target = target; this.target = target;
return this; return this;
} }
public InterpolatedChasingValue start(float value) {
lastValue = this.value = value;
target(value);
return this;
}
public float getTarget() {
return target;
}
} }

View file

@ -28,7 +28,7 @@ public class RaycastHelper {
return rayTraceUntil(origin, target, predicate); return rayTraceUntil(origin, target, predicate);
} }
private static Vec3d getTraceTarget(PlayerEntity playerIn, double range, Vec3d origin) { public static Vec3d getTraceTarget(PlayerEntity playerIn, double range, Vec3d origin) {
float f = playerIn.rotationPitch; float f = playerIn.rotationPitch;
float f1 = playerIn.rotationYaw; float f1 = playerIn.rotationYaw;
float f2 = MathHelper.cos(-f1 * 0.017453292F - (float) Math.PI); float f2 = MathHelper.cos(-f1 * 0.017453292F - (float) Math.PI);
@ -42,7 +42,7 @@ public class RaycastHelper {
return vec3d1; return vec3d1;
} }
private static Vec3d getTraceOrigin(PlayerEntity playerIn) { public static Vec3d getTraceOrigin(PlayerEntity playerIn) {
double d0 = playerIn.posX; double d0 = playerIn.posX;
double d1 = playerIn.posY + (double) playerIn.getEyeHeight(); double d1 = playerIn.posY + (double) playerIn.getEyeHeight();
double d2 = playerIn.posZ; double d2 = playerIn.posZ;
@ -50,7 +50,7 @@ public class RaycastHelper {
return vec3d; return vec3d;
} }
private static PredicateTraceResult rayTraceUntil(Vec3d start, Vec3d end, Predicate<BlockPos> predicate) { public static PredicateTraceResult rayTraceUntil(Vec3d start, Vec3d end, Predicate<BlockPos> predicate) {
if (Double.isNaN(start.x) || Double.isNaN(start.y) || Double.isNaN(start.z)) if (Double.isNaN(start.x) || Double.isNaN(start.y) || Double.isNaN(start.z))
return null; return null;
if (Double.isNaN(end.x) || Double.isNaN(end.y) || Double.isNaN(end.z)) if (Double.isNaN(end.x) || Double.isNaN(end.y) || Double.isNaN(end.z))

View file

@ -2,6 +2,7 @@ package com.simibubi.create.foundation.utility;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.function.Predicate;
import net.minecraft.block.Block; import net.minecraft.block.Block;
import net.minecraft.block.BlockState; import net.minecraft.block.BlockState;
@ -11,6 +12,7 @@ import net.minecraft.fluid.Fluid;
import net.minecraft.item.crafting.RecipeManager; import net.minecraft.item.crafting.RecipeManager;
import net.minecraft.scoreboard.Scoreboard; import net.minecraft.scoreboard.Scoreboard;
import net.minecraft.tags.NetworkTagManager; import net.minecraft.tags.NetworkTagManager;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.SoundCategory; import net.minecraft.util.SoundCategory;
import net.minecraft.util.SoundEvent; import net.minecraft.util.SoundEvent;
import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.BlockPos;
@ -33,6 +35,26 @@ public class WrappedWorld extends World {
return world; return world;
} }
@Override
public BlockState getBlockState(BlockPos pos) {
return world.getBlockState(pos);
}
@Override
public boolean hasBlockState(BlockPos p_217375_1_, Predicate<BlockState> p_217375_2_) {
return world.hasBlockState(p_217375_1_, p_217375_2_);
}
@Override
public TileEntity getTileEntity(BlockPos pos) {
return world.getTileEntity(pos);
}
@Override
public boolean setBlockState(BlockPos pos, BlockState newState, int flags) {
return world.setBlockState(pos, newState, flags);
}
@Override @Override
public int getLight(BlockPos pos) { public int getLight(BlockPos pos) {
return 15; return 15;
@ -59,8 +81,7 @@ public class WrappedWorld extends World {
} }
@Override @Override
public void playEvent(PlayerEntity player, int type, BlockPos pos, int data) { public void playEvent(PlayerEntity player, int type, BlockPos pos, int data) {}
}
@Override @Override
public List<? extends PlayerEntity> getPlayers() { public List<? extends PlayerEntity> getPlayers() {
@ -69,13 +90,11 @@ public class WrappedWorld extends World {
@Override @Override
public void playSound(PlayerEntity player, double x, double y, double z, SoundEvent soundIn, SoundCategory category, public void playSound(PlayerEntity player, double x, double y, double z, SoundEvent soundIn, SoundCategory category,
float volume, float pitch) { float volume, float pitch) {}
}
@Override @Override
public void playMovingSound(PlayerEntity p_217384_1_, Entity p_217384_2_, SoundEvent p_217384_3_, public void playMovingSound(PlayerEntity p_217384_1_, Entity p_217384_2_, SoundEvent p_217384_3_,
SoundCategory p_217384_4_, float p_217384_5_, float p_217384_6_) { SoundCategory p_217384_4_, float p_217384_5_, float p_217384_6_) {}
}
@Override @Override
public Entity getEntityByID(int id) { public Entity getEntityByID(int id) {
@ -94,8 +113,7 @@ public class WrappedWorld extends World {
} }
@Override @Override
public void registerMapData(MapData mapDataIn) { public void registerMapData(MapData mapDataIn) {}
}
@Override @Override
public int getNextMapId() { public int getNextMapId() {
@ -103,8 +121,7 @@ public class WrappedWorld extends World {
} }
@Override @Override
public void sendBlockBreakProgress(int breakerId, BlockPos pos, int progress) { public void sendBlockBreakProgress(int breakerId, BlockPos pos, int progress) {}
}
@Override @Override
public Scoreboard getScoreboard() { public Scoreboard getScoreboard() {

View file

@ -1,16 +1,29 @@
package com.simibubi.create.foundation.utility.outliner; package com.simibubi.create.foundation.utility.outliner;
import org.lwjgl.opengl.GL11;
import com.mojang.blaze3d.platform.GlStateManager;
import com.simibubi.create.AllSpecialTextures; import com.simibubi.create.AllSpecialTextures;
import com.simibubi.create.foundation.utility.ColorHelper; import com.simibubi.create.foundation.utility.ColorHelper;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.BufferBuilder; import net.minecraft.client.renderer.BufferBuilder;
import net.minecraft.util.Direction;
import net.minecraft.util.Direction.Axis;
import net.minecraft.util.math.AxisAlignedBB; import net.minecraft.util.math.AxisAlignedBB;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Vec3d; import net.minecraft.util.math.Vec3d;
public class AABBOutline extends Outline { public class AABBOutline extends Outline {
private AxisAlignedBB bb = new AxisAlignedBB(new BlockPos(25, 70, 90)).expand(0, 1, 0); protected AxisAlignedBB bb;
protected AllSpecialTextures faceTexture;
protected AllSpecialTextures highlightedTexture;
protected Direction highlightedFace;
public boolean disableCull = false;
public AABBOutline(AxisAlignedBB bb) {
this.bb = bb;
}
@Override @Override
public void render(BufferBuilder buffer) { public void render(BufferBuilder buffer) {
@ -18,8 +31,25 @@ public class AABBOutline extends Outline {
Vec3d color = ColorHelper.getRGB(0xFFFFFF); Vec3d color = ColorHelper.getRGB(0xFFFFFF);
float alpha = 1f; float alpha = 1f;
renderBB(bb, buffer, color, alpha, !disableCull);
draw();
}
public void setTextures(AllSpecialTextures faceTexture, AllSpecialTextures highlightTexture) {
this.faceTexture = faceTexture;
this.highlightedTexture = highlightTexture;
}
public void highlightFace(Direction face) {
this.highlightedFace = face;
}
public void renderBB(AxisAlignedBB bb, BufferBuilder buffer, Vec3d color, float alpha, boolean doCulling) {
Vec3d projectedView = Minecraft.getInstance().gameRenderer.getActiveRenderInfo().getProjectedView();
boolean inside = bb.contains(projectedView);
bb = bb.grow(inside ? -1 / 128d : 1 / 128d);
AllSpecialTextures.BLANK.bind();
Vec3d xyz = new Vec3d(bb.minX, bb.minY, bb.minZ); Vec3d xyz = new Vec3d(bb.minX, bb.minY, bb.minZ);
Vec3d Xyz = new Vec3d(bb.maxX, bb.minY, bb.minZ); Vec3d Xyz = new Vec3d(bb.maxX, bb.minY, bb.minZ);
Vec3d xYz = new Vec3d(bb.minX, bb.maxY, bb.minZ); Vec3d xYz = new Vec3d(bb.minX, bb.maxY, bb.minZ);
@ -29,7 +59,24 @@ public class AABBOutline extends Outline {
Vec3d xYZ = new Vec3d(bb.minX, bb.maxY, bb.maxZ); Vec3d xYZ = new Vec3d(bb.minX, bb.maxY, bb.maxZ);
Vec3d XYZ = new Vec3d(bb.maxX, bb.maxY, bb.maxZ); Vec3d XYZ = new Vec3d(bb.maxX, bb.maxY, bb.maxZ);
if (doCulling) {
GlStateManager.enableCull();
if (inside)
GlStateManager.disableCull();
}
renderFace(Direction.NORTH, xYz, XYz, Xyz, xyz, buffer);
renderFace(Direction.SOUTH, XYZ, xYZ, xyZ, XyZ, buffer);
renderFace(Direction.EAST, XYz, XYZ, XyZ, Xyz, buffer);
renderFace(Direction.WEST, xYZ, xYz, xyz, xyZ, buffer);
renderFace(Direction.UP, xYZ, XYZ, XYz, xYz, buffer);
renderFace(Direction.DOWN, xyz, Xyz, XyZ, xyZ, buffer);
if (doCulling)
GlStateManager.enableCull();
Vec3d start = xyz; Vec3d start = xyz;
AllSpecialTextures.BLANK.bind();
renderAACuboidLine(start, Xyz, color, alpha, buffer); renderAACuboidLine(start, Xyz, color, alpha, buffer);
renderAACuboidLine(start, xYz, color, alpha, buffer); renderAACuboidLine(start, xYz, color, alpha, buffer);
renderAACuboidLine(start, xyZ, color, alpha, buffer); renderAACuboidLine(start, xyZ, color, alpha, buffer);
@ -49,7 +96,29 @@ public class AABBOutline extends Outline {
renderAACuboidLine(start, xyZ, color, alpha, buffer); renderAACuboidLine(start, xyZ, color, alpha, buffer);
renderAACuboidLine(start, xYz, color, alpha, buffer); renderAACuboidLine(start, xYz, color, alpha, buffer);
draw(); }
protected void renderFace(Direction direction, Vec3d p1, Vec3d p2, Vec3d p3, Vec3d p4, BufferBuilder buffer) {
GlStateManager.texParameter(GL11.GL_TEXTURE_2D, 10242, GL11.GL_REPEAT);
GlStateManager.texParameter(GL11.GL_TEXTURE_2D, 10243, GL11.GL_REPEAT);
if (direction == highlightedFace && highlightedTexture != null)
highlightedTexture.bind();
else if (faceTexture != null)
faceTexture.bind();
else
return;
GlStateManager.depthMask(false);
Vec3d uDiff = p2.subtract(p1);
Vec3d vDiff = p4.subtract(p1);
Axis axis = direction.getAxis();
float maxU = (float) Math.abs(axis == Axis.X ? uDiff.z : uDiff.x);
float maxV = (float) Math.abs(axis == Axis.Y ? vDiff.z : vDiff.y);
putQuadUV(p1, p2, p3, p4, 0, 0, maxU, maxV, new Vec3d(1, 1, 1), 1, buffer);
flush();
GlStateManager.depthMask(true);
} }
} }

View file

@ -0,0 +1,50 @@
package com.simibubi.create.foundation.utility.outliner;
import com.simibubi.create.foundation.utility.ColorHelper;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.BufferBuilder;
import net.minecraft.util.math.AxisAlignedBB;
import net.minecraft.util.math.MathHelper;
import net.minecraft.util.math.Vec3d;
public class ChasingAABBOutline extends AABBOutline {
AxisAlignedBB targetBB;
AxisAlignedBB prevBB;
public ChasingAABBOutline(AxisAlignedBB bb) {
super(bb);
prevBB = bb.grow(0);
targetBB = bb.grow(0);
}
public void target(AxisAlignedBB target) {
targetBB = target;
}
public void tick() {
prevBB = bb;
bb = interpolateBBs(bb, targetBB, .5f);
}
@Override
public void render(BufferBuilder buffer) {
begin();
Vec3d color = ColorHelper.getRGB(0xFFFFFF);
float alpha = 1f;
renderBB(interpolateBBs(prevBB, bb, Minecraft.getInstance().getRenderPartialTicks()), buffer, color, alpha,
true);
draw();
}
private static AxisAlignedBB interpolateBBs(AxisAlignedBB current, AxisAlignedBB target, float pt) {
return new AxisAlignedBB(MathHelper.lerp(pt, current.minX, target.minX),
MathHelper.lerp(pt, current.minY, target.minY), MathHelper.lerp(pt, current.minZ, target.minZ),
MathHelper.lerp(pt, current.maxX, target.maxX), MathHelper.lerp(pt, current.maxY, target.maxY),
MathHelper.lerp(pt, current.maxZ, target.maxZ));
}
}

View file

@ -32,7 +32,7 @@ public abstract class Outline {
begin(); begin();
} }
protected void renderAACuboidLine(Vec3d start, Vec3d end, Vec3d rgb, float alpha, BufferBuilder buffer) { public void renderAACuboidLine(Vec3d start, Vec3d end, Vec3d rgb, float alpha, BufferBuilder buffer) {
Vec3d diff = end.subtract(start); Vec3d diff = end.subtract(start);
if (diff.x + diff.y + diff.z < 0) { if (diff.x + diff.y + diff.z < 0) {
Vec3d temp = start; Vec3d temp = start;
@ -91,11 +91,16 @@ public abstract class Outline {
putQuad(a1, a2, a3, a4, rgb, alpha, buffer); putQuad(a1, a2, a3, a4, rgb, alpha, buffer);
} }
protected void putQuad(Vec3d v1, Vec3d v2, Vec3d v3, Vec3d v4, Vec3d rgb, float alpha, BufferBuilder buffer) { public void putQuad(Vec3d v1, Vec3d v2, Vec3d v3, Vec3d v4, Vec3d rgb, float alpha, BufferBuilder buffer) {
putVertex(v1, rgb, 0, 0, alpha, buffer); putQuadUV(v1, v2, v3, v4, 0, 0, 1, 1, rgb, alpha, buffer);
putVertex(v2, rgb, 1, 0, alpha, buffer); }
putVertex(v3, rgb, 1, 1, alpha, buffer);
putVertex(v4, rgb, 0, 1, alpha, buffer); public void putQuadUV(Vec3d v1, Vec3d v2, Vec3d v3, Vec3d v4, float minU, float minV, float maxU,
float maxV, Vec3d rgb, float alpha, BufferBuilder buffer) {
putVertex(v1, rgb, minU, minV, alpha, buffer);
putVertex(v2, rgb, maxU, minV, alpha, buffer);
putVertex(v3, rgb, maxU, maxV, alpha, buffer);
putVertex(v4, rgb, minU, maxV, alpha, buffer);
} }
protected void putVertex(Vec3d pos, Vec3d rgb, float u, float v, float alpha, BufferBuilder buffer) { protected void putVertex(Vec3d pos, Vec3d rgb, float u, float v, float alpha, BufferBuilder buffer) {

View file

@ -11,20 +11,20 @@ import net.minecraft.client.renderer.BufferBuilder;
import net.minecraft.util.math.Vec3d; import net.minecraft.util.math.Vec3d;
import net.minecraft.world.World; import net.minecraft.world.World;
public class OutlineParticle extends Particle { public class OutlineParticle<O extends Outline> extends Particle {
private Outline outline; protected O outline;
private OutlineParticle(Outline outline, World worldIn, double xCoordIn, double yCoordIn, double zCoordIn) { protected OutlineParticle(O outline, World worldIn, double xCoordIn, double yCoordIn, double zCoordIn) {
super(worldIn, xCoordIn, yCoordIn, zCoordIn); super(worldIn, xCoordIn, yCoordIn, zCoordIn);
this.outline = outline; this.outline = outline;
this.maxAge = 1024; this.maxAge = 1024;
} }
public static OutlineParticle create(Outline outline) { public static <O extends Outline> OutlineParticle<O> create(O outline) {
Minecraft mc = Minecraft.getInstance(); Minecraft mc = Minecraft.getInstance();
ClientPlayerEntity player = mc.player; ClientPlayerEntity player = mc.player;
OutlineParticle effect = new OutlineParticle(outline, mc.world, player.posX, player.posY, player.posZ); OutlineParticle<O> effect = new OutlineParticle<>(outline, mc.world, player.posX, player.posY, player.posZ);
mc.particles.addEffect(effect); mc.particles.addEffect(effect);
return effect; return effect;
} }
@ -39,14 +39,12 @@ public class OutlineParticle extends Particle {
GlStateManager.pushMatrix(); GlStateManager.pushMatrix();
Vec3d view = entityIn.getProjectedView(); Vec3d view = entityIn.getProjectedView();
GlStateManager.translated(-view.x, -view.y, -view.z); GlStateManager.translated(-view.x, -view.y, -view.z);
GlStateManager.depthMask(false);
GlStateManager.blendFunc(GlStateManager.SourceFactor.SRC_ALPHA, GlStateManager.DestFactor.ONE_MINUS_SRC_ALPHA); GlStateManager.blendFunc(GlStateManager.SourceFactor.SRC_ALPHA, GlStateManager.DestFactor.ONE_MINUS_SRC_ALPHA);
GlStateManager.enableBlend(); GlStateManager.enableBlend();
outline.render(buffer); getOutline().render(buffer);
GlStateManager.disableBlend(); GlStateManager.disableBlend();
GlStateManager.depthMask(true);
GlStateManager.popMatrix(); GlStateManager.popMatrix();
} }
@ -55,4 +53,8 @@ public class OutlineParticle extends Particle {
return IParticleRenderType.CUSTOM; return IParticleRenderType.CUSTOM;
} }
public O getOutline() {
return outline;
}
} }

View file

@ -0,0 +1,111 @@
package com.simibubi.create.foundation.utility.render;
import java.util.Iterator;
import com.mojang.blaze3d.platform.GLX;
import com.mojang.blaze3d.platform.GlStateManager;
import com.simibubi.create.Create;
import com.simibubi.create.config.AllConfigs;
import com.simibubi.create.foundation.utility.VecHelper;
import com.simibubi.create.foundation.utility.WrappedWorld;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.RenderHelper;
import net.minecraft.client.renderer.tileentity.TileEntityRendererDispatcher;
import net.minecraft.crash.ReportedException;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Vec3d;
import net.minecraft.world.World;
public class StructureRenderer {
protected static LightingWorld lightingWorld;
public static void renderTileEntities(World world, Vec3d position, Vec3d rotation,
Iterable<TileEntity> customRenderTEs) {
TileEntityRendererDispatcher dispatcher = TileEntityRendererDispatcher.instance;
float pt = Minecraft.getInstance().getRenderPartialTicks();
World prevDispatcherWorld = dispatcher.world;
if (lightingWorld == null)
lightingWorld = new LightingWorld(world);
lightingWorld.setWorld(world);
lightingWorld.setTransform(position, rotation);
dispatcher.setWorld(lightingWorld);
for (Iterator<TileEntity> iterator = customRenderTEs.iterator(); iterator.hasNext();) {
TileEntity tileEntity = iterator.next();
if (dispatcher.getRenderer(tileEntity) == null) {
iterator.remove();
continue;
}
try {
BlockPos pos = tileEntity.getPos();
if (!tileEntity.hasFastRenderer()) {
RenderHelper.enableStandardItemLighting();
int i = lightingWorld.getCombinedLight(pos, 0);
int j = i % 65536;
int k = i / 65536;
GLX.glMultiTexCoord2f(GLX.GL_TEXTURE1, (float) j, (float) k);
GlStateManager.color4f(1.0F, 1.0F, 1.0F, 1.0F);
}
World prevTileWorld = tileEntity.getWorld();
tileEntity.setWorld(lightingWorld);
GlStateManager.disableCull();
dispatcher.render(tileEntity, pos.getX(), pos.getY(), pos.getZ(), pt, -1, true);
GlStateManager.enableCull();
tileEntity.setWorld(prevTileWorld);
} catch (ReportedException e) {
if (AllConfigs.CLIENT.explainRenderErrors.get()) {
Create.logger.error("TileEntity " + tileEntity.getType().getRegistryName().toString()
+ " didn't want to render while moved.\n", e);
} else {
Create.logger.error("TileEntity " + tileEntity.getType().getRegistryName().toString()
+ " didn't want to render while moved.\n");
}
iterator.remove();
continue;
}
}
dispatcher.setWorld(prevDispatcherWorld);
}
private static class LightingWorld extends WrappedWorld {
private Vec3d offset;
private Vec3d rotation;
public LightingWorld(World world) {
super(world);
}
void setWorld(World world) {
this.world = world;
}
void setTransform(Vec3d offset, Vec3d rotation) {
this.offset = offset;
this.rotation = rotation;
}
@Override
public int getCombinedLight(BlockPos pos, int minLight) {
return super.getCombinedLight(transformPos(pos), minLight);
}
private BlockPos transformPos(BlockPos pos) {
Vec3d vec = VecHelper.getCenterOf(pos);
vec = VecHelper.rotate(vec, rotation.x, rotation.y, rotation.z);
vec = vec.add(offset).subtract(VecHelper.getCenterOf(BlockPos.ZERO));
return new BlockPos(vec);
}
}
}

View file

@ -14,6 +14,7 @@ import com.simibubi.create.AllItems;
import com.simibubi.create.AllKeys; import com.simibubi.create.AllKeys;
import com.simibubi.create.foundation.utility.TessellatorHelper; import com.simibubi.create.foundation.utility.TessellatorHelper;
import com.simibubi.create.foundation.utility.outliner.BlockClusterOutline; import com.simibubi.create.foundation.utility.outliner.BlockClusterOutline;
import com.simibubi.create.foundation.utility.outliner.Outline;
import com.simibubi.create.foundation.utility.outliner.OutlineParticle; import com.simibubi.create.foundation.utility.outliner.OutlineParticle;
import com.simibubi.create.modules.contraptions.components.contraptions.chassis.ChassisTileEntity; import com.simibubi.create.modules.contraptions.components.contraptions.chassis.ChassisTileEntity;
@ -32,7 +33,7 @@ public class ChassisRangeDisplay {
private static class Entry { private static class Entry {
BlockClusterOutline outline; BlockClusterOutline outline;
OutlineParticle particle; OutlineParticle<Outline> particle;
ChassisTileEntity te; ChassisTileEntity te;
int timer; int timer;

View file

@ -1,22 +1,16 @@
package com.simibubi.create.modules.contraptions.components.contraptions; package com.simibubi.create.modules.contraptions.components.contraptions;
import java.util.Iterator;
import java.util.Random; import java.util.Random;
import java.util.function.Consumer; import java.util.function.Consumer;
import org.apache.commons.lang3.tuple.Pair; import org.apache.commons.lang3.tuple.Pair;
import org.lwjgl.opengl.GL11; import org.lwjgl.opengl.GL11;
import com.mojang.blaze3d.platform.GLX;
import com.mojang.blaze3d.platform.GlStateManager;
import com.simibubi.create.Create;
import com.simibubi.create.CreateClient; import com.simibubi.create.CreateClient;
import com.simibubi.create.config.AllConfigs;
import com.simibubi.create.foundation.utility.PlacementSimulationWorld; import com.simibubi.create.foundation.utility.PlacementSimulationWorld;
import com.simibubi.create.foundation.utility.SuperByteBuffer; import com.simibubi.create.foundation.utility.SuperByteBuffer;
import com.simibubi.create.foundation.utility.SuperByteBufferCache.Compartment; import com.simibubi.create.foundation.utility.SuperByteBufferCache.Compartment;
import com.simibubi.create.foundation.utility.VecHelper; import com.simibubi.create.foundation.utility.render.StructureRenderer;
import com.simibubi.create.foundation.utility.WrappedWorld;
import net.minecraft.block.BlockRenderType; import net.minecraft.block.BlockRenderType;
import net.minecraft.block.BlockState; import net.minecraft.block.BlockState;
@ -24,12 +18,8 @@ import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.BlockModelRenderer; import net.minecraft.client.renderer.BlockModelRenderer;
import net.minecraft.client.renderer.BlockRendererDispatcher; import net.minecraft.client.renderer.BlockRendererDispatcher;
import net.minecraft.client.renderer.BufferBuilder; import net.minecraft.client.renderer.BufferBuilder;
import net.minecraft.client.renderer.RenderHelper;
import net.minecraft.client.renderer.model.IBakedModel; import net.minecraft.client.renderer.model.IBakedModel;
import net.minecraft.client.renderer.tileentity.TileEntityRendererDispatcher;
import net.minecraft.client.renderer.vertex.DefaultVertexFormats; import net.minecraft.client.renderer.vertex.DefaultVertexFormats;
import net.minecraft.crash.ReportedException;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.BlockPos.MutableBlockPos; import net.minecraft.util.math.BlockPos.MutableBlockPos;
import net.minecraft.util.math.Vec3d; import net.minecraft.util.math.Vec3d;
@ -42,7 +32,6 @@ public class ContraptionRenderer {
public static final Compartment<Contraption> CONTRAPTION = new Compartment<>(); public static final Compartment<Contraption> CONTRAPTION = new Compartment<>();
protected static PlacementSimulationWorld renderWorld; protected static PlacementSimulationWorld renderWorld;
protected static LightingWorld lightingWorld;
public static void render(World world, Contraption c, Consumer<SuperByteBuffer> transform, BufferBuilder buffer) { public static void render(World world, Contraption c, Consumer<SuperByteBuffer> transform, BufferBuilder buffer) {
SuperByteBuffer contraptionBuffer = CreateClient.bufferCache.get(CONTRAPTION, c, () -> renderContraption(c)); SuperByteBuffer contraptionBuffer = CreateClient.bufferCache.get(CONTRAPTION, c, () -> renderContraption(c));
@ -52,54 +41,7 @@ public class ContraptionRenderer {
} }
public static void renderTEsWithGL(World world, Contraption c, Vec3d position, Vec3d rotation) { public static void renderTEsWithGL(World world, Contraption c, Vec3d position, Vec3d rotation) {
TileEntityRendererDispatcher dispatcher = TileEntityRendererDispatcher.instance; StructureRenderer.renderTileEntities(world, position, rotation, c.customRenderTEs);
float pt = Minecraft.getInstance().getRenderPartialTicks();
World prevDispatcherWorld = dispatcher.world;
if (lightingWorld == null)
lightingWorld = new LightingWorld(world);
lightingWorld.setWorld(world);
lightingWorld.setTransform(position, rotation);
dispatcher.setWorld(lightingWorld);
for (Iterator<TileEntity> iterator = c.customRenderTEs.iterator(); iterator.hasNext();) {
TileEntity tileEntity = iterator.next();
if (dispatcher.getRenderer(tileEntity) == null) {
iterator.remove();
continue;
}
try {
BlockPos pos = tileEntity.getPos();
if (!tileEntity.hasFastRenderer()) {
RenderHelper.enableStandardItemLighting();
int i = lightingWorld.getCombinedLight(pos, 0);
int j = i % 65536;
int k = i / 65536;
GLX.glMultiTexCoord2f(GLX.GL_TEXTURE1, (float) j, (float) k);
GlStateManager.color4f(1.0F, 1.0F, 1.0F, 1.0F);
}
World prevTileWorld = tileEntity.getWorld();
tileEntity.setWorld(lightingWorld);
dispatcher.render(tileEntity, pos.getX(), pos.getY(), pos.getZ(), pt, -1, true);
tileEntity.setWorld(prevTileWorld);
} catch (ReportedException e) {
if (AllConfigs.CLIENT.explainRenderErrors.get()) {
Create.logger.error("TileEntity " + tileEntity.getType().getRegistryName().toString()
+ " didn't want to render while moved.\n", e);
} else {
Create.logger.error("TileEntity " + tileEntity.getType().getRegistryName().toString()
+ " didn't want to render while moved.\n");
}
iterator.remove();
continue;
}
}
dispatcher.setWorld(prevDispatcherWorld);
} }
private static SuperByteBuffer renderContraption(Contraption c) { private static SuperByteBuffer renderContraption(Contraption c) {
@ -173,36 +115,4 @@ public class ContraptionRenderer {
return ((int) sky) << 20 | ((int) block) << 4; return ((int) sky) << 20 | ((int) block) << 4;
} }
private static class LightingWorld extends WrappedWorld {
private Vec3d offset;
private Vec3d rotation;
public LightingWorld(World world) {
super(world);
}
void setWorld(World world) {
this.world = world;
}
void setTransform(Vec3d offset, Vec3d rotation) {
this.offset = offset;
this.rotation = rotation;
}
@Override
public int getCombinedLight(BlockPos pos, int minLight) {
return super.getCombinedLight(transformPos(pos), minLight);
}
private BlockPos transformPos(BlockPos pos) {
Vec3d vec = VecHelper.getCenterOf(pos);
vec = VecHelper.rotate(vec, rotation.x, rotation.y, rotation.z);
vec = vec.add(offset).subtract(VecHelper.getCenterOf(BlockPos.ZERO));
return new BlockPos(vec);
}
}
} }

View file

@ -1,6 +1,7 @@
package com.simibubi.create.modules.schematics; package com.simibubi.create.modules.schematics;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
@ -29,13 +30,16 @@ import net.minecraft.world.biome.Biomes;
public class SchematicWorld extends WrappedWorld { public class SchematicWorld extends WrappedWorld {
private Map<BlockPos, BlockState> blocks; private Map<BlockPos, BlockState> blocks;
private Map<BlockPos, TileEntity> tileEntities;
private Cuboid bounds; private Cuboid bounds;
public BlockPos anchor; public BlockPos anchor;
public boolean renderMode;
public SchematicWorld(Map<BlockPos, BlockState> blocks, Cuboid bounds, BlockPos anchor, World original) { public SchematicWorld(BlockPos anchor, World original) {
super(original); super(original);
this.blocks = blocks; this.blocks = new HashMap<>();
this.setBounds(bounds); this.tileEntities = new HashMap<>();
this.bounds = new Cuboid();
this.anchor = anchor; this.anchor = anchor;
} }
@ -45,6 +49,19 @@ public class SchematicWorld extends WrappedWorld {
@Override @Override
public TileEntity getTileEntity(BlockPos pos) { public TileEntity getTileEntity(BlockPos pos) {
if (isOutsideBuildHeight(pos))
return null;
if (tileEntities.containsKey(pos))
return tileEntities.get(pos);
if (!blocks.containsKey(pos.subtract(anchor)))
return null;
BlockState blockState = getBlockState(pos);
if (blockState.hasTileEntity()) {
TileEntity tileEntity = blockState.createTileEntity(this);
tileEntities.put(pos, tileEntity);
return tileEntity;
}
return null; return null;
} }
@ -52,7 +69,7 @@ public class SchematicWorld extends WrappedWorld {
public BlockState getBlockState(BlockPos globalPos) { public BlockState getBlockState(BlockPos globalPos) {
BlockPos pos = globalPos.subtract(anchor); BlockPos pos = globalPos.subtract(anchor);
if (pos.getY() - bounds.y == -1) { if (pos.getY() - bounds.y == -1 && !renderMode) {
return Blocks.GRASS_BLOCK.getDefaultState(); return Blocks.GRASS_BLOCK.getDefaultState();
} }
@ -171,4 +188,8 @@ public class SchematicWorld extends WrappedWorld {
this.bounds = bounds; this.bounds = bounds;
} }
public Iterable<TileEntity> getTileEntities() {
return tileEntities.values();
}
} }

View file

@ -44,7 +44,8 @@ public class SchematicTableContainer extends Container {
inputSlot = new SlotItemHandler(te.inventory, 0, -9, 40) { inputSlot = new SlotItemHandler(te.inventory, 0, -9, 40) {
@Override @Override
public boolean isItemValid(ItemStack stack) { public boolean isItemValid(ItemStack stack) {
return AllItems.EMPTY_BLUEPRINT.typeOf(stack) || AllItems.BLUEPRINT_AND_QUILL.typeOf(stack); return AllItems.EMPTY_BLUEPRINT.typeOf(stack) || AllItems.BLUEPRINT_AND_QUILL.typeOf(stack)
|| AllItems.BLUEPRINT.typeOf(stack);
} }
}; };

View file

@ -1,6 +1,5 @@
package com.simibubi.create.modules.schematics.block; package com.simibubi.create.modules.schematics.block;
import java.util.HashMap;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
@ -12,7 +11,6 @@ import com.simibubi.create.config.AllConfigs;
import com.simibubi.create.config.CSchematics; import com.simibubi.create.config.CSchematics;
import com.simibubi.create.foundation.behaviour.base.SmartTileEntity; import com.simibubi.create.foundation.behaviour.base.SmartTileEntity;
import com.simibubi.create.foundation.behaviour.base.TileEntityBehaviour; import com.simibubi.create.foundation.behaviour.base.TileEntityBehaviour;
import com.simibubi.create.foundation.type.Cuboid;
import com.simibubi.create.modules.schematics.MaterialChecklist; import com.simibubi.create.modules.schematics.MaterialChecklist;
import com.simibubi.create.modules.schematics.SchematicWorld; import com.simibubi.create.modules.schematics.SchematicWorld;
import com.simibubi.create.modules.schematics.item.SchematicItem; import com.simibubi.create.modules.schematics.item.SchematicItem;
@ -466,7 +464,7 @@ public class SchematicannonTileEntity extends SmartTileEntity implements INamedC
} }
// Load blocks into reader // Load blocks into reader
Template activeTemplate = SchematicItem.getSchematic(blueprint); Template activeTemplate = SchematicItem.loadSchematic(blueprint);
BlockPos anchor = NBTUtil.readBlockPos(blueprint.getTag().getCompound("Anchor")); BlockPos anchor = NBTUtil.readBlockPos(blueprint.getTag().getCompound("Anchor"));
if (activeTemplate.getSize().equals(BlockPos.ZERO)) { if (activeTemplate.getSize().equals(BlockPos.ZERO)) {
@ -484,7 +482,7 @@ public class SchematicannonTileEntity extends SmartTileEntity implements INamedC
} }
schematicAnchor = anchor; schematicAnchor = anchor;
blockReader = new SchematicWorld(new HashMap<>(), new Cuboid(), schematicAnchor, world); blockReader = new SchematicWorld(schematicAnchor, world);
activeTemplate.addBlocksToWorld(blockReader, schematicAnchor, SchematicItem.getSettings(blueprint)); activeTemplate.addBlocksToWorld(blockReader, schematicAnchor, SchematicItem.getSettings(blueprint));
schematicLoaded = true; schematicLoaded = true;
state = State.PAUSED; state = State.PAUSED;

View file

@ -8,7 +8,6 @@ import java.nio.file.StandardOpenOption;
import org.apache.commons.io.IOUtils; import org.apache.commons.io.IOUtils;
import com.mojang.blaze3d.platform.GlStateManager;
import com.simibubi.create.AllItems; import com.simibubi.create.AllItems;
import com.simibubi.create.AllKeys; import com.simibubi.create.AllKeys;
import com.simibubi.create.AllSpecialTextures; import com.simibubi.create.AllSpecialTextures;
@ -18,13 +17,13 @@ import com.simibubi.create.foundation.utility.FilesHelper;
import com.simibubi.create.foundation.utility.Lang; import com.simibubi.create.foundation.utility.Lang;
import com.simibubi.create.foundation.utility.RaycastHelper; import com.simibubi.create.foundation.utility.RaycastHelper;
import com.simibubi.create.foundation.utility.RaycastHelper.PredicateTraceResult; import com.simibubi.create.foundation.utility.RaycastHelper.PredicateTraceResult;
import com.simibubi.create.foundation.utility.TessellatorHelper; import com.simibubi.create.foundation.utility.VecHelper;
import com.simibubi.create.foundation.utility.outliner.ChasingAABBOutline;
import com.simibubi.create.foundation.utility.outliner.OutlineParticle;
import net.minecraft.block.Blocks; import net.minecraft.block.Blocks;
import net.minecraft.client.Minecraft; import net.minecraft.client.Minecraft;
import net.minecraft.client.entity.player.ClientPlayerEntity; import net.minecraft.client.entity.player.ClientPlayerEntity;
import net.minecraft.client.renderer.Tessellator;
import net.minecraft.client.renderer.WorldRenderer;
import net.minecraft.item.BlockItemUseContext; import net.minecraft.item.BlockItemUseContext;
import net.minecraft.item.ItemUseContext; import net.minecraft.item.ItemUseContext;
import net.minecraft.nbt.CompoundNBT; import net.minecraft.nbt.CompoundNBT;
@ -32,6 +31,7 @@ import net.minecraft.nbt.CompressedStreamTools;
import net.minecraft.util.Direction; import net.minecraft.util.Direction;
import net.minecraft.util.Direction.AxisDirection; import net.minecraft.util.Direction.AxisDirection;
import net.minecraft.util.Hand; import net.minecraft.util.Hand;
import net.minecraft.util.math.AxisAlignedBB;
import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.BlockRayTraceResult; import net.minecraft.util.math.BlockRayTraceResult;
import net.minecraft.util.math.MathHelper; import net.minecraft.util.math.MathHelper;
@ -49,14 +49,7 @@ public class SchematicAndQuillHandler {
private Direction selectedFace; private Direction selectedFace;
private int range = 10; private int range = 10;
private boolean isActive() { private OutlineParticle<ChasingAABBOutline> particle;
return isPresent() && AllItems.BLUEPRINT_AND_QUILL.typeOf(Minecraft.getInstance().player.getHeldItemMainhand());
}
private boolean isPresent() {
return Minecraft.getInstance() != null && Minecraft.getInstance().world != null
&& Minecraft.getInstance().currentScreen == null;
}
public boolean mouseScrolled(double delta) { public boolean mouseScrolled(double delta) {
if (!isActive()) if (!isActive())
@ -65,27 +58,32 @@ public class SchematicAndQuillHandler {
return false; return false;
if (secondPos == null) if (secondPos == null)
range = (int) MathHelper.clamp(range + delta, 1, 100); range = (int) MathHelper.clamp(range + delta, 1, 100);
if (selectedFace != null) { if (selectedFace == null)
MutableBoundingBox bb = new MutableBoundingBox(firstPos, secondPos); return true;
Vec3i vec = selectedFace.getDirectionVec();
int x = (int) (vec.getX() * delta); AxisAlignedBB bb = new AxisAlignedBB(firstPos, secondPos);
int y = (int) (vec.getY() * delta); Vec3i vec = selectedFace.getDirectionVec();
int z = (int) (vec.getZ() * delta); Vec3d projectedView = Minecraft.getInstance().gameRenderer.getActiveRenderInfo().getProjectedView();
if (bb.contains(projectedView))
delta *= -1;
AxisDirection axisDirection = selectedFace.getAxisDirection(); int x = (int) (vec.getX() * delta);
if (axisDirection == AxisDirection.NEGATIVE) int y = (int) (vec.getY() * delta);
bb.offset(-x, -y, -z); int z = (int) (vec.getZ() * delta);
bb.maxX = Math.max(bb.maxX - x * axisDirection.getOffset(), bb.minX); AxisDirection axisDirection = selectedFace.getAxisDirection();
bb.maxY = Math.max(bb.maxY - y * axisDirection.getOffset(), bb.minY); if (axisDirection == AxisDirection.NEGATIVE)
bb.maxZ = Math.max(bb.maxZ - z * axisDirection.getOffset(), bb.minZ); bb = bb.offset(-x, -y, -z);
firstPos = new BlockPos(bb.minX, bb.minY, bb.minZ); double maxX = Math.max(bb.maxX - x * axisDirection.getOffset(), bb.minX);
secondPos = new BlockPos(bb.maxX, bb.maxY, bb.maxZ); double maxY = Math.max(bb.maxY - y * axisDirection.getOffset(), bb.minY);
Lang.sendStatus(Minecraft.getInstance().player, "schematicAndQuill.dimensions", bb.getXSize(), double maxZ = Math.max(bb.maxZ - z * axisDirection.getOffset(), bb.minZ);
bb.getYSize(), bb.getZSize()); bb = new AxisAlignedBB(bb.minX, bb.minY, bb.minZ, maxX, maxY, maxZ);
}
firstPos = new BlockPos(bb.minX, bb.minY, bb.minZ);
secondPos = new BlockPos(bb.maxX, bb.maxY, bb.maxZ);
Lang.sendStatus(Minecraft.getInstance().player, "schematicAndQuill.dimensions", (int) bb.getXSize() + 1,
(int) bb.getYSize() + 1, (int) bb.getZSize() + 1);
return true; return true;
} }
@ -106,8 +104,7 @@ public class SchematicAndQuillHandler {
} }
if (secondPos != null) { if (secondPos != null) {
TextInputPromptScreen guiScreenIn = new TextInputPromptScreen(this::saveSchematic, s -> { TextInputPromptScreen guiScreenIn = new TextInputPromptScreen(this::saveSchematic, s -> {});
});
guiScreenIn.setTitle(Lang.translate("schematicAndQuill.prompt")); guiScreenIn.setTitle(Lang.translate("schematicAndQuill.prompt"));
guiScreenIn.setButtonTextConfirm(Lang.translate("action.saveToFile")); guiScreenIn.setButtonTextConfirm(Lang.translate("action.saveToFile"));
guiScreenIn.setButtonTextAbort(Lang.translate("action.discard")); guiScreenIn.setButtonTextAbort(Lang.translate("action.discard"));
@ -130,6 +127,100 @@ public class SchematicAndQuillHandler {
Lang.sendStatus(player, "schematicAndQuill.firstPos"); Lang.sendStatus(player, "schematicAndQuill.firstPos");
} }
public void tick() {
if (!isActive()) {
if (particle != null) {
particle.setExpired();
particle = null;
}
return;
}
ClientPlayerEntity player = Minecraft.getInstance().player;
if (AllKeys.ACTIVATE_TOOL.isPressed()) {
float pt = Minecraft.getInstance().getRenderPartialTicks();
Vec3d targetVec = player.getEyePosition(pt).add(player.getLookVec().scale(range));
setCursor(new BlockPos(targetVec));
} else {
BlockRayTraceResult trace = RaycastHelper.rayTraceRange(player.world, player, 75);
if (trace != null && trace.getType() == Type.BLOCK) {
BlockPos hit = trace.getPos();
boolean replaceable = player.world.getBlockState(hit)
.isReplaceable(new BlockItemUseContext(new ItemUseContext(player, Hand.MAIN_HAND, trace)));
if (trace.getFace().getAxis().isVertical() && !replaceable)
hit = hit.offset(trace.getFace());
setCursor(hit);
} else
setCursor(null);
}
if (particle == null)
return;
ChasingAABBOutline outline = particle.getOutline();
if (particle.isAlive())
outline.tick();
if (secondPos == null) {
selectedFace = null;
outline.highlightFace(null);
return;
}
AxisAlignedBB bb = new AxisAlignedBB(firstPos, secondPos).expand(1, 1, 1).grow(.45f);
Vec3d projectedView = Minecraft.getInstance().gameRenderer.getActiveRenderInfo().getProjectedView();
boolean inside = bb.contains(projectedView);
PredicateTraceResult result =
RaycastHelper.rayTraceUntil(player, 70, pos -> inside ^ bb.contains(VecHelper.getCenterOf(pos)));
selectedFace = result.missed() ? null : inside ? result.getFacing().getOpposite() : result.getFacing();
outline.highlightFace(AllKeys.ACTIVATE_TOOL.isPressed() ? selectedFace : null);
}
private void setCursor(BlockPos pos) {
selectedPos = pos;
AxisAlignedBB bb = getCurrentSelectionBox();
if (particle != null && !particle.isAlive())
particle = null;
if (bb == null) {
if (particle != null)
particle.setExpired();
return;
}
if (particle == null) {
ChasingAABBOutline outline = new ChasingAABBOutline(bb);
outline.setTextures(AllSpecialTextures.CHECKERED, AllSpecialTextures.HIGHLIGHT_CHECKERED);
particle = OutlineParticle.create(outline);
}
ChasingAABBOutline outline = particle.getOutline();
outline.target(bb);
}
private AxisAlignedBB getCurrentSelectionBox() {
if (secondPos == null) {
if (firstPos == null)
return selectedPos == null ? null : new AxisAlignedBB(selectedPos);
return selectedPos == null ? new AxisAlignedBB(firstPos)
: new AxisAlignedBB(firstPos, selectedPos).expand(1, 1, 1);
}
return new AxisAlignedBB(firstPos, secondPos).expand(1, 1, 1);
}
private boolean isActive() {
return isPresent() && AllItems.BLUEPRINT_AND_QUILL.typeOf(Minecraft.getInstance().player.getHeldItemMainhand());
}
private boolean isPresent() {
return Minecraft.getInstance() != null && Minecraft.getInstance().world != null
&& Minecraft.getInstance().currentScreen == null;
}
public void saveSchematic(String string) { public void saveSchematic(String string) {
Template t = new Template(); Template t = new Template();
MutableBoundingBox bb = new MutableBoundingBox(firstPos, secondPos); MutableBoundingBox bb = new MutableBoundingBox(firstPos, secondPos);
@ -160,115 +251,4 @@ public class SchematicAndQuillHandler {
Lang.sendStatus(Minecraft.getInstance().player, "schematicAndQuill.saved", filepath); Lang.sendStatus(Minecraft.getInstance().player, "schematicAndQuill.saved", filepath);
} }
public void render() {
if (!isActive())
return;
TessellatorHelper.prepareForDrawing();
GlStateManager.lineWidth(2);
GlStateManager.color4f(1, 1, 1, 1);
GlStateManager.disableTexture();
if (secondPos == null) {
// 1st Step
if (firstPos != null && selectedPos == null) {
MutableBoundingBox bb = new MutableBoundingBox(firstPos, firstPos.add(1, 1, 1));
BlockPos min = new BlockPos(bb.minX, bb.minY, bb.minZ);
BlockPos max = new BlockPos(bb.maxX, bb.maxY, bb.maxZ);
drawBox(min, max, true);
}
if (firstPos != null && selectedPos != null) {
MutableBoundingBox bb = new MutableBoundingBox(firstPos, selectedPos);
BlockPos min = new BlockPos(bb.minX, bb.minY, bb.minZ);
BlockPos max = new BlockPos(bb.maxX + 1, bb.maxY + 1, bb.maxZ + 1);
drawBox(min, max, true);
}
if (firstPos == null && selectedPos != null) {
MutableBoundingBox bb = new MutableBoundingBox(selectedPos, selectedPos.add(1, 1, 1));
BlockPos min = new BlockPos(bb.minX, bb.minY, bb.minZ);
BlockPos max = new BlockPos(bb.maxX, bb.maxY, bb.maxZ);
drawBox(min, max, true);
}
} else {
// 2nd Step
MutableBoundingBox bb = new MutableBoundingBox(firstPos, secondPos);
BlockPos min = new BlockPos(bb.minX, bb.minY, bb.minZ);
BlockPos max = new BlockPos(bb.maxX + 1, bb.maxY + 1, bb.maxZ + 1);
drawBox(min, max, false);
if (selectedFace != null) {
Vec3d vec = new Vec3d(selectedFace.getDirectionVec());
Vec3d center = new Vec3d(min.add(max)).scale(1 / 2f);
Vec3d radii = new Vec3d(max.subtract(min)).scale(1 / 2f);
Vec3d onFaceOffset = new Vec3d(1 - Math.abs(vec.x), 1 - Math.abs(vec.y), 1 - Math.abs(vec.z))
.mul(radii);
Vec3d faceMin = center.add(vec.mul(radii).add(onFaceOffset));
Vec3d faceMax = center.add(vec.mul(radii).subtract(onFaceOffset));
GlStateManager.enableTexture();
TessellatorHelper.begin();
AllSpecialTextures.SELECTION.bind();
TessellatorHelper.doubleFace(Tessellator.getInstance().getBuffer(), new BlockPos(faceMin),
new BlockPos(faceMax.subtract(faceMin)), 1 / 16f * selectedFace.getAxisDirection().getOffset(),
false, false, false);
TessellatorHelper.draw();
GlStateManager.disableTexture();
}
}
GlStateManager.lineWidth(1);
GlStateManager.enableTexture();
TessellatorHelper.cleanUpAfterDrawing();
}
protected static void drawBox(BlockPos min, BlockPos max, boolean blue) {
float red = blue ? .8f : 1;
float green = blue ? .9f : 1;
WorldRenderer.drawBoundingBox(min.getX() - 1 / 16d, min.getY() + 1 / 16d, min.getZ() - 1 / 16d,
max.getX() + 1 / 16d, max.getY() + 1 / 16d, max.getZ() + 1 / 16d, red, green, 1, 1);
}
public void tick() {
if (!isActive())
return;
ClientPlayerEntity player = Minecraft.getInstance().player;
selectedPos = null;
if (AllKeys.ACTIVATE_TOOL.isPressed()) {
selectedPos = new BlockPos(player.getEyePosition(Minecraft.getInstance().getRenderPartialTicks())
.add(player.getLookVec().scale(range)));
} else {
BlockRayTraceResult trace = RaycastHelper.rayTraceRange(player.world, player, 75);
if (trace != null && trace.getType() == Type.BLOCK) {
BlockPos hit = new BlockPos(trace.getHitVec());
boolean replaceable = player.world.getBlockState(hit)
.isReplaceable(new BlockItemUseContext(new ItemUseContext(player, Hand.MAIN_HAND, trace)));
if (trace.getFace().getAxis().isVertical() && !replaceable)
hit = hit.offset(trace.getFace());
selectedPos = hit;
} else {
selectedPos = null;
}
}
if (secondPos == null) {
selectedFace = null;
return;
}
MutableBoundingBox bb = new MutableBoundingBox(firstPos, secondPos);
bb.maxX++;
bb.maxY++;
bb.maxZ++;
PredicateTraceResult result = RaycastHelper.rayTraceUntil(player, 70, pos -> bb.isVecInside(pos));
selectedFace = result.missed() ? null : result.getFacing();
}
} }

View file

@ -15,9 +15,11 @@ import com.simibubi.create.foundation.utility.Lang;
import net.minecraft.client.gui.widget.TextFieldWidget; import net.minecraft.client.gui.widget.TextFieldWidget;
import net.minecraft.item.ItemStack; import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NBTUtil;
import net.minecraft.util.Mirror; import net.minecraft.util.Mirror;
import net.minecraft.util.Rotation; import net.minecraft.util.Rotation;
import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.BlockPos;
import net.minecraft.world.gen.feature.template.PlacementSettings;
public class SchematicEditScreen extends AbstractSimiScreen { public class SchematicEditScreen extends AbstractSimiScreen {
@ -25,10 +27,10 @@ public class SchematicEditScreen extends AbstractSimiScreen {
private TextFieldWidget yInput; private TextFieldWidget yInput;
private TextFieldWidget zInput; private TextFieldWidget zInput;
private final List<String> rotationOptions = Lang.translatedOptions("schematic.rotation", "none", "cw90", "cw180", private final List<String> rotationOptions =
"cw270"); Lang.translatedOptions("schematic.rotation", "none", "cw90", "cw180", "cw270");
private final List<String> mirrorOptions = Lang.translatedOptions("schematic.mirror", "none", "leftRight", private final List<String> mirrorOptions =
"frontBack"); Lang.translatedOptions("schematic.mirror", "none", "leftRight", "frontBack");
private final String positionLabel = Lang.translate("schematic.position"); private final String positionLabel = Lang.translate("schematic.position");
private final String rotationLabel = Lang.translate("schematic.rotation"); private final String rotationLabel = Lang.translate("schematic.rotation");
private final String mirrorLabel = Lang.translate("schematic.mirror"); private final String mirrorLabel = Lang.translate("schematic.mirror");
@ -48,10 +50,11 @@ public class SchematicEditScreen extends AbstractSimiScreen {
yInput = new TextFieldWidget(font, x + 115, y + 32, 32, 10, ""); yInput = new TextFieldWidget(font, x + 115, y + 32, 32, 10, "");
zInput = new TextFieldWidget(font, x + 155, y + 32, 32, 10, ""); zInput = new TextFieldWidget(font, x + 155, y + 32, 32, 10, "");
if (handler.deployed) { BlockPos anchor = handler.getTransformation().getAnchor();
xInput.setText("" + handler.anchor.getX()); if (handler.isDeployed()) {
yInput.setText("" + handler.anchor.getY()); xInput.setText("" + anchor.getX());
zInput.setText("" + handler.anchor.getZ()); yInput.setText("" + anchor.getY());
zInput.setText("" + anchor.getZ());
} else { } else {
BlockPos alt = minecraft.player.getPosition(); BlockPos alt = minecraft.player.getPosition();
xInput.setText("" + alt.getX()); xInput.setText("" + alt.getX());
@ -77,13 +80,14 @@ public class SchematicEditScreen extends AbstractSimiScreen {
}); });
} }
PlacementSettings settings = handler.getTransformation().toSettings();
Label labelR = new Label(x + 99, y + 52, "").withShadow(); Label labelR = new Label(x + 99, y + 52, "").withShadow();
rotationArea = new SelectionScrollInput(x + 96, y + 49, 94, 14).forOptions(rotationOptions).titled("Rotation") rotationArea = new SelectionScrollInput(x + 96, y + 49, 94, 14).forOptions(rotationOptions).titled("Rotation")
.setState(handler.cachedSettings.getRotation().ordinal()).writingTo(labelR); .setState(settings.getRotation().ordinal()).writingTo(labelR);
Label labelM = new Label(x + 99, y + 72, "").withShadow(); Label labelM = new Label(x + 99, y + 72, "").withShadow();
mirrorArea = new SelectionScrollInput(x + 96, y + 69, 94, 14).forOptions(mirrorOptions).titled("Mirror") mirrorArea = new SelectionScrollInput(x + 96, y + 69, 94, 14).forOptions(mirrorOptions).titled("Mirror")
.setState(handler.cachedSettings.getMirror().ordinal()).writingTo(labelM); .setState(settings.getMirror().ordinal()).writingTo(labelM);
Collections.addAll(widgets, xInput, yInput, zInput); Collections.addAll(widgets, xInput, yInput, zInput);
Collections.addAll(widgets, labelR, labelM, rotationArea, mirrorArea); Collections.addAll(widgets, labelR, labelM, rotationArea, mirrorArea);
@ -127,8 +131,8 @@ public class SchematicEditScreen extends AbstractSimiScreen {
int y = guiTop; int y = guiTop;
ScreenResources.SCHEMATIC.draw(this, x, y); ScreenResources.SCHEMATIC.draw(this, x, y);
font.drawStringWithShadow(handler.cachedSchematicName, font.drawStringWithShadow(handler.getCurrentSchematicName(),
x + 103 - font.getStringWidth(handler.cachedSchematicName) / 2, y + 10, 0xDDEEFF); x + 103 - font.getStringWidth(handler.getCurrentSchematicName()) / 2, y + 10, 0xDDEEFF);
font.drawString(positionLabel, x + 10, y + 32, ScreenResources.FONT_COLOR); font.drawString(positionLabel, x + 10, y + 32, ScreenResources.FONT_COLOR);
font.drawString(rotationLabel, x + 10, y + 52, ScreenResources.FONT_COLOR); font.drawString(rotationLabel, x + 10, y + 52, ScreenResources.FONT_COLOR);
@ -152,10 +156,22 @@ public class SchematicEditScreen extends AbstractSimiScreen {
validCoords = false; validCoords = false;
} }
if (validCoords) PlacementSettings settings = new PlacementSettings();
handler.moveTo(newLocation); settings.setRotation(Rotation.values()[rotationArea.getState()]);
handler.setRotation(Rotation.values()[rotationArea.getState()]); settings.setMirror(Mirror.values()[mirrorArea.getState()]);
handler.setMirror(Mirror.values()[mirrorArea.getState()]);
if (validCoords && newLocation != null) {
ItemStack item = handler.getActiveSchematicItem();
if (item != null) {
item.getTag().putBoolean("Deployed", true);
item.getTag().put("Anchor", NBTUtil.writeBlockPos(newLocation));
}
handler.getTransformation().init(newLocation, settings, handler.getBounds());
handler.markDirty();
handler.deploy();
}
} }
} }

View file

@ -1,19 +1,16 @@
package com.simibubi.create.modules.schematics.client; package com.simibubi.create.modules.schematics.client;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List; import java.util.List;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import com.mojang.blaze3d.platform.GlStateManager;
import com.simibubi.create.AllItems; import com.simibubi.create.AllItems;
import com.simibubi.create.AllKeys; import com.simibubi.create.AllKeys;
import com.simibubi.create.AllPackets; import com.simibubi.create.AllPackets;
import com.simibubi.create.CreateClient;
import com.simibubi.create.foundation.gui.ToolSelectionScreen; import com.simibubi.create.foundation.gui.ToolSelectionScreen;
import com.simibubi.create.foundation.packet.NbtPacket; import com.simibubi.create.foundation.packet.NbtPacket;
import com.simibubi.create.foundation.type.Cuboid;
import com.simibubi.create.foundation.utility.Lang;
import com.simibubi.create.foundation.utility.TessellatorHelper; import com.simibubi.create.foundation.utility.TessellatorHelper;
import com.simibubi.create.foundation.utility.outliner.AABBOutline;
import com.simibubi.create.modules.schematics.SchematicWorld; import com.simibubi.create.modules.schematics.SchematicWorld;
import com.simibubi.create.modules.schematics.client.tools.Tools; import com.simibubi.create.modules.schematics.client.tools.Tools;
import com.simibubi.create.modules.schematics.item.SchematicItem; import com.simibubi.create.modules.schematics.item.SchematicItem;
@ -26,79 +23,62 @@ import net.minecraft.entity.player.PlayerInventory;
import net.minecraft.item.ItemStack; import net.minecraft.item.ItemStack;
import net.minecraft.nbt.CompoundNBT; import net.minecraft.nbt.CompoundNBT;
import net.minecraft.nbt.NBTUtil; import net.minecraft.nbt.NBTUtil;
import net.minecraft.util.Direction.Axis; import net.minecraft.util.math.AxisAlignedBB;
import net.minecraft.util.Mirror;
import net.minecraft.util.Rotation;
import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.BlockPos;
import net.minecraft.util.text.StringTextComponent;
import net.minecraft.world.gen.feature.template.PlacementSettings; import net.minecraft.world.gen.feature.template.PlacementSettings;
import net.minecraft.world.gen.feature.template.Template; import net.minecraft.world.gen.feature.template.Template;
public class SchematicHandler { public class SchematicHandler {
public Template cachedSchematic; private String displayedSchematic;
public String cachedSchematicName; private SchematicTransformation transformation;
public PlacementSettings cachedSettings; private AxisAlignedBB bounds; // local space
private AABBOutline outline;
private boolean deployed;
private boolean active;
private Tools currentTool;
public BlockPos anchor; private static final int SYNC_DELAY = 10;
public BlockPos size; private int syncCooldown;
public boolean active; private int activeHotbarSlot;
public boolean deployed; private ItemStack activeSchematicItem;
public int slot;
public ItemStack item;
private final List<String> mirrors = Arrays.asList("none", "leftRight", "frontBack"); private SchematicRenderer renderer;
private final List<String> rotations = Arrays.asList("none", "cw90", "cw180", "cw270"); private SchematicHotbarSlotOverlay overlay;
private ToolSelectionScreen selectionScreen;
public Tools currentTool;
public ToolSelectionScreen selectionScreen;
public static final int SYNC_DELAY = 20;
public int syncCooldown;
private BlueprintHotbarOverlay overlay;
public SchematicHandler() { public SchematicHandler() {
overlay = new SchematicHotbarSlotOverlay();
renderer = new SchematicRenderer();
currentTool = Tools.Deploy; currentTool = Tools.Deploy;
overlay = new BlueprintHotbarOverlay();
selectionScreen = new ToolSelectionScreen(ImmutableList.of(Tools.Deploy), this::equip); selectionScreen = new ToolSelectionScreen(ImmutableList.of(Tools.Deploy), this::equip);
transformation = new SchematicTransformation();
} }
public void tick() { public void tick() {
ClientPlayerEntity player = Minecraft.getInstance().player; ClientPlayerEntity player = Minecraft.getInstance().player;
if (activeSchematicItem != null && transformation != null)
transformation.tick();
ItemStack stack = findBlueprintInHand(player); ItemStack stack = findBlueprintInHand(player);
if (stack == null) { if (stack == null) {
active = false; active = false;
syncCooldown = 0; syncCooldown = 0;
if (item != null && itemLost(player)) { if (activeSchematicItem != null && itemLost(player)) {
slot = 0; activeHotbarSlot = 0;
item = null; activeSchematicItem = null;
CreateClient.schematicHologram.setActive(false); renderer.setActive(false);
} }
return; return;
} }
// Newly equipped if (!active || !stack.getTag().getString("File").equals(displayedSchematic))
if (!active || !stack.getTag().getString("File").equals(cachedSchematicName)) { init(player, stack);
loadSettings(stack);
cachedSchematicName = stack.getTag().getString("File");
active = true;
if (deployed) {
Tools toolBefore = currentTool;
selectionScreen = new ToolSelectionScreen(Tools.getTools(player.isCreative()), this::equip);
if (toolBefore != null) {
selectionScreen.setSelectedElement(toolBefore);
equip(toolBefore);
}
} else
selectionScreen = new ToolSelectionScreen(ImmutableList.of(Tools.Deploy), this::equip);
sync();
}
if (!active) if (!active)
return; return;
renderer.tick();
if (syncCooldown > 0) if (syncCooldown > 0)
syncCooldown--; syncCooldown--;
if (syncCooldown == 1) if (syncCooldown == 1)
@ -108,22 +88,62 @@ public class SchematicHandler {
currentTool.getTool().updateSelection(); currentTool.getTool().updateSelection();
} }
public void render() { private void init(ClientPlayerEntity player, ItemStack stack) {
if (!active) loadSettings(stack);
return; displayedSchematic = stack.getTag().getString("File");
if (Minecraft.getInstance().player.isSneaking()) active = true;
if (deployed) {
setupRenderer();
Tools toolBefore = currentTool;
selectionScreen = new ToolSelectionScreen(Tools.getTools(player.isCreative()), this::equip);
if (toolBefore != null) {
selectionScreen.setSelectedElement(toolBefore);
equip(toolBefore);
}
} else
selectionScreen = new ToolSelectionScreen(ImmutableList.of(Tools.Deploy), this::equip);
}
private void setupRenderer() {
Template schematic = SchematicItem.loadSchematic(activeSchematicItem);
if (schematic.getSize().equals(BlockPos.ZERO))
return; return;
SchematicWorld w = new SchematicWorld(BlockPos.ZERO, Minecraft.getInstance().world);
schematic.addBlocksToWorld(w, BlockPos.ZERO, new PlacementSettings());
renderer.startHologram(w);
}
public void render() {
boolean present = activeSchematicItem != null;
if (!active && !present)
return;
if (active) {
TessellatorHelper.prepareForDrawing();
currentTool.getTool().renderTool();
TessellatorHelper.cleanUpAfterDrawing();
}
GlStateManager.pushMatrix();
TessellatorHelper.prepareForDrawing(); TessellatorHelper.prepareForDrawing();
currentTool.getTool().renderTool(); transformation.applyGLTransformations();
renderer.render();
GlStateManager.disableCull();
if (active)
currentTool.getTool().renderToolLocal();
GlStateManager.enableCull();
GlStateManager.depthMask(true);
TessellatorHelper.cleanUpAfterDrawing(); TessellatorHelper.cleanUpAfterDrawing();
GlStateManager.popMatrix();
} }
public void renderOverlay() { public void renderOverlay() {
if (!active) if (!active)
return; return;
if (item != null) if (activeSchematicItem != null)
overlay.renderOn(slot); overlay.renderOn(activeHotbarSlot);
currentTool.getTool().renderOverlay(); currentTool.getTool().renderOverlay();
selectionScreen.renderPassive(Minecraft.getInstance().getRenderPartialTicks()); selectionScreen.renderPassive(Minecraft.getInstance().getRenderPartialTicks());
@ -148,7 +168,6 @@ public class SchematicHandler {
if (pressed && !selectionScreen.focused) if (pressed && !selectionScreen.focused)
selectionScreen.focused = true; selectionScreen.focused = true;
if (!pressed && selectionScreen.focused) { if (!pressed && selectionScreen.focused) {
selectionScreen.focused = false; selectionScreen.focused = false;
selectionScreen.onClose(); selectionScreen.onClose();
@ -156,18 +175,15 @@ public class SchematicHandler {
} }
public boolean mouseScrolled(double delta) { public boolean mouseScrolled(double delta) {
if (!active) if (!active || Minecraft.getInstance().player.isSneaking())
return false;
if (Minecraft.getInstance().player.isSneaking())
return false; return false;
if (selectionScreen.focused) { if (selectionScreen.focused) {
selectionScreen.cycle((int) delta); selectionScreen.cycle((int) delta);
return true; return true;
} }
if (AllKeys.ACTIVATE_TOOL.isPressed()) { if (AllKeys.ACTIVATE_TOOL.isPressed())
return currentTool.getTool().handleMouseWheel(delta); return currentTool.getTool().handleMouseWheel(delta);
}
return false; return false;
} }
@ -178,16 +194,16 @@ public class SchematicHandler {
if (!stack.hasTag()) if (!stack.hasTag())
return null; return null;
item = stack; activeSchematicItem = stack;
slot = player.inventory.currentItem; activeHotbarSlot = player.inventory.currentItem;
return stack; return stack;
} }
private boolean itemLost(PlayerEntity player) { private boolean itemLost(PlayerEntity player) {
for (int i = 0; i < PlayerInventory.getHotbarSize(); i++) { for (int i = 0; i < PlayerInventory.getHotbarSize(); i++) {
if (!player.inventory.getStackInSlot(i).isItemEqual(item)) if (!player.inventory.getStackInSlot(i).isItemEqual(activeSchematicItem))
continue; continue;
if (!ItemStack.areItemStackTagsEqual(player.inventory.getStackInSlot(i), item)) if (!ItemStack.areItemStackTagsEqual(player.inventory.getStackInSlot(i), activeSchematicItem))
continue; continue;
return false; return false;
} }
@ -196,25 +212,20 @@ public class SchematicHandler {
public void markDirty() { public void markDirty() {
syncCooldown = SYNC_DELAY; syncCooldown = SYNC_DELAY;
CreateClient.schematicHologram.setActive(false);
} }
public void sync() { public void sync() {
message(Lang.translate("schematics.synchronizing")); if (activeSchematicItem == null)
AllPackets.channel.sendToServer(new NbtPacket(item, slot)); return;
if (deployed) { PlacementSettings settings = transformation.toSettings();
Template schematic = SchematicItem.getSchematic(item); CompoundNBT tag = activeSchematicItem.getTag();
tag.putBoolean("Deployed", deployed);
tag.put("Anchor", NBTUtil.writeBlockPos(transformation.getAnchor()));
tag.putString("Rotation", settings.getRotation().name());
tag.putString("Mirror", settings.getMirror().name());
if (schematic.getSize().equals(BlockPos.ZERO)) AllPackets.channel.sendToServer(new NbtPacket(activeSchematicItem, activeHotbarSlot));
return;
SchematicWorld w = new SchematicWorld(new HashMap<>(), new Cuboid(), anchor, Minecraft.getInstance().world);
PlacementSettings settings = cachedSettings.copy();
settings.setBoundingBox(null);
schematic.addBlocksToWorld(w, anchor, settings);
CreateClient.schematicHologram.startHologram(w);
}
} }
public void equip(Tools tool) { public void equip(Tools tool) {
@ -224,149 +235,66 @@ public class SchematicHandler {
public void loadSettings(ItemStack blueprint) { public void loadSettings(ItemStack blueprint) {
CompoundNBT tag = blueprint.getTag(); CompoundNBT tag = blueprint.getTag();
cachedSettings = new PlacementSettings(); BlockPos anchor = BlockPos.ZERO;
cachedSettings.setRotation(Rotation.valueOf(tag.getString("Rotation"))); PlacementSettings settings = SchematicItem.getSettings(blueprint);
cachedSettings.setMirror(Mirror.valueOf(tag.getString("Mirror"))); transformation = new SchematicTransformation();
deployed = tag.getBoolean("Deployed"); deployed = tag.getBoolean("Deployed");
if (deployed) if (deployed)
anchor = NBTUtil.readBlockPos(tag.getCompound("Anchor")); anchor = NBTUtil.readBlockPos(tag.getCompound("Anchor"));
BlockPos size = NBTUtil.readBlockPos(tag.getCompound("Bounds"));
size = NBTUtil.readBlockPos(tag.getCompound("Bounds")); bounds = new AxisAlignedBB(BlockPos.ZERO, size);
outline = new AABBOutline(bounds);
outline.disableCull = true;
transformation.init(anchor, settings, bounds);
} }
public void flip(Axis axis) { public void deploy() {
if (!deployed) {
Rotation r = cachedSettings.getRotation(); List<Tools> tools = Tools.getTools(Minecraft.getInstance().player.isCreative());
boolean rotationAt90s = r == Rotation.CLOCKWISE_90 || r == Rotation.COUNTERCLOCKWISE_90; selectionScreen = new ToolSelectionScreen(tools, this::equip);
Mirror mirror = axis == Axis.Z ^ rotationAt90s ? Mirror.FRONT_BACK : Mirror.LEFT_RIGHT;
BlockPos coordModifier = new BlockPos((r == Rotation.NONE || r == Rotation.COUNTERCLOCKWISE_90) ? 1 : -1, 0,
(r == Rotation.NONE || r == Rotation.CLOCKWISE_90) ? 1 : -1);
BlockPos anchorOffset = axis == Axis.Z
? new BlockPos(((rotationAt90s ? size.getZ() : size.getX()) - 1) * coordModifier.getX(), 0, 0)
: new BlockPos(0, 0, ((!rotationAt90s ? size.getZ() : size.getX()) - 1) * coordModifier.getZ());
Mirror m = cachedSettings.getMirror();
if (m == Mirror.NONE) {
cachedSettings.setMirror(mirror);
anchor = anchor.add(anchorOffset);
message(Lang.translate("schematic.mirror") + ": "
+ Lang.translate("schematic.mirror." + mirrors.get(cachedSettings.getMirror().ordinal())));
} else if (m == mirror) {
cachedSettings.setMirror(Mirror.NONE);
anchor = anchor.subtract(anchorOffset);
message(Lang.translate("schematic.mirror") + ": "
+ Lang.translate("schematic.mirror." + mirrors.get(cachedSettings.getMirror().ordinal())));
} else if (m != mirror) {
cachedSettings.setMirror(Mirror.NONE);
anchor = anchor.add(anchorOffset);
cachedSettings.setRotation(r.add(Rotation.CLOCKWISE_180));
message(Lang.translate("schematic.mirror") + ": "
+ Lang.translate("schematic.mirror." + mirrors.get(cachedSettings.getMirror().ordinal())) + ", "
+ Lang.translate("schematic.rotation") + ": "
+ Lang.translate("schematic.rotation." + rotations.get(cachedSettings.getRotation().ordinal())));
} }
item.getTag().put("Anchor", NBTUtil.writeBlockPos(anchor));
item.getTag().putString("Mirror", cachedSettings.getMirror().name());
item.getTag().putString("Rotation", r.name());
markDirty();
}
public void message(String msg) {
Minecraft.getInstance().player.sendStatusMessage(new StringTextComponent(msg), true);
}
public void rotate(Rotation rotation) {
Rotation r = cachedSettings.getRotation();
BlockPos center = centerOfSchematic();
cachedSettings.setRotation(r.add(rotation));
BlockPos diff = center.subtract(anchor);
BlockPos move = diff.subtract(diff.rotate(rotation));
anchor = anchor.add(move);
item.getTag().put("Anchor", NBTUtil.writeBlockPos(anchor));
item.getTag().putString("Rotation", cachedSettings.getRotation().name());
message(Lang.translate("schematic.rotation") + ": "
+ Lang.translate("schematic.rotation." + rotations.get(cachedSettings.getRotation().ordinal())));
markDirty();
}
public void setMirror(Mirror mirror) {
cachedSettings.setMirror(mirror);
item.getTag().putString("Mirror", cachedSettings.getMirror().name());
markDirty();
}
public void setRotation(Rotation rotation) {
cachedSettings.setRotation(rotation);
item.getTag().putString("Rotation", cachedSettings.getRotation().name());
markDirty();
}
public void moveTo(BlockPos anchor) {
if (!deployed)
selectionScreen = new ToolSelectionScreen(Tools.getTools(Minecraft.getInstance().player.isCreative()),
this::equip);
deployed = true; deployed = true;
this.anchor = anchor; setupRenderer();
item.getTag().putBoolean("Deployed", true); }
item.getTag().put("Anchor", NBTUtil.writeBlockPos(anchor));
markDirty(); public String getCurrentSchematicName() {
return displayedSchematic != null ? displayedSchematic : "-";
} }
public void printInstantly() { public void printInstantly() {
AllPackets.channel.sendToServer(new SchematicPlacePacket(item.copy())); AllPackets.channel.sendToServer(new SchematicPlacePacket(activeSchematicItem.copy()));
CompoundNBT nbt = item.getTag(); CompoundNBT nbt = activeSchematicItem.getTag();
nbt.putBoolean("Deployed", false); nbt.putBoolean("Deployed", false);
item.setTag(nbt); activeSchematicItem.setTag(nbt);
CreateClient.schematicHologram.setActive(false); renderer.setActive(false);
active = false; active = false;
markDirty();
} }
public BlockPos getTransformedSize() { public AABBOutline getOutline() {
BlockPos flipped = size; return outline;
if (cachedSettings.getMirror() == Mirror.FRONT_BACK)
flipped = new BlockPos(-flipped.getX(), flipped.getY(), flipped.getZ());
if (cachedSettings.getMirror() == Mirror.LEFT_RIGHT)
flipped = new BlockPos(flipped.getX(), flipped.getY(), -flipped.getZ());
BlockPos rotate = flipped.rotate(cachedSettings.getRotation());
return rotate;
} }
public BlockPos getTransformedAnchor() { public boolean isActive() {
BlockPos anchor = this.anchor; return active;
Rotation r = cachedSettings.getRotation();
BlockPos flipOffset = BlockPos.ZERO;
if (cachedSettings.getMirror() == Mirror.FRONT_BACK)
flipOffset = new BlockPos(1, 0, 0);
if (cachedSettings.getMirror() == Mirror.LEFT_RIGHT)
flipOffset = new BlockPos(0, 0, 1);
flipOffset = flipOffset.rotate(r);
anchor = anchor.add(flipOffset);
if (r == Rotation.CLOCKWISE_90 || r == Rotation.CLOCKWISE_180)
anchor = anchor.add(1, 0, 0);
if (r == Rotation.COUNTERCLOCKWISE_90 || r == Rotation.CLOCKWISE_180)
anchor = anchor.add(0, 0, 1);
return anchor;
} }
public BlockPos centerOfSchematic() { public AxisAlignedBB getBounds() {
BlockPos size = getTransformedSize(); return bounds;
BlockPos center = new BlockPos(size.getX() / 2, 0, size.getZ() / 2); }
return anchor.add(center);
public SchematicTransformation getTransformation() {
return transformation;
}
public boolean isDeployed() {
return deployed;
}
public ItemStack getActiveSchematicItem() {
return activeSchematicItem;
} }
} }

View file

@ -1,200 +0,0 @@
package com.simibubi.create.modules.schematics.client;
import java.nio.ByteBuffer;
import java.util.Arrays;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import org.lwjgl.opengl.GL11;
import com.mojang.blaze3d.platform.GlStateManager;
import com.simibubi.create.foundation.type.Cuboid;
import com.simibubi.create.modules.schematics.SchematicWorld;
import net.minecraft.block.BedBlock;
import net.minecraft.block.BlockState;
import net.minecraft.block.Blocks;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.ActiveRenderInfo;
import net.minecraft.client.renderer.BlockRendererDispatcher;
import net.minecraft.client.renderer.BufferBuilder;
import net.minecraft.client.renderer.RegionRenderCacheBuilder;
import net.minecraft.client.renderer.texture.AtlasTexture;
import net.minecraft.client.renderer.vertex.DefaultVertexFormats;
import net.minecraft.client.renderer.vertex.VertexFormat;
import net.minecraft.client.renderer.vertex.VertexFormatElement;
import net.minecraft.client.renderer.vertex.VertexFormatElement.Usage;
import net.minecraft.entity.Entity;
import net.minecraft.util.BlockRenderLayer;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Vec3d;
import net.minecraft.world.gen.feature.template.PlacementSettings;
import net.minecraft.world.gen.feature.template.Template;
import net.minecraftforge.client.ForgeHooksClient;
import net.minecraftforge.client.model.data.EmptyModelData;
public class SchematicHologram {
private final RegionRenderCacheBuilder bufferCache = new RegionRenderCacheBuilder();
private final boolean[] usedBlockRenderLayers = new boolean[BlockRenderLayer.values().length];
private final boolean[] startedBufferBuilders = new boolean[BlockRenderLayer.values().length];
private boolean active;
private boolean changed;
private SchematicWorld schematic;
private BlockPos anchor;
public SchematicHologram() {
changed = false;
}
public void startHologram(Template schematic, BlockPos anchor) {
SchematicWorld world = new SchematicWorld(new HashMap<>(), new Cuboid(BlockPos.ZERO, BlockPos.ZERO), anchor,
Minecraft.getInstance().world);
schematic.addBlocksToWorld(world, anchor, new PlacementSettings());
startHologram(world);
}
public void startHologram(SchematicWorld world) {
this.anchor = world.anchor;
this.schematic = world;
this.active = true;
this.changed = true;
}
public void setActive(boolean active) {
this.active = active;
}
public void update() {
changed = true;
}
public void tick() {
if (!active)
return;
Minecraft minecraft = Minecraft.getInstance();
if (minecraft.world == null)
return;
if (minecraft.player == null)
return;
if (changed) {
redraw(minecraft);
changed = false;
}
}
private void redraw(Minecraft minecraft) {
Arrays.fill(usedBlockRenderLayers, false);
Arrays.fill(startedBufferBuilders, false);
final SchematicWorld blockAccess = schematic;
final BlockRendererDispatcher blockRendererDispatcher = minecraft.getBlockRendererDispatcher();
List<BlockState> blockstates = new LinkedList<>();
for (BlockPos localPos : BlockPos.getAllInBoxMutable(blockAccess.getBounds().getOrigin(),
blockAccess.getBounds().getOrigin().add(blockAccess.getBounds().getSize()))) {
BlockPos pos = localPos.add(anchor);
BlockState state = blockAccess.getBlockState(pos);
for (BlockRenderLayer blockRenderLayer : BlockRenderLayer.values()) {
if (!state.getBlock().canRenderInLayer(state, blockRenderLayer)) {
continue;
}
ForgeHooksClient.setRenderLayer(blockRenderLayer);
final int blockRenderLayerId = blockRenderLayer.ordinal();
final BufferBuilder bufferBuilder = bufferCache.getBuilder(blockRenderLayerId);
if (!startedBufferBuilders[blockRenderLayerId]) {
startedBufferBuilders[blockRenderLayerId] = true;
// Copied from RenderChunk
{
bufferBuilder.begin(GL11.GL_QUADS, DefaultVertexFormats.BLOCK);
}
}
// OptiFine Shaders compatibility
// if (Config.isShaders()) SVertexBuilder.pushEntity(state, pos,
// blockAccess, bufferBuilder);
// Block transformations
if (state.getBlock() instanceof BedBlock) {
state = Blocks.QUARTZ_SLAB.getDefaultState();
}
usedBlockRenderLayers[blockRenderLayerId] |= blockRendererDispatcher.renderBlock(state, pos,
blockAccess, bufferBuilder, minecraft.world.rand, EmptyModelData.INSTANCE);
blockstates.add(state);
// if (Config.isShaders())
// SVertexBuilder.popEntity(bufferBuilder);
}
ForgeHooksClient.setRenderLayer(null);
}
// finishDrawing
for (int blockRenderLayerId = 0; blockRenderLayerId < usedBlockRenderLayers.length; blockRenderLayerId++) {
if (!startedBufferBuilders[blockRenderLayerId]) {
continue;
}
bufferCache.getBuilder(blockRenderLayerId).finishDrawing();
}
}
public void render() {
if (active) {
final Entity entity = Minecraft.getInstance().getRenderViewEntity();
if (entity == null) {
return;
}
ActiveRenderInfo renderInfo = Minecraft.getInstance().gameRenderer.getActiveRenderInfo();
Vec3d view = renderInfo.getProjectedView();
double renderPosX = view.x;
double renderPosY = view.y;
double renderPosZ = view.z;
GlStateManager.enableAlphaTest();
GlStateManager.enableBlend();
Minecraft.getInstance().getTextureManager().bindTexture(AtlasTexture.LOCATION_BLOCKS_TEXTURE);
for (int blockRenderLayerId = 0; blockRenderLayerId < usedBlockRenderLayers.length; blockRenderLayerId++) {
if (!usedBlockRenderLayers[blockRenderLayerId]) {
continue;
}
final BufferBuilder bufferBuilder = bufferCache.getBuilder(blockRenderLayerId);
GlStateManager.pushMatrix();
GlStateManager.translated(-renderPosX, -renderPosY, -renderPosZ);
drawBuffer(bufferBuilder);
GlStateManager.popMatrix();
}
GlStateManager.disableBlend();
}
}
// Coppied from the Tesselator's vboUploader - Draw everything but don't
// reset the buffer
private static void drawBuffer(final BufferBuilder bufferBuilder) {
if (bufferBuilder.getVertexCount() > 0) {
VertexFormat vertexformat = bufferBuilder.getVertexFormat();
int size = vertexformat.getSize();
ByteBuffer bytebuffer = bufferBuilder.getByteBuffer();
List<VertexFormatElement> list = vertexformat.getElements();
for (int index = 0; index < list.size(); ++index) {
VertexFormatElement vertexformatelement = list.get(index);
Usage usage = vertexformatelement.getUsage();
bytebuffer.position(vertexformat.getOffset(index));
usage.preDraw(vertexformat, index, size, bytebuffer);
}
GlStateManager.drawArrays(bufferBuilder.getDrawMode(), 0, bufferBuilder.getVertexCount());
for (int index = 0; index < list.size(); ++index) {
VertexFormatElement vertexformatelement = list.get(index);
Usage usage = vertexformatelement.getUsage();
usage.postDraw(vertexformat, index, size, bytebuffer);
}
}
}
}

View file

@ -7,7 +7,7 @@ import net.minecraft.client.MainWindow;
import net.minecraft.client.Minecraft; import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.AbstractGui; import net.minecraft.client.gui.AbstractGui;
public class BlueprintHotbarOverlay extends AbstractGui { public class SchematicHotbarSlotOverlay extends AbstractGui {
public void renderOn(int slot) { public void renderOn(int slot) {
MainWindow mainWindow = Minecraft.getInstance().mainWindow; MainWindow mainWindow = Minecraft.getInstance().mainWindow;

View file

@ -0,0 +1,166 @@
package com.simibubi.create.modules.schematics.client;
import java.nio.ByteBuffer;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
import org.lwjgl.opengl.GL11;
import com.mojang.blaze3d.platform.GlStateManager;
import com.simibubi.create.AllSpecialTextures;
import com.simibubi.create.foundation.utility.outliner.AABBOutline;
import com.simibubi.create.foundation.utility.render.StructureRenderer;
import com.simibubi.create.modules.schematics.SchematicWorld;
import net.minecraft.block.BlockState;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.BlockRendererDispatcher;
import net.minecraft.client.renderer.BufferBuilder;
import net.minecraft.client.renderer.RegionRenderCacheBuilder;
import net.minecraft.client.renderer.texture.AtlasTexture;
import net.minecraft.client.renderer.vertex.DefaultVertexFormats;
import net.minecraft.client.renderer.vertex.VertexFormat;
import net.minecraft.client.renderer.vertex.VertexFormatElement;
import net.minecraft.client.renderer.vertex.VertexFormatElement.Usage;
import net.minecraft.util.BlockRenderLayer;
import net.minecraft.util.math.AxisAlignedBB;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Vec3d;
import net.minecraftforge.client.ForgeHooksClient;
import net.minecraftforge.client.model.data.EmptyModelData;
public class SchematicRenderer {
private final RegionRenderCacheBuilder bufferCache = new RegionRenderCacheBuilder();
private final boolean[] usedBlockRenderLayers = new boolean[BlockRenderLayer.values().length];
private final boolean[] startedBufferBuilders = new boolean[BlockRenderLayer.values().length];
private boolean active;
private boolean changed;
private SchematicWorld schematic;
private AABBOutline outline;
private BlockPos anchor;
public SchematicRenderer() {
changed = false;
}
public void startHologram(SchematicWorld world) {
this.anchor = world.anchor;
this.schematic = world;
this.active = true;
this.changed = true;
}
public void setActive(boolean active) {
this.active = active;
}
public void update() {
changed = true;
}
public void tick() {
if (!active)
return;
Minecraft mc = Minecraft.getInstance();
if (mc.world == null || mc.player == null || !changed)
return;
redraw(mc);
changed = false;
}
public void render() {
if (!active)
return;
GlStateManager.disableCull();
GlStateManager.enableAlphaTest();
GlStateManager.depthMask(true);
Minecraft.getInstance().getTextureManager().bindTexture(AtlasTexture.LOCATION_BLOCKS_TEXTURE);
for (int blockRenderLayerId = 0; blockRenderLayerId < usedBlockRenderLayers.length; blockRenderLayerId++)
if (usedBlockRenderLayers[blockRenderLayerId])
drawBuffer(bufferCache.getBuilder(blockRenderLayerId));
GlStateManager.pushMatrix();
Vec3d position = new Vec3d(anchor);
Vec3d rotation = Vec3d.ZERO;
StructureRenderer.renderTileEntities(schematic, position, rotation, schematic.getTileEntities());
GlStateManager.popMatrix();
}
private void redraw(Minecraft minecraft) {
Arrays.fill(usedBlockRenderLayers, false);
Arrays.fill(startedBufferBuilders, false);
SchematicWorld blockAccess = schematic;
blockAccess.renderMode = true;
BlockRendererDispatcher blockRendererDispatcher = minecraft.getBlockRendererDispatcher();
List<BlockState> blockstates = new LinkedList<>();
BlockPos min = blockAccess.getBounds().getOrigin();
BlockPos max = min.add(blockAccess.getBounds().getSize());
outline = new AABBOutline(new AxisAlignedBB(min, max));
outline.setTextures(AllSpecialTextures.CHECKERED, AllSpecialTextures.HIGHLIGHT_CHECKERED);
for (BlockPos localPos : BlockPos.getAllInBoxMutable(min, max)) {
BlockPos pos = localPos.add(anchor);
BlockState state = blockAccess.getBlockState(pos);
for (BlockRenderLayer blockRenderLayer : BlockRenderLayer.values()) {
if (!state.getBlock().canRenderInLayer(state, blockRenderLayer))
continue;
ForgeHooksClient.setRenderLayer(blockRenderLayer);
final int blockRenderLayerId = blockRenderLayer.ordinal();
final BufferBuilder bufferBuilder = bufferCache.getBuilder(blockRenderLayerId);
if (!startedBufferBuilders[blockRenderLayerId]) {
startedBufferBuilders[blockRenderLayerId] = true;
bufferBuilder.begin(GL11.GL_QUADS, DefaultVertexFormats.BLOCK);
}
usedBlockRenderLayers[blockRenderLayerId] |= blockRendererDispatcher.renderBlock(state, pos,
blockAccess, bufferBuilder, minecraft.world.rand, EmptyModelData.INSTANCE);
blockstates.add(state);
}
ForgeHooksClient.setRenderLayer(null);
}
// finishDrawing
blockAccess.renderMode = false;
for (int blockRenderLayerId = 0; blockRenderLayerId < usedBlockRenderLayers.length; blockRenderLayerId++) {
if (!startedBufferBuilders[blockRenderLayerId])
continue;
bufferCache.getBuilder(blockRenderLayerId).finishDrawing();
}
}
// Coppied from the Tesselator's vboUploader - Draw everything but don't
// reset the buffer
private static void drawBuffer(final BufferBuilder bufferBuilder) {
if (bufferBuilder.getVertexCount() <= 0)
return;
VertexFormat vertexformat = bufferBuilder.getVertexFormat();
int size = vertexformat.getSize();
ByteBuffer bytebuffer = bufferBuilder.getByteBuffer();
List<VertexFormatElement> list = vertexformat.getElements();
for (int index = 0; index < list.size(); ++index) {
VertexFormatElement vertexformatelement = list.get(index);
Usage usage = vertexformatelement.getUsage();
bytebuffer.position(vertexformat.getOffset(index));
usage.preDraw(vertexformat, index, size, bytebuffer);
}
GlStateManager.drawArrays(bufferBuilder.getDrawMode(), 0, bufferBuilder.getVertexCount());
for (int index = 0; index < list.size(); ++index) {
VertexFormatElement vertexformatelement = list.get(index);
Usage usage = vertexformatelement.getUsage();
usage.postDraw(vertexformat, index, size, bytebuffer);
}
}
}

View file

@ -0,0 +1,208 @@
package com.simibubi.create.modules.schematics.client;
import com.mojang.blaze3d.platform.GlStateManager;
import com.simibubi.create.foundation.gui.widgets.InterpolatedChasingAngle;
import com.simibubi.create.foundation.gui.widgets.InterpolatedChasingValue;
import com.simibubi.create.foundation.utility.VecHelper;
import net.minecraft.client.Minecraft;
import net.minecraft.util.Direction.Axis;
import net.minecraft.util.Mirror;
import net.minecraft.util.Rotation;
import net.minecraft.util.math.AxisAlignedBB;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Vec3d;
import net.minecraft.world.gen.feature.template.PlacementSettings;
public class SchematicTransformation {
private InterpolatedChasingValue x, y, z, scaleFrontBack, scaleLeftRight;
private InterpolatedChasingAngle rotation;
private double xOrigin;
private double zOrigin;
public SchematicTransformation() {
x = new InterpolatedChasingValue();
y = new InterpolatedChasingValue();
z = new InterpolatedChasingValue();
scaleFrontBack = new InterpolatedChasingValue();
scaleLeftRight = new InterpolatedChasingValue();
rotation = new InterpolatedChasingAngle();
}
public void init(BlockPos anchor, PlacementSettings settings, AxisAlignedBB bounds) {
int leftRight = settings.getMirror() == Mirror.LEFT_RIGHT ? -1 : 1;
int frontBack = settings.getMirror() == Mirror.FRONT_BACK ? -1 : 1;
scaleFrontBack.start(frontBack);
scaleLeftRight.start(leftRight);
xOrigin = bounds.getXSize() / 2f;
zOrigin = bounds.getZSize() / 2f;
int r = -(settings.getRotation().ordinal() * 90);
rotation.start(r);
Vec3d vec = fromAnchor(anchor);
x.start((float) vec.x);
y.start((float) vec.y);
z.start((float) vec.z);
}
public void applyGLTransformations() {
float pt = Minecraft.getInstance().getRenderPartialTicks();
// Translation
GlStateManager.translated(x.get(pt), y.get(pt), z.get(pt));
Vec3d rotationOffset = getRotationOffset(true);
// Rotation & Mirror
GlStateManager.translated(xOrigin + rotationOffset.x, 0, zOrigin + rotationOffset.z);
GlStateManager.rotated(rotation.get(pt), 0, 1, 0);
GlStateManager.translated(-rotationOffset.x, 0, -rotationOffset.z);
GlStateManager.scaled(scaleFrontBack.get(pt), 1, scaleLeftRight.get(pt));
GlStateManager.translated(-xOrigin, 0, -zOrigin);
}
public Vec3d getRotationOffset(boolean ignoreMirrors) {
Vec3d rotationOffset = Vec3d.ZERO;
if ((int) (zOrigin * 2) % 2 != (int) (xOrigin * 2) % 2) {
boolean xGreaterZ = xOrigin > zOrigin;
float xIn = (xGreaterZ ? 0 : .5f);
float zIn = (!xGreaterZ ? 0 : .5f);
if (!ignoreMirrors) {
xIn *= getMirrorModifier(Axis.X);
zIn *= getMirrorModifier(Axis.Z);
}
rotationOffset = new Vec3d(xIn, 0, zIn);
}
return rotationOffset;
}
public Vec3d toLocalSpace(Vec3d vec) {
float pt = Minecraft.getInstance().getRenderPartialTicks();
Vec3d rotationOffset = getRotationOffset(true);
vec = vec.subtract(x.get(pt), y.get(pt), z.get(pt));
vec = vec.subtract(xOrigin + rotationOffset.x, 0, zOrigin + rotationOffset.z);
vec = VecHelper.rotate(vec, -rotation.get(pt), Axis.Y);
vec = vec.add(rotationOffset.x, 0, rotationOffset.z);
vec = vec.mul(scaleFrontBack.get(pt), 1, scaleLeftRight.get(pt));
vec = vec.add(xOrigin, 0, zOrigin);
return vec;
}
public PlacementSettings toSettings() {
PlacementSettings settings = new PlacementSettings();
int i = (int) rotation.getTarget();
boolean mirrorlr = scaleLeftRight.getTarget() < 0;
boolean mirrorfb = scaleFrontBack.getTarget() < 0;
if (mirrorlr && mirrorfb) {
mirrorlr = mirrorfb = false;
i += 180;
}
i = i % 360;
if (i < 0)
i += 360;
Rotation rotation = Rotation.NONE;
switch (i) {
case 90:
rotation = Rotation.COUNTERCLOCKWISE_90;
break;
case 180:
rotation = Rotation.CLOCKWISE_180;
break;
case 270:
rotation = Rotation.CLOCKWISE_90;
break;
default:
}
settings.setRotation(rotation);
if (mirrorfb)
settings.setMirror(Mirror.FRONT_BACK);
if (mirrorlr)
settings.setMirror(Mirror.LEFT_RIGHT);
return settings;
}
public BlockPos getAnchor() {
Vec3d vec = Vec3d.ZERO.add(.5, 0, .5);
Vec3d rotationOffset = getRotationOffset(false);
vec = vec.subtract(xOrigin, 0, zOrigin);
vec = vec.subtract(rotationOffset.x, 0, rotationOffset.z);
vec = vec.mul(scaleFrontBack.getTarget(), 1, scaleLeftRight.getTarget());
vec = VecHelper.rotate(vec, rotation.getTarget(), Axis.Y);
vec = vec.add(xOrigin, 0, zOrigin);
vec = vec.add(x.getTarget(), y.getTarget(), z.getTarget());
return new BlockPos(vec.x, vec.y, vec.z);
}
public Vec3d fromAnchor(BlockPos pos) {
Vec3d vec = Vec3d.ZERO.add(.5, 0, .5);
Vec3d rotationOffset = getRotationOffset(false);
vec = vec.subtract(xOrigin, 0, zOrigin);
vec = vec.subtract(rotationOffset.x, 0, rotationOffset.z);
vec = vec.mul(scaleFrontBack.getTarget(), 1, scaleLeftRight.getTarget());
vec = VecHelper.rotate(vec, rotation.getTarget(), Axis.Y);
vec = vec.add(xOrigin, 0, zOrigin);
return new Vec3d(pos.subtract(new BlockPos(vec.x, vec.y, vec.z)));
}
public int getRotationTarget() {
return (int) rotation.getTarget();
}
public int getMirrorModifier(Axis axis) {
if (axis == Axis.Z)
return (int) scaleLeftRight.getTarget();
return (int) scaleFrontBack.getTarget();
}
public float getCurrentRotation() {
float pt = Minecraft.getInstance().getRenderPartialTicks();
return rotation.get(pt);
}
public void tick() {
x.tick();
y.tick();
z.tick();
scaleLeftRight.tick();
scaleFrontBack.tick();
rotation.tick();
}
public void flip(Axis axis) {
if (axis == Axis.X)
scaleLeftRight.target(scaleLeftRight.getTarget() * -1);
if (axis == Axis.Z)
scaleFrontBack.target(scaleFrontBack.getTarget() * -1);
}
public void rotate90(boolean clockwise) {
rotation.target(rotation.getTarget() + (clockwise ? -90 : 90));
}
public void move(float xIn, float yIn, float zIn) {
moveTo(x.getTarget() + xIn, y.getTarget() + yIn, z.getTarget() + zIn);
}
public void moveTo(BlockPos pos) {
moveTo(pos.getX(), pos.getY(), pos.getZ());
}
public void moveTo(float xIn, float yIn, float zIn) {
x.target(xIn);
y.target(yIn);
z.target(zIn);
}
}

View file

@ -2,11 +2,17 @@ package com.simibubi.create.modules.schematics.client.tools;
import com.mojang.blaze3d.platform.GlStateManager; import com.mojang.blaze3d.platform.GlStateManager;
import com.simibubi.create.AllKeys; import com.simibubi.create.AllKeys;
import com.simibubi.create.AllSpecialTextures;
import com.simibubi.create.modules.schematics.client.SchematicTransformation;
import net.minecraft.client.renderer.WorldRenderer; import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.RenderHelper;
import net.minecraft.client.renderer.Tessellator;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NBTUtil;
import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.MathHelper; import net.minecraft.util.math.MathHelper;
import net.minecraft.util.math.MutableBoundingBox; import net.minecraft.util.math.Vec3d;
public class DeployTool extends PlacementToolBase { public class DeployTool extends PlacementToolBase {
@ -15,68 +21,78 @@ public class DeployTool extends PlacementToolBase {
super.init(); super.init();
selectionRange = -1; selectionRange = -1;
} }
@Override @Override
public void updateSelection() { public void updateSelection() {
if (schematicHandler.active && selectionRange == -1) { if (schematicHandler.isActive() && selectionRange == -1) {
selectionRange = (int) schematicHandler.size.manhattanDistance(BlockPos.ZERO) / 2; selectionRange = (int) (schematicHandler.getBounds().getCenter().length() / 2);
selectionRange = MathHelper.clamp(selectionRange, 1, 100); selectionRange = MathHelper.clamp(selectionRange, 1, 100);
} }
selectIgnoreBlocks = AllKeys.ACTIVATE_TOOL.isPressed(); selectIgnoreBlocks = AllKeys.ACTIVATE_TOOL.isPressed();
super.updateSelection(); super.updateSelection();
} }
@Override @Override
public void renderTool() { public void renderTool() {
super.renderTool(); super.renderTool();
if (selectedPos == null) if (selectedPos == null)
return; return;
BlockPos size = schematicHandler.getTransformedSize(); GlStateManager.pushMatrix();
BlockPos min = selectedPos.add(Math.round(size.getX() * -.5f), 0, Math.round(size.getZ() * -.5f)); RenderHelper.disableStandardItemLighting();
BlockPos max = min.add(size.getX(), size.getY(), size.getZ()); GlStateManager.enableBlend();
float pt = Minecraft.getInstance().getRenderPartialTicks();
if (schematicHandler.deployed) { double x = MathHelper.lerp(pt, lastChasingSelectedPos.x, chasingSelectedPos.x);
MutableBoundingBox bb = new MutableBoundingBox(min, min.add(schematicHandler.getTransformedSize())); double y = MathHelper.lerp(pt, lastChasingSelectedPos.y, chasingSelectedPos.y);
min = new BlockPos(bb.minX, bb.minY, bb.minZ); double z = MathHelper.lerp(pt, lastChasingSelectedPos.z, chasingSelectedPos.z);
max = new BlockPos(bb.maxX, bb.maxY, bb.maxZ);
} SchematicTransformation transformation = schematicHandler.getTransformation();
Vec3d center = schematicHandler.getBounds().getCenter();
GlStateManager.lineWidth(2); Vec3d offset = transformation.getRotationOffset(true);
GlStateManager.color4f(.5f, .8f, 1, 1);
GlStateManager.disableTexture(); if (schematicHandler.getBounds().getXSize() % 2 == 1 || schematicHandler.getBounds().getZSize() % 2 == 1)
GlStateManager.translated(.5f, 0, .5f);
WorldRenderer.drawBoundingBox(min.getX() - 1 / 8d, min.getY() + 1 / 16d, min.getZ() - 1 / 8d, GlStateManager.translated(x, y, z);
max.getX() + 1 / 8d, max.getY() + 1 / 8d, max.getZ() + 1 / 8d, .8f, .9f, 1, 1); GlStateManager.rotated(transformation.getCurrentRotation(), 0, 1, 0);
GlStateManager.translated(-offset.x, 0, -offset.z);
GlStateManager.lineWidth(1); GlStateManager.translated(-(center.x), 0, -(center.z));
GlStateManager.enableTexture();
schematicHandler.getOutline().setTextures(AllSpecialTextures.CHECKERED, null);
schematicHandler.getOutline().render(Tessellator.getInstance().getBuffer());
schematicHandler.getOutline().setTextures(null, null);
GlStateManager.popMatrix();
} }
@Override @Override
public boolean handleMouseWheel(double delta) { public boolean handleMouseWheel(double delta) {
if (selectIgnoreBlocks) { if (selectIgnoreBlocks) {
selectionRange += delta; selectionRange += delta;
selectionRange = MathHelper.clamp(selectionRange, 1, 100); selectionRange = MathHelper.clamp(selectionRange, 1, 100);
return true; return true;
} }
return super.handleMouseWheel(delta); return super.handleMouseWheel(delta);
} }
@Override @Override
public boolean handleRightClick() { public boolean handleRightClick() {
if (selectedPos == null) if (selectedPos == null)
return super.handleRightClick(); return super.handleRightClick();
Vec3d center = schematicHandler.getBounds().getCenter();
BlockPos size = schematicHandler.getTransformedSize(); BlockPos target = selectedPos.add(-((int) center.x), 0, -((int) center.z));
schematicHandler.moveTo(selectedPos.add(Math.round(size.getX() * -.5f), 0, Math.round(size.getZ() * -.5f)));
ItemStack item = schematicHandler.getActiveSchematicItem();
if (item != null) {
item.getTag().putBoolean("Deployed", true);
item.getTag().put("Anchor", NBTUtil.writeBlockPos(target));
}
schematicHandler.getTransformation().moveTo(target);
schematicHandler.markDirty();
schematicHandler.deploy();
return true; return true;
} }
} }

View file

@ -1,11 +1,28 @@
package com.simibubi.create.modules.schematics.client.tools; package com.simibubi.create.modules.schematics.client.tools;
import org.lwjgl.opengl.GL11;
import com.mojang.blaze3d.platform.GlStateManager;
import com.simibubi.create.AllSpecialTextures;
import com.simibubi.create.foundation.utility.ColorHelper;
import com.simibubi.create.foundation.utility.VecHelper;
import com.simibubi.create.foundation.utility.outliner.AABBOutline;
import net.minecraft.client.renderer.BufferBuilder;
import net.minecraft.client.renderer.RenderHelper;
import net.minecraft.client.renderer.Tessellator;
import net.minecraft.client.renderer.vertex.DefaultVertexFormats;
import net.minecraft.util.Direction;
import net.minecraft.util.Direction.Axis;
import net.minecraft.util.math.AxisAlignedBB;
import net.minecraft.util.math.Vec3d;
public class FlipTool extends PlacementToolBase { public class FlipTool extends PlacementToolBase {
@Override @Override
public void init() { public void init() {
super.init(); super.init();
renderSelectedFace = true; renderSelectedFace = false;
} }
@Override @Override
@ -23,17 +40,69 @@ public class FlipTool extends PlacementToolBase {
@Override @Override
public void updateSelection() { public void updateSelection() {
super.updateSelection(); super.updateSelection();
if (!schematicSelected)
return;
renderSelectedFace = selectedFace.getAxis().isHorizontal();
} }
private void mirror() { private void mirror() {
if (schematicSelected && selectedFace.getAxis().isHorizontal()) { if (schematicSelected && selectedFace.getAxis().isHorizontal()) {
schematicHandler.flip(selectedFace.getAxis()); schematicHandler.getTransformation().flip(selectedFace.getAxis());
schematicHandler.markDirty();
} }
} }
@Override
public void renderToolLocal() {
super.renderToolLocal();
if (!schematicSelected || !selectedFace.getAxis().isHorizontal())
return;
GlStateManager.pushMatrix();
RenderHelper.disableStandardItemLighting();
GlStateManager.enableBlend();
Direction facing = selectedFace.rotateY();
Axis axis = facing.getAxis();
Vec3d color = ColorHelper.getRGB(0x4d80e4);
AxisAlignedBB bounds = schematicHandler.getBounds();
Vec3d plane = VecHelper.planeByNormal(new Vec3d(facing.getDirectionVec()));
plane = plane.mul(bounds.getXSize() / 2f + 1, bounds.getYSize() / 2f + 1, bounds.getZSize() / 2f + 1);
Vec3d center = bounds.getCenter();
Vec3d v1 = plane.add(center);
plane = plane.mul(-1, 1, -1);
Vec3d v2 = plane.add(center);
plane = plane.mul(1, -1, 1);
Vec3d v3 = plane.add(center);
plane = plane.mul(-1, 1, -1);
Vec3d v4 = plane.add(center);
BufferBuilder buffer = Tessellator.getInstance().getBuffer();
buffer.begin(GL11.GL_QUADS, DefaultVertexFormats.PARTICLE_POSITION_TEX_COLOR_LMAP);
AABBOutline outline = schematicHandler.getOutline();
AllSpecialTextures.BLANK.bind();
outline.renderAACuboidLine(v1, v2, color, 1, buffer);
outline.renderAACuboidLine(v2, v3, color, 1, buffer);
outline.renderAACuboidLine(v3, v4, color, 1, buffer);
outline.renderAACuboidLine(v4, v1, color, 1, buffer);
Tessellator.getInstance().draw();
buffer.begin(GL11.GL_QUADS, DefaultVertexFormats.PARTICLE_POSITION_TEX_COLOR_LMAP);
GlStateManager.texParameter(GL11.GL_TEXTURE_2D, 10242, GL11.GL_REPEAT);
GlStateManager.texParameter(GL11.GL_TEXTURE_2D, 10243, GL11.GL_REPEAT);
GlStateManager.depthMask(false);
Vec3d uDiff = v2.subtract(v1);
Vec3d vDiff = v4.subtract(v1);
float maxU = (float) Math.abs(axis == Axis.X ? uDiff.z : uDiff.x);
float maxV = (float) Math.abs(axis == Axis.Y ? vDiff.z : vDiff.y);
GlStateManager.enableCull();
AllSpecialTextures.HIGHLIGHT_CHECKERED.bind();
outline.putQuadUV(v1, v2, v3, v4, 0, 0, maxU, maxV, color, 1, buffer);
outline.putQuadUV(v2, v1, v4, v3, 0, 0, maxU, maxV, color, 1, buffer);
Tessellator.getInstance().draw();
GlStateManager.popMatrix();
}
} }

View file

@ -10,6 +10,7 @@ public interface ISchematicTool {
public void renderTool(); public void renderTool();
public void renderOverlay(); public void renderOverlay();
public void renderToolLocal();
} }

View file

@ -1,5 +1,11 @@
package com.simibubi.create.modules.schematics.client.tools; package com.simibubi.create.modules.schematics.client.tools;
import com.simibubi.create.foundation.utility.VecHelper;
import com.simibubi.create.modules.schematics.client.SchematicTransformation;
import net.minecraft.util.Direction.Axis;
import net.minecraft.util.math.Vec3d;
public class MoveTool extends PlacementToolBase { public class MoveTool extends PlacementToolBase {
@Override @Override
@ -17,13 +23,19 @@ public class MoveTool extends PlacementToolBase {
renderSelectedFace = selectedFace.getAxis().isHorizontal(); renderSelectedFace = selectedFace.getAxis().isHorizontal();
} }
@Override @Override
public boolean handleMouseWheel(double delta) { public boolean handleMouseWheel(double delta) {
if (schematicSelected && selectedFace.getAxis().isHorizontal()) { if (!schematicSelected || !selectedFace.getAxis().isHorizontal())
schematicHandler.moveTo(delta < 0 ? schematicHandler.anchor.add(selectedFace.getDirectionVec()) return true;
: schematicHandler.anchor.subtract(selectedFace.getDirectionVec()));
} SchematicTransformation transformation = schematicHandler.getTransformation();
Vec3d vec = new Vec3d(selectedFace.getDirectionVec()).scale(-Math.signum(delta));
vec = vec.mul(transformation.getMirrorModifier(Axis.X), 1, transformation.getMirrorModifier(Axis.Z));
vec = VecHelper.rotate(vec, transformation.getRotationTarget(), Axis.Y);
transformation.move((float) vec.x, 0, (float) vec.z);
schematicHandler.markDirty();
return true; return true;
} }

View file

@ -4,8 +4,9 @@ public class MoveVerticalTool extends PlacementToolBase {
@Override @Override
public boolean handleMouseWheel(double delta) { public boolean handleMouseWheel(double delta) {
if (schematicHandler.deployed) { if (schematicHandler.isDeployed()) {
schematicHandler.moveTo(schematicHandler.anchor.add(0, delta, 0)); schematicHandler.getTransformation().move(0, (float) Math.signum(delta), 0);
schematicHandler.markDirty();
} }
return true; return true;
} }

View file

@ -1,13 +1,47 @@
package com.simibubi.create.modules.schematics.client.tools; package com.simibubi.create.modules.schematics.client.tools;
import net.minecraft.util.Rotation; import org.lwjgl.opengl.GL11;
import com.mojang.blaze3d.platform.GlStateManager;
import com.simibubi.create.foundation.utility.ColorHelper;
import net.minecraft.client.renderer.BufferBuilder;
import net.minecraft.client.renderer.RenderHelper;
import net.minecraft.client.renderer.Tessellator;
import net.minecraft.client.renderer.vertex.DefaultVertexFormats;
import net.minecraft.util.math.AxisAlignedBB;
import net.minecraft.util.math.Vec3d;
public class RotateTool extends PlacementToolBase { public class RotateTool extends PlacementToolBase {
@Override @Override
public boolean handleMouseWheel(double delta) { public boolean handleMouseWheel(double delta) {
schematicHandler.rotate(delta > 0 ? Rotation.CLOCKWISE_90 : Rotation.COUNTERCLOCKWISE_90); schematicHandler.getTransformation().rotate90(delta > 0);
schematicHandler.markDirty();
return true; return true;
} }
@Override
public void renderToolLocal() {
super.renderToolLocal();
GlStateManager.pushMatrix();
RenderHelper.disableStandardItemLighting();
GlStateManager.enableBlend();
Vec3d color = ColorHelper.getRGB(0x4d80e4);
AxisAlignedBB bounds = schematicHandler.getBounds();
double height = bounds.getYSize() + Math.max(20, bounds.getYSize());
Vec3d center = bounds.getCenter().add(schematicHandler.getTransformation().getRotationOffset(false));
Vec3d start = center.subtract(0, height / 2, 0);
Vec3d end = center.add(0, height / 2, 0);
BufferBuilder buffer = Tessellator.getInstance().getBuffer();
buffer.begin(GL11.GL_QUADS, DefaultVertexFormats.PARTICLE_POSITION_TEX_COLOR_LMAP);
schematicHandler.getOutline().renderAACuboidLine(start, end, color, 1, buffer);
Tessellator.getInstance().draw();
GlStateManager.popMatrix();
}
} }

View file

@ -1,22 +1,27 @@
package com.simibubi.create.modules.schematics.client.tools; package com.simibubi.create.modules.schematics.client.tools;
import java.util.Arrays;
import java.util.List;
import com.mojang.blaze3d.platform.GlStateManager; import com.mojang.blaze3d.platform.GlStateManager;
import com.simibubi.create.AllKeys; import com.simibubi.create.AllKeys;
import com.simibubi.create.AllSpecialTextures;
import com.simibubi.create.CreateClient; import com.simibubi.create.CreateClient;
import com.simibubi.create.foundation.utility.RaycastHelper; import com.simibubi.create.foundation.utility.RaycastHelper;
import com.simibubi.create.foundation.utility.RaycastHelper.PredicateTraceResult; import com.simibubi.create.foundation.utility.RaycastHelper.PredicateTraceResult;
import com.simibubi.create.foundation.utility.VecHelper;
import com.simibubi.create.foundation.utility.outliner.AABBOutline;
import com.simibubi.create.modules.schematics.client.SchematicHandler; import com.simibubi.create.modules.schematics.client.SchematicHandler;
import com.simibubi.create.modules.schematics.client.SchematicTransformation;
import net.minecraft.client.Minecraft; import net.minecraft.client.Minecraft;
import net.minecraft.client.entity.player.ClientPlayerEntity; import net.minecraft.client.entity.player.ClientPlayerEntity;
import net.minecraft.client.renderer.WorldRenderer; import net.minecraft.client.renderer.RenderHelper;
import net.minecraft.item.BlockItemUseContext; import net.minecraft.client.renderer.Tessellator;
import net.minecraft.item.ItemUseContext;
import net.minecraft.util.Direction; import net.minecraft.util.Direction;
import net.minecraft.util.Hand; import net.minecraft.util.math.AxisAlignedBB;
import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.BlockRayTraceResult; import net.minecraft.util.math.BlockRayTraceResult;
import net.minecraft.util.math.MutableBoundingBox;
import net.minecraft.util.math.RayTraceResult.Type; import net.minecraft.util.math.RayTraceResult.Type;
import net.minecraft.util.math.Vec3d; import net.minecraft.util.math.Vec3d;
@ -24,13 +29,18 @@ public abstract class SchematicToolBase implements ISchematicTool {
protected SchematicHandler schematicHandler; protected SchematicHandler schematicHandler;
public BlockPos selectedPos; protected BlockPos selectedPos;
public boolean selectIgnoreBlocks; protected Vec3d chasingSelectedPos;
public int selectionRange; protected Vec3d lastChasingSelectedPos;
public boolean schematicSelected; protected boolean selectIgnoreBlocks;
public boolean renderSelectedFace; protected int selectionRange;
public Direction selectedFace; protected boolean schematicSelected;
protected boolean renderSelectedFace;
protected Direction selectedFace;
protected final List<String> mirrors = Arrays.asList("none", "leftRight", "frontBack");
protected final List<String> rotations = Arrays.asList("none", "cw90", "cw180", "cw270");
@Override @Override
public void init() { public void init() {
@ -38,86 +48,95 @@ public abstract class SchematicToolBase implements ISchematicTool {
selectedPos = null; selectedPos = null;
selectedFace = null; selectedFace = null;
schematicSelected = false; schematicSelected = false;
chasingSelectedPos = Vec3d.ZERO;
lastChasingSelectedPos = Vec3d.ZERO;
} }
@Override @Override
public void updateSelection() { public void updateSelection() {
updateTargetPos();
if (selectedPos == null)
return;
lastChasingSelectedPos = chasingSelectedPos;
Vec3d target = new Vec3d(selectedPos);
if (target.distanceTo(chasingSelectedPos) < 1 / 512f) {
chasingSelectedPos = target;
return;
}
chasingSelectedPos = chasingSelectedPos.add(target.subtract(chasingSelectedPos).scale(1 / 2f));
}
public void updateTargetPos() {
ClientPlayerEntity player = Minecraft.getInstance().player; ClientPlayerEntity player = Minecraft.getInstance().player;
// Select Blueprint // Select Blueprint
if (schematicHandler.deployed) { if (schematicHandler.isDeployed()) {
BlockPos min = schematicHandler.getTransformedAnchor(); SchematicTransformation transformation = schematicHandler.getTransformation();
MutableBoundingBox bb = new MutableBoundingBox(min, min.add(schematicHandler.getTransformedSize())); AxisAlignedBB localBounds = schematicHandler.getBounds();
PredicateTraceResult result = RaycastHelper.rayTraceUntil(player, 70,
pos -> bb.isVecInside(pos)); Vec3d traceOrigin = RaycastHelper.getTraceOrigin(player);
Vec3d start = transformation.toLocalSpace(traceOrigin);
Vec3d end = transformation.toLocalSpace(RaycastHelper.getTraceTarget(player, 70, traceOrigin));
PredicateTraceResult result =
RaycastHelper.rayTraceUntil(start, end, pos -> localBounds.contains(VecHelper.getCenterOf(pos)));
schematicSelected = !result.missed(); schematicSelected = !result.missed();
selectedFace = schematicSelected ? result.getFacing() : null; selectedFace = schematicSelected ? result.getFacing() : null;
} }
boolean snap = this.selectedPos == null;
// Select location at distance // Select location at distance
if (selectIgnoreBlocks) { if (selectIgnoreBlocks) {
selectedPos = new BlockPos(player.getEyePosition(Minecraft.getInstance().getRenderPartialTicks()) float pt = Minecraft.getInstance().getRenderPartialTicks();
.add(player.getLookVec().scale(selectionRange))); selectedPos = new BlockPos(player.getEyePosition(pt).add(player.getLookVec().scale(selectionRange)));
if (snap)
lastChasingSelectedPos = chasingSelectedPos = new Vec3d(selectedPos);
return; return;
} }
// Select targeted Block // Select targeted Block
selectedPos = null;
BlockRayTraceResult trace = RaycastHelper.rayTraceRange(player.world, player, 75); BlockRayTraceResult trace = RaycastHelper.rayTraceRange(player.world, player, 75);
if (trace != null && trace.getType() == Type.BLOCK) { if (trace == null || trace.getType() != Type.BLOCK)
return;
BlockPos hit = new BlockPos(trace.getHitVec()); BlockPos hit = new BlockPos(trace.getHitVec());
boolean replaceable = player.world.getBlockState(hit) boolean replaceable = player.world.getBlockState(hit).getMaterial().isReplaceable();
.isReplaceable(new BlockItemUseContext(new ItemUseContext(player, Hand.MAIN_HAND, trace))); if (trace.getFace().getAxis().isVertical() && !replaceable)
if (trace.getFace().getAxis().isVertical() && !replaceable) hit = hit.offset(trace.getFace());
hit = hit.offset(trace.getFace()); selectedPos = hit;
if (snap)
selectedPos = hit; lastChasingSelectedPos = chasingSelectedPos = new Vec3d(selectedPos);
} else {
selectedPos = null;
}
} }
@Override @Override
public void renderTool() { public void renderToolLocal() {
if (!schematicHandler.isDeployed())
if (schematicHandler.deployed) { return;
GlStateManager.lineWidth(2);
GlStateManager.color4f(1, 1, 1, 1);
GlStateManager.disableTexture();
BlockPos min = schematicHandler.getTransformedAnchor();
MutableBoundingBox bb = new MutableBoundingBox(min, min.add(schematicHandler.getTransformedSize()));
min = new BlockPos(bb.minX, bb.minY, bb.minZ);
BlockPos max = new BlockPos(bb.maxX, bb.maxY, bb.maxZ);
WorldRenderer.drawBoundingBox(min.getX() - 1 / 8d, min.getY() + 1 / 16d, min.getZ() - 1 / 8d,
max.getX() + 1 / 8d, max.getY() + 1 / 8d, max.getZ() + 1 / 8d, 1, 1, 1, 1);
if (schematicSelected && renderSelectedFace && AllKeys.ACTIVATE_TOOL.isPressed()) {
Vec3d vec = new Vec3d(selectedFace.getDirectionVec());
Vec3d center = new Vec3d(min.add(max)).scale(1 / 2f);
Vec3d radii = new Vec3d(max.subtract(min)).scale(1 / 2f);
Vec3d onFaceOffset = new Vec3d(1 - Math.abs(vec.x), 1 - Math.abs(vec.y), 1 - Math.abs(vec.z))
.mul(radii);
Vec3d faceMin = center.add(vec.mul(radii).add(onFaceOffset)).add(vec.scale(1/8f));
Vec3d faceMax = center.add(vec.mul(radii).subtract(onFaceOffset)).add(vec.scale(1/8f));
GlStateManager.lineWidth(6);
WorldRenderer.drawBoundingBox(faceMin.getX(), faceMin.getY() + 1 / 16d, faceMin.getZ(), faceMax.getX(),
faceMax.getY() + 1 / 8d, faceMax.getZ(), .6f, .7f, 1, 1);
}
GlStateManager.lineWidth(1);
GlStateManager.enableTexture();
AABBOutline outline = schematicHandler.getOutline();
if (renderSelectedFace) {
schematicHandler.getOutline().setTextures(null,
AllKeys.ctrlDown() ? AllSpecialTextures.HIGHLIGHT_CHECKERED : AllSpecialTextures.CHECKERED);
outline.highlightFace(selectedFace);
} }
RenderHelper.disableStandardItemLighting();
GlStateManager.pushMatrix();
GlStateManager.enableBlend();
outline.render(Tessellator.getInstance().getBuffer());
GlStateManager.popMatrix();
outline.setTextures(null, null);
} }
@Override @Override
public void renderOverlay() { public void renderTool() {}
} @Override
public void renderOverlay() {}
} }

View file

@ -75,22 +75,20 @@ public class SchematicItem extends Item {
public static void writeSize(ItemStack blueprint) { public static void writeSize(ItemStack blueprint) {
CompoundNBT tag = blueprint.getTag(); CompoundNBT tag = blueprint.getTag();
Template t = getSchematic(blueprint); Template t = loadSchematic(blueprint);
tag.put("Bounds", NBTUtil.writeBlockPos(t.getSize())); tag.put("Bounds", NBTUtil.writeBlockPos(t.getSize()));
blueprint.setTag(tag); blueprint.setTag(tag);
} }
public static PlacementSettings getSettings(ItemStack blueprint) { public static PlacementSettings getSettings(ItemStack blueprint) {
CompoundNBT tag = blueprint.getTag(); CompoundNBT tag = blueprint.getTag();
PlacementSettings settings = new PlacementSettings(); PlacementSettings settings = new PlacementSettings();
settings.setRotation(Rotation.valueOf(tag.getString("Rotation"))); settings.setRotation(Rotation.valueOf(tag.getString("Rotation")));
settings.setMirror(Mirror.valueOf(tag.getString("Mirror"))); settings.setMirror(Mirror.valueOf(tag.getString("Mirror")));
return settings; return settings;
} }
public static Template getSchematic(ItemStack blueprint) { public static Template loadSchematic(ItemStack blueprint) {
Template t = new Template(); Template t = new Template();
String owner = blueprint.getTag().getString("Owner"); String owner = blueprint.getTag().getString("Owner");
String schematic = blueprint.getTag().getString("File"); String schematic = blueprint.getTag().getString("File");
@ -120,14 +118,25 @@ public class SchematicItem extends Item {
@Override @Override
public ActionResultType onItemUse(ItemUseContext context) { public ActionResultType onItemUse(ItemUseContext context) {
if (context.isPlacerSneaking() && context.getHand() == Hand.MAIN_HAND) { if (!onItemUse(context.getPlayer(), context.getHand()))
DistExecutor.runWhenOn(Dist.CLIENT, () -> () -> { return super.onItemUse(context);
displayBlueprintScreen(); return ActionResultType.SUCCESS;
}); }
return ActionResultType.SUCCESS;
}
return super.onItemUse(context); @Override
public ActionResult<ItemStack> onItemRightClick(World worldIn, PlayerEntity playerIn, Hand handIn) {
if (!onItemUse(playerIn, handIn))
return super.onItemRightClick(worldIn, playerIn, handIn);
return new ActionResult<ItemStack>(ActionResultType.SUCCESS, playerIn.getHeldItem(handIn));
}
private boolean onItemUse(PlayerEntity player, Hand hand) {
if (!player.isSneaking() || hand != Hand.MAIN_HAND)
return false;
if (!player.getHeldItem(hand).hasTag())
return false;
DistExecutor.runWhenOn(Dist.CLIENT, () -> this::displayBlueprintScreen);
return true;
} }
@OnlyIn(value = Dist.CLIENT) @OnlyIn(value = Dist.CLIENT)
@ -135,17 +144,4 @@ public class SchematicItem extends Item {
ScreenOpener.open(new SchematicEditScreen()); ScreenOpener.open(new SchematicEditScreen());
} }
@Override
public ActionResult<ItemStack> onItemRightClick(World worldIn, PlayerEntity playerIn, Hand handIn) {
if (playerIn.isSneaking() && handIn == Hand.MAIN_HAND) {
if (playerIn.getHeldItem(handIn).hasTag())
DistExecutor.runWhenOn(Dist.CLIENT, () -> () -> {
displayBlueprintScreen();
});
return new ActionResult<ItemStack>(ActionResultType.SUCCESS, playerIn.getHeldItem(handIn));
}
return super.onItemRightClick(worldIn, playerIn, handIn);
}
} }

View file

@ -31,7 +31,7 @@ public class SchematicPlacePacket extends SimplePacketBase {
public void handle(Supplier<Context> context) { public void handle(Supplier<Context> context) {
context.get().enqueueWork(() -> { context.get().enqueueWork(() -> {
ServerPlayerEntity player = context.get().getSender(); ServerPlayerEntity player = context.get().getSender();
Template t = SchematicItem.getSchematic(stack); Template t = SchematicItem.loadSchematic(stack);
t.addBlocksToWorld(player.getServerWorld(), NBTUtil.readBlockPos(stack.getTag().getCompound("Anchor")), t.addBlocksToWorld(player.getServerWorld(), NBTUtil.readBlockPos(stack.getTag().getCompound("Anchor")),
SchematicItem.getSettings(stack)); SchematicItem.getSettings(stack));
}); });

View file

@ -881,8 +881,8 @@
"block.create.mechanical_press.tooltip.behaviour1": "_Starts_ to compress items dropped below it.", "block.create.mechanical_press.tooltip.behaviour1": "_Starts_ to compress items dropped below it.",
"block.create.mechanical_press.tooltip.condition2": "When Above a Mechanical Belt", "block.create.mechanical_press.tooltip.condition2": "When Above a Mechanical Belt",
"block.create.mechanical_press.tooltip.behaviour2": "_Automatically_ compresses bypassing items on the Belt.", "block.create.mechanical_press.tooltip.behaviour2": "_Automatically_ compresses bypassing items on the Belt.",
"block.create.mechanical_mixer.tooltip.condition3": "When above Basin", "block.create.mechanical_press.tooltip.condition3": "When above Basin",
"block.create.mechanical_mixer.tooltip.behaviour3": "Starts to _compact_ _items_ in the basin whenever all necessary ingredients are present.", "block.create.mechanical_press.tooltip.behaviour3": "Starts to _compact_ _items_ in the basin whenever all necessary ingredients are present.",
"block.create.basin.tooltip": "BASIN", "block.create.basin.tooltip": "BASIN",
"block.create.basin.tooltip.summary": "A handy _item_ _container_ used in processing with the _Mechanical_ _Mixer_ and the _Mechanical_ _Press_. Supports _Redstone_ _Comparators_.", "block.create.basin.tooltip.summary": "A handy _item_ _container_ used in processing with the _Mechanical_ _Mixer_ and the _Mechanical_ _Press_. Supports _Redstone_ _Comparators_.",

Binary file not shown.

Before

Width:  |  Height:  |  Size: 179 B

After

Width:  |  Height:  |  Size: 150 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 368 B

After

Width:  |  Height:  |  Size: 518 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 317 B

After

Width:  |  Height:  |  Size: 373 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 340 B

After

Width:  |  Height:  |  Size: 436 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 156 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 156 B